@getvitops/create 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,133 @@
1
+ ---
2
+ import VitopsHead from '@getvitops/astro/Head.astro';
3
+ import { getMenu, getSiteSettings } from 'emdash';
4
+ import { createPublicPageContext } from 'emdash/page';
5
+ import { EmDashHead } from 'emdash/ui';
6
+ // Design-system CSS, layout-scoped (getvitops css.inject: false): only pages
7
+ // rendered through this layout get it — keeps it out of /_emdash/admin, while
8
+ // EmDash previews (rendered through this layout) keep it.
9
+ import '../styles/tailwind.css';
10
+ // Single source of truth for the brand mark — the same file the favicon set
11
+ // is generated from. `?raw` inlines it at build time (no extra request).
12
+ import logo from '../assets/logo.svg?raw';
13
+
14
+ interface Props {
15
+ title?: string;
16
+ description?: string;
17
+ image?: string;
18
+ }
19
+
20
+ const { title, description, image } = Astro.props;
21
+ const settings = await getSiteSettings();
22
+ const siteTitle = settings?.title || 'My EmDash Site';
23
+ const fullTitle = title ? `${title} — ${siteTitle}` : siteTitle;
24
+ const siteDescription = settings?.tagline || 'An EmDash website with the Vitops design system';
25
+
26
+ const pageCtx = createPublicPageContext({
27
+ Astro,
28
+ kind: 'custom',
29
+ pageType: 'website',
30
+ title: fullTitle,
31
+ pageTitle: title ?? siteTitle,
32
+ description: description || siteDescription,
33
+ canonical: new URL(Astro.url.pathname, Astro.url).href,
34
+ image,
35
+ seo: { ogImage: image },
36
+ siteName: siteTitle,
37
+ });
38
+
39
+ // Editor-managed navigation (seeded as "primary" and "footer"; edit under
40
+ // Menus in the admin). Falls back to a bare home link if the menu is deleted.
41
+ const menu = await getMenu('primary');
42
+ const navLinks = menu?.items?.length
43
+ ? menu.items.map((item) => ({ label: item.label, href: item.url ?? '/' }))
44
+ : [{ label: 'Home', href: '/' }];
45
+ const footerMenu = await getMenu('footer');
46
+ const footerLinks = (footerMenu?.items ?? []).map((item) => ({
47
+ label: item.label,
48
+ href: item.url ?? '/',
49
+ }));
50
+ const year = new Date().getFullYear();
51
+ ---
52
+
53
+ <!doctype html>
54
+ <html lang="en">
55
+ <head>
56
+ <meta charset="UTF-8" />
57
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
58
+ <title>{fullTitle}</title>
59
+ <EmDashHead page={pageCtx} />
60
+ <!-- Favicon/PWA links + the vitops web-component runtime. The runtime
61
+ progressively enhances the @getvitops/emdash editor blocks
62
+ (wc-carousel, wc-image-compare, ...) wherever Portable Text renders. -->
63
+ <VitopsHead />
64
+ </head>
65
+ <body>
66
+ <header class="navbar-sticky centered">
67
+ <div class="navbar">
68
+ <a class="brand flex items-center" href="/" aria-label={`${siteTitle} home`}>
69
+ <span class="brand-mark" set:html={logo} aria-hidden="true" />
70
+ <span class="font-heading">{siteTitle}</span>
71
+ </a>
72
+ <nav aria-label="Primary">
73
+ <ul class="nav-items justify-center">
74
+ {
75
+ navLinks.map((link) => (
76
+ <li>
77
+ <a href={link.href}>{link.label}</a>
78
+ </li>
79
+ ))
80
+ }
81
+ </ul>
82
+ </nav>
83
+ </div>
84
+ </header>
85
+
86
+ <main class="centered rhythm">
87
+ <slot />
88
+ </main>
89
+
90
+ <footer class="centered">
91
+ {
92
+ footerLinks.length > 0 && (
93
+ <nav aria-label="Footer">
94
+ <ul class="nav-items justify-center">
95
+ {footerLinks.map((link) => (
96
+ <li>
97
+ <a class="font-caption" href={link.href}>
98
+ {link.label}
99
+ </a>
100
+ </li>
101
+ ))}
102
+ </ul>
103
+ </nav>
104
+ )
105
+ }
106
+ <p class="font-caption text-neutral-muted">© {year} {siteTitle}</p>
107
+ </footer>
108
+
109
+ <style>
110
+ .brand {
111
+ gap: 0.55rem;
112
+ text-decoration: none;
113
+ color: inherit;
114
+ }
115
+
116
+ .brand-mark :global(svg) {
117
+ display: block;
118
+ inline-size: 1.75rem;
119
+ block-size: 1.75rem;
120
+ }
121
+
122
+ .navbar {
123
+ block-size: 64px;
124
+ }
125
+
126
+ footer {
127
+ margin-block-start: var(--space-2xl);
128
+ padding-block: var(--space-l);
129
+ border-block-start: 1px solid var(--neutral-border);
130
+ }
131
+ </style>
132
+ </body>
133
+ </html>
@@ -0,0 +1,12 @@
1
+ /**
2
+ * EmDash Live Content Collections.
3
+ *
4
+ * Defines the _emdash collection that serves all content types from the
5
+ * database. Query it with getEmDashCollection() / getEmDashEntry().
6
+ */
7
+ import { defineLiveCollection } from 'astro:content';
8
+ import { emdashLoader } from 'emdash/runtime';
9
+
10
+ export const collections = {
11
+ _emdash: defineLiveCollection({ loader: emdashLoader() }),
12
+ };
@@ -0,0 +1,11 @@
1
+ ---
2
+ import Layout from '../layouts/Layout.astro';
3
+ ---
4
+
5
+ <Layout title="Page not found">
6
+ <section class="rhythm">
7
+ <h1>Page not found</h1>
8
+ <p>The page you're looking for doesn't exist (or isn't published yet).</p>
9
+ <p><a href="/">Back to the home page</a></p>
10
+ </section>
11
+ </Layout>
@@ -0,0 +1,20 @@
1
+ ---
2
+ import { getEmDashEntry } from 'emdash';
3
+ import { PortableText } from 'emdash/ui';
4
+ import Layout from '../layouts/Layout.astro';
5
+
6
+ // Catch-all: any entry in the `pages` collection renders at its slug.
7
+ const { slug } = Astro.params;
8
+ const { entry: page } = slug ? await getEmDashEntry('pages', slug) : { entry: null };
9
+
10
+ if (!page) {
11
+ return Astro.rewrite('/404');
12
+ }
13
+ ---
14
+
15
+ <Layout title={page.data.title}>
16
+ <article class="rhythm">
17
+ <h1>{page.data.title}</h1>
18
+ {page.data.content && <PortableText value={page.data.content} />}
19
+ </article>
20
+ </Layout>
@@ -0,0 +1,27 @@
1
+ ---
2
+ import { getEmDashEntry } from 'emdash';
3
+ import { PortableText } from 'emdash/ui';
4
+ import Layout from '../layouts/Layout.astro';
5
+
6
+ // The seeded home page (seed/seed.json → pages/home). Edit it in the admin;
7
+ // the Vitops blocks from @getvitops/emdash render automatically — EmDash
8
+ // merges the plugin's block components into <PortableText>.
9
+ const { entry: page } = await getEmDashEntry('pages', 'home');
10
+ ---
11
+
12
+ <Layout description={page?.data.title}>
13
+ <section class="rhythm">
14
+ <h1>{page?.data.title ?? 'Welcome'}</h1>
15
+ {
16
+ page?.data.content ? (
17
+ <PortableText value={page.data.content} />
18
+ ) : (
19
+ <p>
20
+ No home page yet. Complete the setup wizard at <a href="/_emdash/admin">/_emdash/admin</a>{' '}
21
+ and choose to include the sample content, or create a page with the slug{' '}
22
+ <code>home</code> in the admin.
23
+ </p>
24
+ )
25
+ }
26
+ </section>
27
+ </Layout>
@@ -0,0 +1,4 @@
1
+ // Worker entry: Astro's fetch handler plus EmDash's scheduled() handler, which
2
+ // the Cron Trigger in wrangler.jsonc drives. PluginBridge is the sandbox
3
+ // Durable Object, re-exported here so its binding resolves.
4
+ export { default, PluginBridge } from '@emdash-cms/cloudflare/worker';
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "include": [".astro/types.d.ts", "**/*"],
4
+ "exclude": ["dist", "public"]
5
+ }
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from 'vite-plus';
2
+
3
+ export default defineConfig({
4
+ staged: {
5
+ '*': 'vp check --fix',
6
+ },
7
+ fmt: {
8
+ ignorePatterns: ['dist/**', 'node_modules/**', 'src/styles/**', 'public/vitops/**'],
9
+ },
10
+ lint: {
11
+ ignorePatterns: ['dist/**', 'node_modules/**', 'src/styles/**', 'public/vitops/**'],
12
+ options: {
13
+ typeAware: true,
14
+ typeCheck: true,
15
+ },
16
+ },
17
+ });
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "node_modules/wrangler/config-schema.json",
3
+ "name": "my-emdash-site",
4
+ "main": "./src/worker.ts",
5
+ "compatibility_date": "2026-02-24",
6
+ "compatibility_flags": ["nodejs_compat"],
7
+ "workers_dev": true,
8
+ // Custom domain: uncomment and set your apex. Cloudflare provisions the DNS
9
+ // record + TLS cert on deploy (the zone must already exist in the account).
10
+ // The dev deploy (scripts/build-dev-wrangler.mjs) automatically serves the
11
+ // dev worker from dev.<domain> when this is set.
12
+ // "routes": [{ "pattern": "example.com", "custom_domain": true }],
13
+ // No database_id / bucket ids needed up front: wrangler provisions the D1
14
+ // database and R2 bucket on the first `pnpm deploy` if they don't exist.
15
+ // EmDash runs its migrations + applies seed/seed.json on first boot.
16
+ "d1_databases": [
17
+ {
18
+ "binding": "DB",
19
+ "database_name": "my-emdash-site",
20
+ },
21
+ ],
22
+ "r2_buckets": [
23
+ {
24
+ "binding": "MEDIA",
25
+ "bucket_name": "my-emdash-site-media",
26
+ },
27
+ ],
28
+ // Drives scheduled publishing, plugin cron, and maintenance (see
29
+ // src/worker.ts). Without it, content scheduled in the admin never
30
+ // publishes on Cloudflare Workers. Local `astro dev` doesn't need it.
31
+ "triggers": {
32
+ "crons": ["* * * * *"],
33
+ },
34
+ }