Spaces:
Sleeping
Sleeping
| // --------------------------------------------------------------------------- | |
| // Component Registry | |
| // | |
| // Each entry describes a custom MDX component that can be used inside the | |
| // editor. Factories (factory.ts) consume these definitions to auto-generate | |
| // TipTap extensions, NodeViews, slash-menu items and MDX serializers. | |
| // | |
| // Structural data (name, kind, fields, content) comes from the shared module. | |
| // This file adds UI metadata (tag, icon, label, description, placeholders). | |
| // --------------------------------------------------------------------------- | |
| import { SHARED_COMPONENT_DEFS, type SharedComponentDef } from "#shared/component-defs"; | |
| export interface ComponentField { | |
| name: string; | |
| type: "string" | "boolean" | "select"; | |
| default?: unknown; | |
| options?: string[]; | |
| label: string; | |
| placeholder?: string; | |
| } | |
| export interface ComponentDef { | |
| /** Internal TipTap node name, e.g. "accordion" */ | |
| name: string; | |
| /** MDX / Astro tag name, e.g. "Accordion" */ | |
| tag: string; | |
| /** Single-char or emoji icon for the slash menu */ | |
| icon: string; | |
| /** Human-readable label shown in the slash menu */ | |
| label: string; | |
| /** Short description shown below the label */ | |
| description: string; | |
| /** "wrapper" = has editable children, "atomic" = self-closing */ | |
| kind: "wrapper" | "atomic"; | |
| /** Editable attributes exposed in the toolbar */ | |
| fields: ComponentField[]; | |
| /** ProseMirror content expression (wrapper only, defaults to "block+") */ | |
| content?: string; | |
| } | |
| // UI metadata per component (tag, icon, label, description, field labels/placeholders) | |
| interface UIMeta { | |
| tag: string; | |
| icon: string; | |
| label: string; | |
| description: string; | |
| fieldMeta: Record<string, { label: string; placeholder?: string }>; | |
| } | |
| const UI_META: Record<string, UIMeta> = { | |
| accordion: { | |
| tag: "Accordion", icon: "▸", label: "Accordion", description: "Collapsible content section", | |
| fieldMeta: { title: { label: "Title", placeholder: "Section title…" }, open: { label: "Open by default" } }, | |
| }, | |
| note: { | |
| tag: "Note", icon: "ℹ", label: "Note / Callout", description: "Highlighted callout box", | |
| fieldMeta: { title: { label: "Title", placeholder: "Optional title…" }, emoji: { label: "Emoji", placeholder: "e.g. 💡" }, variant: { label: "Variant" } }, | |
| }, | |
| quoteBlock: { | |
| tag: "Quote", icon: "❝", label: "Quote block", description: "Quote with author attribution", | |
| fieldMeta: { author: { label: "Author", placeholder: "Author name…" }, source: { label: "Source", placeholder: "Book, URL…" } }, | |
| }, | |
| wide: { | |
| tag: "Wide", icon: "↔", label: "Wide", description: "Wider-than-column container", | |
| fieldMeta: {}, | |
| }, | |
| fullWidth: { | |
| tag: "FullWidth", icon: "⟷", label: "Full width", description: "Full-width container", | |
| fieldMeta: {}, | |
| }, | |
| sidenote: { | |
| tag: "Sidenote", icon: "¶", label: "Sidenote", description: "Margin note alongside content", | |
| fieldMeta: {}, | |
| }, | |
| reference: { | |
| tag: "Reference", icon: "🏷", label: "Reference / Figure", description: "Captioned figure with anchor ID", | |
| fieldMeta: { id: { label: "ID", placeholder: "fig-1" }, caption: { label: "Caption", placeholder: "Figure caption…" } }, | |
| }, | |
| htmlEmbed: { | |
| tag: "HtmlEmbed", icon: "📊", label: "HTML Embed", description: "Embed an external HTML visualization", | |
| fieldMeta: { src: { label: "Source file", placeholder: "d3-chart.html" }, title: { label: "Title", placeholder: "Chart title…" }, desc: { label: "Description", placeholder: "Chart description…" }, wide: { label: "Wide" }, downloadable: { label: "Downloadable" }, height: { label: "Height (px)", placeholder: "400" } }, | |
| }, | |
| hfUser: { | |
| tag: "HfUser", icon: "👤", label: "HF User card", description: "Hugging Face user profile card", | |
| fieldMeta: { username: { label: "Username", placeholder: "username" }, name: { label: "Display name", placeholder: "Full Name" }, url: { label: "URL", placeholder: "https://huggingface.co/username" } }, | |
| }, | |
| iframe: { | |
| tag: "Iframe", icon: "🔗", label: "Iframe", description: "Embed a remote URL (Space, widget, demo…)", | |
| fieldMeta: { | |
| src: { label: "URL", placeholder: "https://my-space.hf.space/" }, | |
| title: { label: "Title", placeholder: "Embed title…" }, | |
| desc: { label: "Description", placeholder: "Caption shown below…" }, | |
| height: { label: "Height (px)", placeholder: "600" }, | |
| wide: { label: "Wide" }, | |
| }, | |
| }, | |
| rawHtml: { | |
| tag: "RawHtml", icon: "</>", label: "Raw HTML", description: "Inject raw HTML content", | |
| fieldMeta: { html: { label: "HTML", placeholder: "<div>…</div>" } }, | |
| }, | |
| mermaid: { | |
| tag: "Mermaid", icon: "◈", label: "Mermaid diagram", description: "Flowchart, sequence, Gantt, etc.", | |
| fieldMeta: { code: { label: "Code", placeholder: "graph LR\\n A --> B" } }, | |
| }, | |
| }; | |
| function buildComponentDef(shared: SharedComponentDef): ComponentDef { | |
| const ui = UI_META[shared.name]; | |
| if (!ui) throw new Error(`Missing UI_META for component "${shared.name}"`); | |
| return { | |
| name: shared.name, | |
| tag: ui.tag, | |
| icon: ui.icon, | |
| label: ui.label, | |
| description: ui.description, | |
| kind: shared.kind, | |
| content: shared.content, | |
| fields: shared.fields.map((f) => ({ | |
| name: f.name, | |
| type: f.type, | |
| default: f.default, | |
| options: f.options, | |
| label: ui.fieldMeta[f.name]?.label ?? f.name, | |
| placeholder: ui.fieldMeta[f.name]?.placeholder, | |
| })), | |
| }; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Registry - built from shared definitions + UI metadata | |
| // --------------------------------------------------------------------------- | |
| export const COMPONENTS: ComponentDef[] = SHARED_COMPONENT_DEFS.map(buildComponentDef); | |