Expose reusable builder kit surfaces and email preset pack
Quality / quality (ubuntu-latest) (push) Failing after 10m2s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
Clintchiz committed 2026-09-05 19:51:57 +05:30
1 parent e019ac34c8
commit eb1875f0bc
20 files changed
+635 -4

No files matched your search

+7
View File
@@ -1,6 +1,13 @@
import type { BlockNode } from "@wrnexus/blocks";
import { emailCoreBlocks } from "./blocks.ts";
/** Complete trusted editor pack, including schema-valid content factories. */
export const emailEditorBlocks = emailCoreBlocks.map(definition =>
definition.manifest.category === "content" || definition.manifest.type.startsWith("commerce.")
? { ...definition, create: (context: { id(): string }) => createEmailBlock(definition.manifest.type, context) }
: definition,
);
/** Canonical initial content for palette blocks; time and instance IDs are injectable. */
export function createEmailBlock(type: string, context: { id(): string; now?: number }): BlockNode {
const now = context.now ?? Date.now();
+58
View File
@@ -0,0 +1,58 @@
import type { BlockNode, JsonObject } from "@wrnexus/blocks";
export function createSectionPreset(name: string, id: () => string): BlockNode {
const node = (
type: string,
props: JsonObject,
slots: Record<string, BlockNode[]> = {},
): BlockNode => ({
id: id(),
type,
version: 1,
props,
styles: {},
slots,
});
const text = (value: string) => node("content.text", { text: value });
const heading = (value: string) => node("content.heading", { text: value, level: "2" });
const button = (label: string, href: string) => node("content.button", { label, href });
let content: BlockNode[];
if (name === "welcome")
content = [
heading("Welcome, {{recipientName|friend}}!"),
text("We’re glad you’re here. Discover what’s new and find your next favorite."),
button("Explore now", "https://example.com"),
];
else if (name === "feature")
content = [
node("content.image", {
src: "https://placehold.co/600x300",
alt: "Replace with your featured image",
width: "600",
height: "300",
decorative: false,
}),
heading("Something worth sharing"),
text("Introduce your product, tell your story, or share your latest announcement."),
button("Learn more", "https://example.com"),
];
else if (name === "footer")
content = [
node("content.divider", {}),
text("Thank you for being part of our community."),
text("{{organizationAddress|Add your organization address}}"),
button("Unsubscribe", "{{unsubscribeUrl|https://example.com/unsubscribe}}"),
];
else throw new Error("Unknown section preset.");
const column = node("layout.column", { width: "100%" }, { content });
const columns = node("layout.columns", {}, { columns: [column] });
return node("layout.section", { background: "#ffffff", padding: "24px" }, { content: [columns] });
}
/** Native, editable section presets; no HTML snapshots. */
export const emailPresets = ["welcome", "feature", "footer"].map(id => ({
id,
name: { key: "email.presets." + id, fallback: id[0].toUpperCase() + id.slice(1) },
icon: "icon-[lucide--layout-template]",
create: (context: { id(): string }) => createSectionPreset(id, context.id),
}));