@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.
- package/README.md +27 -2
- package/app/admin/assets/page.tsx +6 -6
- package/app/admin/database/page.tsx +302 -302
- package/app/admin/error.tsx +53 -53
- package/app/admin/layout.tsx +332 -320
- package/app/admin/navigation/page.tsx +255 -255
- package/app/admin/pages/[slug]/page.tsx +44 -27
- package/app/admin/pages/page.tsx +24 -19
- package/app/admin/projects/page.tsx +30 -21
- package/app/admin/setup/page.tsx +1 -1
- package/app/admin/styles/page.tsx +1 -1
- package/app/api/admin/assets/register/route.ts +51 -14
- package/app/api/admin/assets/registry/route.ts +4 -1
- package/app/api/admin/assets/relink/confirm/route.ts +4 -1
- package/app/api/admin/assets/relink/route.ts +4 -1
- package/app/api/admin/assets/scan/route.ts +4 -1
- package/app/api/admin/backups/restore-data/route.ts +4 -1
- package/app/api/admin/r2/connect/route.ts +4 -1
- package/app/api/admin/r2/delete/route.ts +4 -1
- package/app/api/admin/r2/rename/route.ts +4 -1
- package/app/api/admin/r2/upload-url/route.ts +4 -1
- package/app/api/admin/revalidate/route.ts +4 -1
- package/app/api/admin/storage/switch/route.ts +4 -1
- package/app/api/custom-sections/[id]/route.ts +5 -6
- package/components/admin/MetadataEditor.tsx +6 -6
- package/components/admin/PublishToggle.tsx +2 -2
- package/components/admin/nav-builder/NavBuilder.tsx +1 -1
- package/components/admin/nav-builder/NavBuilderGrid.tsx +3 -3
- package/components/admin/nav-builder/NavGridCell.tsx +48 -48
- package/components/admin/nav-builder/NavGridItem.tsx +8 -6
- package/components/admin/nav-builder/NavItemSettings.tsx +331 -331
- package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -102
- package/components/admin/nav-builder/NavLivePreview.tsx +1 -1
- package/components/admin/nav-builder/NavMobileLivePreview.tsx +226 -226
- package/components/admin/nav-builder/NavMobileSettings.tsx +242 -242
- package/components/admin/nav-builder/NavSettingsFields.tsx +518 -514
- package/components/admin/setup-wizard/BrandingStep.tsx +3 -3
- package/components/admin/setup-wizard/DatabaseStep.tsx +2 -2
- package/components/admin/setup-wizard/DoneStep.tsx +1 -1
- package/components/admin/setup-wizard/SetupWizard.tsx +4 -4
- package/components/admin/setup-wizard/StorageStep.tsx +2 -2
- package/components/admin/setup-wizard/WelcomeStep.tsx +2 -2
- package/components/admin/styles/ColorsEditor.tsx +9 -8
- package/components/admin/styles/FontsEditor.tsx +9 -7
- package/components/admin/styles/GridLayoutEditor.tsx +9 -9
- package/components/admin/styles/LinksButtonsEditor.tsx +5 -5
- package/components/admin/styles/TypographyEditor.tsx +6 -6
- package/components/admin/styles/shared.tsx +68 -68
- package/components/blocks/AudioBlockRenderer.tsx +286 -286
- package/components/blocks/CoverSectionRenderer.tsx +7 -1
- package/components/blocks/MarqueeBlockRenderer.tsx +316 -0
- package/components/blocks/ProjectCarouselBlockRenderer.tsx +1 -1
- package/components/blocks/SectionV2Renderer.tsx +8 -1
- package/components/builder/BlockCardIcons.tsx +316 -316
- package/components/builder/BlockTypePicker.tsx +1 -1
- package/components/builder/BubbleIcons.tsx +104 -0
- package/components/builder/BuilderCanvas.tsx +2 -0
- package/components/builder/CanvasMinimap.tsx +66 -49
- package/components/builder/CanvasToolbar.tsx +31 -41
- package/components/builder/CoverSectionCanvas.tsx +363 -363
- package/components/builder/DeviceFrame.tsx +1 -1
- package/components/builder/DndWrapper.tsx +3 -3
- package/components/builder/InsertionLines.tsx +1 -1
- package/components/builder/SectionCardIcons.tsx +421 -320
- package/components/builder/SectionEditorBar.tsx +5 -3
- package/components/builder/SectionTypePicker.tsx +7 -5
- package/components/builder/SectionV2Canvas.tsx +1 -1
- package/components/builder/SectionV2Column.tsx +82 -68
- package/components/builder/SettingsPanel.tsx +21 -17
- package/components/builder/SortableBlock.tsx +93 -73
- package/components/builder/SortableRow.tsx +33 -35
- package/components/builder/VirtualAssetGrid.tsx +10 -4
- package/components/builder/asset-browser/R2BrowserContent.tsx +18 -14
- package/components/builder/blockStyles.tsx +192 -185
- package/components/builder/color-picker/AlphaSlider.tsx +141 -141
- package/components/builder/color-picker/ColorInputs.tsx +105 -105
- package/components/builder/color-picker/EyedropperButton.tsx +75 -74
- package/components/builder/color-picker/HueSlider.tsx +124 -124
- package/components/builder/color-picker/SaturationCanvas.tsx +142 -142
- package/components/builder/color-picker/SwatchBar.tsx +98 -93
- package/components/builder/color-picker/UnifiedColorPicker.tsx +11 -6
- package/components/builder/editors/AudioBlockEditor.tsx +242 -242
- package/components/builder/editors/BeforeAfterBlockEditor.tsx +360 -360
- package/components/builder/editors/ButtonBlockEditor.tsx +4 -4
- package/components/builder/editors/EnterAnimationPicker.tsx +2 -2
- package/components/builder/editors/HoverEffectPicker.tsx +2 -2
- package/components/builder/editors/ImageBlockEditor.tsx +2 -2
- package/components/builder/editors/ImageGridBlockEditor.tsx +8 -6
- package/components/builder/editors/MarqueeBlockEditor.tsx +622 -0
- package/components/builder/editors/ProjectCarouselBlockEditor.tsx +443 -443
- package/components/builder/editors/ProjectGridEditor.tsx +21 -16
- package/components/builder/editors/SpacerBlockEditor.tsx +29 -27
- package/components/builder/editors/StaggerSettings.tsx +109 -109
- package/components/builder/editors/TextBlockEditor.tsx +22 -17
- package/components/builder/editors/TextStylePicker.tsx +1 -1
- package/components/builder/editors/VideoBlockEditor.tsx +2 -2
- package/components/builder/editors/index.ts +11 -10
- package/components/builder/editors/shared.tsx +10 -8
- package/components/builder/live-preview/LiveAudioPreview.tsx +120 -120
- package/components/builder/live-preview/LiveBeforeAfterPreview.tsx +1 -1
- package/components/builder/live-preview/LiveImageGridPreview.tsx +10 -2
- package/components/builder/live-preview/LiveImagePreview.tsx +4 -2
- package/components/builder/live-preview/LiveMarqueePreview.tsx +39 -0
- package/components/builder/live-preview/LiveProjectCarouselPreview.tsx +1 -1
- package/components/builder/live-preview/LiveVideoPreview.tsx +1 -1
- package/components/builder/live-preview/ProjectCardWrapper.tsx +293 -291
- package/components/builder/live-preview/RichTextBubbleMenu.tsx +10 -6
- package/components/builder/live-preview/shared.tsx +5 -2
- package/components/builder/settings-panel/AnimationTab.tsx +138 -138
- package/components/builder/settings-panel/BlockLayoutTab.tsx +11 -9
- package/components/builder/settings-panel/CardEntranceSection.tsx +114 -114
- package/components/builder/settings-panel/ColumnV2LayoutTab.tsx +242 -0
- package/components/builder/settings-panel/ColumnV2Settings.tsx +5 -5
- package/components/builder/settings-panel/CoverSectionLayoutTab.tsx +71 -71
- package/components/builder/settings-panel/CoverSectionSettings.tsx +337 -335
- package/components/builder/settings-panel/PageSettings.tsx +3 -3
- package/components/builder/settings-panel/ParallaxSlideSettings.tsx +2 -2
- package/components/builder/settings-panel/SectionV2AnimationTab.tsx +4 -4
- package/components/builder/settings-panel/SectionV2LayoutTab.tsx +356 -356
- package/components/builder/settings-panel/SectionV2Settings.tsx +25 -20
- package/components/builder/settings-panel/TRBLInputs.tsx +1 -1
- package/components/builder/settings-panel/index.ts +1 -0
- package/lib/animation/enter-types.ts +1 -0
- package/lib/animation/hover-effect-presets.ts +210 -210
- package/lib/animation/hover-effect-types.ts +1 -0
- package/lib/builder/block-registrations.ts +468 -417
- package/lib/builder/constants.ts +111 -111
- package/lib/builder/serializer/normalizers.ts +14 -0
- package/lib/builder/serializer/serializers.ts +27 -0
- package/lib/builder/store-sections.ts +23 -2
- package/lib/builder/types-slices.ts +428 -414
- package/lib/builder/types.ts +4 -1
- package/lib/config/index.ts +27 -27
- package/lib/sanity/queries.ts +48 -0
- package/lib/sanity/types.ts +112 -1
- package/lib/version.ts +1 -1
- package/package.json +7 -5
- package/sanity/schemas/blocks/audioBlock.ts +69 -69
- package/sanity/schemas/blocks/index.ts +12 -11
- package/sanity/schemas/blocks/marqueeBlock.ts +292 -0
- package/sanity/schemas/index.ts +120 -117
- package/sanity/schemas/objects/coverSection.ts +32 -0
- package/sanity/schemas/objects/parallaxSlide.ts +32 -0
- package/sanity/schemas/pageSectionV2.ts +32 -0
- package/styles/admin.css +85 -85
- package/styles/animations.css +237 -237
- package/styles/base.css +114 -114
package/lib/builder/types.ts
CHANGED
|
@@ -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
|
|
package/lib/config/index.ts
CHANGED
|
@@ -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";
|
package/lib/sanity/queries.ts
CHANGED
|
@@ -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,
|
package/lib/sanity/types.ts
CHANGED
|
@@ -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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morphika/andami",
|
|
3
|
-
"version": "0.5.
|
|
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.
|
|
214
|
+
"next-sanity": "^12.3.0",
|
|
215
215
|
"ogl": "^1.0.8",
|
|
216
|
-
"sanity": "^5.
|
|
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.
|
|
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: "#
|
|
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 (
|
|
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";
|