@avocadostudio-ai/shared 0.1.0

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 (76) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +80 -0
  3. package/contract/operation.schema.json +752 -0
  4. package/dist/api-responses.d.ts +62 -0
  5. package/dist/api-responses.js +68 -0
  6. package/dist/block-manifest.d.ts +28 -0
  7. package/dist/block-manifest.js +247 -0
  8. package/dist/block-names.d.ts +19 -0
  9. package/dist/block-names.js +52 -0
  10. package/dist/blocks/_helpers.d.ts +14 -0
  11. package/dist/blocks/_helpers.js +26 -0
  12. package/dist/blocks/_registry.d.ts +116 -0
  13. package/dist/blocks/_registry.js +271 -0
  14. package/dist/blocks/banner.d.ts +1 -0
  15. package/dist/blocks/banner.js +34 -0
  16. package/dist/blocks/card-grid.d.ts +1 -0
  17. package/dist/blocks/card-grid.js +73 -0
  18. package/dist/blocks/card.d.ts +1 -0
  19. package/dist/blocks/card.js +37 -0
  20. package/dist/blocks/carousel.d.ts +1 -0
  21. package/dist/blocks/carousel.js +51 -0
  22. package/dist/blocks/cta.d.ts +1 -0
  23. package/dist/blocks/cta.js +35 -0
  24. package/dist/blocks/embed.d.ts +1 -0
  25. package/dist/blocks/embed.js +30 -0
  26. package/dist/blocks/faq-accordion.d.ts +1 -0
  27. package/dist/blocks/faq-accordion.js +30 -0
  28. package/dist/blocks/feature-grid.d.ts +1 -0
  29. package/dist/blocks/feature-grid.js +46 -0
  30. package/dist/blocks/footer.d.ts +1 -0
  31. package/dist/blocks/footer.js +31 -0
  32. package/dist/blocks/gallery.d.ts +1 -0
  33. package/dist/blocks/gallery.js +47 -0
  34. package/dist/blocks/hero.d.ts +1 -0
  35. package/dist/blocks/hero.js +48 -0
  36. package/dist/blocks/index.d.ts +3 -0
  37. package/dist/blocks/index.js +53 -0
  38. package/dist/blocks/quote.d.ts +1 -0
  39. package/dist/blocks/quote.js +32 -0
  40. package/dist/blocks/rich-text.d.ts +1 -0
  41. package/dist/blocks/rich-text.js +41 -0
  42. package/dist/blocks/site-header.d.ts +1 -0
  43. package/dist/blocks/site-header.js +48 -0
  44. package/dist/blocks/stats.d.ts +1 -0
  45. package/dist/blocks/stats.js +42 -0
  46. package/dist/blocks/table.d.ts +1 -0
  47. package/dist/blocks/table.js +37 -0
  48. package/dist/blocks/tabs.d.ts +1 -0
  49. package/dist/blocks/tabs.js +39 -0
  50. package/dist/blocks/testimonials.d.ts +1 -0
  51. package/dist/blocks/testimonials.js +45 -0
  52. package/dist/blocks/two-column.d.ts +1 -0
  53. package/dist/blocks/two-column.js +61 -0
  54. package/dist/blocks/video.d.ts +1 -0
  55. package/dist/blocks/video.js +33 -0
  56. package/dist/chat-events.d.ts +475 -0
  57. package/dist/chat-events.js +137 -0
  58. package/dist/demo-seed-content.d.ts +3 -0
  59. package/dist/demo-seed-content.js +1128 -0
  60. package/dist/draft-mode.d.ts +10 -0
  61. package/dist/draft-mode.js +29 -0
  62. package/dist/editable-path.d.ts +20 -0
  63. package/dist/editable-path.js +101 -0
  64. package/dist/index.d.ts +13 -0
  65. package/dist/index.js +26 -0
  66. package/dist/ops/builders.d.ts +62 -0
  67. package/dist/ops/builders.js +111 -0
  68. package/dist/ops/theme-tokens.d.ts +50 -0
  69. package/dist/ops/theme-tokens.js +73 -0
  70. package/dist/protocol.d.ts +7 -0
  71. package/dist/protocol.js +7 -0
  72. package/dist/publish-diff.d.ts +67 -0
  73. package/dist/publish-diff.js +9 -0
  74. package/dist/schemas.d.ts +321 -0
  75. package/dist/schemas.js +238 -0
  76. package/package.json +48 -0
@@ -0,0 +1,271 @@
1
+ import { z } from "zod";
2
+ // ---------------------------------------------------------------------------
3
+ // Placeholder image — used as default value for all image slots.
4
+ // Treat this as "no image set" throughout the UI.
5
+ // ---------------------------------------------------------------------------
6
+ export const IMAGE_PLACEHOLDER = "/hero-generated.svg";
7
+ export function isImagePlaceholder(url) {
8
+ if (!url)
9
+ return true;
10
+ const cleaned = url.split("?")[0].replace(/^\/+/, "");
11
+ return cleaned === IMAGE_PLACEHOLDER.replace(/^\/+/, "");
12
+ }
13
+ // ---------------------------------------------------------------------------
14
+ // Block registry
15
+ // ---------------------------------------------------------------------------
16
+ // Use globalThis to ensure a single registry survives Next.js webpack module duplication
17
+ // across RSC / SSR / API route layers. Without this, registerBlock() in custom blocks
18
+ // populates a different registry copy than buildBlockManifest() reads.
19
+ const G = globalThis;
20
+ const _blockSchemas = G.__ase_blockSchemas ?? (G.__ase_blockSchemas = {});
21
+ const _blockMeta = G.__ase_blockMeta ?? (G.__ase_blockMeta = {});
22
+ /**
23
+ * Register a block type. Can be called at module load time.
24
+ * Re-registering the same type overwrites the previous registration.
25
+ */
26
+ export function registerBlock(type, config) {
27
+ _blockSchemas[type] = config.schema;
28
+ // Auto-derive `required` on each FieldMeta from the Zod schema shape
29
+ const shape = config.schema.shape;
30
+ if (shape) {
31
+ for (const [key, field] of Object.entries(config.meta.fields)) {
32
+ if (field.required !== undefined)
33
+ continue; // explicit override
34
+ const zodField = shape[key];
35
+ if (zodField) {
36
+ field.required = !zodField.isOptional();
37
+ }
38
+ }
39
+ // List item fields
40
+ if (config.meta.listFields) {
41
+ for (const [listKey, listMeta] of Object.entries(config.meta.listFields)) {
42
+ const listZod = shape[listKey];
43
+ // Unwrap ZodArray → element (ZodObject)
44
+ const elementShape = listZod?.element?.shape;
45
+ if (!elementShape)
46
+ continue;
47
+ for (const [itemKey, itemField] of Object.entries(listMeta.itemFields)) {
48
+ if (itemField.required !== undefined)
49
+ continue;
50
+ const zodItem = elementShape[itemKey];
51
+ if (zodItem) {
52
+ itemField.required = !zodItem.isOptional();
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
58
+ _blockMeta[type] = config.meta;
59
+ if (!config.meta.chrome && !allowedBlockTypes.includes(type)) {
60
+ allowedBlockTypes.push(type);
61
+ }
62
+ }
63
+ /** Get metadata for a registered block type, or undefined. */
64
+ export function getBlockMeta(type) {
65
+ return _blockMeta[type];
66
+ }
67
+ /** Get all registered block metadata. */
68
+ export function getAllBlockMeta() {
69
+ return _blockMeta;
70
+ }
71
+ /** Cache for getImageFields results. */
72
+ const _imageFieldsCache = new Map();
73
+ /** Get the set of prop keys that are image fields for a block type. */
74
+ export function getImageFields(blockType) {
75
+ const cached = _imageFieldsCache.get(blockType);
76
+ if (cached)
77
+ return cached;
78
+ const meta = _blockMeta[blockType];
79
+ const result = new Set();
80
+ if (meta) {
81
+ for (const [key, fm] of Object.entries(meta.fields)) {
82
+ if (fm.kind === "image")
83
+ result.add(key);
84
+ }
85
+ }
86
+ _imageFieldsCache.set(blockType, result);
87
+ return result;
88
+ }
89
+ /** Cache for getListImageFields results. */
90
+ const _listImageFieldsCache = new Map();
91
+ /** Get list props that contain image fields: Map<listKey, Set<imageFieldKey>>. */
92
+ export function getListImageFields(blockType) {
93
+ const cached = _listImageFieldsCache.get(blockType);
94
+ if (cached)
95
+ return cached;
96
+ const meta = _blockMeta[blockType];
97
+ const result = new Map();
98
+ if (meta?.listFields) {
99
+ for (const [listKey, listMeta] of Object.entries(meta.listFields)) {
100
+ const imageKeys = new Set();
101
+ for (const [fieldKey, fieldMeta] of Object.entries(listMeta.itemFields)) {
102
+ if (fieldMeta.kind === "image")
103
+ imageKeys.add(fieldKey);
104
+ }
105
+ if (imageKeys.size > 0)
106
+ result.set(listKey, imageKeys);
107
+ }
108
+ }
109
+ _listImageFieldsCache.set(blockType, result);
110
+ return result;
111
+ }
112
+ /** Check if a block type is a chrome block (structurally pinned). */
113
+ export function isChrome(type) {
114
+ return _blockMeta[type]?.chrome === true;
115
+ }
116
+ /** Get all registered chrome block type names. */
117
+ export function getChromeTypes() {
118
+ return Object.entries(_blockMeta).filter(([, meta]) => meta.chrome).map(([type]) => type);
119
+ }
120
+ /** Check if a field is inline-editable based on its metadata kind. */
121
+ export function isFieldInlineEditable(type, fieldPath) {
122
+ const meta = _blockMeta[type];
123
+ if (!meta)
124
+ return true; // no metadata = allow (backwards compat)
125
+ // Handle nested paths like "features[0].title" → list "features", item field "title"
126
+ const listMatch = fieldPath.match(/^([a-zA-Z_]+)\[\d+\]\.(.+)$/);
127
+ if (listMatch) {
128
+ const [, listKey, itemField] = listMatch;
129
+ const listMeta = meta.listFields?.[listKey];
130
+ if (!listMeta)
131
+ return true;
132
+ const fm = listMeta.itemFields[itemField];
133
+ if (!fm)
134
+ return true;
135
+ if (fm.inlineEditable !== undefined)
136
+ return fm.inlineEditable;
137
+ return fm.kind === "text" || fm.kind === "richtext";
138
+ }
139
+ const fm = meta.fields[fieldPath];
140
+ if (!fm)
141
+ return true;
142
+ if (fm.inlineEditable !== undefined)
143
+ return fm.inlineEditable;
144
+ return fm.kind === "text" || fm.kind === "richtext";
145
+ }
146
+ /** Resolve the ImageSpec for a block field, handling both scalar and list item paths. */
147
+ export function getImageSpec(blockType, fieldPath) {
148
+ const meta = _blockMeta[blockType];
149
+ if (!meta)
150
+ return undefined;
151
+ // Handle list item paths like "cards[0].imageUrl" → listField "cards", itemField "imageUrl"
152
+ const listMatch = fieldPath.match(/^([a-zA-Z_]+)\[\d+\]\.(.+)$/);
153
+ if (listMatch) {
154
+ const [, listKey, itemField] = listMatch;
155
+ return meta.listFields?.[listKey]?.itemFields[itemField]?.imageSpec;
156
+ }
157
+ // Also support bare "listKey.itemField" (no index) as a convenience lookup
158
+ const dotMatch = fieldPath.match(/^([a-zA-Z_]+)\.(.+)$/);
159
+ if (dotMatch) {
160
+ const [, listKey, itemField] = dotMatch;
161
+ const fromList = meta.listFields?.[listKey]?.itemFields[itemField]?.imageSpec;
162
+ if (fromList)
163
+ return fromList;
164
+ }
165
+ return meta.fields[fieldPath]?.imageSpec;
166
+ }
167
+ // ---------------------------------------------------------------------------
168
+ // Backwards-compatible exports
169
+ // ---------------------------------------------------------------------------
170
+ /**
171
+ * Block schemas keyed by type name.
172
+ * Prefer `registerBlock()` for new blocks; this object is kept for backwards compat.
173
+ */
174
+ export const blockSchemas = _blockSchemas;
175
+ export const allowedBlockTypes = G.__ase_allowedBlockTypes ?? (G.__ase_allowedBlockTypes = []);
176
+ export function getPropDisplayName(blockType, propKey) {
177
+ if (!blockType)
178
+ return propKey;
179
+ const meta = _blockMeta[blockType];
180
+ if (!meta)
181
+ return propKey;
182
+ // Check scalar fields
183
+ const fm = meta.fields[propKey];
184
+ if (fm?.label)
185
+ return fm.label;
186
+ // Check list fields
187
+ const lm = meta.listFields?.[propKey];
188
+ if (lm?.label)
189
+ return lm.label;
190
+ return propKey;
191
+ }
192
+ function defaultScalarForField(field, fieldKey) {
193
+ const label = field.label?.trim() || fieldKey;
194
+ if (field.kind === "text" || field.kind === "richtext" || field.kind === "imageAlt")
195
+ return `New ${label}`;
196
+ if (field.kind === "url")
197
+ return "/";
198
+ if (field.kind === "image")
199
+ return IMAGE_PLACEHOLDER;
200
+ if (field.kind === "color")
201
+ return "#0f766e";
202
+ if (field.kind === "number")
203
+ return 0;
204
+ if (field.kind === "enum")
205
+ return Array.isArray(field.options) && field.options.length > 0 ? field.options[0] : "";
206
+ return `New ${label}`;
207
+ }
208
+ export function defaultListItemForBlock(type, listKey) {
209
+ const meta = _blockMeta[type];
210
+ const listMeta = meta?.listFields?.[listKey];
211
+ if (!listMeta)
212
+ return null;
213
+ const item = {};
214
+ for (const [fieldKey, fieldMeta] of Object.entries(listMeta.itemFields)) {
215
+ item[fieldKey] = defaultScalarForField(fieldMeta, fieldKey);
216
+ }
217
+ return item;
218
+ }
219
+ /** Base schema — accepts any block type. Used for ingesting external site content with custom blocks. */
220
+ export const blockInstanceSchemaLenient = z.object({
221
+ id: z.string().min(1),
222
+ type: z.string().min(1),
223
+ props: z.record(z.string(), z.unknown())
224
+ });
225
+ /** Strict variant — rejects block types not in the shared registry. */
226
+ export const blockInstanceSchema = blockInstanceSchemaLenient.extend({
227
+ type: z.string().min(1).refine((t) => t in _blockSchemas, { message: "Unknown block type" }),
228
+ });
229
+ export function validateBlockProps(type, props) {
230
+ const schema = _blockSchemas[type];
231
+ if (!schema)
232
+ return { success: false, error: new z.ZodError([{ code: "custom", message: `Unknown block type: ${type}`, path: [] }]) };
233
+ return schema.safeParse(props);
234
+ }
235
+ // ---------------------------------------------------------------------------
236
+ // JSON Schema generation
237
+ // ---------------------------------------------------------------------------
238
+ /**
239
+ * Recursively strip validation-only constraints from a JSON schema object.
240
+ * The editor only needs structural info (types, properties, enums), not
241
+ * validation rules like minLength, minimum, $schema, additionalProperties.
242
+ */
243
+ function stripValidationConstraints(obj) {
244
+ if (Array.isArray(obj))
245
+ return obj.map(stripValidationConstraints);
246
+ if (obj === null || typeof obj !== "object")
247
+ return obj;
248
+ const result = {};
249
+ for (const [key, value] of Object.entries(obj)) {
250
+ if (key === "$schema" || key === "additionalProperties" || key === "minLength" || key === "minimum" || key === "minItems" || key === "propertyNames" || key === "default")
251
+ continue;
252
+ // Strip "required" only when it's the JSON Schema keyword (array of field names),
253
+ // not when it's a property definition (e.g. ContactForm's "required" boolean field)
254
+ if (key === "required" && Array.isArray(value))
255
+ continue;
256
+ result[key] = stripValidationConstraints(value);
257
+ }
258
+ return result;
259
+ }
260
+ /**
261
+ * Get a structural JSON schema for a registered block type.
262
+ * Strips validation constraints (minLength, etc.) — the editor only needs
263
+ * the shape to know which fields exist and their types.
264
+ */
265
+ export function getBlockJsonSchema(type) {
266
+ const schema = _blockSchemas[type];
267
+ if (!schema)
268
+ return undefined;
269
+ const raw = z.toJSONSchema(schema);
270
+ return stripValidationConstraints(raw);
271
+ }
@@ -0,0 +1 @@
1
+ export declare function bannerDefaultProps(): Record<string, unknown>;
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ import { registerBlock } from "./_registry.js";
3
+ import { f } from "./_helpers.js";
4
+ registerBlock("Banner", {
5
+ schema: z.object({
6
+ text: z.string().min(1),
7
+ variant: z.enum(["info", "success", "warning"]).default("info").catch("info"),
8
+ ctaText: z.string().optional(),
9
+ ctaHref: z.string().optional(),
10
+ backgroundColor: z.string().optional(),
11
+ textColor: z.string().optional(),
12
+ }),
13
+ meta: {
14
+ displayName: "Banner",
15
+ description: "Full-width announcement or alert bar with optional call-to-action button. Use variant for preset themes, or backgroundColor/textColor for custom colors.",
16
+ category: "content",
17
+ fields: {
18
+ text: f.text("Banner text"),
19
+ variant: { kind: "enum", label: "Variant", options: ["info", "success", "warning"], inlineEditable: false },
20
+ ctaText: f.text("Button label"),
21
+ ctaHref: f.url("Button link"),
22
+ backgroundColor: { kind: "color", label: "Background color", inlineEditable: false },
23
+ textColor: { kind: "color", label: "Text color", inlineEditable: false },
24
+ },
25
+ }
26
+ });
27
+ export function bannerDefaultProps() {
28
+ return {
29
+ text: "We just launched something new — check it out!",
30
+ variant: "info",
31
+ ctaText: "Learn more",
32
+ ctaHref: "/",
33
+ };
34
+ }
@@ -0,0 +1 @@
1
+ export declare function cardGridDefaultProps(): Record<string, unknown>;
@@ -0,0 +1,73 @@
1
+ import { z } from "zod";
2
+ import { registerBlock } from "./_registry.js";
3
+ import { f } from "./_helpers.js";
4
+ registerBlock("CardGrid", {
5
+ schema: z.object({
6
+ title: z.string().min(1),
7
+ subtitle: z.string().optional(),
8
+ columns: z.enum(["2", "3", "4"]).default("3").catch("3"),
9
+ cardVariant: z.enum(["default", "full-bleed"]).default("default").catch("default"),
10
+ cards: z
11
+ .array(z.object({
12
+ id: z.string().optional(),
13
+ title: z.string().min(1),
14
+ description: z.string().min(1),
15
+ ctaText: z.string().min(1),
16
+ ctaHref: z.string().min(1),
17
+ imageUrl: z.string().min(1).optional(),
18
+ imageAlt: z.string().min(1).optional()
19
+ }))
20
+ .min(1)
21
+ }),
22
+ meta: {
23
+ displayName: "Card Grid",
24
+ description: "Grid of cards, each with title, description, and CTA.",
25
+ category: "content",
26
+ fields: {
27
+ title: f.text("Section title"),
28
+ subtitle: f.text("Subtitle"),
29
+ columns: { kind: "enum", label: "Columns", options: ["2", "3", "4"], inlineEditable: false },
30
+ cardVariant: { kind: "enum", label: "Card style", options: ["default", "full-bleed"], inlineEditable: false },
31
+ headingLevel: f.headingLevel(),
32
+ },
33
+ listFields: {
34
+ cards: {
35
+ label: "Cards",
36
+ itemFields: {
37
+ title: f.text("Card title"),
38
+ description: f.longtext("Card description"),
39
+ ctaText: f.text("Button text"),
40
+ ctaHref: f.url("Button link"),
41
+ imageUrl: f.image("Card image", { aspectRatio: "landscape", width: 768, height: 512 }),
42
+ imageAlt: f.imageAlt("Card image alt text"),
43
+ }
44
+ }
45
+ }
46
+ }
47
+ });
48
+ export function cardGridDefaultProps() {
49
+ return {
50
+ title: "Explore more",
51
+ columns: "3",
52
+ cards: [
53
+ {
54
+ title: "Fast setup",
55
+ description: "Create and ship updates quickly.",
56
+ ctaText: "Get started",
57
+ ctaHref: "/"
58
+ },
59
+ {
60
+ title: "Safe updates",
61
+ description: "Schema-validated edits reduce breakage.",
62
+ ctaText: "See how",
63
+ ctaHref: "/pricing"
64
+ },
65
+ {
66
+ title: "Team workflow",
67
+ description: "Collaborate with clear, reviewable changes.",
68
+ ctaText: "Read guide",
69
+ ctaHref: "/"
70
+ }
71
+ ]
72
+ };
73
+ }
@@ -0,0 +1 @@
1
+ export declare function cardDefaultProps(): Record<string, unknown>;
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+ import { registerBlock } from "./_registry.js";
3
+ import { f } from "./_helpers.js";
4
+ registerBlock("Card", {
5
+ schema: z.object({
6
+ title: z.string().min(1),
7
+ description: z.string().min(1),
8
+ ctaText: z.string().min(1),
9
+ ctaHref: z.string().min(1),
10
+ imageUrl: z.string().min(1).optional(),
11
+ imageAlt: z.string().min(1).optional(),
12
+ variant: z.enum(["default", "full-bleed"]).default("default").catch("default"),
13
+ }),
14
+ meta: {
15
+ displayName: "Card",
16
+ description: "Single prominent card with a CTA. Use 'full-bleed' variant for background image with dark overlay and white text.",
17
+ category: "content",
18
+ fields: {
19
+ title: f.text("Card title"),
20
+ description: f.longtext("Card description"),
21
+ ctaText: f.text("Button text"),
22
+ ctaHref: f.url("Button link"),
23
+ imageUrl: f.image("Card image", { aspectRatio: "landscape", width: 768, height: 512 }),
24
+ imageAlt: f.imageAlt("Card image alt text"),
25
+ variant: { kind: "enum", label: "Variant", options: ["default", "full-bleed"], inlineEditable: false },
26
+ headingLevel: f.headingLevel(),
27
+ }
28
+ }
29
+ });
30
+ export function cardDefaultProps() {
31
+ return {
32
+ title: "Launch faster",
33
+ description: "Go from idea to published changes in minutes.",
34
+ ctaText: "Learn more",
35
+ ctaHref: "/pricing"
36
+ };
37
+ }
@@ -0,0 +1 @@
1
+ export declare function carouselDefaultProps(): Record<string, unknown>;
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import { registerBlock, IMAGE_PLACEHOLDER } from "./_registry.js";
3
+ import { f } from "./_helpers.js";
4
+ registerBlock("Carousel", {
5
+ schema: z.object({
6
+ items: z.array(z.object({
7
+ id: z.string().optional(),
8
+ imageUrl: z.string().min(1),
9
+ imageAlt: z.string().optional(),
10
+ heading: z.string().optional(),
11
+ description: z.string().optional(),
12
+ ctaText: z.string().optional(),
13
+ ctaHref: z.string().optional(),
14
+ })).min(1),
15
+ autoplay: z.enum(["true", "false"]).default("false").catch("false"),
16
+ interval: z.number().optional(),
17
+ }),
18
+ meta: {
19
+ displayName: "Carousel",
20
+ description: "Image/content slideshow with prev/next navigation and dot indicators.",
21
+ category: "content",
22
+ fields: {
23
+ autoplay: { kind: "enum", label: "Autoplay", options: ["true", "false"], inlineEditable: false },
24
+ interval: { kind: "number", label: "Interval (ms)", inlineEditable: false },
25
+ },
26
+ listFields: {
27
+ items: {
28
+ label: "Slides",
29
+ itemFields: {
30
+ imageUrl: f.image("Slide image", { aspectRatio: "landscape", width: 1200, height: 600 }),
31
+ imageAlt: f.imageAlt("Image alt text"),
32
+ heading: f.text("Heading"),
33
+ description: f.longtext("Description"),
34
+ ctaText: f.text("Button label"),
35
+ ctaHref: f.url("Button link"),
36
+ }
37
+ }
38
+ }
39
+ }
40
+ });
41
+ export function carouselDefaultProps() {
42
+ return {
43
+ items: [
44
+ { imageUrl: IMAGE_PLACEHOLDER, imageAlt: "First slide", heading: "Welcome", description: "Get started with our platform.", ctaText: "Get started", ctaHref: "/" },
45
+ { imageUrl: IMAGE_PLACEHOLDER, imageAlt: "Second slide", heading: "Features", description: "Discover what makes us different." },
46
+ { imageUrl: IMAGE_PLACEHOLDER, imageAlt: "Third slide", heading: "Get Started", description: "Sign up today and start building.", ctaText: "Sign up", ctaHref: "/pricing" },
47
+ ],
48
+ autoplay: "false",
49
+ interval: 5000,
50
+ };
51
+ }
@@ -0,0 +1 @@
1
+ export declare function ctaDefaultProps(): Record<string, unknown>;
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ import { registerBlock } from "./_registry.js";
3
+ import { f } from "./_helpers.js";
4
+ registerBlock("CTA", {
5
+ schema: z.object({
6
+ title: z.string().min(1),
7
+ description: z.string().min(1),
8
+ ctaText: z.string().min(1),
9
+ ctaHref: z.string().min(1),
10
+ secondaryCtaText: z.string().optional(),
11
+ secondaryCtaHref: z.string().optional()
12
+ }),
13
+ meta: {
14
+ displayName: "Call to Action",
15
+ description: "Centered promotional section with primary and optional secondary button.",
16
+ category: "conversion",
17
+ fields: {
18
+ title: f.text("Headline"),
19
+ description: f.longtext("Description"),
20
+ ctaText: f.text("Button text"),
21
+ ctaHref: f.url("Button link"),
22
+ secondaryCtaText: f.text("Secondary button text"),
23
+ secondaryCtaHref: f.url("Secondary button link"),
24
+ headingLevel: f.headingLevel(),
25
+ }
26
+ }
27
+ });
28
+ export function ctaDefaultProps() {
29
+ return {
30
+ title: "Ready to get started?",
31
+ description: "Apply your next change in seconds.",
32
+ ctaText: "Start now",
33
+ ctaHref: "/"
34
+ };
35
+ }
@@ -0,0 +1 @@
1
+ export declare function embedDefaultProps(): Record<string, unknown>;
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { registerBlock } from "./_registry.js";
3
+ import { f } from "./_helpers.js";
4
+ registerBlock("Embed", {
5
+ schema: z.object({
6
+ embedType: z.enum(["map", "social", "custom"]).default("map").catch("map"),
7
+ url: z.string().min(1),
8
+ title: z.string().optional(),
9
+ aspectRatio: z.enum(["16:9", "4:3", "1:1"]).default("16:9").catch("16:9"),
10
+ }),
11
+ meta: {
12
+ displayName: "Embed",
13
+ description: "Embed external content — Google Maps, social media posts, or a custom iframe. For video use the Video block instead.",
14
+ category: "media",
15
+ fields: {
16
+ embedType: { kind: "enum", label: "Embed type", options: ["map", "social", "custom"], inlineEditable: false },
17
+ url: f.url("URL"),
18
+ title: f.text("Title / caption"),
19
+ aspectRatio: { kind: "enum", label: "Aspect ratio", options: ["16:9", "4:3", "1:1"], inlineEditable: false },
20
+ },
21
+ }
22
+ });
23
+ export function embedDefaultProps() {
24
+ return {
25
+ embedType: "map",
26
+ url: "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3153.0!2d-122.4194!3d37.7749!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x0!2zMzfCsDQ2JzI5LjYiTiAxMjLCsDI1JzA5LjgiVw!5e0!3m2!1sen!2sus!4v1",
27
+ title: "",
28
+ aspectRatio: "16:9",
29
+ };
30
+ }
@@ -0,0 +1 @@
1
+ export declare function faqAccordionDefaultProps(): Record<string, unknown>;
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { registerBlock } from "./_registry.js";
3
+ import { f } from "./_helpers.js";
4
+ registerBlock("FAQAccordion", {
5
+ schema: z.object({
6
+ title: z.string().min(1),
7
+ items: z.array(z.object({ id: z.string().optional(), q: z.string().min(1), a: z.string().min(1) })).min(1)
8
+ }),
9
+ meta: {
10
+ displayName: "FAQ Accordion",
11
+ description: "Expandable question-and-answer section.",
12
+ category: "content",
13
+ fields: { title: f.text("Section title"), headingLevel: f.headingLevel() },
14
+ listFields: {
15
+ items: {
16
+ label: "FAQ items",
17
+ itemFields: { q: f.text("Question"), a: f.richtext("Answer") }
18
+ }
19
+ }
20
+ }
21
+ });
22
+ export function faqAccordionDefaultProps() {
23
+ return {
24
+ title: "Frequently asked questions",
25
+ items: [
26
+ { q: "How fast can we publish?", a: "Most teams ship updates in minutes." },
27
+ { q: "Can we revise later?", a: "Yes, every block can be updated anytime." }
28
+ ]
29
+ };
30
+ }
@@ -0,0 +1 @@
1
+ export declare function featureGridDefaultProps(): Record<string, unknown>;
@@ -0,0 +1,46 @@
1
+ import { z } from "zod";
2
+ import { registerBlock } from "./_registry.js";
3
+ import { f } from "./_helpers.js";
4
+ registerBlock("FeatureGrid", {
5
+ schema: z.object({
6
+ title: z.string().min(1),
7
+ columns: z.enum(["2", "3", "4"]).default("3").catch("3"),
8
+ features: z.array(z.object({
9
+ id: z.string().optional(),
10
+ icon: z.string().optional(),
11
+ title: z.string().min(1),
12
+ description: z.string().min(1)
13
+ })).min(1)
14
+ }),
15
+ meta: {
16
+ displayName: "Feature Grid",
17
+ description: "Grid of feature cards with optional icon, title, and description.",
18
+ category: "content",
19
+ fields: {
20
+ title: f.text("Section title"),
21
+ columns: { kind: "enum", label: "Columns", options: ["2", "3", "4"], inlineEditable: false },
22
+ headingLevel: f.headingLevel(),
23
+ },
24
+ listFields: {
25
+ features: {
26
+ label: "Features",
27
+ itemFields: {
28
+ icon: f.text("Icon (single emoji)"),
29
+ title: f.text("Feature title"),
30
+ description: f.longtext("Feature description"),
31
+ }
32
+ }
33
+ }
34
+ }
35
+ });
36
+ export function featureGridDefaultProps() {
37
+ return {
38
+ title: "Key features",
39
+ columns: "3",
40
+ features: [
41
+ { icon: "\u26A1", title: "Fast setup", description: "Launch quickly with guided defaults." },
42
+ { icon: "\uD83D\uDEE1\uFE0F", title: "Safe edits", description: "Structured operations keep content valid." },
43
+ { icon: "\uD83D\uDD04", title: "Live updates", description: "Preview changes immediately." }
44
+ ]
45
+ };
46
+ }
@@ -0,0 +1 @@
1
+ export declare function footerDefaultProps(): Record<string, unknown>;