@morphika/andami 0.2.17 → 0.2.19

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.
@@ -395,7 +395,11 @@ export default function BlockRenderer({
395
395
  // ── Enter animation: apply non-typewriter presets after layout wrapper ──
396
396
  // Typewriter was already applied BEFORE the layout wrapper (above) so that
397
397
  // block padding wraps both phases. All other presets wrap after layout.
398
- if (resolvedEnter && resolvedEnter.preset !== "none" && !isTypewriter) {
398
+ // Fill blocks (position: absolute backgrounds) skip enter animations
399
+ // their zero-height wrapper prevents IntersectionObserver from triggering.
400
+ const isFillBlock = (resolved._type === "imageBlock" || resolved._type === "videoBlock") &&
401
+ (resolved as unknown as { width?: string }).width === "fill";
402
+ if (resolvedEnter && resolvedEnter.preset !== "none" && !isTypewriter && !isFillBlock) {
399
403
  content = (
400
404
  <EnterAnimationWrapper config={resolvedEnter}>
401
405
  {content}
@@ -106,6 +106,7 @@ export default function CoverSectionRenderer({ section, pageEnterAnimation }: Co
106
106
  const bgVideoSrc = section.background_type === "video" && section.background_video
107
107
  ? assetUrl(section.background_video)
108
108
  : null;
109
+ const bgColor = section.background_type === "color" ? section.background_color : null;
109
110
  const overlayOpacity = section.background_overlay_opacity ?? 0;
110
111
 
111
112
  const responsiveCss = buildCoverResponsiveCss(section);
@@ -178,6 +179,17 @@ export default function CoverSectionRenderer({ section, pageEnterAnimation }: Co
178
179
  </video>
179
180
  )}
180
181
 
182
+ {/* Background color */}
183
+ {bgColor && (
184
+ <div
185
+ style={{
186
+ position: "absolute",
187
+ inset: 0,
188
+ backgroundColor: bgColor,
189
+ }}
190
+ />
191
+ )}
192
+
181
193
  {/* Overlay */}
182
194
  {overlayOpacity > 0 && (
183
195
  <div
@@ -260,7 +272,7 @@ export default function CoverSectionRenderer({ section, pageEnterAnimation }: Co
260
272
  </div>
261
273
  );
262
274
 
263
- if (blockEnter && blockEnter.preset !== "none") {
275
+ if (blockEnter && blockEnter.preset !== "none" && !isFillBlock) {
264
276
  return (
265
277
  <EnterAnimationWrapper key={block._key} config={blockEnter}>
266
278
  {rendered}
@@ -148,6 +148,16 @@ export default function CoverSectionCanvas({
148
148
  }}
149
149
  />
150
150
  )}
151
+ {section.background_type === "color" && section.background_color && (
152
+ <div
153
+ className="absolute inset-0 pointer-events-none"
154
+ style={{
155
+ backgroundColor: section.background_color,
156
+ opacity: 0.25,
157
+ overflow: "hidden",
158
+ }}
159
+ />
160
+ )}
151
161
 
152
162
  {/* Overlay preview */}
153
163
  {(section.background_overlay_opacity ?? 0) > 0 && (
@@ -412,6 +412,15 @@ const ReadOnlyCoverSection = memo(function ReadOnlyCoverSection({
412
412
  }}
413
413
  />
414
414
  )}
415
+ {section.background_type === "color" && section.background_color && (
416
+ <div
417
+ className="absolute inset-0 pointer-events-none"
418
+ style={{
419
+ backgroundColor: section.background_color,
420
+ opacity: 0.25,
421
+ }}
422
+ />
423
+ )}
415
424
  {(section.background_overlay_opacity ?? 0) > 0 && (
416
425
  <div
417
426
  className="absolute inset-0 pointer-events-none"
@@ -81,7 +81,7 @@ export default function CoverSectionSettings({ section }: CoverSectionSettingsPr
81
81
  const overlayOpacity = section.background_overlay_opacity ?? 0;
82
82
 
83
83
  const updateBg = (fields: Partial<Pick<CoverSection,
84
- "background_type" | "background_image" | "background_video" |
84
+ "background_type" | "background_color" | "background_image" | "background_video" |
85
85
  "background_position" | "background_size" |
86
86
  "background_overlay_color" | "background_overlay_opacity"
87
87
  >>) => {
@@ -94,7 +94,7 @@ export default function CoverSectionSettings({ section }: CoverSectionSettingsPr
94
94
  <SettingsSection title="Background" defaultOpen icon={<BackgroundIcon />}>
95
95
  <SettingsField label="Type">
96
96
  <div className="flex rounded-lg bg-[#f0f0f0] p-[3px]">
97
- {(["image", "video"] as const).map((type) => (
97
+ {(["image", "video", "color"] as const).map((type) => (
98
98
  <button
99
99
  key={type}
100
100
  onClick={() => updateBg({ background_type: type })}
@@ -104,50 +104,62 @@ export default function CoverSectionSettings({ section }: CoverSectionSettingsPr
104
104
  : "text-neutral-400 hover:text-neutral-500"
105
105
  }`}
106
106
  >
107
- {type === "image" ? "Image" : "Video"}
107
+ {type === "image" ? "Image" : type === "video" ? "Video" : "Color"}
108
108
  </button>
109
109
  ))}
110
110
  </div>
111
111
  </SettingsField>
112
112
 
113
- <SettingsField label={bgType === "image" ? "Image" : "Video"}>
114
- <AssetPathInput
115
- value={bgType === "image" ? (section.background_image || "") : (section.background_video || "")}
116
- onChange={(path) => {
117
- if (bgType === "image") {
118
- updateBg({ background_image: path });
119
- } else {
120
- updateBg({ background_video: path });
121
- }
122
- }}
123
- filterType={bgType === "image" ? "image" : "video"}
124
- placeholder={bgType === "image" ? "path/to/image.jpg" : "path/to/video.mp4"}
125
- />
126
- </SettingsField>
113
+ {bgType === "color" ? (
114
+ <SettingsField label="Color">
115
+ <ColorSwatchPicker
116
+ value={section.background_color || "#000000"}
117
+ onChange={(val) => updateBg({ background_color: typeof val === "string" ? val : "" })}
118
+ swatches={paletteSwatches}
119
+ />
120
+ </SettingsField>
121
+ ) : (
122
+ <>
123
+ <SettingsField label={bgType === "image" ? "Image" : "Video"}>
124
+ <AssetPathInput
125
+ value={bgType === "image" ? (section.background_image || "") : (section.background_video || "")}
126
+ onChange={(path) => {
127
+ if (bgType === "image") {
128
+ updateBg({ background_image: path });
129
+ } else {
130
+ updateBg({ background_video: path });
131
+ }
132
+ }}
133
+ filterType={bgType === "image" ? "image" : "video"}
134
+ placeholder={bgType === "image" ? "path/to/image.jpg" : "path/to/video.mp4"}
135
+ />
136
+ </SettingsField>
127
137
 
128
- <SettingsField label="Position">
129
- <select
130
- value={bgPosition}
131
- onChange={(e) => updateBg({ background_position: e.target.value })}
132
- className={SELECT_CLASS}
133
- >
134
- {BG_POSITION_OPTIONS.map((opt) => (
135
- <option key={opt.value} value={opt.value}>{opt.label}</option>
136
- ))}
137
- </select>
138
- </SettingsField>
138
+ <SettingsField label="Position">
139
+ <select
140
+ value={bgPosition}
141
+ onChange={(e) => updateBg({ background_position: e.target.value })}
142
+ className={SELECT_CLASS}
143
+ >
144
+ {BG_POSITION_OPTIONS.map((opt) => (
145
+ <option key={opt.value} value={opt.value}>{opt.label}</option>
146
+ ))}
147
+ </select>
148
+ </SettingsField>
139
149
 
140
- <SettingsField label="Size">
141
- <select
142
- value={bgSize}
143
- onChange={(e) => updateBg({ background_size: e.target.value as CoverSection["background_size"] })}
144
- className={SELECT_CLASS}
145
- >
146
- {BG_SIZE_OPTIONS.map((opt) => (
147
- <option key={opt.value} value={opt.value}>{opt.label}</option>
148
- ))}
149
- </select>
150
- </SettingsField>
150
+ <SettingsField label="Size">
151
+ <select
152
+ value={bgSize}
153
+ onChange={(e) => updateBg({ background_size: e.target.value as CoverSection["background_size"] })}
154
+ className={SELECT_CLASS}
155
+ >
156
+ {BG_SIZE_OPTIONS.map((opt) => (
157
+ <option key={opt.value} value={opt.value}>{opt.label}</option>
158
+ ))}
159
+ </select>
160
+ </SettingsField>
161
+ </>
162
+ )}
151
163
  </SettingsSection>
152
164
 
153
165
  {/* Overlay */}
@@ -175,7 +175,7 @@ export function createCoverActions(set: StoreSet, get: StoreGet) {
175
175
  updateCoverBackground: (
176
176
  sectionKey: string,
177
177
  fields: Partial<Pick<CoverSection,
178
- "background_type" | "background_image" | "background_video" |
178
+ "background_type" | "background_color" | "background_image" | "background_video" |
179
179
  "background_position" | "background_size" |
180
180
  "background_overlay_color" | "background_overlay_opacity"
181
181
  >>
@@ -383,7 +383,7 @@ export interface BuilderActions {
383
383
  removeCoverRow: (sectionKey: string, rowKey: string) => void;
384
384
  resizeCoverRow: (sectionKey: string, handleIndex: number, deltaPercent: number, startAbove: number, startBelow: number) => void;
385
385
  updateCoverRowAlign: (sectionKey: string, rowKey: string, align: CoverRow["vertical_align"]) => void;
386
- updateCoverBackground: (sectionKey: string, fields: Partial<Pick<CoverSection, "background_type" | "background_image" | "background_video" | "background_position" | "background_size" | "background_overlay_color" | "background_overlay_opacity">>) => void;
386
+ updateCoverBackground: (sectionKey: string, fields: Partial<Pick<CoverSection, "background_type" | "background_color" | "background_image" | "background_video" | "background_position" | "background_size" | "background_overlay_color" | "background_overlay_opacity">>) => void;
387
387
  updateCoverSettings: (sectionKey: string, settings: Partial<CoverSectionSettings>) => void;
388
388
  updateCoverHeight: (sectionKey: string, height: CoverSection["height"]) => void;
389
389
 
@@ -491,7 +491,8 @@ export interface CoverSection {
491
491
  _key: string;
492
492
 
493
493
  // Background
494
- background_type: "image" | "video";
494
+ background_type: "image" | "video" | "color";
495
+ background_color?: string;
495
496
  background_image?: string;
496
497
  background_video?: string;
497
498
  background_position?: string;
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.2.17";
9
+ export const ANDAMI_VERSION = "0.2.19";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morphika/andami",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
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",
@@ -115,10 +115,17 @@ export default defineType({
115
115
  list: [
116
116
  { title: "Image", value: "image" },
117
117
  { title: "Video", value: "video" },
118
+ { title: "Color", value: "color" },
118
119
  ],
119
120
  },
120
121
  initialValue: "image",
121
122
  }),
123
+ defineField({
124
+ name: "background_color",
125
+ title: "Background Color",
126
+ type: "string",
127
+ description: "Hex color for solid background",
128
+ }),
122
129
  defineField({
123
130
  name: "background_image",
124
131
  title: "Background Image",