@karaoke-cms/create 0.9.3 → 0.9.6

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.
package/README.md CHANGED
@@ -1,10 +1,8 @@
1
1
  # @karaoke-cms/create
2
2
 
3
- Scaffolding tool for karaoke-cms. Creates a complete new project from scratch with a single command.
3
+ Scaffolding tool for karaoke-cms. Creates a complete new project from scratch with a single command, then optionally deploys a live preview to Cloudflare Pages.
4
4
 
5
- ## Where it belongs
6
-
7
- `packages/create/` in the monorepo. Published to npm as `@karaoke-cms/create` and invoked via npm's `create` shorthand:
5
+ ## Usage
8
6
 
9
7
  ```bash
10
8
  npm create @karaoke-cms@latest
@@ -12,37 +10,26 @@ npm create @karaoke-cms@latest
12
10
  npm create @karaoke-cms@latest my-site
13
11
  ```
14
12
 
15
- This resolves to running `packages/create/src/index.js` via the `create-karaoke-cms` bin entry.
16
-
17
- ## What it does
13
+ The wizard asks for your project name, site title, URL, and description, then generates everything and installs dependencies.
18
14
 
19
- Runs an interactive CLI that prompts for project configuration, then generates a complete project directory:
15
+ At the end you're offered a one-step deploy to Cloudflare Pages (defaults to **yes** — your site can be live before you open your code editor).
20
16
 
21
- **Prompts:**
22
- 1. Project directory name
23
- 2. Site title
24
- 3. Site URL (for sitemap and OG tags)
25
- 4. Description
26
- 5. Theme (`default`, `minimal`, or `blog`)
27
- 6. Enable Pagefind search? (y/n)
28
- 7. Enable Giscus comments? (y/n) — if yes, prompts for repo, repoId, category, categoryId
17
+ ## What gets created
29
18
 
30
- **Generated files:**
31
19
  ```
32
20
  my-site/
33
- package.json # deps pinned to same version as create package
34
- astro.config.mjs # site URL, karaoke() integration
35
- karaoke.config.ts # title, description, theme, modules, vault path
21
+ package.json # @karaoke-cms/* deps pinned to current version
22
+ astro.config.mjs # karaoke() integration, site URL
23
+ karaoke.config.ts # title, description, theme, blog module
36
24
  src/
37
- content.config.ts # makeCollections() wired to vault/
38
- env.d.ts # /// <reference types="@karaoke-cms/astro/client" />
25
+ content.config.ts # makeCollections() wired to vault/
26
+ env.d.ts # TypeScript types for virtual:karaoke-cms/config
39
27
  tsconfig.json
40
28
  .gitignore
41
- .env.default # KARAOKE_VAULT=./vault/
42
- public/
43
- _redirects # Cloudflare Pages 404 config
44
- vault/ # Obsidian vault with sample blog post and doc
45
- blog/
29
+ .env.default # KARAOKE_VAULT=./vault/
30
+ public/_redirects # Cloudflare Pages 404 config
31
+ vault/ # Obsidian vault with sample blog posts
32
+ blog/ # 30 demo posts (publish: true)
46
33
  docs/
47
34
  karaoke-cms/
48
35
  config/
@@ -50,27 +37,46 @@ my-site/
50
37
  menus.yaml
51
38
  ```
52
39
 
53
- After scaffolding, `git init` + initial commit runs automatically if `git` is available.
40
+ A git repository is initialised with an initial commit. Opening `vault/` in Obsidian lets you write content immediately.
54
41
 
55
- ## How to use
42
+ ## Generated karaoke.config.ts
56
43
 
57
- ```bash
58
- npm create @karaoke-cms@latest
44
+ ```ts
45
+ import { defineConfig } from '@karaoke-cms/astro';
46
+ import { blog } from '@karaoke-cms/module-blog';
47
+ import { themeDefault } from '@karaoke-cms/theme-default';
48
+ import { loadEnv } from '@karaoke-cms/astro/env';
49
+
50
+ const env = loadEnv(new URL('.', import.meta.url));
59
51
 
52
+ export default defineConfig({
53
+ vault: env.KARAOKE_VAULT,
54
+ title: 'My Site',
55
+ description: 'What this site is about.',
56
+ modules: [blog({ mount: '/blog' })],
57
+ theme: themeDefault(),
58
+ });
59
+ ```
60
+
61
+ ## Local dev
62
+
63
+ ```bash
60
64
  cd my-site
61
- npm install
62
65
  npm run dev # → http://localhost:4321
63
66
  ```
64
67
 
65
- Open `my-site/vault/` in Obsidian to write content. Files with `publish: true` in frontmatter appear on the live site.
68
+ ## Deploy
66
69
 
67
- Zero runtime dependencies — the scaffolder uses Node.js built-ins only.
70
+ ```bash
71
+ npm run deploy # publishes to Cloudflare Pages
72
+ ```
68
73
 
69
- ## How it changes the behavior of the system
74
+ Requires a Cloudflare account and the `wrangler` CLI. The wizard handles the first deploy interactively.
70
75
 
71
- The create package is not imported by running sites — it only runs once at project setup time. Its output determines:
76
+ ## What's new in 0.9.5
72
77
 
73
- - Which version of `@karaoke-cms/astro` and the theme packages are installed (pinned to the same version as the create package, since packages ship in lockstep)
74
- - The initial `karaoke.config.ts` shape: which theme, which modules, vault location
75
- - The initial vault structure: two sample posts so the site is not empty on first `dev`
76
- - Whether search and comments infrastructure is included in `karaoke.config.ts`
78
+ - **Deploy defaults to yes** the Cloudflare Pages deploy prompt now defaults to YES, so new sites are live by the end of the wizard
79
+ - **Three success screens** distinct output for deploy success, declined, and failed (failed surfaces the error and next steps)
80
+ - **isTTY color guard** ANSI color codes are disabled in CI and piped output
81
+ - **30 demo blog posts** included in the scaffolded vault so the site is populated on first visit
82
+ - **Generated `karaoke.config.ts`** uses `themeDefault()` + `blog()` module API (replaces the old string-based theme config)
@@ -20,23 +20,8 @@
20
20
  "icon": "lucide-file",
21
21
  "title": "getting-started"
22
22
  }
23
- },
24
- {
25
- "id": "52e565c48d7a7c39",
26
- "type": "leaf",
27
- "state": {
28
- "type": "markdown",
29
- "state": {
30
- "file": "karaoke-cms/manual/deployment.md",
31
- "mode": "source",
32
- "source": false
33
- },
34
- "icon": "lucide-file",
35
- "title": "deployment"
36
- }
37
23
  }
38
- ],
39
- "currentTab": 1
24
+ ]
40
25
  }
41
26
  ],
42
27
  "direction": "vertical"
@@ -183,10 +168,10 @@
183
168
  "state": {
184
169
  "type": "file-properties",
185
170
  "state": {
186
- "file": "karaoke-cms/manual/deployment.md"
171
+ "file": "docs/getting-started.md"
187
172
  },
188
173
  "icon": "lucide-info",
189
- "title": "File properties for deployment"
174
+ "title": "File properties for getting-started"
190
175
  }
191
176
  }
192
177
  ],
@@ -208,8 +193,9 @@
208
193
  "templater-obsidian:Templater": false
209
194
  }
210
195
  },
211
- "active": "52e565c48d7a7c39",
196
+ "active": "d663da17c7b12a45",
212
197
  "lastOpenFiles": [
198
+ "karaoke-cms/manual/deployment.md",
213
199
  "karaoke-cms/manual/configuration.md",
214
200
  "karaoke-cms/templates/index.md",
215
201
  "karaoke-cms/config/index.md",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@karaoke-cms/create",
3
3
  "type": "module",
4
- "version": "0.9.3",
4
+ "version": "0.9.6",
5
5
  "description": "Scaffold a new karaoke-cms project",
6
6
  "bin": {
7
7
  "create-karaoke-cms": "./src/index.js"
package/src/index.js CHANGED
@@ -17,12 +17,13 @@ import {
17
17
  } from './templates.js';
18
18
 
19
19
  // ── ANSI helpers ─────────────────────────────────────────────────────────────
20
- const R = '\x1b[0m';
21
- const BOLD = '\x1b[1m';
22
- const GREEN = '\x1b[32m';
23
- const CYAN = '\x1b[36m';
24
- const GRAY = '\x1b[90m';
25
- const RED = '\x1b[31m';
20
+ const NO_COLOR = !process.stdout.isTTY;
21
+ const R = NO_COLOR ? '' : '\x1b[0m';
22
+ const BOLD = NO_COLOR ? '' : '\x1b[1m';
23
+ const GREEN = NO_COLOR ? '' : '\x1b[32m';
24
+ const CYAN = NO_COLOR ? '' : '\x1b[36m';
25
+ const GRAY = NO_COLOR ? '' : '\x1b[90m';
26
+ const RED = NO_COLOR ? '' : '\x1b[31m';
26
27
 
27
28
  // Use create package's own version as the @karaoke-cms/astro dep range
28
29
  // (packages ship in lockstep)
@@ -174,34 +175,40 @@ async function main() {
174
175
  }
175
176
 
176
177
  // ── npm install ──────────────────────────────────────────────────────────────
177
- console.log(`\n Installing dependencies...\n`);
178
+ console.log(`\n Installing dependencies... ${GRAY}(this may take a minute)${R}\n`);
178
179
  const installResult = spawnSync('npm', ['install'], { cwd: targetDir, stdio: 'inherit' });
179
180
  if (installResult.status !== 0) {
180
181
  console.log(` ${GRAY}(install failed — run npm install manually)${R}`);
181
182
  }
182
183
 
183
184
  // ── Deploy prompt ────────────────────────────────────────────────────────────
184
- const deployNow = await askYesNo('Publish a live preview to Cloudflare Pages now?', false);
185
-
186
- rl.close();
185
+ const deployNow = await askYesNo('Publish a live preview to Cloudflare Pages now?', true);
187
186
 
188
187
  let liveUrl = null;
188
+ let deployFailed = false;
189
189
  if (deployNow) {
190
190
  const projectName = `${slugify(dir)}-preview`;
191
191
  liveUrl = await deployToCloudflare(targetDir, projectName);
192
+ if (!liveUrl) deployFailed = true;
192
193
  }
193
194
 
195
+ rl.close();
196
+
194
197
  // ── Success screen ───────────────────────────────────────────────────────────
195
- console.log(`\n${GREEN}✓${R} Done! Created ${BOLD}${dir}/${R}\n`);
198
+ const failNote = deployFailed ? ` ${GRAY}(deploy failed see above)${R}` : '';
199
+ console.log(`\n${GREEN}✓${R} Done! Created ${BOLD}${dir}/${R}${failNote}\n`);
196
200
  if (liveUrl) {
197
- console.log(` ${BOLD}🎤 Live:${R} ${liveUrl}`);
198
- console.log(` ${BOLD}💻 Dev:${R} ${CYAN}cd ${dir} && npm run dev${R}`);
201
+ console.log(` ${BOLD}🎤 Live URL:${R} ${GREEN}${liveUrl}${R}`);
202
+ console.log(` ${BOLD}💻 Local dev:${R} ${CYAN}cd ${dir} && npm run dev${R}`);
199
203
  console.log(` ${BOLD}🔄 Redeploy:${R} ${CYAN}npm run deploy${R}\n`);
204
+ } else if (deployFailed) {
205
+ console.log(` The project is ready. Fix the error above, then:`);
206
+ console.log(` ${CYAN}cd ${dir}${R}`);
207
+ console.log(` ${CYAN}npm run deploy${R} ${GRAY}→ retry publishing to Cloudflare Pages${R}\n`);
200
208
  } else {
201
- console.log(` Next steps:\n`);
202
209
  console.log(` ${CYAN}cd ${dir}${R}`);
203
- console.log(` ${CYAN}npm run dev${R} ${GRAY}→ http://localhost:4321${R}`);
204
- console.log(` ${CYAN}npm run deploy${R} ${GRAY}→ publish to Cloudflare Pages${R}\n`);
210
+ console.log(` ${CYAN}npm run dev${R} ${GRAY}→ start local dev server${R}`);
211
+ console.log(` ${CYAN}npm run deploy${R} ${GRAY}→ publish to Cloudflare Pages when ready${R}\n`);
205
212
  }
206
213
  console.log(` ${GRAY}Open ${BOLD}${dir}/vault/${R}${GRAY} in Obsidian to write content.${R}\n`);
207
214
  }
@@ -230,7 +237,7 @@ export async function deployToCloudflare(targetDir, projectName) {
230
237
  }
231
238
 
232
239
  // 2. Build
233
- console.log(` ${CYAN}Building...${R}`);
240
+ console.log(` ${CYAN}Building... ${GRAY}(15–30 seconds)${R}`);
234
241
  const build = spawnSync('npm', ['run', 'build'],
235
242
  { cwd: targetDir, stdio: 'inherit' });
236
243
  if (build.status !== 0) {
@@ -261,9 +268,13 @@ export async function deployToCloudflare(targetDir, projectName) {
261
268
  const liveUrl = `https://${projectName}.pages.dev`;
262
269
 
263
270
  // 6. Write real URL back to astro.config.mjs (replaces placeholder set during scaffold)
264
- const configPath = join(targetDir, 'astro.config.mjs');
265
- const configSrc = readFileSync(configPath, 'utf8');
266
- writeFileSync(configPath, configSrc.replace('https://your-site.pages.dev', liveUrl));
271
+ try {
272
+ const configPath = join(targetDir, 'astro.config.mjs');
273
+ const configSrc = readFileSync(configPath, 'utf8');
274
+ writeFileSync(configPath, configSrc.replace('https://your-site.pages.dev', liveUrl));
275
+ } catch {
276
+ console.log(` ${GRAY}Could not update site URL — edit astro.config.mjs manually.${R}`);
277
+ }
267
278
 
268
279
  return liveUrl;
269
280
  }
package/src/templates.js CHANGED
@@ -75,32 +75,28 @@ export default defineConfig({
75
75
  * title: string,
76
76
  * description: string,
77
77
  * theme: string,
78
- * search: boolean,
79
78
  * comments: { repo: string, repoId: string, category: string, categoryId: string } | null,
80
79
  * }} opts
81
80
  */
82
- export function karaokeConfig({ title, description, theme, search, comments }) {
83
- const modules = {};
84
- if (search) modules.search = { enabled: true };
85
- if (comments) modules.comments = { enabled: true, ...comments };
86
-
87
- const modulesStr = Object.keys(modules).length > 0
88
- ? `\n modules: ${JSON.stringify(modules, null, 2).replace(/\n/g, '\n ')},`
81
+ export function karaokeConfig({ title, description, theme, comments }) {
82
+ const commentsStr = comments
83
+ ? `\n comments: ${JSON.stringify({ enabled: true, ...comments }, null, 2).replace(/\n/g, '\n ')},`
89
84
  : '';
90
85
 
91
- return `import type { KaraokeConfig } from '@karaoke-cms/astro';
86
+ return `import { defineConfig } from '@karaoke-cms/astro';
87
+ import { blog } from '@karaoke-cms/module-blog';
88
+ import { themeDefault } from '@karaoke-cms/theme-default';
92
89
  import { loadEnv } from '@karaoke-cms/astro/env';
93
90
 
94
91
  const env = loadEnv(new URL('.', import.meta.url));
95
92
 
96
- const config: KaraokeConfig = {
93
+ export default defineConfig({
97
94
  vault: env.KARAOKE_VAULT,
98
95
  title: ${JSON.stringify(title)},
99
96
  description: ${JSON.stringify(description)},
100
- theme: ${JSON.stringify(theme)},${modulesStr}
101
- };
102
-
103
- export default config;
97
+ modules: [blog({ mount: '/blog' })],
98
+ theme: themeDefault(),${commentsStr}
99
+ });
104
100
  `;
105
101
  }
106
102
 
@@ -1,10 +0,0 @@
1
- collections:
2
- blog:
3
- modes: [dev, prod]
4
- label: Blog
5
- docs:
6
- modes: [dev, prod]
7
- label: Docs
8
- karaoke-cms:
9
- modes: [dev]
10
- label: Handbook
@@ -1,14 +0,0 @@
1
- ---
2
- title: "Config"
3
- ---
4
-
5
- # Config
6
-
7
- Configuration files for your karaoke-cms vault.
8
-
9
- | File | Purpose |
10
- |------|---------|
11
- | `collections.yaml` | Which collections are visible in dev vs production |
12
- | `menus.yaml` | Site navigation: header menu, footer columns, and their entries |
13
-
14
- Edit these files to change what appears on your site and in the navigation. Changes take effect on the next dev server restart or build.
@@ -1,58 +0,0 @@
1
- # menus.yaml — define named menus for your site.
2
- #
3
- # Each menu has an orientation (horizontal or vertical) and a list of entries.
4
- # Entries are sorted by weight (ascending). Submenus are defined by nesting entries.
5
- #
6
- # when: collection:name — hide entry when the collection is disabled or has no
7
- # published posts. Omit `when` to always show.
8
- #
9
- # If this file is absent, karaoke-cms generates a default main menu (Blog/Docs/Tags)
10
- # and a default footer menu (RSS) from your active collections.
11
-
12
- menus:
13
- main:
14
- orientation: horizontal
15
- entries:
16
- - text: Blog
17
- href: /blog
18
- weight: 10
19
- when: collection:blog
20
- - text: Docs
21
- href: /docs
22
- weight: 20
23
- when: collection:docs
24
- - text: Tags
25
- href: /tags
26
- weight: 30
27
- - text: karaoke-cms
28
- href: /karaoke-cms
29
- weight: 40
30
- when: collection:karaoke-cms
31
-
32
- footer-2:
33
- orientation: vertical
34
- entries:
35
- - text: karaoke-cms
36
- href: /karaoke-cms/manual/index
37
- weight: 10
38
- when: collection:karaoke-cms
39
- entries:
40
- - text: Manual
41
- href: /karaoke-cms/manual/index
42
- weight: 10
43
- when: collection:karaoke-cms
44
- - text: Config
45
- href: /karaoke-cms/config/index
46
- weight: 20
47
- when: collection:karaoke-cms
48
- - text: Templates
49
- href: /karaoke-cms/templates/index
50
- weight: 30
51
- when: collection:karaoke-cms
52
-
53
- footer:
54
- orientation: vertical
55
- entries:
56
- - text: RSS
57
- href: /rss.xml
58
- weight: 10
@@ -1,77 +0,0 @@
1
- ---
2
- title: "Configuration"
3
- ---
4
-
5
- # Configuration
6
-
7
- > **Handbook — dev only.**
8
-
9
- ## karaoke.config.ts
10
-
11
- The main config file at your project root. TypeScript — fully typed.
12
-
13
- ```ts
14
- import type { KaraokeConfig } from '@karaoke-cms/astro';
15
-
16
- const config: KaraokeConfig = {
17
- title: "My Site",
18
- description: "What this site is about.",
19
- theme: "default", // "default" | "minimal"
20
- modules: {
21
- search: {
22
- enabled: true, // Pagefind full-text search
23
- },
24
- comments: {
25
- enabled: true, // Giscus (GitHub Discussions)
26
- repo: "owner/repo",
27
- repoId: "R_...",
28
- category: "General",
29
- categoryId: "DIC_...",
30
- },
31
- },
32
- };
33
-
34
- export default config;
35
- ```
36
-
37
- Get Giscus values from [giscus.app](https://giscus.app) after enabling Discussions on your repo.
38
-
39
- ## collections.yaml
40
-
41
- `content/karaoke-cms/config/collections.yaml` controls which collections are visible in dev vs prod:
42
-
43
- ```yaml
44
- collections:
45
- blog:
46
- modes: [dev, prod] # visible in both dev and production
47
- label: Blog
48
- docs:
49
- modes: [dev, prod]
50
- label: Docs
51
- karaoke-cms:
52
- modes: [dev] # dev-only — never ships
53
- label: Handbook
54
- ```
55
-
56
- To add a custom collection (e.g., `content/notes/`):
57
-
58
- ```yaml
59
- collections:
60
- notes:
61
- modes: [dev, prod]
62
- label: Notes
63
- ```
64
-
65
- ## Layout regions
66
-
67
- Place UI components in the sidebar or header via `karaoke.config.ts`:
68
-
69
- ```ts
70
- layout: {
71
- regions: {
72
- right: { components: ['recent-posts'] },
73
- },
74
- },
75
- ```
76
-
77
- Available components: `header`, `main-menu`, `search`, `recent-posts`, `footer`.
@@ -1,38 +0,0 @@
1
- ---
2
- title: "Writing Content"
3
- ---
4
-
5
- # Writing Content
6
-
7
- > **Handbook — dev only.**
8
-
9
- ## Frontmatter fields
10
-
11
- ```yaml
12
- ---
13
- title: "My Post" # required — build fails without it
14
- publish: true # required to appear on site (default: false)
15
- date: 2026-01-15 # optional — YYYY-MM-DD, used for sorting
16
- author: "Name" # optional — string or array ["Alice", "Bob"]
17
- description: "..." # optional — used in OG meta tags and RSS
18
- tags: [writing, tutorial] # optional — enables tag pages at /tags/[tag]
19
- ---
20
- ```
21
-
22
- ## Privacy model
23
-
24
- Every file is **private by default**. `publish: false` (or a missing `publish` field) means the file stays in your vault and never enters the build — not in HTML, not in RSS, not in the sitemap, not in the Pagefind search index.
25
-
26
- Only `publish: true` files appear on your site.
27
-
28
- ## Wikilinks
29
-
30
- Use `[[slug]]` syntax to link between notes. karaoke-cms resolves wikilinks automatically:
31
-
32
- - `[[blog/hello-world]]` → `/blog/hello-world/`
33
- - `[[docs/getting-started]]` → `/docs/getting-started/`
34
- - `[[docs/getting-started|Getting Started]]` → link text "Getting Started"
35
-
36
- ## AI enrichment (optional)
37
-
38
- Run `npx @karaoke-cms/enrich` to auto-generate `tags`, `description`, and `reading_time` for published notes using OpenAI or Anthropic. Skips already-enriched notes via a local cache.
@@ -1,46 +0,0 @@
1
- ---
2
- title: "Deployment"
3
- ---
4
-
5
- # Deployment
6
-
7
- > **Handbook — dev only.**
8
-
9
- ## Cloudflare Pages (recommended)
10
-
11
- 1. Push your project to a GitHub repo
12
- 2. Go to [Cloudflare Pages](https://pages.cloudflare.com) → Create a project → Connect to your repo
13
- 3. Build settings:
14
- - **Build command:** `npm run build`
15
- - **Build output directory:** `dist`
16
- - **Node.js version:** 22
17
- 4. Deploy
18
-
19
- On every push to `main`, Cloudflare Pages rebuilds and redeploys automatically.
20
-
21
- ## GitHub Actions (privacy gate)
22
-
23
- The included `deploy.yml` workflow runs `assert-privacy` on every build to verify that no private content leaks into `dist/`. If a private note accidentally gets into the build, the deploy fails before it reaches production.
24
-
25
- ## Custom domain
26
-
27
- In Cloudflare Pages → your project → Custom domains → Add a domain. Update `site:` in `astro.config.mjs` to match:
28
-
29
- ```js
30
- export default defineConfig({
31
- site: 'https://your-domain.com',
32
- // ...
33
- });
34
- ```
35
-
36
- ## Environment variables
37
-
38
- If you use `@karaoke-cms/enrich` in CI:
39
-
40
- | Variable | Purpose |
41
- |----------|---------|
42
- | `OPENAI_API_KEY` | Required if `ENRICH_PROVIDER=openai` (default) |
43
- | `ANTHROPIC_API_KEY` | Required if `ENRICH_PROVIDER=anthropic` |
44
- | `ENRICH_ENABLED` | Set to `true` to enable AI enrichment in CI |
45
- | `CLOUDFLARE_API_TOKEN` | For Cloudflare Pages deploy via GitHub Actions |
46
- | `CLOUDFLARE_ACCOUNT_ID` | For Cloudflare Pages deploy via GitHub Actions |
@@ -1,41 +0,0 @@
1
- ---
2
- title: "Handbook"
3
- ---
4
-
5
- # karaoke-cms Handbook
6
-
7
- > **Handbook — dev only.** This section is visible at `/karaoke-cms` in dev mode and never ships to production.
8
-
9
- karaoke-cms is an Astro framework for publishing Obsidian vaults as static sites. Private-by-default: only files with `publish: true` in frontmatter appear on your site.
10
-
11
- ## How it works
12
-
13
- ```
14
- Obsidian vault (this directory)
15
- ↓ write notes with publish: true
16
- ↓ push to main
17
- ↓ GitHub Actions: astro build + assert-privacy
18
- ↓ Cloudflare Pages
19
- ↓ live site
20
- ```
21
-
22
- Your vault is also your deploy pipeline config, your documentation, and your content collection — all in one place.
23
-
24
- ## Handbook sections
25
-
26
- - [[content]] — Writing content and frontmatter
27
- - [[configuration]] — karaoke.config.ts reference
28
- - [[deployment]] — Deploying to Cloudflare Pages
29
- - [[privacy]] — How publish:true works
30
-
31
- ## Collections
32
-
33
- This vault has three content collections:
34
-
35
- | Collection | Mode | Purpose |
36
- |------------|------|---------|
37
- | `blog/` | dev + prod | Published blog posts |
38
- | `docs/` | dev + prod | Published documentation |
39
- | `karaoke-cms/` | dev only | This handbook — never ships |
40
-
41
- Edit `content/karaoke-cms/config/collections.yaml` to add your own collections or change their visibility.
@@ -1,37 +0,0 @@
1
- ---
2
- title: "Privacy"
3
- ---
4
-
5
- # Privacy Model
6
-
7
- > **Handbook — dev only.**
8
-
9
- ## The rule
10
-
11
- **Every file is private by default.** Only files with `publish: true` in frontmatter appear on your site.
12
-
13
- A file without `publish: true` never enters the build — not in HTML, RSS, sitemap, or the Pagefind search index.
14
-
15
- ## How it's enforced
16
-
17
- Two independent layers:
18
-
19
- 1. **Astro content filter** — `getCollection('blog', ({ data }) => data.publish === true)` filters at the query level. Pages only receive published entries.
20
-
21
- 2. **assert-privacy** — a post-build check that scans `dist/` for known private note titles and content. If any private content leaks, the build fails before deploy.
22
-
23
- The collection mode system adds a third layer: `karaoke-cms/` is excluded from `makeCollections()` in production entirely. Even if a handbook page had `publish: true`, it would never enter the prod build graph.
24
-
25
- ## What "private" means
26
-
27
- | Field | Builds? | Searchable? | In RSS? | In sitemap? |
28
- |-------|---------|-------------|---------|-------------|
29
- | `publish: true` | Yes | Yes | Yes | Yes |
30
- | `publish: false` | No | No | No | No |
31
- | missing | No | No | No | No |
32
-
33
- ## Audit your site
34
-
35
- After every build, `assert-privacy.js` outputs a summary of what shipped. Check it in your CI logs.
36
-
37
- For a manual check: `node node_modules/@karaoke-cms/assert-privacy/dist/index.js dist`
@@ -1,9 +0,0 @@
1
- ---
2
- title: "" # required — build fails without it
3
- publish: false # required to appear on site (default: false)
4
- date: 2026-01-15 # optional — YYYY-MM-DD, used for sorting
5
- author: "" # optional — string or array ["Alice", "Bob"]
6
- description: "" # optional — used in OG meta tags and RSS
7
- tags: [general] # optional — enables tag pages at /tags/[tag]
8
- notetype: blog
9
- ---
@@ -1,9 +0,0 @@
1
- ---
2
- title: "" # required — build fails without it
3
- publish: false # required to appear on site (default: false)
4
- date: 2026-01-15 # optional — YYYY-MM-DD, used for sorting
5
- author: "" # optional — string or array ["Alice", "Bob"]
6
- description: "" # optional — used in OG meta tags and RSS
7
- tags: [general] # optional — enables tag pages at /tags/[tag]
8
- notetype: blog
9
- ---
@@ -1,8 +0,0 @@
1
- ---
2
- title: "" # required — build fails without it
3
- publish: false # required to appear on site (default: false)
4
- date: 2026-01-15 # optional — YYYY-MM-DD, used for sorting
5
- author: "" # optional — string or array ["Alice", "Bob"]
6
- description: "..." # optional — used in OG meta tags and RSS
7
- tags: [general] # optional — enables tag pages at /tags/[tag]
8
- ---
@@ -1,15 +0,0 @@
1
- ---
2
- title: "Templates"
3
- ---
4
-
5
- # Templates
6
-
7
- Obsidian templates for common note types. Use with the [Templater](https://github.com/SilentVoid13/Templater) plugin.
8
-
9
- | Template | Use for |
10
- |----------|---------|
11
- | `blog-header.md` | New blog posts — sets `title`, `date`, `publish: false` |
12
- | `docs-header.md` | New documentation pages |
13
- | `index-by-foldernote.md` | Folder index notes (requires the folder-notes Obsidian plugin) |
14
-
15
- In Templater settings, set the template folder to `karaoke-cms/templates`.