domma-cms 1.6.60 → 1.7.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.
Files changed (137) hide show
  1. package/CHANGELOG.md +4344 -0
  2. package/README.md +1 -1
  3. package/admin/dist/domma/domma-tools.css +3 -3
  4. package/admin/dist/domma/domma-tools.min.js +3 -3
  5. package/admin/js/lib/effect-defs.js +1 -1
  6. package/admin/js/lib/effects-builder.js +2 -2
  7. package/admin/js/lib/page-editor-reference.js +5 -5
  8. package/admin/js/lib/shortcode-tags.js +1 -1
  9. package/admin/js/templates/docs/usage-shortcodes.html +39 -0
  10. package/docs/DommaFeatures.md +3005 -0
  11. package/docs/MultiTenant.md +176 -0
  12. package/docs/api-reference.md +1667 -0
  13. package/docs/cache.md +96 -0
  14. package/docs/configuration.md +1811 -0
  15. package/docs/editions-and-licences.md +190 -0
  16. package/docs/getting-started.md +201 -0
  17. package/docs/guides/form-triggers.md +260 -0
  18. package/docs/guides/mongodb-storage-adapter.md +282 -0
  19. package/docs/guides/multi-page-forms.md +121 -0
  20. package/docs/images/scribe-composer.png +0 -0
  21. package/docs/markdown-shortcodes.md +1738 -0
  22. package/docs/pentest-report-2026-04-03.md +181 -0
  23. package/docs/plans/2026-02-28-admin-topbar-login-forms.md +349 -0
  24. package/docs/plans/2026-02-28-domma-ui-polish.md +286 -0
  25. package/docs/plans/2026-02-28-reset-seed-fresh-commands.md +520 -0
  26. package/docs/plans/2026-03-02-cookie-consent-design.md +86 -0
  27. package/docs/plans/2026-03-02-cookie-consent-plugin.md +453 -0
  28. package/docs/plans/2026-03-02-tooltip-sweep-and-back-to-top-plugin-design.md +126 -0
  29. package/docs/plans/2026-03-02-tooltip-sweep-back-to-top.md +427 -0
  30. package/docs/plans/2026-03-05-cookie-consent-btt-core.md +115 -0
  31. package/docs/plans/2026-03-05-custom-css-migrate-to-core-design.md +35 -0
  32. package/docs/plans/2026-03-05-custom-css-migrate-to-core.md +352 -0
  33. package/docs/plans/2026-03-05-scribe-script-shortcode.md +373 -0
  34. package/docs/plans/2026-03-05-user-types-design.md +139 -0
  35. package/docs/plans/2026-03-05-user-types-implementation.md +1035 -0
  36. package/docs/plans/2026-03-09-page-editor-view-cta-inserts-design.md +78 -0
  37. package/docs/plans/2026-03-09-page-editor-view-cta-inserts.md +388 -0
  38. package/docs/plans/2026-03-09-pro-documentation-design.md +80 -0
  39. package/docs/plans/2026-03-09-pro-documentation.md +1017 -0
  40. package/docs/plans/2026-06-10-core-project-design.md +240 -0
  41. package/docs/plugin-development.md +827 -0
  42. package/docs/recruitment-recipe.md +501 -0
  43. package/docs/scaffolding.md +312 -0
  44. package/docs/screenshots/calculator-floating.png +0 -0
  45. package/docs/screenshots/calculator-standard.png +0 -0
  46. package/docs/screenshots/email-settings.png +0 -0
  47. package/docs/screenshots/messages-count.png +0 -0
  48. package/docs/structure/user-types.md +107 -0
  49. package/docs/superpowers/plans/2026-05-25-admin-sidebar-via-menus.md +1112 -0
  50. package/docs/superpowers/plans/2026-06-08-navbar-mobile-drawer-and-menu-enrichment.md +1161 -0
  51. package/docs/superpowers/plans/2026-06-25-domma-docs-project.md +695 -0
  52. package/docs/superpowers/plans/2026-06-26-surveys-HANDOFF.md +68 -0
  53. package/docs/superpowers/plans/2026-06-26-surveys-pro-plugin.md +1902 -0
  54. package/docs/superpowers/plans/2026-08-19-collection-context-sort-export.md +1577 -0
  55. package/docs/superpowers/plans/2026-10-09-content-api.md +1192 -0
  56. package/docs/superpowers/specs/2026-03-12-form-followup-actions-design.md +152 -0
  57. package/docs/superpowers/specs/2026-03-12-page-editor-form-insert-and-per-page-theme-design.md +115 -0
  58. package/docs/superpowers/specs/2026-03-17-nav-hidden-items-design.md +85 -0
  59. package/docs/superpowers/specs/2026-03-19-accordion-insert-design.md +175 -0
  60. package/docs/superpowers/specs/2026-03-25-static-block-shortcode-design.md +153 -0
  61. package/docs/superpowers/specs/2026-03-25-text-shortcode-design.md +168 -0
  62. package/docs/superpowers/specs/2026-04-05-card-builder-design.md +315 -0
  63. package/docs/superpowers/specs/2026-04-06-insert-builders-design.md +278 -0
  64. package/docs/superpowers/specs/2026-04-06-layouts-design.md +226 -0
  65. package/docs/superpowers/specs/2026-04-07-blog-plugin-design.md +300 -0
  66. package/docs/superpowers/specs/2026-04-07-calendar-plugin-design.md +538 -0
  67. package/docs/superpowers/specs/2026-04-17-components-area-design.md +368 -0
  68. package/docs/superpowers/specs/2026-05-02-effects-slideover-overhaul-design.md +193 -0
  69. package/docs/superpowers/specs/2026-05-06-dashboard-rebuild-design.md +257 -0
  70. package/docs/superpowers/specs/2026-05-24-menus-design.md +466 -0
  71. package/docs/superpowers/specs/2026-05-25-admin-sidebar-via-menus-design.md +358 -0
  72. package/docs/superpowers/specs/2026-05-25-projects-design.md +380 -0
  73. package/docs/superpowers/specs/2026-06-08-navbar-mobile-drawer-and-menu-enrichment-design.md +292 -0
  74. package/docs/superpowers/specs/2026-06-08-sidebar-tools-plugins-folders-design.md +59 -0
  75. package/docs/superpowers/specs/2026-06-25-domma-docs-project-design.md +192 -0
  76. package/docs/superpowers/specs/2026-06-26-domma-charts-upstream-proposal.md +44 -0
  77. package/docs/superpowers/specs/2026-06-26-manager-scheduler-upstream-proposal.md +46 -0
  78. package/docs/superpowers/specs/2026-06-26-surveys-pro-plugin-design.md +206 -0
  79. package/docs/superpowers/specs/2026-07-02-shortcode-context-menu-design.md +96 -0
  80. package/docs/superpowers/specs/2026-08-19-collection-context-sort-export-design.md +330 -0
  81. package/docs/superpowers/specs/2026-09-22-dcms-marketplace-design.md +1150 -0
  82. package/docs/superpowers/specs/2026-10-09-content-api-design.md +325 -0
  83. package/docs/superpowers/specs/plans/2026-03-12-form-followup-actions.md +624 -0
  84. package/docs/superpowers/specs/plans/2026-03-12-page-editor-form-insert-and-per-page-theme.md +125 -0
  85. package/docs/superpowers/specs/plans/2026-03-17-nav-hidden-items.md +578 -0
  86. package/docs/superpowers/specs/plans/2026-03-19-accordion-insert.md +635 -0
  87. package/docs/superpowers/specs/plans/2026-03-25-text-shortcode.md +545 -0
  88. package/docs/superpowers/specs/plans/2026-04-05-card-builder.md +1970 -0
  89. package/docs/superpowers/specs/plans/2026-04-06-insert-builders.md +1049 -0
  90. package/docs/superpowers/specs/plans/2026-04-06-layouts.md +1015 -0
  91. package/docs/superpowers/specs/plans/2026-04-07-calendar-plugin.md +1526 -0
  92. package/docs/superpowers/specs/plans/2026-04-08-plugin-monetisation-split.md +242 -0
  93. package/docs/superpowers/specs/plans/2026-04-17-components-area.md +2038 -0
  94. package/docs/superpowers/specs/plans/2026-05-02-effects-slideover-overhaul.md +2335 -0
  95. package/docs/superpowers/specs/plans/2026-05-06-dashboard-rebuild.md +1999 -0
  96. package/docs/superpowers/specs/plans/2026-05-17-cache-system.md +1436 -0
  97. package/docs/superpowers/specs/plans/2026-05-24-menus.md +3096 -0
  98. package/docs/superpowers/specs/plans/2026-05-25-projects.md +2869 -0
  99. package/docs/superpowers/specs/plans/2026-09-22-marketplace-step2-consolidation.md +505 -0
  100. package/docs/superpowers/specs/plans/2026-09-22-marketplace-step3-catalogue-and-key.md +154 -0
  101. package/docs/superpowers/specs/plans/2026-09-22-tools-promotion.md +260 -0
  102. package/docs/theming.md +320 -0
  103. package/package.json +2 -2
  104. package/public/js/celebrations/core/canvas.js +1 -1
  105. package/public/js/celebrations/index.js +1 -1
  106. package/public/js/celebrations/licenses/ABeeZee-Regular-OFL.txt +93 -0
  107. package/public/js/celebrations/licenses/Allura-Regular-OFL.txt +93 -0
  108. package/public/js/celebrations/message-attrs.mjs +1 -0
  109. package/public/js/celebrations/themes/anniversary.js +1 -0
  110. package/public/js/celebrations/themes/autumn.js +1 -0
  111. package/public/js/celebrations/themes/birthday.js +1 -0
  112. package/public/js/celebrations/themes/butterfly-flight.js +1 -0
  113. package/public/js/celebrations/themes/calendar.js +1 -0
  114. package/public/js/celebrations/themes/chinese-new-year-dates.js +1 -0
  115. package/public/js/celebrations/themes/chinese-new-year.js +1 -0
  116. package/public/js/celebrations/themes/christmas.js +1 -1
  117. package/public/js/celebrations/themes/easter.js +1 -0
  118. package/public/js/celebrations/themes/garden-drawing.js +1 -0
  119. package/public/js/celebrations/themes/garden.js +1 -0
  120. package/public/js/celebrations/themes/graduation.js +1 -0
  121. package/public/js/celebrations/themes/greeting.js +1 -0
  122. package/public/js/celebrations/themes/make-it-rain.js +1 -0
  123. package/public/js/celebrations/themes/money-rain.js +1 -0
  124. package/public/js/celebrations/themes/new-year.js +1 -0
  125. package/public/js/celebrations/themes/occasion-drawing.js +1 -0
  126. package/public/js/celebrations/themes/occasions.js +1 -0
  127. package/public/js/celebrations/themes/rain-control.js +1 -0
  128. package/public/js/celebrations/themes/sparkler-font-data.js +1 -0
  129. package/public/js/celebrations/themes/sparkler-font.js +1 -0
  130. package/public/js/celebrations/themes/sparkler-writing.js +1 -0
  131. package/public/js/celebrations/themes/summer-garden.js +1 -0
  132. package/public/js/celebrations/themes/winter.js +1 -0
  133. package/public/js/celebrations/trait-data.mjs +1 -1
  134. package/public/js/effects.js +1 -1
  135. package/scripts/build.js +4 -0
  136. package/server/services/markdown.js +5 -2
  137. package/server/templates/page.html +1 -1
@@ -0,0 +1,520 @@
1
+ # Reset, Seed & Fresh Commands - Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Add three npm scripts - `npm run reset` (full factory reset), `npm run seed` (demo content), and `npm run fresh` (reset → setup → seed in one go) - that let an end user get a working CMS in under two minutes.
6
+
7
+ **Architecture:** Three standalone ESM scripts in `scripts/`. Each operates directly on files (no server imports). `fresh.js` chains the other three scripts using `node:child_process` `spawnSync` with `stdio: 'inherit'` so all interactive prompts work through. `seed.js` writes markdown files directly as template strings (no gray-matter dependency needed - format is straightforward YAML frontmatter + markdown body).
8
+
9
+ **Tech Stack:** `node:fs/promises`, `node:readline/promises`, `node:child_process`, `node:path` - zero new dependencies.
10
+
11
+ ---
12
+
13
+ ### Task 1: `scripts/reset.js` - Full Factory Reset
14
+
15
+ **Files:**
16
+ - Create: `scripts/reset.js`
17
+ - Modify: `package.json`
18
+
19
+ Wipes all user-created data and resets config to factory defaults. Requires typing `yes` to confirm - protects against accidental runs.
20
+
21
+ **What it deletes/resets:**
22
+ - `content/users/*.json` - all user accounts
23
+ - `content/pages/**/*.md` - all pages (recursive)
24
+ - `content/media/*` - all uploaded files
25
+ - `config/site.json` - reset to defaults
26
+ - `config/navigation.json` - reset to defaults
27
+ - `.env` - JWT_SECRET reset to placeholder (so `npm run setup` will regenerate it)
28
+
29
+ **Step 1: Create `scripts/reset.js`**
30
+
31
+ ```javascript
32
+ #!/usr/bin/env node
33
+ /**
34
+ * Domma CMS - Factory Reset
35
+ * Wipes all content, users, and resets config to defaults.
36
+ * Run: npm run reset
37
+ */
38
+ import { createInterface } from 'node:readline/promises';
39
+ import { readFile, writeFile, readdir, rm, mkdir } from 'node:fs/promises';
40
+ import { existsSync } from 'node:fs';
41
+ import path from 'node:path';
42
+ import { fileURLToPath } from 'node:url';
43
+
44
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
45
+ const ROOT = path.resolve(__dirname, '..');
46
+
47
+ const USERS_DIR = path.join(ROOT, 'content', 'users');
48
+ const PAGES_DIR = path.join(ROOT, 'content', 'pages');
49
+ const MEDIA_DIR = path.join(ROOT, 'content', 'media');
50
+ const SITE_CFG = path.join(ROOT, 'config', 'site.json');
51
+ const NAV_CFG = path.join(ROOT, 'config', 'navigation.json');
52
+ const ENV_FILE = path.join(ROOT, '.env');
53
+
54
+ const DEFAULT_SITE = {
55
+ title: 'My Site',
56
+ tagline: 'Powered by Domma CMS',
57
+ theme: 'charcoal-dark',
58
+ seo: {
59
+ defaultTitle: 'My Site',
60
+ titleSeparator: ' | ',
61
+ defaultDescription: 'A site built with Domma CMS'
62
+ },
63
+ footer: {
64
+ copyright: `© ${new Date().getFullYear()} My Site. All rights reserved.`,
65
+ links: [
66
+ { text: 'Privacy Policy', url: '/privacy' },
67
+ { text: 'Contact', url: '/contact' }
68
+ ]
69
+ }
70
+ };
71
+
72
+ const DEFAULT_NAV = {
73
+ brand: { text: 'My Site', logo: null, url: '/' },
74
+ items: [
75
+ { text: 'Home', url: '/', icon: 'home' },
76
+ { text: 'About', url: '/about', icon: 'info' },
77
+ { text: 'Contact', url: '/contact', icon: 'mail' }
78
+ ],
79
+ variant: 'dark',
80
+ position: 'sticky'
81
+ };
82
+
83
+ // ---------------------------------------------------------------------------
84
+
85
+ async function deleteDir(dir) {
86
+ try {
87
+ await rm(dir, { recursive: true, force: true });
88
+ await mkdir(dir, { recursive: true });
89
+ } catch { /* dir may not exist */ }
90
+ }
91
+
92
+ async function resetEnv() {
93
+ if (!existsSync(ENV_FILE)) return;
94
+ const lines = (await readFile(ENV_FILE, 'utf8')).split('\n');
95
+ const updated = lines.map(l => {
96
+ if (l.startsWith('JWT_SECRET=')) {
97
+ return 'JWT_SECRET=change-me-to-a-64-char-random-string-before-deploying-to-production';
98
+ }
99
+ return l;
100
+ });
101
+ await writeFile(ENV_FILE, updated.join('\n'), 'utf8');
102
+ }
103
+
104
+ // ---------------------------------------------------------------------------
105
+
106
+ console.log('');
107
+ console.log(' ┌──────────────────────────────────┐');
108
+ console.log(' │ Domma CMS - Factory Reset │');
109
+ console.log(' └──────────────────────────────────┘');
110
+ console.log('');
111
+ console.log(' This will permanently delete:');
112
+ console.log(' • All user accounts (content/users/)');
113
+ console.log(' • All pages (content/pages/)');
114
+ console.log(' • All media uploads (content/media/)');
115
+ console.log(' • config/site.json and config/navigation.json');
116
+ console.log(' • JWT_SECRET in .env');
117
+ console.log('');
118
+
119
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
120
+ const answer = (await rl.question(' Type "yes" to confirm: ')).trim();
121
+ rl.close();
122
+
123
+ if (answer !== 'yes') {
124
+ console.log('\n Reset cancelled.\n');
125
+ process.exit(0);
126
+ }
127
+
128
+ console.log('');
129
+ process.stdout.write(' Deleting users…');
130
+ await deleteDir(USERS_DIR);
131
+ console.log(' done.');
132
+
133
+ process.stdout.write(' Deleting pages…');
134
+ await deleteDir(PAGES_DIR);
135
+ console.log(' done.');
136
+
137
+ process.stdout.write(' Deleting media…');
138
+ await deleteDir(MEDIA_DIR);
139
+ console.log(' done.');
140
+
141
+ process.stdout.write(' Resetting config/site.json…');
142
+ await writeFile(SITE_CFG, JSON.stringify(DEFAULT_SITE, null, 4) + '\n', 'utf8');
143
+ console.log(' done.');
144
+
145
+ process.stdout.write(' Resetting config/navigation.json…');
146
+ await writeFile(NAV_CFG, JSON.stringify(DEFAULT_NAV, null, 4) + '\n', 'utf8');
147
+ console.log(' done.');
148
+
149
+ process.stdout.write(' Resetting JWT_SECRET in .env…');
150
+ await resetEnv();
151
+ console.log(' done.');
152
+
153
+ console.log('');
154
+ console.log(' ✓ Factory reset complete.');
155
+ console.log(' Run `npm run setup` to configure a new admin account.');
156
+ console.log('');
157
+ ```
158
+
159
+ **Step 2: Add to `package.json` scripts**
160
+
161
+ ```json
162
+ "reset": "node scripts/reset.js"
163
+ ```
164
+
165
+ **Step 3: Verify**
166
+
167
+ ```bash
168
+ node --check scripts/reset.js
169
+ ```
170
+ Expected: no output (syntax OK).
171
+
172
+ ---
173
+
174
+ ### Task 2: `scripts/seed.js` - Demo Content
175
+
176
+ **Files:**
177
+ - Create: `scripts/seed.js`
178
+ - Modify: `package.json`
179
+
180
+ Writes five sample pages and updates navigation. Safe to run on a fresh install or alongside existing content (overwrites pages at the same paths). Does **not** touch users or `.env`.
181
+
182
+ **Pages created:**
183
+ | URL | File |
184
+ |-----|------|
185
+ | `/` | `content/pages/index.md` |
186
+ | `/about` | `content/pages/about.md` |
187
+ | `/contact` | `content/pages/contact.md` |
188
+ | `/blog` | `content/pages/blog/index.md` |
189
+ | `/blog/hello-world` | `content/pages/blog/hello-world.md` |
190
+
191
+ **Step 1: Create `scripts/seed.js`**
192
+
193
+ ```javascript
194
+ #!/usr/bin/env node
195
+ /**
196
+ * Domma CMS - Seed Demo Content
197
+ * Creates sample pages and updates navigation.
198
+ * Safe to run multiple times - overwrites seed paths only.
199
+ * Run: npm run seed
200
+ */
201
+ import { writeFile, mkdir } from 'node:fs/promises';
202
+ import path from 'node:path';
203
+ import { fileURLToPath } from 'node:url';
204
+
205
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
206
+ const ROOT = path.resolve(__dirname, '..');
207
+
208
+ const PAGES_DIR = path.join(ROOT, 'content', 'pages');
209
+ const NAV_CFG = path.join(ROOT, 'config', 'navigation.json');
210
+
211
+ const now = new Date().toISOString();
212
+
213
+ // ---------------------------------------------------------------------------
214
+ // Page definitions
215
+ // ---------------------------------------------------------------------------
216
+
217
+ const pages = [
218
+ {
219
+ file: 'index.md',
220
+ frontmatter: {
221
+ title: 'Welcome', slug: 'index',
222
+ description: 'Your new website, powered by Domma CMS',
223
+ layout: 'default', status: 'published',
224
+ sortOrder: 1, showInNav: false, sidebar: false,
225
+ seo: { title: 'Welcome', description: 'Your new website, powered by Domma CMS' },
226
+ createdAt: now, updatedAt: now
227
+ },
228
+ body: `# Welcome
229
+
230
+ This is your new website, powered by **Domma CMS**.
231
+
232
+ Edit this page from the [admin panel](/admin/), or modify \`content/pages/index.md\` directly.
233
+
234
+ ## What you can do
235
+
236
+ - **Write pages** - create and publish content in Markdown
237
+ - **Manage navigation** - build your site's menu from the admin panel
238
+ - **Upload media** - attach images and files to your pages
239
+ - **Choose a theme** - pick from 16 built-in colour schemes
240
+
241
+ [Sign in to the admin panel →](/admin/)
242
+ `
243
+ },
244
+ {
245
+ file: 'about.md',
246
+ frontmatter: {
247
+ title: 'About', slug: 'about',
248
+ description: 'Learn more about us',
249
+ layout: 'default', status: 'published',
250
+ sortOrder: 2, showInNav: true, sidebar: false,
251
+ seo: { title: 'About', description: 'Learn more about us' },
252
+ createdAt: now, updatedAt: now
253
+ },
254
+ body: `# About
255
+
256
+ This is the About page - tell your visitors who you are, what you do, and why it matters.
257
+
258
+ ## Our story
259
+
260
+ Replace this content with your own. You can edit it from the [admin panel](/admin/) at any time.
261
+
262
+ ## What we do
263
+
264
+ Add your services, mission statement, or team information here.
265
+ `
266
+ },
267
+ {
268
+ file: 'contact.md',
269
+ frontmatter: {
270
+ title: 'Contact', slug: 'contact',
271
+ description: 'Get in touch',
272
+ layout: 'default', status: 'published',
273
+ sortOrder: 4, showInNav: true, sidebar: false,
274
+ seo: { title: 'Contact', description: 'Get in touch with us' },
275
+ createdAt: now, updatedAt: now
276
+ },
277
+ body: `# Contact
278
+
279
+ Get in touch - we'd love to hear from you.
280
+
281
+ ## How to reach us
282
+
283
+ Replace this with your own contact details, a contact form, or whatever works best for your site.
284
+
285
+ You can edit this page from the [admin panel](/admin/).
286
+ `
287
+ },
288
+ {
289
+ file: path.join('blog', 'index.md'),
290
+ frontmatter: {
291
+ title: 'Blog', slug: 'blog',
292
+ description: 'News, updates and articles',
293
+ layout: 'default', status: 'published',
294
+ sortOrder: 3, showInNav: true, sidebar: false,
295
+ seo: { title: 'Blog', description: 'News, updates and articles' },
296
+ createdAt: now, updatedAt: now
297
+ },
298
+ body: `# Blog
299
+
300
+ Welcome to the blog. Read on for articles, updates and news.
301
+
302
+ ---
303
+
304
+ - [Hello, World!](/blog/hello-world) - Your first post
305
+ `
306
+ },
307
+ {
308
+ file: path.join('blog', 'hello-world.md'),
309
+ frontmatter: {
310
+ title: 'Hello, World!', slug: 'hello-world',
311
+ description: 'The first post on this site',
312
+ layout: 'default', status: 'published',
313
+ sortOrder: 1, showInNav: false, sidebar: false,
314
+ seo: { title: 'Hello, World!', description: 'The first post on this site' },
315
+ createdAt: now, updatedAt: now
316
+ },
317
+ body: `# Hello, World!
318
+
319
+ Welcome to your first blog post, created by the Domma CMS seed script.
320
+
321
+ Edit or delete this from the [admin panel](/admin/), then start writing your own content.
322
+
323
+ ## Writing in Markdown
324
+
325
+ Domma CMS uses **Markdown** for all page content:
326
+
327
+ - **Bold** with \`**double asterisks**\`
328
+ - *Italic* with \`*single asterisks*\`
329
+ - \`Inline code\` with backticks
330
+ - [Links](https://example.com) with \`[text](url)\`
331
+
332
+ > Blockquotes with \`>\`
333
+
334
+ \`\`\`
335
+ Code blocks with triple backticks
336
+ \`\`\`
337
+
338
+ Happy writing!
339
+ `
340
+ }
341
+ ];
342
+
343
+ // ---------------------------------------------------------------------------
344
+ // Seed navigation
345
+ // ---------------------------------------------------------------------------
346
+
347
+ const seedNav = {
348
+ brand: { text: 'My Site', logo: null, url: '/' },
349
+ items: [
350
+ { text: 'Home', url: '/', icon: 'home' },
351
+ { text: 'About', url: '/about', icon: 'info' },
352
+ { text: 'Blog', url: '/blog', icon: 'book-open' },
353
+ { text: 'Contact', url: '/contact', icon: 'mail' }
354
+ ],
355
+ variant: 'dark',
356
+ position: 'sticky'
357
+ };
358
+
359
+ // ---------------------------------------------------------------------------
360
+ // Helpers
361
+ // ---------------------------------------------------------------------------
362
+
363
+ /**
364
+ * Serialise frontmatter + body to a markdown file string.
365
+ * Uses the same YAML-frontmatter format gray-matter produces.
366
+ *
367
+ * @param {object} fm
368
+ * @param {string} body
369
+ * @returns {string}
370
+ */
371
+ function toMarkdown(fm, body) {
372
+ const yaml = Object.entries(fm).map(([k, v]) => {
373
+ if (v !== null && typeof v === 'object' && !Array.isArray(v)) {
374
+ const nested = Object.entries(v)
375
+ .map(([nk, nv]) => ` ${nk}: ${JSON.stringify(nv)}`)
376
+ .join('\n');
377
+ return `${k}:\n${nested}`;
378
+ }
379
+ return `${k}: ${JSON.stringify(v)}`;
380
+ }).join('\n');
381
+ return `---\n${yaml}\n---\n\n${body}`;
382
+ }
383
+
384
+ // ---------------------------------------------------------------------------
385
+ // Main
386
+ // ---------------------------------------------------------------------------
387
+
388
+ console.log('');
389
+ console.log(' ┌──────────────────────────────────┐');
390
+ console.log(' │ Domma CMS - Seed Demo Content │');
391
+ console.log(' └──────────────────────────────────┘');
392
+ console.log('');
393
+
394
+ for (const page of pages) {
395
+ const filePath = path.join(PAGES_DIR, page.file);
396
+ await mkdir(path.dirname(filePath), { recursive: true });
397
+ await writeFile(filePath, toMarkdown(page.frontmatter, page.body), 'utf8');
398
+ console.log(` ✓ Created /${page.file.replace(/\\/g, '/').replace('index.md', '').replace('.md', '') || ''}`);
399
+ }
400
+
401
+ process.stdout.write(' Updating navigation…');
402
+ await writeFile(NAV_CFG, JSON.stringify(seedNav, null, 4) + '\n', 'utf8');
403
+ console.log(' done.');
404
+
405
+ console.log('');
406
+ console.log(' ✓ Seed complete - 5 pages created, navigation updated.');
407
+ console.log(' Visit your site at http://localhost:4096/');
408
+ console.log('');
409
+ ```
410
+
411
+ **Step 2: Add to `package.json` scripts**
412
+
413
+ ```json
414
+ "seed": "node scripts/seed.js"
415
+ ```
416
+
417
+ **Step 3: Verify**
418
+
419
+ ```bash
420
+ node --check scripts/seed.js
421
+ ```
422
+ Expected: no output (syntax OK).
423
+
424
+ ---
425
+
426
+ ### Task 3: `scripts/fresh.js` - Reset → Setup → Seed
427
+
428
+ **Files:**
429
+ - Create: `scripts/fresh.js`
430
+ - Modify: `package.json`
431
+
432
+ Chains all three scripts in order with full stdio inheritance so interactive prompts (reset confirmation, setup questions) pass through directly to the terminal. Each sub-script runs in the same working directory with `cwd: ROOT`.
433
+
434
+ **Step 1: Create `scripts/fresh.js`**
435
+
436
+ ```javascript
437
+ #!/usr/bin/env node
438
+ /**
439
+ * Domma CMS - Fresh Install
440
+ * Chains: reset → setup → seed
441
+ * Gives you a fully configured CMS with demo content in one command.
442
+ * Run: npm run fresh
443
+ */
444
+ import { spawnSync } from 'node:child_process';
445
+ import path from 'node:path';
446
+ import { fileURLToPath } from 'node:url';
447
+
448
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
449
+ const ROOT = path.resolve(__dirname, '..');
450
+
451
+ function run(script) {
452
+ const result = spawnSync(process.execPath, [path.join('scripts', script)], {
453
+ cwd: ROOT,
454
+ stdio: 'inherit'
455
+ });
456
+ if (result.status !== 0) {
457
+ process.exit(result.status ?? 1);
458
+ }
459
+ }
460
+
461
+ console.log('');
462
+ console.log(' ╔══════════════════════════════════════╗');
463
+ console.log(' ║ Domma CMS - Fresh Install ║');
464
+ console.log(' ║ reset → setup → seed ║');
465
+ console.log(' ╚══════════════════════════════════════╝');
466
+
467
+ run('reset.js');
468
+ run('setup.js');
469
+ run('seed.js');
470
+
471
+ console.log('');
472
+ console.log(' ═══════════════════════════════════════════');
473
+ console.log(' Fresh install complete! Start the server:');
474
+ console.log(' npm start');
475
+ console.log(' ═══════════════════════════════════════════');
476
+ console.log('');
477
+ ```
478
+
479
+ **Step 2: Add to `package.json` scripts**
480
+
481
+ ```json
482
+ "fresh": "node scripts/fresh.js"
483
+ ```
484
+
485
+ **Step 3: Verify all three scripts**
486
+
487
+ ```bash
488
+ node --check scripts/reset.js && node --check scripts/seed.js && node --check scripts/fresh.js && echo "ALL OK"
489
+ ```
490
+ Expected: `ALL OK`
491
+
492
+ ---
493
+
494
+ ### Task 4: Final `package.json` - confirm all four scripts together
495
+
496
+ The scripts block should read:
497
+
498
+ ```json
499
+ "scripts": {
500
+ "start": "node server/server.js",
501
+ "dev": "node --watch server/server.js",
502
+ "setup": "node scripts/setup.js",
503
+ "reset": "node scripts/reset.js",
504
+ "seed": "node scripts/seed.js",
505
+ "fresh": "node scripts/fresh.js",
506
+ "copy-domma": "node scripts/copy-domma.js"
507
+ }
508
+ ```
509
+
510
+ ---
511
+
512
+ ### Verification Checklist
513
+
514
+ 1. `node --check scripts/reset.js` → no errors
515
+ 2. `node --check scripts/seed.js` → no errors
516
+ 3. `node --check scripts/fresh.js` → no errors
517
+ 4. `npm run seed` on a fresh install - creates 5 pages, check `content/pages/`
518
+ 5. `npm run reset` - type "yes", verify `content/users/` and `content/pages/` are empty, `config/site.json` is reset
519
+ 6. `npm run fresh` - walks through reset → setup → seed, server starts cleanly after
520
+ 7. Public site at `http://localhost:4096/` shows Home, About, Blog, Contact in nav
@@ -0,0 +1,86 @@
1
+ # Cookie Consent Plugin Design
2
+
3
+ **Date:** 2026-03-02
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ A CMS plugin that injects Domma's built-in `Domma.components.cookieConsent()` banner into every public page, with all
10
+ wording, appearance, and category settings configurable from the admin panel.
11
+
12
+ ## Decisions
13
+
14
+ - **Banner only** - no script blocking or GTM/GA integration. Stores consent preferences in localStorage via Domma's
15
+ component. Site owner hooks into consent themselves if needed.
16
+ - **All four categories** - Necessary (always required), Functional, Analytics, Marketing. Admin can toggle
17
+ Functional/Analytics/Marketing on or off.
18
+ - **Both layout and position configurable** - `bar` or `modal` layout; `top` or `bottom` position; `light` or `dark`
19
+ theme.
20
+ - **Uses Domma's component directly** - `inject-body.html` calls `Domma.components.cookieConsent(cfg)`. Domma is already
21
+ loaded on public pages via `site.js`, so no extra dependencies.
22
+
23
+ ## File Structure
24
+
25
+ ```
26
+ plugins/cookie-consent/
27
+ ├── plugin.json
28
+ ├── plugin.js - GET /settings (public), PUT /settings (admin-auth)
29
+ ├── config.js - defaults
30
+ ├── admin/
31
+ │ ├── views/cookie-consent-settings.js
32
+ │ └── templates/cookie-consent-settings.html
33
+ └── public/
34
+ └── inject-body.html - IIFE
35
+ ```
36
+
37
+ ## Config Defaults
38
+
39
+ | Key | Default | Notes |
40
+ |-----------------------|----------------------|--------------------------------|
41
+ | `message` | `'We use cookies…'` | Main banner text |
42
+ | `acceptAllText` | `'Accept All'` | Accept button label |
43
+ | `rejectAllText` | `'Reject All'` | Reject button label |
44
+ | `customizeText` | `'Customize'` | Customize button label |
45
+ | `savePreferencesText` | `'Save Preferences'` | Save button in customize panel |
46
+ | `privacyPolicyText` | `'Privacy Policy'` | Privacy link label |
47
+ | `privacyPolicyUrl` | `'/privacy-policy'` | Privacy link URL |
48
+ | `cookiePolicyText` | `'Cookie Policy'` | Cookie policy link label |
49
+ | `cookiePolicyUrl` | `''` | Empty = link not shown |
50
+ | `position` | `'bottom'` | `'bottom'` or `'top'` |
51
+ | `layout` | `'bar'` | `'bar'` or `'modal'` |
52
+ | `theme` | `'dark'` | `'light'` or `'dark'` |
53
+ | `showFunctional` | `true` | Show Functional category |
54
+ | `showAnalytics` | `true` | Show Analytics category |
55
+ | `showMarketing` | `true` | Show Marketing category |
56
+ | `consentVersion` | `'1.0'` | Increment to re-prompt users |
57
+
58
+ ## Admin Settings Form
59
+
60
+ **Banner Wording card:**
61
+
62
+ - Textarea: Main message
63
+ - Text inputs: Accept All, Reject All, Customize, Save Preferences button labels
64
+ - Text input + URL pairs: Privacy Policy, Cookie Policy (empty URL hides the link)
65
+
66
+ **Appearance & Categories card:**
67
+
68
+ - Select: Position (Bottom / Top)
69
+ - Select: Layout (Bar / Modal)
70
+ - Select: Theme (Dark / Light)
71
+ - Checkboxes: Show Functional, Show Analytics, Show Marketing categories
72
+ - Text input: Consent version
73
+
74
+ ## inject-body.html
75
+
76
+ IIFE that:
77
+
78
+ 1. Fetches `/api/plugins/cookie-consent/settings` (unauthenticated)
79
+ 2. Maps flat config → Domma `categories` object (only includes enabled categories)
80
+ 3. Calls `Domma.components.cookieConsent(cfg)`
81
+ 4. Catches errors silently (no banner shown if fetch fails)
82
+
83
+ ## Note on Public GET Endpoint
84
+
85
+ Same pattern as back-to-top: `GET /settings` is unauthenticated. Returns only display config - no credentials.
86
+ Intentional and safe.