@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
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { adminAssetUrl, adminThumbUrl } from "../../../lib/assets";
|
|
4
|
-
import type { AudioBlock } from "../../../lib/sanity/types";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* LiveAudioPreview — Static preview for builder canvas.
|
|
8
|
-
*
|
|
9
|
-
* Same layout as the runtime renderer but no audio element / no playback —
|
|
10
|
-
* a frozen snapshot with a 0% progress bar, a play glyph, and a dummy
|
|
11
|
-
* `0:00 / 0:00` time label. Metadata (title / artist) and cover art
|
|
12
|
-
* render when present.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const widthStyleMap: Record<string, { width: string; margin?: string }> = {
|
|
16
|
-
full: { width: "100%" },
|
|
17
|
-
contained: { width: "75%", margin: "0 auto" },
|
|
18
|
-
small: { width: "50%", margin: "0 auto" },
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export default function LiveAudioPreview({ block }: { block: AudioBlock }) {
|
|
22
|
-
const accent = block.accent_color || "#
|
|
23
|
-
const coverSrc = block.cover_path ? (adminThumbUrl(block.cover_path) || adminAssetUrl(block.cover_path)) : null;
|
|
24
|
-
|
|
25
|
-
const isFill = block.width === "fill";
|
|
26
|
-
const widthStyle = isFill ? { width: "100%" } : (widthStyleMap[block.width ?? "contained"] || widthStyleMap.contained);
|
|
27
|
-
|
|
28
|
-
const rawRadius = block.border_radius ? String(block.border_radius).replace(/[a-z%]+$/i, "") : "";
|
|
29
|
-
const borderRadius = rawRadius && !isNaN(Number(rawRadius)) ? `${rawRadius}px` : "12px";
|
|
30
|
-
|
|
31
|
-
const hasMetaText = !!(block.title || block.artist);
|
|
32
|
-
const hasAsset = !!block.asset_path;
|
|
33
|
-
|
|
34
|
-
const containerStyle: React.CSSProperties = {
|
|
35
|
-
...widthStyle,
|
|
36
|
-
display: "flex",
|
|
37
|
-
alignItems: "center",
|
|
38
|
-
gap: 14,
|
|
39
|
-
padding: "12px 16px",
|
|
40
|
-
background: "#fafafa",
|
|
41
|
-
border: "1px solid #ececec",
|
|
42
|
-
borderRadius,
|
|
43
|
-
boxShadow: block.shadow ? "0 8px 24px -12px rgba(0,0,0,0.25)" : undefined,
|
|
44
|
-
overflow: "hidden",
|
|
45
|
-
opacity: hasAsset ? 1 : 0.75,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<div style={containerStyle}>
|
|
50
|
-
{coverSrc ? (
|
|
51
|
-
<div style={{ width: 52, height: 52, flexShrink: 0, borderRadius: 8, overflow: "hidden", background: "#eee" }}>
|
|
52
|
-
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
53
|
-
<img src={coverSrc} alt={block.alt || block.title || ""} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
|
|
54
|
-
</div>
|
|
55
|
-
) : null}
|
|
56
|
-
|
|
57
|
-
<div
|
|
58
|
-
aria-hidden
|
|
59
|
-
style={{
|
|
60
|
-
width: 40,
|
|
61
|
-
height: 40,
|
|
62
|
-
flexShrink: 0,
|
|
63
|
-
borderRadius: "50%",
|
|
64
|
-
background: accent,
|
|
65
|
-
color: "#fff",
|
|
66
|
-
display: "flex",
|
|
67
|
-
alignItems: "center",
|
|
68
|
-
justifyContent: "center",
|
|
69
|
-
}}
|
|
70
|
-
>
|
|
71
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style={{ marginLeft: 2 }}>
|
|
72
|
-
<path d="M8 5v14l11-7z" />
|
|
73
|
-
</svg>
|
|
74
|
-
</div>
|
|
75
|
-
|
|
76
|
-
<div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 4 }}>
|
|
77
|
-
{hasMetaText ? (
|
|
78
|
-
<div style={{ display: "flex", alignItems: "baseline", gap: 6, minWidth: 0 }}>
|
|
79
|
-
{block.title && (
|
|
80
|
-
<span style={{ fontSize: 13, fontWeight: 600, color: "#111", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
|
|
81
|
-
{block.title}
|
|
82
|
-
</span>
|
|
83
|
-
)}
|
|
84
|
-
{block.artist && (
|
|
85
|
-
<span style={{ fontSize: 12, color: "#777", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
|
|
86
|
-
{block.artist}
|
|
87
|
-
</span>
|
|
88
|
-
)}
|
|
89
|
-
</div>
|
|
90
|
-
) : (
|
|
91
|
-
!hasAsset && (
|
|
92
|
-
<span style={{ fontSize: 11, color: "#8a8f98" }}>Audio — pick a file</span>
|
|
93
|
-
)
|
|
94
|
-
)}
|
|
95
|
-
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
96
|
-
<div style={{ flex: 1, height: 4, background: "#e5e5e5", borderRadius: 999, position: "relative" }}>
|
|
97
|
-
<div style={{ position: "absolute", inset: 0, width: "0%", background: accent, borderRadius: 999 }} />
|
|
98
|
-
<div
|
|
99
|
-
style={{
|
|
100
|
-
position: "absolute",
|
|
101
|
-
top: "50%",
|
|
102
|
-
left: "0%",
|
|
103
|
-
width: 10,
|
|
104
|
-
height: 10,
|
|
105
|
-
marginTop: -5,
|
|
106
|
-
marginLeft: -5,
|
|
107
|
-
borderRadius: "50%",
|
|
108
|
-
background: "#fff",
|
|
109
|
-
boxShadow: `0 0 0 2px ${accent}`,
|
|
110
|
-
}}
|
|
111
|
-
/>
|
|
112
|
-
</div>
|
|
113
|
-
<span style={{ fontSize: 11, color: "#777", fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" }}>
|
|
114
|
-
0:00 / 0:00
|
|
115
|
-
</span>
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
);
|
|
120
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { adminAssetUrl, adminThumbUrl } from "../../../lib/assets";
|
|
4
|
+
import type { AudioBlock } from "../../../lib/sanity/types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* LiveAudioPreview — Static preview for builder canvas.
|
|
8
|
+
*
|
|
9
|
+
* Same layout as the runtime renderer but no audio element / no playback —
|
|
10
|
+
* a frozen snapshot with a 0% progress bar, a play glyph, and a dummy
|
|
11
|
+
* `0:00 / 0:00` time label. Metadata (title / artist) and cover art
|
|
12
|
+
* render when present.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const widthStyleMap: Record<string, { width: string; margin?: string }> = {
|
|
16
|
+
full: { width: "100%" },
|
|
17
|
+
contained: { width: "75%", margin: "0 auto" },
|
|
18
|
+
small: { width: "50%", margin: "0 auto" },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default function LiveAudioPreview({ block }: { block: AudioBlock }) {
|
|
22
|
+
const accent = block.accent_color || "#3580f9";
|
|
23
|
+
const coverSrc = block.cover_path ? (adminThumbUrl(block.cover_path) || adminAssetUrl(block.cover_path)) : null;
|
|
24
|
+
|
|
25
|
+
const isFill = block.width === "fill";
|
|
26
|
+
const widthStyle = isFill ? { width: "100%" } : (widthStyleMap[block.width ?? "contained"] || widthStyleMap.contained);
|
|
27
|
+
|
|
28
|
+
const rawRadius = block.border_radius ? String(block.border_radius).replace(/[a-z%]+$/i, "") : "";
|
|
29
|
+
const borderRadius = rawRadius && !isNaN(Number(rawRadius)) ? `${rawRadius}px` : "12px";
|
|
30
|
+
|
|
31
|
+
const hasMetaText = !!(block.title || block.artist);
|
|
32
|
+
const hasAsset = !!block.asset_path;
|
|
33
|
+
|
|
34
|
+
const containerStyle: React.CSSProperties = {
|
|
35
|
+
...widthStyle,
|
|
36
|
+
display: "flex",
|
|
37
|
+
alignItems: "center",
|
|
38
|
+
gap: 14,
|
|
39
|
+
padding: "12px 16px",
|
|
40
|
+
background: "#fafafa",
|
|
41
|
+
border: "1px solid #ececec",
|
|
42
|
+
borderRadius,
|
|
43
|
+
boxShadow: block.shadow ? "0 8px 24px -12px rgba(0,0,0,0.25)" : undefined,
|
|
44
|
+
overflow: "hidden",
|
|
45
|
+
opacity: hasAsset ? 1 : 0.75,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div style={containerStyle}>
|
|
50
|
+
{coverSrc ? (
|
|
51
|
+
<div style={{ width: 52, height: 52, flexShrink: 0, borderRadius: 8, overflow: "hidden", background: "#eee" }}>
|
|
52
|
+
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
53
|
+
<img src={coverSrc} alt={block.alt || block.title || ""} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
|
|
54
|
+
</div>
|
|
55
|
+
) : null}
|
|
56
|
+
|
|
57
|
+
<div
|
|
58
|
+
aria-hidden
|
|
59
|
+
style={{
|
|
60
|
+
width: 40,
|
|
61
|
+
height: 40,
|
|
62
|
+
flexShrink: 0,
|
|
63
|
+
borderRadius: "50%",
|
|
64
|
+
background: accent,
|
|
65
|
+
color: "#fff",
|
|
66
|
+
display: "flex",
|
|
67
|
+
alignItems: "center",
|
|
68
|
+
justifyContent: "center",
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style={{ marginLeft: 2 }}>
|
|
72
|
+
<path d="M8 5v14l11-7z" />
|
|
73
|
+
</svg>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 4 }}>
|
|
77
|
+
{hasMetaText ? (
|
|
78
|
+
<div style={{ display: "flex", alignItems: "baseline", gap: 6, minWidth: 0 }}>
|
|
79
|
+
{block.title && (
|
|
80
|
+
<span style={{ fontSize: 13, fontWeight: 600, color: "#111", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
|
|
81
|
+
{block.title}
|
|
82
|
+
</span>
|
|
83
|
+
)}
|
|
84
|
+
{block.artist && (
|
|
85
|
+
<span style={{ fontSize: 12, color: "#777", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
|
|
86
|
+
{block.artist}
|
|
87
|
+
</span>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
) : (
|
|
91
|
+
!hasAsset && (
|
|
92
|
+
<span style={{ fontSize: 11, color: "#8a8f98" }}>Audio — pick a file</span>
|
|
93
|
+
)
|
|
94
|
+
)}
|
|
95
|
+
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
96
|
+
<div style={{ flex: 1, height: 4, background: "#e5e5e5", borderRadius: 999, position: "relative" }}>
|
|
97
|
+
<div style={{ position: "absolute", inset: 0, width: "0%", background: accent, borderRadius: 999 }} />
|
|
98
|
+
<div
|
|
99
|
+
style={{
|
|
100
|
+
position: "absolute",
|
|
101
|
+
top: "50%",
|
|
102
|
+
left: "0%",
|
|
103
|
+
width: 10,
|
|
104
|
+
height: 10,
|
|
105
|
+
marginTop: -5,
|
|
106
|
+
marginLeft: -5,
|
|
107
|
+
borderRadius: "50%",
|
|
108
|
+
background: "#fff",
|
|
109
|
+
boxShadow: `0 0 0 2px ${accent}`,
|
|
110
|
+
}}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
<span style={{ fontSize: 11, color: "#777", fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" }}>
|
|
114
|
+
0:00 / 0:00
|
|
115
|
+
</span>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -89,7 +89,7 @@ export default function LiveBeforeAfterPreview({ block }: { block: BeforeAfterBl
|
|
|
89
89
|
: { width: "100%" };
|
|
90
90
|
return (
|
|
91
91
|
<div style={wrapperStyle}>
|
|
92
|
-
<div className="w-full h-full min-h-[240px]
|
|
92
|
+
<div className="w-full h-full min-h-[240px] flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
|
|
93
93
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" aria-hidden="true">
|
|
94
94
|
<rect x="6" y="10" width="44" height="36" rx="3" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
|
|
95
95
|
<line x1="28" y1="10" x2="28" y2="46" stroke="#b0b5bd" strokeWidth="1.5" />
|
|
@@ -55,8 +55,16 @@ export default function LiveImageGridPreview({ block }: { block: ImageGridBlock
|
|
|
55
55
|
const images = block.images || [];
|
|
56
56
|
if (images.length === 0) {
|
|
57
57
|
return (
|
|
58
|
-
<div
|
|
59
|
-
<
|
|
58
|
+
<div style={{ width: "100%" }}>
|
|
59
|
+
<div className="w-full h-full min-h-[240px] flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
|
|
60
|
+
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" aria-hidden="true">
|
|
61
|
+
<rect x="8" y="8" width="18" height="18" rx="2" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
|
|
62
|
+
<rect x="30" y="8" width="18" height="18" rx="2" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
|
|
63
|
+
<rect x="8" y="30" width="18" height="18" rx="2" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
|
|
64
|
+
<rect x="30" y="30" width="18" height="18" rx="2" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
|
|
65
|
+
</svg>
|
|
66
|
+
<span className="text-[11px] text-neutral-500">No images yet</span>
|
|
67
|
+
</div>
|
|
60
68
|
</div>
|
|
61
69
|
);
|
|
62
70
|
}
|
|
@@ -4,6 +4,7 @@ import { useState } from "react";
|
|
|
4
4
|
import { adminAssetUrl, adminThumbUrl } from "../../../lib/assets";
|
|
5
5
|
import { ThumbBadge } from "./shared";
|
|
6
6
|
import type { ImageBlock } from "../../../lib/sanity/types";
|
|
7
|
+
import { BubbleTooltip } from "../BubbleIcons";
|
|
7
8
|
|
|
8
9
|
export default function LiveImagePreview({ block }: { block: ImageBlock }) {
|
|
9
10
|
const [imgError, setImgError] = useState(false);
|
|
@@ -20,7 +21,7 @@ export default function LiveImagePreview({ block }: { block: ImageBlock }) {
|
|
|
20
21
|
: { width: "100%" };
|
|
21
22
|
return (
|
|
22
23
|
<div style={wrapperStyle}>
|
|
23
|
-
<div className="w-full h-full min-h-[240px]
|
|
24
|
+
<div className="w-full h-full min-h-[240px] flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
|
|
24
25
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" aria-hidden="true">
|
|
25
26
|
<rect x="6" y="10" width="44" height="36" rx="3" stroke="#b0b5bd" strokeWidth="1.5" fill="#FFFFFF" />
|
|
26
27
|
<circle cx="18" cy="21" r="3" fill="#b0b5bd" />
|
|
@@ -81,8 +82,9 @@ export default function LiveImagePreview({ block }: { block: ImageBlock }) {
|
|
|
81
82
|
>
|
|
82
83
|
<span className="text-red-400 text-lg">{"\u26A0"}</span>
|
|
83
84
|
<span className="text-red-400 text-xs">Image failed to load</span>
|
|
84
|
-
<span className="text-red-300 text-[10px] max-w-[200px] truncate"
|
|
85
|
+
<span className="group/bb relative text-red-300 text-[10px] max-w-[200px] truncate" aria-label={block.asset_path}>
|
|
85
86
|
{block.asset_path}
|
|
87
|
+
<BubbleTooltip>{block.asset_path}</BubbleTooltip>
|
|
86
88
|
</span>
|
|
87
89
|
</div>
|
|
88
90
|
) : (
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LiveMarqueePreview — Builder canvas preview for marqueeBlock.
|
|
5
|
+
*
|
|
6
|
+
* Unlike LiveProjectCarouselPreview (which renders mockup cards because
|
|
7
|
+
* the public carousel hits the projects API), the marquee content IS the
|
|
8
|
+
* user's items — same text, same images. So this preview just delegates
|
|
9
|
+
* to the public renderer with two tweaks:
|
|
10
|
+
*
|
|
11
|
+
* - speedMultiplier = 0.3 → motion is visible but gentle, won't distract
|
|
12
|
+
* while editing. User's configured speed still drives the public site.
|
|
13
|
+
* - alwaysPlay = true → disables the IntersectionObserver pause, since
|
|
14
|
+
* the builder canvas has its own scrolling and the observer can yield
|
|
15
|
+
* false "off-screen" reports.
|
|
16
|
+
*
|
|
17
|
+
* Intentionally thin — mirrors the one-liner delegation pattern used for
|
|
18
|
+
* simple block types whose builder preview and public render don't diverge.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { MarqueeBlock } from "../../../lib/sanity/types";
|
|
22
|
+
import type { DeviceViewport } from "../../../lib/builder/types";
|
|
23
|
+
import MarqueeBlockRenderer from "../../blocks/MarqueeBlockRenderer";
|
|
24
|
+
|
|
25
|
+
export interface LiveMarqueePreviewProps {
|
|
26
|
+
block: MarqueeBlock;
|
|
27
|
+
viewport?: DeviceViewport;
|
|
28
|
+
editable?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function LiveMarqueePreview({ block }: LiveMarqueePreviewProps) {
|
|
32
|
+
return (
|
|
33
|
+
<MarqueeBlockRenderer
|
|
34
|
+
block={block}
|
|
35
|
+
speedMultiplier={0.3}
|
|
36
|
+
alwaysPlay
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -141,7 +141,7 @@ export default function LiveProjectCarouselPreview({
|
|
|
141
141
|
<div ref={containerCallbackRef} className="relative w-full">
|
|
142
142
|
{/* Scroll track — same mechanics as public renderer so layout is faithful */}
|
|
143
143
|
<div
|
|
144
|
-
className="flex overflow-x-auto"
|
|
144
|
+
className="flex overflow-x-auto py-3"
|
|
145
145
|
style={{
|
|
146
146
|
gap: `${gap}px`,
|
|
147
147
|
scrollSnapType: snapScroll ? "x mandatory" : undefined,
|
|
@@ -26,7 +26,7 @@ export default function LiveVideoPreview({ block }: { block: VideoBlock }) {
|
|
|
26
26
|
: { width: "100%" };
|
|
27
27
|
return (
|
|
28
28
|
<div style={wrapperStyle}>
|
|
29
|
-
<div className="w-full h-full min-h-[240px]
|
|
29
|
+
<div className="w-full h-full min-h-[240px] flex flex-col items-center justify-center gap-2.5" style={{ background: "#f4f4f4" }}>
|
|
30
30
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" aria-hidden="true">
|
|
31
31
|
<circle cx="28" cy="28" r="22" fill="#FFFFFF" stroke="#b0b5bd" strokeWidth="1.5" />
|
|
32
32
|
<path d="M24 20 L37 28 L24 36 Z" fill="#b0b5bd" />
|