EmDash CMS

repository·main·Indexed 10 days ago

https://github.com/emdash-cms/emdash

A full-stack, type-safe TypeScript CMS built on Astro and Cloudflare. It serves as a modern alternative to WordPress, featuring a serverless architecture compatible with Cloudflare or Node.js servers with SQLite, sandboxed plugins, and structured Portable Text content.

Tokens
385.1K
Snippets
1.2K
Records
1.7K
Agent score
95%

What's inside EmDash

  1. What is EmDash?

    main

    EmDash is an Astro-native content management system (CMS). Unlike a headless CMS that operates as a separate service, EmDash is tightly integrated into your Astro site and runs within the same deployment. It leverages Astro 6's Live Content Collections to serve content at runtime, ensuring that edits made in the admin UI appear immediately.

    Key Features

    • Visual Content Modelling: Define and modify collections and fields directly from the admin UI.
    • Live Collections: Content is served at runtime for immediate updates.
    • Plugin System: Extend functionality using WordPress-inspired hooks, storage, settings, and admin UI extensions.
    • Cloud-Portable: Supports deployment on Cloudflare (Workers + D1 + R2) or Node.js (SQLite, libSQL, PostgreSQL, and S3-compatible storage).
  2. Overview of @emdash-cms/marketplace

    main
    The @emdash-cms/marketplace package is a standalone Cloudflare Worker that hosts the EmDash plugin marketplace. It provides functionality for plugin discovery, publishing, and moderation. The marketplace uses Workers AI to perform code and image audits for plugin safety.
  3. Overview of EmDash CLI commands

    main

    The EmDash CLI (emdash or ec) is used to manage EmDash CMS instances. Commands are categorized into two types:

    1. Local commands: Operate directly on a local SQLite file without requiring a running server. Examples include init, dev, seed, export-seed, and auth secret.
    2. Remote commands: Interact with a running EmDash instance via HTTP. Examples include types, login, content, schema, media, search, taxonomy, and menu.
  4. Understand the Marketing Template structure and pages

    main

    The Marketing Template is a SaaS-style landing page template designed for product marketing. It uses a modular content block approach where content is authored as Portable Text documents within the pages collection rather than using a posts collection.

    Available Pages

    • Home (/): Displays marketing blocks (hero, features, testimonials, pricing, FAQ) in any order.
    • Pricing (/pricing): A dedicated pricing page using the pricing block.
    • Contact (/contact): A page featuring contact methods (Email, Support, Sales) and a contact form.

    Schema Overview

    • pages collection: Contains title and content (Portable Text).
    • Menus: Supports four menus: primary, footer_product, footer_company, and footer_support.
    • Site Settings: Includes title (renders in header) and tagline (used in footer/metadata).
  5. Core Features of EmDash

    main

    EmDash provides several built-in capabilities for managing content and site structure:

    • Visual Schema Builder: Create collections and fields directly from the admin panel.
    • Rich Text Editor: TipTap-powered editing using Portable Text for storage.
    • Media Library: Supports drag-and-drop uploads with signed URL support.
    • Navigation Menus: Admin-editable menus with support for nested items.
    • Taxonomies: Built-in support for categories, tags, and custom classification systems.
    • Widget Areas: Configurable content regions for areas like sidebars and footers.
    • WordPress Import: Migrate existing content via WXR exports or the WordPress REST API.
    • Preview System: Token-based previewing for draft content.
  6. Understand the Portfolio Template structure and pages

    main

    The Portfolio template is an editorial, near-monochrome design optimized for showcasing creative work through photography and generous whitespace. It uses a restrained design language where the work itself serves as the primary decoration.

    Available Pages:

    • Home (/): Features a centered serif title and tagline, followed by a "Selected Work" grid.
    • Work index (/work): Displays a heading, summary, tag filter chips, and a full project grid.
    • Project detail (/work/[slug]): Shows project metadata, a large serif title, summary, featured image, Portable Text body, an optional gallery, and a URL.
    • About (/about): Renders page content using Portable Text.
    • Contact (/contact): Includes a form and a column for email, location, and social links.
  7. Understand the EmDash architecture and core capabilities

    main

    EmDash is an Astro-native CMS designed to run within the same deployment as your Astro site. Unlike decoupled headless CMSs, EmDash content is queried through Astro's Live Content Collections within the same process, enabling a single deployment for both frontend and content management.

    Key Capabilities:

    • Single Deployment: Content and frontend are managed in one codebase and deployed together.
    • Type Safety: Automatically generates TypeScript types from your content model, providing full autocomplete from queries to templates.
    • Live Updates: Leverages Astro's Live Content Collections so content changes appear instantly.
    • Cloud-Portable: Supports multiple runtimes including Cloudflare Workers (with D1 and R2) and Node.js (with SQLite, libSQL, PostgreSQL, and S3-compatible storage).
  8. Explore EmDash Plugins in the Demo

    main

    The plugins demo includes the following packages, each demonstrating specific EmDash hook capabilities:

    • @emdash-cms/plugin-audit-log: Tracks content changes (create, update, delete) and media uploads. It uses hooks like content:beforeSave, content:afterSave, content:beforeDelete, and media:afterUpload to compare states and maintain an admin history.
    • @emdash-cms/plugin-webhook-notifier: Sends JSON payloads to external URLs on content or media events. It listens to content:afterSave, content:afterDelete, and media:afterUpload and includes features like exponential backoff and SSRF protection.
    • @emdash-cms/plugin-embeds: Extends Portable Text with block types for external content like YouTube, Vimeo, Twitter/X, Bluesky, Mastodon, and GitHub Gists, as well as Open Graph link previews.
    • @emdash-cms/plugin-api-test: A utility plugin that provides routes to exercise all v2 plugin APIs (kv, storage, content, media, http) via a test/all route.
  9. Understand EmDash key files and configuration

    main

    EmDash projects rely on several key files for configuration and content management:

    • astro.config.mjs: Contains the Astro configuration, including the emdash() integration, database settings, and storage configuration.
    • src/live.config.ts: Handles EmDash loader registration (boilerplate; do not modify).
    • seed/seed.json: The source of truth for schema definitions and demo content, including collections, fields, taxonomies, menus, and widgets.
    • emdash-env.d.ts: Auto-generated TypeScript types for your collections, updated when the dev server starts.
    • src/layouts/Base.astro: The base layout containing EmDash wiring for menus, search, and page contributions.
    • src/pages/: Contains the Astro pages, which must all be server-rendered.
  10. Understand the Blog Template structure and routes

    main

    The Blog Template is designed for writing-focused sites (personal, technical, or newsletters) and includes built-in support for posts, pages, categories, tags, full-text search, and RSS.

    Available Routes:

    • / : Home page with featured post hero and latest posts grid.
    • /posts : Full list of articles with excerpts and tag chips.
    • /posts/[slug] : Detailed article view with a three-column layout (left meta, center body, right gutter).
    • /search : Full-text search interface.
    • /pages/[slug] : Static content pages (e.g., /about).
    • /category/[slug] : Filtered post list by category.
    • /tag/[slug] : Filtered post list by tag.
    • /rss.xml : Automatically generated RSS feed.
  11. What is Block Kit and how does it work?

    main

    Block Kit is a declarative JSON-based UI system used for sandboxed plugin admin pages. Instead of running plugin JavaScript in the browser, the host (EmDash) renders blocks based on a JSON response from the plugin. This ensures security for runtime-installed plugins.

    Lifecycle:

    1. User navigates to the plugin admin page.
    2. The admin sends a page_load interaction to the plugin's admin route.
    3. The plugin returns a BlockResponse containing an array of blocks.
    4. The admin renders these blocks using a BlockRenderer.
    5. User interactions (e.g., button clicks, form submissions) trigger new interactions sent back to the plugin.
    6. The plugin responds with new blocks or updates.

    Note: Trusted plugins (declared in astro.config.ts) can bypass this by shipping custom React components. Block Kit is specifically for sandboxed, runtime-installed plugins.

    import type { BlockInteraction } from "@emdash-cms/blocks";
    
    routes: {
    	admin: {
    		handler: async (ctx) => {
    			// EmDash parses the request body once and exposes it as ctx.input;
    			// BlockInteraction is the discriminated union of page_load, 
    			// block_action, and form_submit payloads.
    			const interaction = ctx.input as BlockInteraction;
    
    			if (interaction.type === "page_load") {
    				return {
    					blocks: [
    							{ type: "header", text: "My Plugin Settings" },
    							{
    								type: "form",
    								block_id: "settings",
    								fields: [
    									{ type: "text_input", action_id: "api_url", label: "API URL" },
    									{ type: "toggle", action_id: "enabled", label: "Enabled", initial_value: true },
    								],
    								submit: { label: "Save", action_id: "save" },
    							},
    						],
    					};
    				}
    
    				if (interaction.type === "form_submit" && interaction.action_id === "save") {
    					await ctx.kv.set("settings", interaction.values);
    					return {
    						blocks: [/* updated blocks */],
    						toast: { message: "Settings saved", type: "success" },
    					};
    				}
    			};
    		},
    	},
    }