diff --git a/blog-cloudflare/.agents/skills/building-emdash-site/SKILL.md b/blog-cloudflare/.agents/skills/building-emdash-site/SKILL.md
index ad3facd..3fe607e 100644
--- a/blog-cloudflare/.agents/skills/building-emdash-site/SKILL.md
+++ b/blog-cloudflare/.agents/skills/building-emdash-site/SKILL.md
@@ -64,10 +64,10 @@ Write `seed/seed.json` with collections, fields, taxonomies, menus, widgets, and
### 6. Run and verify
```bash
-npx emdash dev # Start dev server (runs migrations + seeds, and generates types)
+pnpm dev # Start the Astro dev server
```
-The admin UI is at `http://localhost:4321/_emdash/admin`.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`.
## Quick API Cheat Sheet
@@ -102,13 +102,12 @@ import { getByline, getBylineBySlug } from "emdash";
import {
PortableText,
Image,
- Comments,
- CommentForm,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
diff --git a/blog-cloudflare/.agents/skills/building-emdash-site/references/configuration.md b/blog-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
index a1eb1ec..93fc783 100644
--- a/blog-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
+++ b/blog-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
@@ -211,8 +211,8 @@ For Cloudflare, replace `@astrojs/node` with `@astrojs/cloudflare` and add `@emd
## Dev Server
```bash
-npx emdash dev # Start dev server (runs migrations, applies seed)
-npx emdash dev --types # Start and generate types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Refresh types from the running site
```
-The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
diff --git a/blog-cloudflare/.agents/skills/building-emdash-site/references/site-features.md b/blog-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
index cbfbe83..f957f95 100644
--- a/blog-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
+++ b/blog-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
@@ -288,7 +288,7 @@ Built-in comments system:
```astro
---
-import { Comments, CommentForm } from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
---
diff --git a/blog-cloudflare/.agents/skills/creating-plugins/references/block-kit.md b/blog-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
index 8f3f3ce..a73441c 100644
--- a/blog-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
+++ b/blog-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
@@ -105,7 +105,7 @@ routes: {
{
"type": "section",
"text": "Configure your plugin settings below.",
- "accessory": { "type": "button", "text": "Refresh", "action_id": "refresh" }
+ "accessory": { "type": "button", "label": "Refresh", "action_id": "refresh" }
}
```
@@ -132,13 +132,17 @@ routes: {
```json
{
"type": "stats",
- "stats": [
- { "label": "Total", "value": "1,234", "trend": "+12%", "trend_direction": "up" },
+ "items": [
+ { "label": "Total", "value": "1,234", "trend": "up", "description": "+12% vs last week" },
{ "label": "Active", "value": "567" }
]
}
```
+- `items` — the array key is `items`, not `stats`
+- `trend` — `"up"`, `"down"`, or `"neutral"`; renders an arrow icon next to the value
+- `description` — secondary line beneath the value, for context such as "+12% vs last week"
+
### Table
```json
@@ -149,18 +153,24 @@ routes: {
{ "key": "status", "label": "Status" },
{ "key": "date", "label": "Date" }
],
- "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }]
+ "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }],
+ "page_action_id": "browse_items",
+ "empty_text": "No items yet."
}
```
+- `page_action_id` — required. The admin sends it as the `block_action` id when the user sorts a column or pages through results.
+- `empty_text` — shown in place of the table when `rows` is empty
+- `next_cursor` — set it to render a "Load more" control
+
### Actions
```json
{
"type": "actions",
"elements": [
- { "type": "button", "text": "Save", "action_id": "save", "style": "primary" },
- { "type": "button", "text": "Cancel", "action_id": "cancel" }
+ { "type": "button", "label": "Save", "action_id": "save", "style": "primary" },
+ { "type": "button", "label": "Cancel", "action_id": "cancel" }
]
}
```
@@ -196,12 +206,20 @@ routes: {
{
"type": "columns",
"columns": [
- { "blocks": [{ "type": "header", "text": "Left" }] },
- { "blocks": [{ "type": "header", "text": "Right" }] }
+ [
+ { "type": "header", "text": "Usage" },
+ { "type": "meter", "label": "Storage used", "value": 65 }
+ ],
+ [
+ { "type": "header", "text": "Activity" },
+ { "type": "context", "text": "Last sync 2 hours ago" }
+ ]
]
}
```
+- `columns` — 2 or 3 columns, each one an array of blocks
+
### Chart (Timeseries)
```json
@@ -395,7 +413,7 @@ return {
```json
{
"type": "button",
- "text": "Delete All",
+ "label": "Delete All",
"action_id": "delete_all",
"style": "danger",
"confirm": {
diff --git a/blog-cloudflare/.agents/skills/emdash-cli/SKILL.md b/blog-cloudflare/.agents/skills/emdash-cli/SKILL.md
index 5386e09..4fdc08f 100644
--- a/blog-cloudflare/.agents/skills/emdash-cli/SKILL.md
+++ b/blog-cloudflare/.agents/skills/emdash-cli/SKILL.md
@@ -70,14 +70,11 @@ npx emdash login --url https://example.com -H "X-API-Key: secret123"
### Database Setup
-Migrations and seed application happen automatically inside the runtime — there's no separate init/seed step. Just start the dev server (or deploy) and the first request runs pending migrations and applies the bundled seed if the database is empty.
+For normal site startup, use the project's package script. The first request runs pending migrations and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts.
```bash
-# Start dev server (runs migrations, applies seed on empty DB, starts Astro)
-npx emdash dev
-
-# Start dev server and generate types from remote
-npx emdash dev --types
+# Start the site with its package script
+pnpm dev
# Export an existing database as a seed file
# (the runtime auto-discovers .emdash/seed.json on first boot;
diff --git a/blog-cloudflare/AGENTS.md b/blog-cloudflare/AGENTS.md
index 3a15e60..d312975 100644
--- a/blog-cloudflare/AGENTS.md
+++ b/blog-cloudflare/AGENTS.md
@@ -3,8 +3,8 @@ This is an EmDash site -- a CMS built on Astro with a full admin UI.
## Commands
```bash
-npx emdash dev # Start dev server (runs migrations, seeds, generates types)
-npx emdash types # Regenerate TypeScript types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Regenerate TypeScript types from a running site
```
The admin UI is at `http://localhost:4321/_emdash/admin`.
diff --git a/blog-cloudflare/package.json b/blog-cloudflare/package.json
index 110235f..271eaee 100644
--- a/blog-cloudflare/package.json
+++ b/blog-cloudflare/package.json
@@ -16,18 +16,18 @@
"dependencies": {
"@astrojs/cloudflare": "^14.0.0",
"@astrojs/react": "^6.0.0",
- "@emdash-cms/cloudflare": "^0.30.0",
- "@emdash-cms/plugin-forms": "^0.2.4",
+ "@emdash-cms/cloudflare": "^0.34.0",
+ "@emdash-cms/plugin-forms": "^0.2.5",
"@emdash-cms/plugin-webhook-notifier": "^0.2.0",
"astro": "^7.0.0",
- "emdash": "^0.30.0",
+ "emdash": "^0.34.0",
"react": "19.2.4",
"react-dom": "19.2.4"
},
"devDependencies": {
"@astrojs/check": "^0.9.7",
"@cloudflare/workers-types": "^4.20260305.1",
- "wrangler": "^4.99.0"
+ "wrangler": "^4.123.0"
},
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
}
diff --git a/blog-cloudflare/src/pages/posts/[slug].astro b/blog-cloudflare/src/pages/posts/[slug].astro
index 0d45757..89deb7d 100644
--- a/blog-cloudflare/src/pages/posts/[slug].astro
+++ b/blog-cloudflare/src/pages/posts/[slug].astro
@@ -10,10 +10,9 @@ import {
import {
Image,
PortableText,
- Comments,
- CommentForm,
WidgetArea,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import Base from "../../layouts/Base.astro";
import PostCard from "../../components/PostCard.astro";
import { getReadingTime } from "../../utils/reading-time";
diff --git a/blog-cloudflare/src/worker.ts b/blog-cloudflare/src/worker.ts
index df373a0..75fe3e2 100644
--- a/blog-cloudflare/src/worker.ts
+++ b/blog-cloudflare/src/worker.ts
@@ -1,4 +1,8 @@
-// Worker entry: Astro's fetch handler plus EmDash's scheduled() handler, which
-// the Cron Trigger in wrangler.jsonc drives. PluginBridge is the sandbox
-// Durable Object, re-exported here so its binding resolves.
-export { default, PluginBridge } from "@emdash-cms/cloudflare/worker";
+import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
+
+export { PluginBridge };
+
+export default {
+ ...handler,
+ scheduled: createScheduledHandler(),
+};
diff --git a/blog-cloudflare/wrangler.jsonc b/blog-cloudflare/wrangler.jsonc
index f6baeec..23632b0 100644
--- a/blog-cloudflare/wrangler.jsonc
+++ b/blog-cloudflare/wrangler.jsonc
@@ -22,8 +22,8 @@
"binding": "LOADER",
},
],
- // Drives scheduled publishing, plugin cron, and maintenance (see src/worker.ts)
+ // General maintenance plus the bounded Media Usage lane (see src/worker.ts)
"triggers": {
- "crons": ["* * * * *"],
+ "crons": ["* * * * *", "*/2 * * * *"],
},
}
diff --git a/blog/.agents/skills/building-emdash-site/SKILL.md b/blog/.agents/skills/building-emdash-site/SKILL.md
index ad3facd..3fe607e 100644
--- a/blog/.agents/skills/building-emdash-site/SKILL.md
+++ b/blog/.agents/skills/building-emdash-site/SKILL.md
@@ -64,10 +64,10 @@ Write `seed/seed.json` with collections, fields, taxonomies, menus, widgets, and
### 6. Run and verify
```bash
-npx emdash dev # Start dev server (runs migrations + seeds, and generates types)
+pnpm dev # Start the Astro dev server
```
-The admin UI is at `http://localhost:4321/_emdash/admin`.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`.
## Quick API Cheat Sheet
@@ -102,13 +102,12 @@ import { getByline, getBylineBySlug } from "emdash";
import {
PortableText,
Image,
- Comments,
- CommentForm,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
diff --git a/blog/.agents/skills/building-emdash-site/references/configuration.md b/blog/.agents/skills/building-emdash-site/references/configuration.md
index a1eb1ec..93fc783 100644
--- a/blog/.agents/skills/building-emdash-site/references/configuration.md
+++ b/blog/.agents/skills/building-emdash-site/references/configuration.md
@@ -211,8 +211,8 @@ For Cloudflare, replace `@astrojs/node` with `@astrojs/cloudflare` and add `@emd
## Dev Server
```bash
-npx emdash dev # Start dev server (runs migrations, applies seed)
-npx emdash dev --types # Start and generate types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Refresh types from the running site
```
-The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
diff --git a/blog/.agents/skills/building-emdash-site/references/site-features.md b/blog/.agents/skills/building-emdash-site/references/site-features.md
index cbfbe83..f957f95 100644
--- a/blog/.agents/skills/building-emdash-site/references/site-features.md
+++ b/blog/.agents/skills/building-emdash-site/references/site-features.md
@@ -288,7 +288,7 @@ Built-in comments system:
```astro
---
-import { Comments, CommentForm } from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
---
diff --git a/blog/.agents/skills/creating-plugins/references/block-kit.md b/blog/.agents/skills/creating-plugins/references/block-kit.md
index 8f3f3ce..a73441c 100644
--- a/blog/.agents/skills/creating-plugins/references/block-kit.md
+++ b/blog/.agents/skills/creating-plugins/references/block-kit.md
@@ -105,7 +105,7 @@ routes: {
{
"type": "section",
"text": "Configure your plugin settings below.",
- "accessory": { "type": "button", "text": "Refresh", "action_id": "refresh" }
+ "accessory": { "type": "button", "label": "Refresh", "action_id": "refresh" }
}
```
@@ -132,13 +132,17 @@ routes: {
```json
{
"type": "stats",
- "stats": [
- { "label": "Total", "value": "1,234", "trend": "+12%", "trend_direction": "up" },
+ "items": [
+ { "label": "Total", "value": "1,234", "trend": "up", "description": "+12% vs last week" },
{ "label": "Active", "value": "567" }
]
}
```
+- `items` — the array key is `items`, not `stats`
+- `trend` — `"up"`, `"down"`, or `"neutral"`; renders an arrow icon next to the value
+- `description` — secondary line beneath the value, for context such as "+12% vs last week"
+
### Table
```json
@@ -149,18 +153,24 @@ routes: {
{ "key": "status", "label": "Status" },
{ "key": "date", "label": "Date" }
],
- "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }]
+ "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }],
+ "page_action_id": "browse_items",
+ "empty_text": "No items yet."
}
```
+- `page_action_id` — required. The admin sends it as the `block_action` id when the user sorts a column or pages through results.
+- `empty_text` — shown in place of the table when `rows` is empty
+- `next_cursor` — set it to render a "Load more" control
+
### Actions
```json
{
"type": "actions",
"elements": [
- { "type": "button", "text": "Save", "action_id": "save", "style": "primary" },
- { "type": "button", "text": "Cancel", "action_id": "cancel" }
+ { "type": "button", "label": "Save", "action_id": "save", "style": "primary" },
+ { "type": "button", "label": "Cancel", "action_id": "cancel" }
]
}
```
@@ -196,12 +206,20 @@ routes: {
{
"type": "columns",
"columns": [
- { "blocks": [{ "type": "header", "text": "Left" }] },
- { "blocks": [{ "type": "header", "text": "Right" }] }
+ [
+ { "type": "header", "text": "Usage" },
+ { "type": "meter", "label": "Storage used", "value": 65 }
+ ],
+ [
+ { "type": "header", "text": "Activity" },
+ { "type": "context", "text": "Last sync 2 hours ago" }
+ ]
]
}
```
+- `columns` — 2 or 3 columns, each one an array of blocks
+
### Chart (Timeseries)
```json
@@ -395,7 +413,7 @@ return {
```json
{
"type": "button",
- "text": "Delete All",
+ "label": "Delete All",
"action_id": "delete_all",
"style": "danger",
"confirm": {
diff --git a/blog/.agents/skills/emdash-cli/SKILL.md b/blog/.agents/skills/emdash-cli/SKILL.md
index 5386e09..4fdc08f 100644
--- a/blog/.agents/skills/emdash-cli/SKILL.md
+++ b/blog/.agents/skills/emdash-cli/SKILL.md
@@ -70,14 +70,11 @@ npx emdash login --url https://example.com -H "X-API-Key: secret123"
### Database Setup
-Migrations and seed application happen automatically inside the runtime — there's no separate init/seed step. Just start the dev server (or deploy) and the first request runs pending migrations and applies the bundled seed if the database is empty.
+For normal site startup, use the project's package script. The first request runs pending migrations and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts.
```bash
-# Start dev server (runs migrations, applies seed on empty DB, starts Astro)
-npx emdash dev
-
-# Start dev server and generate types from remote
-npx emdash dev --types
+# Start the site with its package script
+pnpm dev
# Export an existing database as a seed file
# (the runtime auto-discovers .emdash/seed.json on first boot;
diff --git a/blog/AGENTS.md b/blog/AGENTS.md
index 3a15e60..d312975 100644
--- a/blog/AGENTS.md
+++ b/blog/AGENTS.md
@@ -3,8 +3,8 @@ This is an EmDash site -- a CMS built on Astro with a full admin UI.
## Commands
```bash
-npx emdash dev # Start dev server (runs migrations, seeds, generates types)
-npx emdash types # Regenerate TypeScript types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Regenerate TypeScript types from a running site
```
The admin UI is at `http://localhost:4321/_emdash/admin`.
diff --git a/blog/package.json b/blog/package.json
index 63becca..ae2cf2a 100644
--- a/blog/package.json
+++ b/blog/package.json
@@ -19,7 +19,7 @@
"@emdash-cms/plugin-audit-log": "^0.2.0",
"astro": "^7.0.0",
"better-sqlite3": "^12.8.0",
- "emdash": "^0.30.0",
+ "emdash": "^0.34.0",
"react": "19.2.4",
"react-dom": "19.2.4"
},
diff --git a/blog/src/pages/posts/[slug].astro b/blog/src/pages/posts/[slug].astro
index 0d45757..89deb7d 100644
--- a/blog/src/pages/posts/[slug].astro
+++ b/blog/src/pages/posts/[slug].astro
@@ -10,10 +10,9 @@ import {
import {
Image,
PortableText,
- Comments,
- CommentForm,
WidgetArea,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import Base from "../../layouts/Base.astro";
import PostCard from "../../components/PostCard.astro";
import { getReadingTime } from "../../utils/reading-time";
diff --git a/marketing-cloudflare/.agents/skills/building-emdash-site/SKILL.md b/marketing-cloudflare/.agents/skills/building-emdash-site/SKILL.md
index ad3facd..3fe607e 100644
--- a/marketing-cloudflare/.agents/skills/building-emdash-site/SKILL.md
+++ b/marketing-cloudflare/.agents/skills/building-emdash-site/SKILL.md
@@ -64,10 +64,10 @@ Write `seed/seed.json` with collections, fields, taxonomies, menus, widgets, and
### 6. Run and verify
```bash
-npx emdash dev # Start dev server (runs migrations + seeds, and generates types)
+pnpm dev # Start the Astro dev server
```
-The admin UI is at `http://localhost:4321/_emdash/admin`.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`.
## Quick API Cheat Sheet
@@ -102,13 +102,12 @@ import { getByline, getBylineBySlug } from "emdash";
import {
PortableText,
Image,
- Comments,
- CommentForm,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
diff --git a/marketing-cloudflare/.agents/skills/building-emdash-site/references/configuration.md b/marketing-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
index a1eb1ec..93fc783 100644
--- a/marketing-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
+++ b/marketing-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
@@ -211,8 +211,8 @@ For Cloudflare, replace `@astrojs/node` with `@astrojs/cloudflare` and add `@emd
## Dev Server
```bash
-npx emdash dev # Start dev server (runs migrations, applies seed)
-npx emdash dev --types # Start and generate types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Refresh types from the running site
```
-The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
diff --git a/marketing-cloudflare/.agents/skills/building-emdash-site/references/site-features.md b/marketing-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
index cbfbe83..f957f95 100644
--- a/marketing-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
+++ b/marketing-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
@@ -288,7 +288,7 @@ Built-in comments system:
```astro
---
-import { Comments, CommentForm } from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
---
diff --git a/marketing-cloudflare/.agents/skills/creating-plugins/references/block-kit.md b/marketing-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
index 8f3f3ce..a73441c 100644
--- a/marketing-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
+++ b/marketing-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
@@ -105,7 +105,7 @@ routes: {
{
"type": "section",
"text": "Configure your plugin settings below.",
- "accessory": { "type": "button", "text": "Refresh", "action_id": "refresh" }
+ "accessory": { "type": "button", "label": "Refresh", "action_id": "refresh" }
}
```
@@ -132,13 +132,17 @@ routes: {
```json
{
"type": "stats",
- "stats": [
- { "label": "Total", "value": "1,234", "trend": "+12%", "trend_direction": "up" },
+ "items": [
+ { "label": "Total", "value": "1,234", "trend": "up", "description": "+12% vs last week" },
{ "label": "Active", "value": "567" }
]
}
```
+- `items` — the array key is `items`, not `stats`
+- `trend` — `"up"`, `"down"`, or `"neutral"`; renders an arrow icon next to the value
+- `description` — secondary line beneath the value, for context such as "+12% vs last week"
+
### Table
```json
@@ -149,18 +153,24 @@ routes: {
{ "key": "status", "label": "Status" },
{ "key": "date", "label": "Date" }
],
- "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }]
+ "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }],
+ "page_action_id": "browse_items",
+ "empty_text": "No items yet."
}
```
+- `page_action_id` — required. The admin sends it as the `block_action` id when the user sorts a column or pages through results.
+- `empty_text` — shown in place of the table when `rows` is empty
+- `next_cursor` — set it to render a "Load more" control
+
### Actions
```json
{
"type": "actions",
"elements": [
- { "type": "button", "text": "Save", "action_id": "save", "style": "primary" },
- { "type": "button", "text": "Cancel", "action_id": "cancel" }
+ { "type": "button", "label": "Save", "action_id": "save", "style": "primary" },
+ { "type": "button", "label": "Cancel", "action_id": "cancel" }
]
}
```
@@ -196,12 +206,20 @@ routes: {
{
"type": "columns",
"columns": [
- { "blocks": [{ "type": "header", "text": "Left" }] },
- { "blocks": [{ "type": "header", "text": "Right" }] }
+ [
+ { "type": "header", "text": "Usage" },
+ { "type": "meter", "label": "Storage used", "value": 65 }
+ ],
+ [
+ { "type": "header", "text": "Activity" },
+ { "type": "context", "text": "Last sync 2 hours ago" }
+ ]
]
}
```
+- `columns` — 2 or 3 columns, each one an array of blocks
+
### Chart (Timeseries)
```json
@@ -395,7 +413,7 @@ return {
```json
{
"type": "button",
- "text": "Delete All",
+ "label": "Delete All",
"action_id": "delete_all",
"style": "danger",
"confirm": {
diff --git a/marketing-cloudflare/.agents/skills/emdash-cli/SKILL.md b/marketing-cloudflare/.agents/skills/emdash-cli/SKILL.md
index 5386e09..4fdc08f 100644
--- a/marketing-cloudflare/.agents/skills/emdash-cli/SKILL.md
+++ b/marketing-cloudflare/.agents/skills/emdash-cli/SKILL.md
@@ -70,14 +70,11 @@ npx emdash login --url https://example.com -H "X-API-Key: secret123"
### Database Setup
-Migrations and seed application happen automatically inside the runtime — there's no separate init/seed step. Just start the dev server (or deploy) and the first request runs pending migrations and applies the bundled seed if the database is empty.
+For normal site startup, use the project's package script. The first request runs pending migrations and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts.
```bash
-# Start dev server (runs migrations, applies seed on empty DB, starts Astro)
-npx emdash dev
-
-# Start dev server and generate types from remote
-npx emdash dev --types
+# Start the site with its package script
+pnpm dev
# Export an existing database as a seed file
# (the runtime auto-discovers .emdash/seed.json on first boot;
diff --git a/marketing-cloudflare/AGENTS.md b/marketing-cloudflare/AGENTS.md
index 6bea048..ea2b278 100644
--- a/marketing-cloudflare/AGENTS.md
+++ b/marketing-cloudflare/AGENTS.md
@@ -3,8 +3,8 @@ This is an EmDash site -- a CMS built on Astro with a full admin UI.
## Commands
```bash
-npx emdash dev # Start dev server (runs migrations, seeds, generates types)
-npx emdash types # Regenerate TypeScript types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Regenerate TypeScript types from a running site
```
The admin UI is at `http://localhost:4321/_emdash/admin`.
diff --git a/marketing-cloudflare/package.json b/marketing-cloudflare/package.json
index 0880066..ec1554c 100644
--- a/marketing-cloudflare/package.json
+++ b/marketing-cloudflare/package.json
@@ -16,18 +16,18 @@
"dependencies": {
"@astrojs/cloudflare": "^14.0.0",
"@astrojs/react": "^6.0.0",
- "@emdash-cms/cloudflare": "^0.30.0",
+ "@emdash-cms/cloudflare": "^0.34.0",
"@iconify-json/ph": "^1.2.2",
"astro": "^7.0.0",
"astro-iconset": "^0.0.4",
- "emdash": "^0.30.0",
+ "emdash": "^0.34.0",
"react": "19.2.4",
"react-dom": "19.2.4"
},
"devDependencies": {
"@astrojs/check": "^0.9.7",
"@cloudflare/workers-types": "^4.20260305.1",
- "wrangler": "^4.99.0"
+ "wrangler": "^4.123.0"
},
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
}
diff --git a/marketing-cloudflare/src/worker.ts b/marketing-cloudflare/src/worker.ts
index df373a0..75fe3e2 100644
--- a/marketing-cloudflare/src/worker.ts
+++ b/marketing-cloudflare/src/worker.ts
@@ -1,4 +1,8 @@
-// Worker entry: Astro's fetch handler plus EmDash's scheduled() handler, which
-// the Cron Trigger in wrangler.jsonc drives. PluginBridge is the sandbox
-// Durable Object, re-exported here so its binding resolves.
-export { default, PluginBridge } from "@emdash-cms/cloudflare/worker";
+import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
+
+export { PluginBridge };
+
+export default {
+ ...handler,
+ scheduled: createScheduledHandler(),
+};
diff --git a/marketing-cloudflare/wrangler.jsonc b/marketing-cloudflare/wrangler.jsonc
index c020df0..e5e23e7 100644
--- a/marketing-cloudflare/wrangler.jsonc
+++ b/marketing-cloudflare/wrangler.jsonc
@@ -22,8 +22,8 @@
"binding": "LOADER",
},
],
- // Drives scheduled publishing, plugin cron, and maintenance (see src/worker.ts)
+ // General maintenance plus the bounded Media Usage lane (see src/worker.ts)
"triggers": {
- "crons": ["* * * * *"],
+ "crons": ["* * * * *", "*/2 * * * *"],
},
}
diff --git a/marketing/.agents/skills/building-emdash-site/SKILL.md b/marketing/.agents/skills/building-emdash-site/SKILL.md
index ad3facd..3fe607e 100644
--- a/marketing/.agents/skills/building-emdash-site/SKILL.md
+++ b/marketing/.agents/skills/building-emdash-site/SKILL.md
@@ -64,10 +64,10 @@ Write `seed/seed.json` with collections, fields, taxonomies, menus, widgets, and
### 6. Run and verify
```bash
-npx emdash dev # Start dev server (runs migrations + seeds, and generates types)
+pnpm dev # Start the Astro dev server
```
-The admin UI is at `http://localhost:4321/_emdash/admin`.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`.
## Quick API Cheat Sheet
@@ -102,13 +102,12 @@ import { getByline, getBylineBySlug } from "emdash";
import {
PortableText,
Image,
- Comments,
- CommentForm,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
diff --git a/marketing/.agents/skills/building-emdash-site/references/configuration.md b/marketing/.agents/skills/building-emdash-site/references/configuration.md
index a1eb1ec..93fc783 100644
--- a/marketing/.agents/skills/building-emdash-site/references/configuration.md
+++ b/marketing/.agents/skills/building-emdash-site/references/configuration.md
@@ -211,8 +211,8 @@ For Cloudflare, replace `@astrojs/node` with `@astrojs/cloudflare` and add `@emd
## Dev Server
```bash
-npx emdash dev # Start dev server (runs migrations, applies seed)
-npx emdash dev --types # Start and generate types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Refresh types from the running site
```
-The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
diff --git a/marketing/.agents/skills/building-emdash-site/references/site-features.md b/marketing/.agents/skills/building-emdash-site/references/site-features.md
index cbfbe83..f957f95 100644
--- a/marketing/.agents/skills/building-emdash-site/references/site-features.md
+++ b/marketing/.agents/skills/building-emdash-site/references/site-features.md
@@ -288,7 +288,7 @@ Built-in comments system:
```astro
---
-import { Comments, CommentForm } from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
---
diff --git a/marketing/.agents/skills/creating-plugins/references/block-kit.md b/marketing/.agents/skills/creating-plugins/references/block-kit.md
index 8f3f3ce..a73441c 100644
--- a/marketing/.agents/skills/creating-plugins/references/block-kit.md
+++ b/marketing/.agents/skills/creating-plugins/references/block-kit.md
@@ -105,7 +105,7 @@ routes: {
{
"type": "section",
"text": "Configure your plugin settings below.",
- "accessory": { "type": "button", "text": "Refresh", "action_id": "refresh" }
+ "accessory": { "type": "button", "label": "Refresh", "action_id": "refresh" }
}
```
@@ -132,13 +132,17 @@ routes: {
```json
{
"type": "stats",
- "stats": [
- { "label": "Total", "value": "1,234", "trend": "+12%", "trend_direction": "up" },
+ "items": [
+ { "label": "Total", "value": "1,234", "trend": "up", "description": "+12% vs last week" },
{ "label": "Active", "value": "567" }
]
}
```
+- `items` — the array key is `items`, not `stats`
+- `trend` — `"up"`, `"down"`, or `"neutral"`; renders an arrow icon next to the value
+- `description` — secondary line beneath the value, for context such as "+12% vs last week"
+
### Table
```json
@@ -149,18 +153,24 @@ routes: {
{ "key": "status", "label": "Status" },
{ "key": "date", "label": "Date" }
],
- "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }]
+ "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }],
+ "page_action_id": "browse_items",
+ "empty_text": "No items yet."
}
```
+- `page_action_id` — required. The admin sends it as the `block_action` id when the user sorts a column or pages through results.
+- `empty_text` — shown in place of the table when `rows` is empty
+- `next_cursor` — set it to render a "Load more" control
+
### Actions
```json
{
"type": "actions",
"elements": [
- { "type": "button", "text": "Save", "action_id": "save", "style": "primary" },
- { "type": "button", "text": "Cancel", "action_id": "cancel" }
+ { "type": "button", "label": "Save", "action_id": "save", "style": "primary" },
+ { "type": "button", "label": "Cancel", "action_id": "cancel" }
]
}
```
@@ -196,12 +206,20 @@ routes: {
{
"type": "columns",
"columns": [
- { "blocks": [{ "type": "header", "text": "Left" }] },
- { "blocks": [{ "type": "header", "text": "Right" }] }
+ [
+ { "type": "header", "text": "Usage" },
+ { "type": "meter", "label": "Storage used", "value": 65 }
+ ],
+ [
+ { "type": "header", "text": "Activity" },
+ { "type": "context", "text": "Last sync 2 hours ago" }
+ ]
]
}
```
+- `columns` — 2 or 3 columns, each one an array of blocks
+
### Chart (Timeseries)
```json
@@ -395,7 +413,7 @@ return {
```json
{
"type": "button",
- "text": "Delete All",
+ "label": "Delete All",
"action_id": "delete_all",
"style": "danger",
"confirm": {
diff --git a/marketing/.agents/skills/emdash-cli/SKILL.md b/marketing/.agents/skills/emdash-cli/SKILL.md
index 5386e09..4fdc08f 100644
--- a/marketing/.agents/skills/emdash-cli/SKILL.md
+++ b/marketing/.agents/skills/emdash-cli/SKILL.md
@@ -70,14 +70,11 @@ npx emdash login --url https://example.com -H "X-API-Key: secret123"
### Database Setup
-Migrations and seed application happen automatically inside the runtime — there's no separate init/seed step. Just start the dev server (or deploy) and the first request runs pending migrations and applies the bundled seed if the database is empty.
+For normal site startup, use the project's package script. The first request runs pending migrations and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts.
```bash
-# Start dev server (runs migrations, applies seed on empty DB, starts Astro)
-npx emdash dev
-
-# Start dev server and generate types from remote
-npx emdash dev --types
+# Start the site with its package script
+pnpm dev
# Export an existing database as a seed file
# (the runtime auto-discovers .emdash/seed.json on first boot;
diff --git a/marketing/AGENTS.md b/marketing/AGENTS.md
index 6bea048..ea2b278 100644
--- a/marketing/AGENTS.md
+++ b/marketing/AGENTS.md
@@ -3,8 +3,8 @@ This is an EmDash site -- a CMS built on Astro with a full admin UI.
## Commands
```bash
-npx emdash dev # Start dev server (runs migrations, seeds, generates types)
-npx emdash types # Regenerate TypeScript types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Regenerate TypeScript types from a running site
```
The admin UI is at `http://localhost:4321/_emdash/admin`.
diff --git a/marketing/package.json b/marketing/package.json
index 7592a97..b3944cc 100644
--- a/marketing/package.json
+++ b/marketing/package.json
@@ -20,7 +20,7 @@
"astro": "^7.0.0",
"astro-iconset": "^0.0.4",
"better-sqlite3": "^12.8.0",
- "emdash": "^0.30.0",
+ "emdash": "^0.34.0",
"react": "19.2.4",
"react-dom": "19.2.4"
},
diff --git a/portfolio-cloudflare/.agents/skills/building-emdash-site/SKILL.md b/portfolio-cloudflare/.agents/skills/building-emdash-site/SKILL.md
index ad3facd..3fe607e 100644
--- a/portfolio-cloudflare/.agents/skills/building-emdash-site/SKILL.md
+++ b/portfolio-cloudflare/.agents/skills/building-emdash-site/SKILL.md
@@ -64,10 +64,10 @@ Write `seed/seed.json` with collections, fields, taxonomies, menus, widgets, and
### 6. Run and verify
```bash
-npx emdash dev # Start dev server (runs migrations + seeds, and generates types)
+pnpm dev # Start the Astro dev server
```
-The admin UI is at `http://localhost:4321/_emdash/admin`.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`.
## Quick API Cheat Sheet
@@ -102,13 +102,12 @@ import { getByline, getBylineBySlug } from "emdash";
import {
PortableText,
Image,
- Comments,
- CommentForm,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
diff --git a/portfolio-cloudflare/.agents/skills/building-emdash-site/references/configuration.md b/portfolio-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
index a1eb1ec..93fc783 100644
--- a/portfolio-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
+++ b/portfolio-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
@@ -211,8 +211,8 @@ For Cloudflare, replace `@astrojs/node` with `@astrojs/cloudflare` and add `@emd
## Dev Server
```bash
-npx emdash dev # Start dev server (runs migrations, applies seed)
-npx emdash dev --types # Start and generate types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Refresh types from the running site
```
-The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
diff --git a/portfolio-cloudflare/.agents/skills/building-emdash-site/references/site-features.md b/portfolio-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
index cbfbe83..f957f95 100644
--- a/portfolio-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
+++ b/portfolio-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
@@ -288,7 +288,7 @@ Built-in comments system:
```astro
---
-import { Comments, CommentForm } from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
---
diff --git a/portfolio-cloudflare/.agents/skills/creating-plugins/references/block-kit.md b/portfolio-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
index 8f3f3ce..a73441c 100644
--- a/portfolio-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
+++ b/portfolio-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
@@ -105,7 +105,7 @@ routes: {
{
"type": "section",
"text": "Configure your plugin settings below.",
- "accessory": { "type": "button", "text": "Refresh", "action_id": "refresh" }
+ "accessory": { "type": "button", "label": "Refresh", "action_id": "refresh" }
}
```
@@ -132,13 +132,17 @@ routes: {
```json
{
"type": "stats",
- "stats": [
- { "label": "Total", "value": "1,234", "trend": "+12%", "trend_direction": "up" },
+ "items": [
+ { "label": "Total", "value": "1,234", "trend": "up", "description": "+12% vs last week" },
{ "label": "Active", "value": "567" }
]
}
```
+- `items` — the array key is `items`, not `stats`
+- `trend` — `"up"`, `"down"`, or `"neutral"`; renders an arrow icon next to the value
+- `description` — secondary line beneath the value, for context such as "+12% vs last week"
+
### Table
```json
@@ -149,18 +153,24 @@ routes: {
{ "key": "status", "label": "Status" },
{ "key": "date", "label": "Date" }
],
- "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }]
+ "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }],
+ "page_action_id": "browse_items",
+ "empty_text": "No items yet."
}
```
+- `page_action_id` — required. The admin sends it as the `block_action` id when the user sorts a column or pages through results.
+- `empty_text` — shown in place of the table when `rows` is empty
+- `next_cursor` — set it to render a "Load more" control
+
### Actions
```json
{
"type": "actions",
"elements": [
- { "type": "button", "text": "Save", "action_id": "save", "style": "primary" },
- { "type": "button", "text": "Cancel", "action_id": "cancel" }
+ { "type": "button", "label": "Save", "action_id": "save", "style": "primary" },
+ { "type": "button", "label": "Cancel", "action_id": "cancel" }
]
}
```
@@ -196,12 +206,20 @@ routes: {
{
"type": "columns",
"columns": [
- { "blocks": [{ "type": "header", "text": "Left" }] },
- { "blocks": [{ "type": "header", "text": "Right" }] }
+ [
+ { "type": "header", "text": "Usage" },
+ { "type": "meter", "label": "Storage used", "value": 65 }
+ ],
+ [
+ { "type": "header", "text": "Activity" },
+ { "type": "context", "text": "Last sync 2 hours ago" }
+ ]
]
}
```
+- `columns` — 2 or 3 columns, each one an array of blocks
+
### Chart (Timeseries)
```json
@@ -395,7 +413,7 @@ return {
```json
{
"type": "button",
- "text": "Delete All",
+ "label": "Delete All",
"action_id": "delete_all",
"style": "danger",
"confirm": {
diff --git a/portfolio-cloudflare/.agents/skills/emdash-cli/SKILL.md b/portfolio-cloudflare/.agents/skills/emdash-cli/SKILL.md
index 5386e09..4fdc08f 100644
--- a/portfolio-cloudflare/.agents/skills/emdash-cli/SKILL.md
+++ b/portfolio-cloudflare/.agents/skills/emdash-cli/SKILL.md
@@ -70,14 +70,11 @@ npx emdash login --url https://example.com -H "X-API-Key: secret123"
### Database Setup
-Migrations and seed application happen automatically inside the runtime — there's no separate init/seed step. Just start the dev server (or deploy) and the first request runs pending migrations and applies the bundled seed if the database is empty.
+For normal site startup, use the project's package script. The first request runs pending migrations and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts.
```bash
-# Start dev server (runs migrations, applies seed on empty DB, starts Astro)
-npx emdash dev
-
-# Start dev server and generate types from remote
-npx emdash dev --types
+# Start the site with its package script
+pnpm dev
# Export an existing database as a seed file
# (the runtime auto-discovers .emdash/seed.json on first boot;
diff --git a/portfolio-cloudflare/AGENTS.md b/portfolio-cloudflare/AGENTS.md
index 51f960f..4aa7f45 100644
--- a/portfolio-cloudflare/AGENTS.md
+++ b/portfolio-cloudflare/AGENTS.md
@@ -3,8 +3,8 @@ This is an EmDash site -- a CMS built on Astro with a full admin UI.
## Commands
```bash
-npx emdash dev # Start dev server (runs migrations, seeds, generates types)
-npx emdash types # Regenerate TypeScript types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Regenerate TypeScript types from a running site
```
The admin UI is at `http://localhost:4321/_emdash/admin`.
diff --git a/portfolio-cloudflare/package.json b/portfolio-cloudflare/package.json
index 9ed4351..26f7707 100644
--- a/portfolio-cloudflare/package.json
+++ b/portfolio-cloudflare/package.json
@@ -16,16 +16,16 @@
"dependencies": {
"@astrojs/cloudflare": "^14.0.0",
"@astrojs/react": "^6.0.0",
- "@emdash-cms/cloudflare": "^0.30.0",
+ "@emdash-cms/cloudflare": "^0.34.0",
"astro": "^7.0.0",
- "emdash": "^0.30.0",
+ "emdash": "^0.34.0",
"react": "19.2.4",
"react-dom": "19.2.4"
},
"devDependencies": {
"@astrojs/check": "^0.9.7",
"@cloudflare/workers-types": "^4.20260305.1",
- "wrangler": "^4.99.0"
+ "wrangler": "^4.123.0"
},
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
}
diff --git a/portfolio-cloudflare/src/pages/work/index.astro b/portfolio-cloudflare/src/pages/work/index.astro
index b7b804b..21c1b7d 100644
--- a/portfolio-cloudflare/src/pages/work/index.astro
+++ b/portfolio-cloudflare/src/pages/work/index.astro
@@ -20,7 +20,8 @@ const [{ entries: sortedProjects, cacheHint }, tags] = await Promise.all([
orderBy: { published_at: "desc" },
...(activeTag ? { where: { tag: activeTag } } : {}),
}),
- getTaxonomyTerms("tag"),
+ // The filter pills show labels only, so skip the visible-entry counts.
+ getTaxonomyTerms("tag", { includeCounts: false }),
]);
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
diff --git a/portfolio-cloudflare/src/worker.ts b/portfolio-cloudflare/src/worker.ts
index df373a0..75fe3e2 100644
--- a/portfolio-cloudflare/src/worker.ts
+++ b/portfolio-cloudflare/src/worker.ts
@@ -1,4 +1,8 @@
-// Worker entry: Astro's fetch handler plus EmDash's scheduled() handler, which
-// the Cron Trigger in wrangler.jsonc drives. PluginBridge is the sandbox
-// Durable Object, re-exported here so its binding resolves.
-export { default, PluginBridge } from "@emdash-cms/cloudflare/worker";
+import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
+
+export { PluginBridge };
+
+export default {
+ ...handler,
+ scheduled: createScheduledHandler(),
+};
diff --git a/portfolio-cloudflare/wrangler.jsonc b/portfolio-cloudflare/wrangler.jsonc
index 62ee430..792c1fd 100644
--- a/portfolio-cloudflare/wrangler.jsonc
+++ b/portfolio-cloudflare/wrangler.jsonc
@@ -22,8 +22,8 @@
"binding": "LOADER",
},
],
- // Drives scheduled publishing, plugin cron, and maintenance (see src/worker.ts)
+ // General maintenance plus the bounded Media Usage lane (see src/worker.ts)
"triggers": {
- "crons": ["* * * * *"],
+ "crons": ["* * * * *", "*/2 * * * *"],
},
}
diff --git a/portfolio/.agents/skills/building-emdash-site/SKILL.md b/portfolio/.agents/skills/building-emdash-site/SKILL.md
index ad3facd..3fe607e 100644
--- a/portfolio/.agents/skills/building-emdash-site/SKILL.md
+++ b/portfolio/.agents/skills/building-emdash-site/SKILL.md
@@ -64,10 +64,10 @@ Write `seed/seed.json` with collections, fields, taxonomies, menus, widgets, and
### 6. Run and verify
```bash
-npx emdash dev # Start dev server (runs migrations + seeds, and generates types)
+pnpm dev # Start the Astro dev server
```
-The admin UI is at `http://localhost:4321/_emdash/admin`.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`.
## Quick API Cheat Sheet
@@ -102,13 +102,12 @@ import { getByline, getBylineBySlug } from "emdash";
import {
PortableText,
Image,
- Comments,
- CommentForm,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
diff --git a/portfolio/.agents/skills/building-emdash-site/references/configuration.md b/portfolio/.agents/skills/building-emdash-site/references/configuration.md
index a1eb1ec..93fc783 100644
--- a/portfolio/.agents/skills/building-emdash-site/references/configuration.md
+++ b/portfolio/.agents/skills/building-emdash-site/references/configuration.md
@@ -211,8 +211,8 @@ For Cloudflare, replace `@astrojs/node` with `@astrojs/cloudflare` and add `@emd
## Dev Server
```bash
-npx emdash dev # Start dev server (runs migrations, applies seed)
-npx emdash dev --types # Start and generate types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Refresh types from the running site
```
-The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
diff --git a/portfolio/.agents/skills/building-emdash-site/references/site-features.md b/portfolio/.agents/skills/building-emdash-site/references/site-features.md
index cbfbe83..f957f95 100644
--- a/portfolio/.agents/skills/building-emdash-site/references/site-features.md
+++ b/portfolio/.agents/skills/building-emdash-site/references/site-features.md
@@ -288,7 +288,7 @@ Built-in comments system:
```astro
---
-import { Comments, CommentForm } from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
---
diff --git a/portfolio/.agents/skills/creating-plugins/references/block-kit.md b/portfolio/.agents/skills/creating-plugins/references/block-kit.md
index 8f3f3ce..a73441c 100644
--- a/portfolio/.agents/skills/creating-plugins/references/block-kit.md
+++ b/portfolio/.agents/skills/creating-plugins/references/block-kit.md
@@ -105,7 +105,7 @@ routes: {
{
"type": "section",
"text": "Configure your plugin settings below.",
- "accessory": { "type": "button", "text": "Refresh", "action_id": "refresh" }
+ "accessory": { "type": "button", "label": "Refresh", "action_id": "refresh" }
}
```
@@ -132,13 +132,17 @@ routes: {
```json
{
"type": "stats",
- "stats": [
- { "label": "Total", "value": "1,234", "trend": "+12%", "trend_direction": "up" },
+ "items": [
+ { "label": "Total", "value": "1,234", "trend": "up", "description": "+12% vs last week" },
{ "label": "Active", "value": "567" }
]
}
```
+- `items` — the array key is `items`, not `stats`
+- `trend` — `"up"`, `"down"`, or `"neutral"`; renders an arrow icon next to the value
+- `description` — secondary line beneath the value, for context such as "+12% vs last week"
+
### Table
```json
@@ -149,18 +153,24 @@ routes: {
{ "key": "status", "label": "Status" },
{ "key": "date", "label": "Date" }
],
- "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }]
+ "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }],
+ "page_action_id": "browse_items",
+ "empty_text": "No items yet."
}
```
+- `page_action_id` — required. The admin sends it as the `block_action` id when the user sorts a column or pages through results.
+- `empty_text` — shown in place of the table when `rows` is empty
+- `next_cursor` — set it to render a "Load more" control
+
### Actions
```json
{
"type": "actions",
"elements": [
- { "type": "button", "text": "Save", "action_id": "save", "style": "primary" },
- { "type": "button", "text": "Cancel", "action_id": "cancel" }
+ { "type": "button", "label": "Save", "action_id": "save", "style": "primary" },
+ { "type": "button", "label": "Cancel", "action_id": "cancel" }
]
}
```
@@ -196,12 +206,20 @@ routes: {
{
"type": "columns",
"columns": [
- { "blocks": [{ "type": "header", "text": "Left" }] },
- { "blocks": [{ "type": "header", "text": "Right" }] }
+ [
+ { "type": "header", "text": "Usage" },
+ { "type": "meter", "label": "Storage used", "value": 65 }
+ ],
+ [
+ { "type": "header", "text": "Activity" },
+ { "type": "context", "text": "Last sync 2 hours ago" }
+ ]
]
}
```
+- `columns` — 2 or 3 columns, each one an array of blocks
+
### Chart (Timeseries)
```json
@@ -395,7 +413,7 @@ return {
```json
{
"type": "button",
- "text": "Delete All",
+ "label": "Delete All",
"action_id": "delete_all",
"style": "danger",
"confirm": {
diff --git a/portfolio/.agents/skills/emdash-cli/SKILL.md b/portfolio/.agents/skills/emdash-cli/SKILL.md
index 5386e09..4fdc08f 100644
--- a/portfolio/.agents/skills/emdash-cli/SKILL.md
+++ b/portfolio/.agents/skills/emdash-cli/SKILL.md
@@ -70,14 +70,11 @@ npx emdash login --url https://example.com -H "X-API-Key: secret123"
### Database Setup
-Migrations and seed application happen automatically inside the runtime — there's no separate init/seed step. Just start the dev server (or deploy) and the first request runs pending migrations and applies the bundled seed if the database is empty.
+For normal site startup, use the project's package script. The first request runs pending migrations and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts.
```bash
-# Start dev server (runs migrations, applies seed on empty DB, starts Astro)
-npx emdash dev
-
-# Start dev server and generate types from remote
-npx emdash dev --types
+# Start the site with its package script
+pnpm dev
# Export an existing database as a seed file
# (the runtime auto-discovers .emdash/seed.json on first boot;
diff --git a/portfolio/AGENTS.md b/portfolio/AGENTS.md
index 51f960f..4aa7f45 100644
--- a/portfolio/AGENTS.md
+++ b/portfolio/AGENTS.md
@@ -3,8 +3,8 @@ This is an EmDash site -- a CMS built on Astro with a full admin UI.
## Commands
```bash
-npx emdash dev # Start dev server (runs migrations, seeds, generates types)
-npx emdash types # Regenerate TypeScript types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Regenerate TypeScript types from a running site
```
The admin UI is at `http://localhost:4321/_emdash/admin`.
diff --git a/portfolio/package.json b/portfolio/package.json
index aae0abf..e223712 100644
--- a/portfolio/package.json
+++ b/portfolio/package.json
@@ -18,7 +18,7 @@
"@astrojs/react": "^6.0.0",
"astro": "^7.0.0",
"better-sqlite3": "^12.8.0",
- "emdash": "^0.30.0",
+ "emdash": "^0.34.0",
"react": "19.2.4",
"react-dom": "19.2.4"
},
diff --git a/portfolio/src/pages/work/index.astro b/portfolio/src/pages/work/index.astro
index b7b804b..21c1b7d 100644
--- a/portfolio/src/pages/work/index.astro
+++ b/portfolio/src/pages/work/index.astro
@@ -20,7 +20,8 @@ const [{ entries: sortedProjects, cacheHint }, tags] = await Promise.all([
orderBy: { published_at: "desc" },
...(activeTag ? { where: { tag: activeTag } } : {}),
}),
- getTaxonomyTerms("tag"),
+ // The filter pills show labels only, so skip the visible-entry counts.
+ getTaxonomyTerms("tag", { includeCounts: false }),
]);
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
diff --git a/starter-cloudflare/.agents/skills/building-emdash-site/SKILL.md b/starter-cloudflare/.agents/skills/building-emdash-site/SKILL.md
index ad3facd..3fe607e 100644
--- a/starter-cloudflare/.agents/skills/building-emdash-site/SKILL.md
+++ b/starter-cloudflare/.agents/skills/building-emdash-site/SKILL.md
@@ -64,10 +64,10 @@ Write `seed/seed.json` with collections, fields, taxonomies, menus, widgets, and
### 6. Run and verify
```bash
-npx emdash dev # Start dev server (runs migrations + seeds, and generates types)
+pnpm dev # Start the Astro dev server
```
-The admin UI is at `http://localhost:4321/_emdash/admin`.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`.
## Quick API Cheat Sheet
@@ -102,13 +102,12 @@ import { getByline, getBylineBySlug } from "emdash";
import {
PortableText,
Image,
- Comments,
- CommentForm,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
diff --git a/starter-cloudflare/.agents/skills/building-emdash-site/references/configuration.md b/starter-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
index a1eb1ec..93fc783 100644
--- a/starter-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
+++ b/starter-cloudflare/.agents/skills/building-emdash-site/references/configuration.md
@@ -211,8 +211,8 @@ For Cloudflare, replace `@astrojs/node` with `@astrojs/cloudflare` and add `@emd
## Dev Server
```bash
-npx emdash dev # Start dev server (runs migrations, applies seed)
-npx emdash dev --types # Start and generate types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Refresh types from the running site
```
-The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
diff --git a/starter-cloudflare/.agents/skills/building-emdash-site/references/site-features.md b/starter-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
index cbfbe83..f957f95 100644
--- a/starter-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
+++ b/starter-cloudflare/.agents/skills/building-emdash-site/references/site-features.md
@@ -288,7 +288,7 @@ Built-in comments system:
```astro
---
-import { Comments, CommentForm } from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
---
diff --git a/starter-cloudflare/.agents/skills/creating-plugins/references/block-kit.md b/starter-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
index 8f3f3ce..a73441c 100644
--- a/starter-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
+++ b/starter-cloudflare/.agents/skills/creating-plugins/references/block-kit.md
@@ -105,7 +105,7 @@ routes: {
{
"type": "section",
"text": "Configure your plugin settings below.",
- "accessory": { "type": "button", "text": "Refresh", "action_id": "refresh" }
+ "accessory": { "type": "button", "label": "Refresh", "action_id": "refresh" }
}
```
@@ -132,13 +132,17 @@ routes: {
```json
{
"type": "stats",
- "stats": [
- { "label": "Total", "value": "1,234", "trend": "+12%", "trend_direction": "up" },
+ "items": [
+ { "label": "Total", "value": "1,234", "trend": "up", "description": "+12% vs last week" },
{ "label": "Active", "value": "567" }
]
}
```
+- `items` — the array key is `items`, not `stats`
+- `trend` — `"up"`, `"down"`, or `"neutral"`; renders an arrow icon next to the value
+- `description` — secondary line beneath the value, for context such as "+12% vs last week"
+
### Table
```json
@@ -149,18 +153,24 @@ routes: {
{ "key": "status", "label": "Status" },
{ "key": "date", "label": "Date" }
],
- "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }]
+ "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }],
+ "page_action_id": "browse_items",
+ "empty_text": "No items yet."
}
```
+- `page_action_id` — required. The admin sends it as the `block_action` id when the user sorts a column or pages through results.
+- `empty_text` — shown in place of the table when `rows` is empty
+- `next_cursor` — set it to render a "Load more" control
+
### Actions
```json
{
"type": "actions",
"elements": [
- { "type": "button", "text": "Save", "action_id": "save", "style": "primary" },
- { "type": "button", "text": "Cancel", "action_id": "cancel" }
+ { "type": "button", "label": "Save", "action_id": "save", "style": "primary" },
+ { "type": "button", "label": "Cancel", "action_id": "cancel" }
]
}
```
@@ -196,12 +206,20 @@ routes: {
{
"type": "columns",
"columns": [
- { "blocks": [{ "type": "header", "text": "Left" }] },
- { "blocks": [{ "type": "header", "text": "Right" }] }
+ [
+ { "type": "header", "text": "Usage" },
+ { "type": "meter", "label": "Storage used", "value": 65 }
+ ],
+ [
+ { "type": "header", "text": "Activity" },
+ { "type": "context", "text": "Last sync 2 hours ago" }
+ ]
]
}
```
+- `columns` — 2 or 3 columns, each one an array of blocks
+
### Chart (Timeseries)
```json
@@ -395,7 +413,7 @@ return {
```json
{
"type": "button",
- "text": "Delete All",
+ "label": "Delete All",
"action_id": "delete_all",
"style": "danger",
"confirm": {
diff --git a/starter-cloudflare/.agents/skills/emdash-cli/SKILL.md b/starter-cloudflare/.agents/skills/emdash-cli/SKILL.md
index 5386e09..4fdc08f 100644
--- a/starter-cloudflare/.agents/skills/emdash-cli/SKILL.md
+++ b/starter-cloudflare/.agents/skills/emdash-cli/SKILL.md
@@ -70,14 +70,11 @@ npx emdash login --url https://example.com -H "X-API-Key: secret123"
### Database Setup
-Migrations and seed application happen automatically inside the runtime — there's no separate init/seed step. Just start the dev server (or deploy) and the first request runs pending migrations and applies the bundled seed if the database is empty.
+For normal site startup, use the project's package script. The first request runs pending migrations and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts.
```bash
-# Start dev server (runs migrations, applies seed on empty DB, starts Astro)
-npx emdash dev
-
-# Start dev server and generate types from remote
-npx emdash dev --types
+# Start the site with its package script
+pnpm dev
# Export an existing database as a seed file
# (the runtime auto-discovers .emdash/seed.json on first boot;
diff --git a/starter-cloudflare/AGENTS.md b/starter-cloudflare/AGENTS.md
index e97ff84..eea149e 100644
--- a/starter-cloudflare/AGENTS.md
+++ b/starter-cloudflare/AGENTS.md
@@ -3,8 +3,8 @@ This is an EmDash site -- a CMS built on Astro with a full admin UI.
## Commands
```bash
-npx emdash dev # Start dev server (runs migrations, seeds, generates types)
-npx emdash types # Regenerate TypeScript types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Regenerate TypeScript types from a running site
```
The admin UI is at `http://localhost:4321/_emdash/admin`.
diff --git a/starter-cloudflare/package.json b/starter-cloudflare/package.json
index 34c6b13..b1c01c5 100644
--- a/starter-cloudflare/package.json
+++ b/starter-cloudflare/package.json
@@ -16,16 +16,16 @@
"dependencies": {
"@astrojs/cloudflare": "^14.0.0",
"@astrojs/react": "^6.0.0",
- "@emdash-cms/cloudflare": "^0.30.0",
+ "@emdash-cms/cloudflare": "^0.34.0",
"astro": "^7.0.0",
- "emdash": "^0.30.0",
+ "emdash": "^0.34.0",
"react": "19.2.4",
"react-dom": "19.2.4"
},
"devDependencies": {
"@astrojs/check": "^0.9.7",
"@cloudflare/workers-types": "^4.20260305.1",
- "wrangler": "^4.99.0"
+ "wrangler": "^4.123.0"
},
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
}
diff --git a/starter-cloudflare/src/worker.ts b/starter-cloudflare/src/worker.ts
index df373a0..75fe3e2 100644
--- a/starter-cloudflare/src/worker.ts
+++ b/starter-cloudflare/src/worker.ts
@@ -1,4 +1,8 @@
-// Worker entry: Astro's fetch handler plus EmDash's scheduled() handler, which
-// the Cron Trigger in wrangler.jsonc drives. PluginBridge is the sandbox
-// Durable Object, re-exported here so its binding resolves.
-export { default, PluginBridge } from "@emdash-cms/cloudflare/worker";
+import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
+
+export { PluginBridge };
+
+export default {
+ ...handler,
+ scheduled: createScheduledHandler(),
+};
diff --git a/starter-cloudflare/wrangler.jsonc b/starter-cloudflare/wrangler.jsonc
index f6baeec..23632b0 100644
--- a/starter-cloudflare/wrangler.jsonc
+++ b/starter-cloudflare/wrangler.jsonc
@@ -22,8 +22,8 @@
"binding": "LOADER",
},
],
- // Drives scheduled publishing, plugin cron, and maintenance (see src/worker.ts)
+ // General maintenance plus the bounded Media Usage lane (see src/worker.ts)
"triggers": {
- "crons": ["* * * * *"],
+ "crons": ["* * * * *", "*/2 * * * *"],
},
}
diff --git a/starter/.agents/skills/building-emdash-site/SKILL.md b/starter/.agents/skills/building-emdash-site/SKILL.md
index ad3facd..3fe607e 100644
--- a/starter/.agents/skills/building-emdash-site/SKILL.md
+++ b/starter/.agents/skills/building-emdash-site/SKILL.md
@@ -64,10 +64,10 @@ Write `seed/seed.json` with collections, fields, taxonomies, menus, widgets, and
### 6. Run and verify
```bash
-npx emdash dev # Start dev server (runs migrations + seeds, and generates types)
+pnpm dev # Start the Astro dev server
```
-The admin UI is at `http://localhost:4321/_emdash/admin`.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`.
## Quick API Cheat Sheet
@@ -102,13 +102,12 @@ import { getByline, getBylineBySlug } from "emdash";
import {
PortableText,
Image,
- Comments,
- CommentForm,
WidgetArea,
EmDashHead,
EmDashBodyStart,
EmDashBodyEnd,
} from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
import LiveSearch from "emdash/ui/search";
// Page context (for plugin contributions)
diff --git a/starter/.agents/skills/building-emdash-site/references/configuration.md b/starter/.agents/skills/building-emdash-site/references/configuration.md
index a1eb1ec..93fc783 100644
--- a/starter/.agents/skills/building-emdash-site/references/configuration.md
+++ b/starter/.agents/skills/building-emdash-site/references/configuration.md
@@ -211,8 +211,8 @@ For Cloudflare, replace `@astrojs/node` with `@astrojs/cloudflare` and add `@emd
## Dev Server
```bash
-npx emdash dev # Start dev server (runs migrations, applies seed)
-npx emdash dev --types # Start and generate types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Refresh types from the running site
```
-The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
+The runtime runs pending migrations on the first request and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts. The admin UI is at `http://localhost:4321/_emdash/admin`. On first run, you'll go through setup to create an admin account.
diff --git a/starter/.agents/skills/building-emdash-site/references/site-features.md b/starter/.agents/skills/building-emdash-site/references/site-features.md
index cbfbe83..f957f95 100644
--- a/starter/.agents/skills/building-emdash-site/references/site-features.md
+++ b/starter/.agents/skills/building-emdash-site/references/site-features.md
@@ -288,7 +288,7 @@ Built-in comments system:
```astro
---
-import { Comments, CommentForm } from "emdash/ui";
+import { Comments, CommentForm } from "emdash/ui/comments";
---
diff --git a/starter/.agents/skills/creating-plugins/references/block-kit.md b/starter/.agents/skills/creating-plugins/references/block-kit.md
index 8f3f3ce..a73441c 100644
--- a/starter/.agents/skills/creating-plugins/references/block-kit.md
+++ b/starter/.agents/skills/creating-plugins/references/block-kit.md
@@ -105,7 +105,7 @@ routes: {
{
"type": "section",
"text": "Configure your plugin settings below.",
- "accessory": { "type": "button", "text": "Refresh", "action_id": "refresh" }
+ "accessory": { "type": "button", "label": "Refresh", "action_id": "refresh" }
}
```
@@ -132,13 +132,17 @@ routes: {
```json
{
"type": "stats",
- "stats": [
- { "label": "Total", "value": "1,234", "trend": "+12%", "trend_direction": "up" },
+ "items": [
+ { "label": "Total", "value": "1,234", "trend": "up", "description": "+12% vs last week" },
{ "label": "Active", "value": "567" }
]
}
```
+- `items` — the array key is `items`, not `stats`
+- `trend` — `"up"`, `"down"`, or `"neutral"`; renders an arrow icon next to the value
+- `description` — secondary line beneath the value, for context such as "+12% vs last week"
+
### Table
```json
@@ -149,18 +153,24 @@ routes: {
{ "key": "status", "label": "Status" },
{ "key": "date", "label": "Date" }
],
- "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }]
+ "rows": [{ "name": "Item 1", "status": "Active", "date": "2025-01-01" }],
+ "page_action_id": "browse_items",
+ "empty_text": "No items yet."
}
```
+- `page_action_id` — required. The admin sends it as the `block_action` id when the user sorts a column or pages through results.
+- `empty_text` — shown in place of the table when `rows` is empty
+- `next_cursor` — set it to render a "Load more" control
+
### Actions
```json
{
"type": "actions",
"elements": [
- { "type": "button", "text": "Save", "action_id": "save", "style": "primary" },
- { "type": "button", "text": "Cancel", "action_id": "cancel" }
+ { "type": "button", "label": "Save", "action_id": "save", "style": "primary" },
+ { "type": "button", "label": "Cancel", "action_id": "cancel" }
]
}
```
@@ -196,12 +206,20 @@ routes: {
{
"type": "columns",
"columns": [
- { "blocks": [{ "type": "header", "text": "Left" }] },
- { "blocks": [{ "type": "header", "text": "Right" }] }
+ [
+ { "type": "header", "text": "Usage" },
+ { "type": "meter", "label": "Storage used", "value": 65 }
+ ],
+ [
+ { "type": "header", "text": "Activity" },
+ { "type": "context", "text": "Last sync 2 hours ago" }
+ ]
]
}
```
+- `columns` — 2 or 3 columns, each one an array of blocks
+
### Chart (Timeseries)
```json
@@ -395,7 +413,7 @@ return {
```json
{
"type": "button",
- "text": "Delete All",
+ "label": "Delete All",
"action_id": "delete_all",
"style": "danger",
"confirm": {
diff --git a/starter/.agents/skills/emdash-cli/SKILL.md b/starter/.agents/skills/emdash-cli/SKILL.md
index 5386e09..4fdc08f 100644
--- a/starter/.agents/skills/emdash-cli/SKILL.md
+++ b/starter/.agents/skills/emdash-cli/SKILL.md
@@ -70,14 +70,11 @@ npx emdash login --url https://example.com -H "X-API-Key: secret123"
### Database Setup
-Migrations and seed application happen automatically inside the runtime — there's no separate init/seed step. Just start the dev server (or deploy) and the first request runs pending migrations and applies the bundled seed if the database is empty.
+For normal site startup, use the project's package script. The first request runs pending migrations and applies the bundled seed when the database is empty and setup has not been completed. The Astro integration generates `emdash-env.d.ts` when the server starts.
```bash
-# Start dev server (runs migrations, applies seed on empty DB, starts Astro)
-npx emdash dev
-
-# Start dev server and generate types from remote
-npx emdash dev --types
+# Start the site with its package script
+pnpm dev
# Export an existing database as a seed file
# (the runtime auto-discovers .emdash/seed.json on first boot;
diff --git a/starter/AGENTS.md b/starter/AGENTS.md
index e97ff84..eea149e 100644
--- a/starter/AGENTS.md
+++ b/starter/AGENTS.md
@@ -3,8 +3,8 @@ This is an EmDash site -- a CMS built on Astro with a full admin UI.
## Commands
```bash
-npx emdash dev # Start dev server (runs migrations, seeds, generates types)
-npx emdash types # Regenerate TypeScript types from schema
+pnpm dev # Start the Astro dev server
+npx emdash types # Regenerate TypeScript types from a running site
```
The admin UI is at `http://localhost:4321/_emdash/admin`.
diff --git a/starter/package.json b/starter/package.json
index 3781260..6a38404 100644
--- a/starter/package.json
+++ b/starter/package.json
@@ -18,7 +18,7 @@
"@astrojs/react": "^6.0.0",
"astro": "^7.0.0",
"better-sqlite3": "^12.8.0",
- "emdash": "^0.30.0",
+ "emdash": "^0.34.0",
"react": "19.2.4",
"react-dom": "19.2.4"
},