first commit
This commit is contained in:
13
demos/plugins-demo/src/live.config.ts
Normal file
13
demos/plugins-demo/src/live.config.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* EmDash Live Content Collections
|
||||
*
|
||||
* Defines the _emdash collection that handles all content types from the database.
|
||||
* Query specific types using getEmDashCollection() and getEmDashEntry().
|
||||
*/
|
||||
|
||||
import { defineLiveCollection } from "astro:content";
|
||||
import { emdashLoader } from "emdash/runtime";
|
||||
|
||||
export const collections = {
|
||||
_emdash: defineLiveCollection({ loader: emdashLoader() }),
|
||||
};
|
||||
182
demos/plugins-demo/src/pages/index.astro
Normal file
182
demos/plugins-demo/src/pages/index.astro
Normal file
@@ -0,0 +1,182 @@
|
||||
---
|
||||
// Demo homepage showcasing plugin functionality
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>EmDash Plugins Demo</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.subtitle {
|
||||
color: #666;
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #333;
|
||||
}
|
||||
.plugin-list {
|
||||
list-style: none;
|
||||
}
|
||||
.plugin {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.plugin h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #2563eb;
|
||||
}
|
||||
.plugin p {
|
||||
color: #555;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.plugin code {
|
||||
background: #e2e8f0;
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.hooks {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.hooks strong {
|
||||
color: #333;
|
||||
}
|
||||
.cta {
|
||||
display: inline-block;
|
||||
background: #2563eb;
|
||||
color: white;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.cta:hover {
|
||||
background: #1d4ed8;
|
||||
}
|
||||
.order {
|
||||
background: #dbeafe;
|
||||
color: #1e40af;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>EmDash Plugins Demo</h1>
|
||||
<p class="subtitle">
|
||||
Demonstrating the plugin hook system with realistic plugins
|
||||
</p>
|
||||
|
||||
<h2>Active Plugins</h2>
|
||||
<ul class="plugin-list">
|
||||
<li class="plugin">
|
||||
<h3>Auto-Slug <span class="order">Priority 10</span></h3>
|
||||
<p>
|
||||
Automatically generates URL-friendly slugs from titles. Handles unicode,
|
||||
removes special characters, and ensures clean URLs.
|
||||
</p>
|
||||
<div class="hooks">
|
||||
<strong>Hook:</strong> <code>content:beforeSave</code>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="plugin">
|
||||
<h3>SEO Demo <span class="order">Priority 50</span></h3>
|
||||
<p>
|
||||
Validates SEO fields (meta title, description) against length limits.
|
||||
Provides admin UI for SEO settings and dashboard widget.
|
||||
</p>
|
||||
<div class="hooks">
|
||||
<strong>Hook:</strong> <code>content:beforeSave</code>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="plugin">
|
||||
<h3>Reading Time <span class="order">Priority 80</span></h3>
|
||||
<p>
|
||||
Calculates reading time based on word count and images. Parses Portable
|
||||
Text content and stores result in the content data.
|
||||
</p>
|
||||
<div class="hooks">
|
||||
<strong>Hook:</strong> <code>content:beforeSave</code>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="plugin">
|
||||
<h3>Audit Log <span class="order">Priority 200</span></h3>
|
||||
<p>
|
||||
Tracks all content changes for compliance and debugging. Logs create,
|
||||
update, delete operations with timestamps.
|
||||
</p>
|
||||
<div class="hooks">
|
||||
<strong>Hooks:</strong>
|
||||
<code>content:beforeSave</code>,
|
||||
<code>content:afterSave</code>,
|
||||
<code>content:beforeDelete</code>,
|
||||
<code>media:afterUpload</code>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="plugin">
|
||||
<h3>Image Optimizer <span class="order">Priority 10</span></h3>
|
||||
<p>
|
||||
Validates image uploads (file type, size limits). Sanitizes filenames
|
||||
and adds timestamps for uniqueness.
|
||||
</p>
|
||||
<div class="hooks">
|
||||
<strong>Hooks:</strong>
|
||||
<code>media:beforeUpload</code>,
|
||||
<code>media:afterUpload</code>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Hook Execution Order</h2>
|
||||
<p>
|
||||
When saving content, hooks execute in priority order (lower numbers first):
|
||||
</p>
|
||||
<ol style="margin: 1rem 0 1rem 1.5rem;">
|
||||
<li><strong>Auto-Slug (10)</strong> - Generates slug from title</li>
|
||||
<li><strong>Audit Log (1)</strong> - Captures "before" state for comparison</li>
|
||||
<li><strong>SEO (50)</strong> - Validates SEO field lengths</li>
|
||||
<li><strong>Reading Time (80)</strong> - Calculates reading time</li>
|
||||
<li><strong>Audit Log (200)</strong> - Logs the final saved state</li>
|
||||
</ol>
|
||||
|
||||
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
||||
<a href="/_emdash/admin" class="cta">Open Admin Panel</a>
|
||||
<a href="/posts" class="cta" style="background: #059669;">View Posts</a>
|
||||
<a href="/test-embeds" class="cta" style="background: #7c3aed;">Test Embeds</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
176
demos/plugins-demo/src/pages/posts/[slug].astro
Normal file
176
demos/plugins-demo/src/pages/posts/[slug].astro
Normal file
@@ -0,0 +1,176 @@
|
||||
---
|
||||
/**
|
||||
* Individual post page with PortableText rendering
|
||||
*
|
||||
* This demonstrates the embeds plugin auto-registering components
|
||||
* for YouTube, Vimeo, etc. with the PortableText renderer.
|
||||
*/
|
||||
import { getEmDashEntry } from "emdash";
|
||||
import { PortableText } from "emdash/ui";
|
||||
import { embedComponents } from "@emdashcms/plugin-embeds/astro";
|
||||
|
||||
const { slug } = Astro.params;
|
||||
|
||||
const { entry: post } = slug
|
||||
? await getEmDashEntry("posts", slug)
|
||||
: { entry: null };
|
||||
|
||||
if (!post) {
|
||||
return Astro.redirect("/404");
|
||||
}
|
||||
|
||||
const title = post.data.title;
|
||||
const content = post.data.content;
|
||||
const metaDescription = post.data.excerpt;
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{title || "Post"} - EmDash Plugins Demo</title>
|
||||
{metaDescription && <meta name="description" content={metaDescription} />}
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.meta {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.back {
|
||||
display: inline-block;
|
||||
margin-bottom: 1rem;
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
/* Content styles */
|
||||
.content {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.content h1 {
|
||||
font-size: 1.75rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.content h2 {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.content h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.content p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.content ul,
|
||||
.content ol {
|
||||
margin-bottom: 1rem;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
.content li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.content blockquote {
|
||||
border-left: 4px solid #e5e7eb;
|
||||
padding-left: 1rem;
|
||||
margin: 1rem 0;
|
||||
color: #4b5563;
|
||||
font-style: italic;
|
||||
}
|
||||
.content pre {
|
||||
background: #f3f4f6;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.content code {
|
||||
background: #f3f4f6;
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
.content pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
.content img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.content a {
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
/* Embed styles */
|
||||
.content iframe {
|
||||
max-width: 100%;
|
||||
border-radius: 8px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 3rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
footer a {
|
||||
color: #2563eb;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/posts" class="back">← Back to posts</a>
|
||||
|
||||
<article>
|
||||
<h1>{title || "Untitled"}</h1>
|
||||
<div class="meta">
|
||||
{post.data.status === "draft" && <span>Draft</span>}
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
{
|
||||
Array.isArray(content) && content.length > 0 ? (
|
||||
<PortableText
|
||||
value={content}
|
||||
components={{ type: embedComponents }}
|
||||
/>
|
||||
) : typeof content === "string" && content ? (
|
||||
<p>{content}</p>
|
||||
) : (
|
||||
<p style="color: #6b7280; font-style: italic;">No content yet.</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<footer>
|
||||
<a href={`/_emdash/admin/content/posts/${post.id}`}>Edit in Admin</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
113
demos/plugins-demo/src/pages/posts/index.astro
Normal file
113
demos/plugins-demo/src/pages/posts/index.astro
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
/**
|
||||
* Posts listing page
|
||||
*/
|
||||
import { getEmDashCollection } from "emdash";
|
||||
|
||||
const { entries: posts } = await getEmDashCollection("posts");
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>Posts - EmDash Plugins Demo</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.back {
|
||||
display: inline-block;
|
||||
margin-bottom: 1rem;
|
||||
color: #2563eb;
|
||||
}
|
||||
.post-list {
|
||||
list-style: none;
|
||||
}
|
||||
.post-item {
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
.post-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.post-title {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.post-title a {
|
||||
color: #2563eb;
|
||||
text-decoration: none;
|
||||
}
|
||||
.post-title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.post-meta {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
.empty {
|
||||
color: #6b7280;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
background: #f9fafb;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/" class="back">← Back to home</a>
|
||||
<h1>Posts</h1>
|
||||
|
||||
{
|
||||
posts.length === 0 ? (
|
||||
<div class="empty">
|
||||
<p>No posts yet.</p>
|
||||
<p>
|
||||
<a href="/_emdash/admin/content/posts/new">
|
||||
Create your first post
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<ul class="post-list">
|
||||
{posts.map((post) => (
|
||||
<li class="post-item">
|
||||
<h2 class="post-title">
|
||||
<a href={`/posts/${post.data.slug || post.id}`}>
|
||||
{post.data.title || "Untitled"}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="post-meta">
|
||||
{post.data.status === "draft" && <span>Draft</span>}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
<footer
|
||||
style="margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #e5e7eb; font-size: 0.875rem;"
|
||||
>
|
||||
<a href="/_emdash/admin" style="color: #2563eb;">Open Admin</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
129
demos/plugins-demo/src/pages/test-embeds.astro
Normal file
129
demos/plugins-demo/src/pages/test-embeds.astro
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
/**
|
||||
* Test page for embed components
|
||||
*
|
||||
* This page renders hardcoded Portable Text with embed blocks.
|
||||
*/
|
||||
import { PortableText } from "emdash/ui";
|
||||
import { embedComponents } from "@emdashcms/plugin-embeds/astro";
|
||||
|
||||
// Sample Portable Text content with various embed types
|
||||
const testContent = [
|
||||
{
|
||||
_type: "block",
|
||||
_key: "intro",
|
||||
style: "normal",
|
||||
children: [
|
||||
{
|
||||
_type: "span",
|
||||
text: "This page tests the auto-registered embed components. If you see the embeds below, the virtual module system is working!",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
_type: "block",
|
||||
_key: "h1",
|
||||
style: "h2",
|
||||
children: [{ _type: "span", text: "YouTube Embed" }],
|
||||
},
|
||||
{
|
||||
_type: "youtube",
|
||||
_key: "yt1",
|
||||
id: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
},
|
||||
{
|
||||
_type: "block",
|
||||
_key: "h2",
|
||||
style: "h2",
|
||||
children: [{ _type: "span", text: "Vimeo Embed" }],
|
||||
},
|
||||
{
|
||||
_type: "vimeo",
|
||||
_key: "vim1",
|
||||
id: "https://vimeo.com/76979871",
|
||||
},
|
||||
{
|
||||
_type: "block",
|
||||
_key: "h3",
|
||||
style: "h2",
|
||||
children: [{ _type: "span", text: "Link Preview" }],
|
||||
},
|
||||
{
|
||||
_type: "linkPreview",
|
||||
_key: "lp1",
|
||||
id: "https://astro.build",
|
||||
},
|
||||
{
|
||||
_type: "block",
|
||||
_key: "outro",
|
||||
style: "normal",
|
||||
children: [
|
||||
{
|
||||
_type: "span",
|
||||
text: "If you see the embeds above rendered correctly, the plugin system is working! No manual component wiring was needed.",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>Test Embeds - EmDash Plugins Demo</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #333;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.content {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
a {
|
||||
color: #2563eb;
|
||||
}
|
||||
.back {
|
||||
display: inline-block;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/" class="back">← Back to home</a>
|
||||
<h1>Embed Components Test</h1>
|
||||
<p>This page tests embed components from the embeds plugin.</p>
|
||||
|
||||
<div class="content">
|
||||
<PortableText
|
||||
value={testContent}
|
||||
components={{ type: embedComponents }}
|
||||
/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user