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,695 @@
1
+ # Domma Docs Project - Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+ >
5
+ > **Commits:** This repo's owner commits separately - do NOT run `git commit`. Where a normal plan would commit, this plan has a **Checkpoint** step: stop, report what changed, and let the owner review/commit.
6
+ >
7
+ > **Environment:** Server is already running on port 4096 and must NOT be killed/restarted. Use `curl` for server-side checks. The owner is on SSH - live browser checks are at `http://192.168.1.4:4096`. Never claim a UI change is "verified" without a real browser load (per project convention).
8
+
9
+ **Goal:** Ship a built-in, disabled-by-default **Domma Docs** project whose pages are the CMS documentation, published on the public site under `/domma-docs/...` and removed from it when the project is toggled off.
10
+
11
+ **Architecture:** A new seeded project (`domma-docs`, `rootUrl: /domma-docs`, `enabled: false`, `protected: true`) reuses the *existing* public kill-switch (`public.js` 404s pages whose project is disabled). The ~30 admin documentation screens are ported into public Markdown pages (bodies lifted verbatim from the admin HTML templates) via a one-shot script, then the admin doc screens are retired.
12
+
13
+ **Tech Stack:** Fastify 5 (ESM), Node built-in test runner (`node --test`), file-based content (Markdown + YAML frontmatter), Domma SPA admin, `marked` + shortcode pipeline.
14
+
15
+ ---
16
+
17
+ ## File Structure
18
+
19
+ **Modify:**
20
+ - `server/services/projects.js` - add `DOCS_PROJECT_SLUG`, `DOCS_PROJECT_SEED`, `seedDocsProject()`; generalise `deleteProject()` and `untagAllForProject()` to refuse any `protected` project.
21
+ - `server/server.js` - call `seedDocsProject()` after `seedCoreProject()`.
22
+ - `server/routes/api/projects.js` - return 403 on delete/untag-all for `protected` projects.
23
+ - `public/css/site.css` - `.docs-body` styling.
24
+ - `public/js/site.js` - run `Domma.syntax.scan()` on rendered page content.
25
+ - `config/menus/admin-sidebar.json` - remove the hardcoded **Documentation** section.
26
+ - `server/services/sidebar-migration.js` - strip the **Documentation** section from already-seeded installs (idempotent).
27
+ - `admin/js/app.js` - remove the doc/tutorial routes (lines 104-136).
28
+ - `admin/js/views/index.js` - remove the doc view imports/registrations.
29
+
30
+ **Create:**
31
+ - `scripts/migrate-docs.js` - one-shot porter: admin doc templates → public page artefacts.
32
+ - `content/pages/domma-docs/index.md`, `usage/index.md`, `tutorials/index.md`, `api/index.md` - hand-authored landing pages.
33
+ - `content/pages/domma-docs/**` - generated by the migration script.
34
+
35
+ **Delete (Phase 3, after migration runs):**
36
+ - `admin/js/views/documentation.js`, `admin/js/views/tutorials.js`, `admin/js/views/doc-pages.js`
37
+ - `admin/js/templates/docs/*.html`
38
+
39
+ **Test:**
40
+ - `tests/services/projects.test.js` - seed + protected-guard tests.
41
+ - `tests/routes/projects.test.js` - route guard tests.
42
+
43
+ ---
44
+
45
+ ## Phase 0 - The engine (seed + undeletable guards)
46
+
47
+ ### Task 1: Seed the Domma Docs project
48
+
49
+ **Files:**
50
+ - Modify: `server/services/projects.js`
51
+ - Test: `tests/services/projects.test.js`
52
+
53
+ - [ ] **Step 1: Write the failing test**
54
+
55
+ Add to `tests/services/projects.test.js` (import `DOCS_PROJECT_SLUG, seedDocsProject` in the existing import block from `../../server/services/projects.js`):
56
+
57
+ ```js
58
+ test('seedDocsProject - creates a disabled, protected docs project after core', async () => {
59
+ await seedDocsProject();
60
+ await seedDocsProject(); // idempotent
61
+ const docs = await getProject(DOCS_PROJECT_SLUG);
62
+ assert.ok(docs, 'docs project exists');
63
+ assert.equal(docs.slug, 'domma-docs');
64
+ assert.equal(docs.name, 'Domma Docs');
65
+ assert.equal(docs.icon, 'document');
66
+ assert.equal(docs.rootUrl, '/domma-docs');
67
+ assert.equal(docs.enabled, false);
68
+ assert.equal(docs.protected, true);
69
+
70
+ // sortOrder 0 sits immediately after core (-1)
71
+ const all = await listProjects();
72
+ const core = all.findIndex(p => p.slug === CORE_PROJECT_SLUG);
73
+ const di = all.findIndex(p => p.slug === DOCS_PROJECT_SLUG);
74
+ assert.ok(core < di, 'core sorts before docs');
75
+ });
76
+
77
+ test('seedDocsProject - never clobbers an existing record (preserves enabled state)', async () => {
78
+ await seedDocsProject();
79
+ await updateProject(DOCS_PROJECT_SLUG, {enabled: true}); // admin turned it on
80
+ await seedDocsProject(); // reboot
81
+ assert.equal((await getProject(DOCS_PROJECT_SLUG)).enabled, true);
82
+ await updateProject(DOCS_PROJECT_SLUG, {enabled: false}); // reset for other tests
83
+ });
84
+ ```
85
+
86
+ - [ ] **Step 2: Run test to verify it fails**
87
+
88
+ Run: `npm test -- --test-name-pattern="seedDocsProject"`
89
+ Expected: FAIL - `seedDocsProject is not a function` / `is not exported`.
90
+
91
+ - [ ] **Step 3: Write minimal implementation**
92
+
93
+ In `server/services/projects.js`, after the `CORE_PROJECT_SLUG`/`CORE_PROJECT_SEED` block, add:
94
+
95
+ ```js
96
+ /** Slug of the built-in, disabled-by-default documentation project. */
97
+ export const DOCS_PROJECT_SLUG = 'domma-docs';
98
+
99
+ /** Seed record for the Domma Docs project (created at boot if absent). */
100
+ const DOCS_PROJECT_SEED = {
101
+ slug: DOCS_PROJECT_SLUG,
102
+ name: 'Domma Docs',
103
+ description: 'The Domma CMS handbook - guides, tutorials, component & API ' +
104
+ 'reference. Switch on to publish it on your own site.',
105
+ icon: 'document',
106
+ rootUrl: '/domma-docs',
107
+ sortOrder: 0,
108
+ enabled: false,
109
+ protected: true
110
+ };
111
+ ```
112
+
113
+ Then add the seed function next to `seedCoreProject`:
114
+
115
+ ```js
116
+ /**
117
+ * Seed the built-in Domma Docs project at boot. Create-if-absent by slug - an
118
+ * existing record (including the admin's enabled/disabled choice and any edits)
119
+ * is returned untouched. Disabled by default: a fresh site ships with the docs
120
+ * off until an admin enables the project.
121
+ *
122
+ * @returns {Promise<object>} The docs project record.
123
+ */
124
+ export async function seedDocsProject() {
125
+ const {entries} = await listEntries(PROJECTS_COLLECTION_SLUG, {limit: 0});
126
+ const existing = entries.find(e => e.data?.slug === DOCS_PROJECT_SLUG);
127
+ if (existing) return normalise(existing);
128
+ const now = new Date().toISOString();
129
+ const data = {...DOCS_PROJECT_SEED, createdAt: now, updatedAt: now};
130
+ await createEntry(PROJECTS_COLLECTION_SLUG, data);
131
+ return data;
132
+ }
133
+ ```
134
+
135
+ - [ ] **Step 4: Run test to verify it passes**
136
+
137
+ Run: `npm test -- --test-name-pattern="seedDocsProject"`
138
+ Expected: PASS (both tests).
139
+
140
+ - [ ] **Step 5: Checkpoint** - report the new export + tests; pause for owner review.
141
+
142
+ ---
143
+
144
+ ### Task 2: Make `protected` projects undeletable (service layer)
145
+
146
+ **Files:**
147
+ - Modify: `server/services/projects.js` (`deleteProject`, `untagAllForProject`)
148
+ - Test: `tests/services/projects.test.js`
149
+
150
+ - [ ] **Step 1: Write the failing test**
151
+
152
+ ```js
153
+ test('deleteProject - refuses to delete a protected project', async () => {
154
+ await seedDocsProject();
155
+ await assert.rejects(() => deleteProject(DOCS_PROJECT_SLUG), /protected/i);
156
+ assert.ok(await getProject(DOCS_PROJECT_SLUG), 'still present');
157
+ });
158
+
159
+ test('untagAllForProject - refuses a protected project', async () => {
160
+ await seedDocsProject();
161
+ await assert.rejects(() => untagAllForProject(DOCS_PROJECT_SLUG), /protected/i);
162
+ });
163
+ ```
164
+
165
+ - [ ] **Step 2: Run test to verify it fails**
166
+
167
+ Run: `npm test -- --test-name-pattern="protected project"`
168
+ Expected: FAIL - `deleteProject` resolves/deletes instead of rejecting.
169
+
170
+ - [ ] **Step 3: Write minimal implementation**
171
+
172
+ In `deleteProject`, after the entry is found and before `deleteEntry`:
173
+
174
+ ```js
175
+ const entry = entries.find(e => e.data?.slug === slug);
176
+ if (!entry) return false;
177
+ if (entry.data?.protected === true) {
178
+ throw new Error(`Cannot delete the protected "${slug}" project`);
179
+ }
180
+ await deleteEntry(PROJECTS_COLLECTION_SLUG, entry.id);
181
+ return true;
182
+ ```
183
+
184
+ In `untagAllForProject`, right after the existing core check:
185
+
186
+ ```js
187
+ if (projectSlug === CORE_PROJECT_SLUG) {
188
+ throw new Error('Cannot untag the core project - artefacts would immediately resolve back to it');
189
+ }
190
+ const proj = await getProject(projectSlug);
191
+ if (proj?.protected === true) {
192
+ throw new Error(`Cannot untag the protected "${projectSlug}" project - its pages would resolve back to core`);
193
+ }
194
+ ```
195
+
196
+ - [ ] **Step 4: Run test to verify it passes**
197
+
198
+ Run: `npm test -- --test-name-pattern="protected project"`
199
+ Expected: PASS. Also run the full pre-existing core guard test to confirm no regression:
200
+ `npm test -- --test-name-pattern="refuses to delete core"` → PASS.
201
+
202
+ - [ ] **Step 5: Checkpoint** - pause for review.
203
+
204
+ ---
205
+
206
+ ### Task 3: Route-level guards + boot wiring
207
+
208
+ **Files:**
209
+ - Modify: `server/routes/api/projects.js`
210
+ - Modify: `server/server.js`
211
+ - Test: `tests/routes/projects.test.js`
212
+
213
+ - [ ] **Step 1: Write the failing test**
214
+
215
+ Open `tests/routes/projects.test.js`, mirror its existing app-bootstrap pattern (it already builds a Fastify instance and authenticates an admin). Add:
216
+
217
+ ```js
218
+ test('DELETE /api/projects/domma-docs → 403 (protected)', async () => {
219
+ const res = await app.inject({
220
+ method: 'DELETE', url: '/api/projects/domma-docs',
221
+ headers: {authorization: `Bearer ${adminToken}`}
222
+ });
223
+ assert.equal(res.statusCode, 403);
224
+ });
225
+
226
+ test('POST /api/projects/domma-docs/untag-all → 403 (protected)', async () => {
227
+ const res = await app.inject({
228
+ method: 'POST', url: '/api/projects/domma-docs/untag-all',
229
+ headers: {authorization: `Bearer ${adminToken}`}
230
+ });
231
+ assert.equal(res.statusCode, 403);
232
+ });
233
+ ```
234
+
235
+ (If the test file's `before` hook does not already seed the docs project, add `await seedDocsProject();` there, importing it from `../../server/services/projects.js`.)
236
+
237
+ - [ ] **Step 2: Run test to verify it fails**
238
+
239
+ Run: `npm test -- tests/routes/projects.test.js`
240
+ Expected: FAIL - delete returns 200/409 (or untag returns 200) instead of 403.
241
+
242
+ - [ ] **Step 3: Write minimal implementation**
243
+
244
+ In `server/routes/api/projects.js`, import `getProject` from the service (add to the existing import block). In the `fastify.delete('/projects/:slug', ...)` handler, after the existing core check:
245
+
246
+ ```js
247
+ if (request.params.slug === CORE_PROJECT_SLUG) {
248
+ return reply.status(403).send({error: 'Cannot delete the core project'});
249
+ }
250
+ const target = await getProject(request.params.slug);
251
+ if (target?.protected === true) {
252
+ return reply.status(403).send({error: 'This project is protected and cannot be deleted'});
253
+ }
254
+ ```
255
+
256
+ In the `fastify.post('/projects/:slug/untag-all', ...)` handler, after its core check:
257
+
258
+ ```js
259
+ const target = await getProject(request.params.slug);
260
+ if (target?.protected === true) {
261
+ return reply.status(403).send({error: 'This project is protected and cannot be untagged'});
262
+ }
263
+ ```
264
+
265
+ In `server/server.js`: extend the existing import to
266
+ `import {seedCoreProject, seedDocsProject} from './services/projects.js';`
267
+ and add immediately after the `await seedCoreProject();` line:
268
+
269
+ ```js
270
+ await seedDocsProject();
271
+ ```
272
+
273
+ - [ ] **Step 4: Run test to verify it passes**
274
+
275
+ Run: `npm test -- tests/routes/projects.test.js`
276
+ Expected: PASS.
277
+
278
+ - [ ] **Step 5: Verify boot seeds it live**
279
+
280
+ Run: `curl -s localhost:4096/ >/dev/null; ls content/collections/projects/data.json && grep -c '"domma-docs"' content/collections/projects/data.json`
281
+ Expected: `1` (server has restarted via the owner's running process, or seed ran on next boot). If the running server predates the change, note that the seed applies on its next restart - do NOT restart it yourself; report this to the owner.
282
+
283
+ - [ ] **Step 6: Checkpoint** - pause for review.
284
+
285
+ ---
286
+
287
+ ## Phase 1 - Public rendering polish
288
+
289
+ ### Task 4: `.docs-body` styling + code highlighting on public pages
290
+
291
+ **Files:**
292
+ - Modify: `public/css/site.css`
293
+ - Modify: `public/js/site.js`
294
+
295
+ - [ ] **Step 1: Add docs styling**
296
+
297
+ Append to `public/css/site.css`:
298
+
299
+ ```css
300
+ /* Domma Docs project - ported documentation pages */
301
+ .docs-body { max-width: 60rem; }
302
+ .docs-body h2 { margin-top: 2rem; }
303
+ .docs-body h3 { margin-top: 1.5rem; }
304
+ .docs-body table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
305
+ .docs-body th, .docs-body td { border: 1px solid var(--dm-border, #ddd); padding: .5rem .75rem; text-align: left; }
306
+ .docs-body pre { overflow-x: auto; }
307
+ .docs-body code { font-size: .9em; }
308
+ ```
309
+
310
+ - [ ] **Step 2: Add syntax highlighting to the public render path**
311
+
312
+ In `public/js/site.js`, find where page content is scanned for icons after the body renders (search for `Domma.icons.scan();` near line ~304 and the post-fragment rescan near ~704). Immediately after each `Domma.icons.scan(...)` that runs over rendered page content, add:
313
+
314
+ ```js
315
+ if (typeof Domma !== 'undefined' && Domma.syntax?.scan) Domma.syntax.scan();
316
+ ```
317
+
318
+ - [ ] **Step 3: Verify (browser, live)** - deferred until a pilot page exists (Task 6). No standalone check here.
319
+
320
+ - [ ] **Step 4: Checkpoint** - pause for review.
321
+
322
+ ---
323
+
324
+ ## Phase 2 - Content migration
325
+
326
+ ### Task 5: The migration script
327
+
328
+ **Files:**
329
+ - Create: `scripts/migrate-docs.js`
330
+
331
+ - [ ] **Step 1: Write the script**
332
+
333
+ ```js
334
+ /**
335
+ * One-shot porter: admin documentation screens → public page artefacts under
336
+ * the Domma Docs project. Reads each admin HTML template, strips the admin-only
337
+ * view header, wraps the body in page frontmatter tagged `project: domma-docs`,
338
+ * and writes content/pages/domma-docs/<path>.md. Re-runnable (overwrites).
339
+ *
340
+ * Run: node scripts/migrate-docs.js
341
+ */
342
+ import {mkdirSync, readFileSync, writeFileSync} from 'node:fs';
343
+ import {dirname, join} from 'node:path';
344
+
345
+ const TPL = 'admin/js/templates/docs';
346
+ const OUT = 'content/pages/domma-docs';
347
+
348
+ // template basename → {path (URL tail = file path), title}
349
+ const MAP = [
350
+ ['usage-pages', 'usage/pages', 'Pages'],
351
+ ['usage-media', 'usage/media', 'Media'],
352
+ ['usage-navigation', 'usage/navigation', 'Navigation'],
353
+ ['usage-dconfig', 'usage/dconfig', 'DConfig'],
354
+ ['usage-shortcodes', 'usage/shortcodes', 'Shortcodes'],
355
+ ['usage-site-settings', 'usage/site-settings', 'Site Settings'],
356
+ ['usage-plugins', 'usage/plugins', 'Plugins'],
357
+ ['usage-users-roles', 'usage/users-roles', 'Users & Roles'],
358
+ ['usage-views', 'usage/views', 'Views'],
359
+ ['usage-actions', 'usage/actions', 'Actions'],
360
+ ['usage-cta-shortcode', 'usage/cta-shortcode', 'CTA Shortcode'],
361
+ ['tutorial-crud', 'tutorials/crud', 'Building a CRUD App'],
362
+ ['tutorial-plugin', 'tutorials/plugin', 'Writing a Plugin'],
363
+ ['tutorial-forms', 'tutorials/forms', 'Form Follow-Up'],
364
+ ['components-reference', 'components', 'Components Reference'],
365
+ ['components-howto', 'components/howto', 'Components How-To'],
366
+ ['components-walkthrough','components/walkthrough','Components Walkthrough'],
367
+ ['components-rules', 'components/rules', 'Components Rules'],
368
+ ['api-authentication', 'api/authentication', 'Authentication'],
369
+ ['api-pages', 'api/pages', 'Pages API'],
370
+ ['api-settings', 'api/settings', 'Settings API'],
371
+ ['api-layouts', 'api/layouts', 'Layouts API'],
372
+ ['api-navigation', 'api/navigation', 'Navigation API'],
373
+ ['api-media', 'api/media', 'Media API'],
374
+ ['api-users', 'api/users', 'Users API'],
375
+ ['api-plugins', 'api/plugins', 'Plugins API'],
376
+ ['api-collections', 'api/collections', 'Collections API'],
377
+ ['api-views', 'api/views', 'Views API'],
378
+ ['api-actions', 'api/actions', 'Actions API']
379
+ ];
380
+
381
+ // Drop the admin-only header block (<div class="view-header"> ... </div>).
382
+ // The header contains no nested <div>, so a non-greedy match to the first
383
+ // </div> is correct.
384
+ function stripHeader(html) {
385
+ return html.replace(/<div class="view-header">[\s\S]*?<\/div>\s*/, '');
386
+ }
387
+
388
+ function frontmatter(title) {
389
+ return `---\ntitle: ${title}\nlayout: default\nvisibility: public\nproject: domma-docs\n---\n\n`;
390
+ }
391
+
392
+ let n = 0;
393
+ for (const [tpl, path, title] of MAP) {
394
+ const src = readFileSync(join(TPL, `${tpl}.html`), 'utf8');
395
+ const body = stripHeader(src).trim();
396
+ const dest = join(OUT, `${path}.md`);
397
+ mkdirSync(dirname(dest), {recursive: true});
398
+ writeFileSync(dest, frontmatter(title) + body + '\n');
399
+ n++;
400
+ }
401
+ console.log(`[migrate-docs] wrote ${n} pages to ${OUT}`);
402
+ ```
403
+
404
+ - [ ] **Step 2: Run the script**
405
+
406
+ Run: `node scripts/migrate-docs.js`
407
+ Expected: `[migrate-docs] wrote 29 pages to content/pages/domma-docs`
408
+
409
+ - [ ] **Step 3: Spot-check output**
410
+
411
+ Run: `head -8 content/pages/domma-docs/usage/pages.md`
412
+ Expected: frontmatter with `project: domma-docs`, then the page HTML with the `view-header` gone.
413
+
414
+ - [ ] **Step 4: Checkpoint** - pause for review.
415
+
416
+ ---
417
+
418
+ ### Task 6: Hand-authored landing pages
419
+
420
+ **Files:**
421
+ - Create: `content/pages/domma-docs/index.md`, `usage/index.md`, `tutorials/index.md`, `api/index.md`
422
+
423
+ - [ ] **Step 1: Write the top landing page**
424
+
425
+ `content/pages/domma-docs/index.md`:
426
+
427
+ ```markdown
428
+ ---
429
+ title: Domma Docs
430
+ layout: default
431
+ visibility: public
432
+ project: domma-docs
433
+ ---
434
+
435
+ # Domma CMS Documentation
436
+
437
+ [grid cols="2" gap="3"]
438
+ [col][card title="Usage"]Day-to-day guides for pages, media, navigation, shortcodes, plugins and more. [link href="/domma-docs/usage"]Browse usage →[/link][/card][/col]
439
+ [col][card title="Tutorials"]Step-by-step walkthroughs. [link href="/domma-docs/tutorials"]Browse tutorials →[/link][/card][/col]
440
+ [col][card title="Components"]The component reference and how-to. [link href="/domma-docs/components"]Browse components →[/link][/card][/col]
441
+ [col][card title="API Reference"]REST endpoints for headless use. [link href="/domma-docs/api"]Browse the API →[/link][/card][/col]
442
+ [/grid]
443
+ ```
444
+
445
+ - [ ] **Step 2: Write the three group landings**
446
+
447
+ `content/pages/domma-docs/usage/index.md` (links to each usage page):
448
+
449
+ ```markdown
450
+ ---
451
+ title: Usage
452
+ layout: default
453
+ visibility: public
454
+ project: domma-docs
455
+ ---
456
+
457
+ # Usage
458
+
459
+ [listgroup]
460
+ [item][link href="/domma-docs/usage/pages"]Pages[/link][/item]
461
+ [item][link href="/domma-docs/usage/media"]Media[/link][/item]
462
+ [item][link href="/domma-docs/usage/navigation"]Navigation[/link][/item]
463
+ [item][link href="/domma-docs/usage/dconfig"]DConfig[/link][/item]
464
+ [item][link href="/domma-docs/usage/shortcodes"]Shortcodes[/link][/item]
465
+ [item][link href="/domma-docs/usage/site-settings"]Site Settings[/link][/item]
466
+ [item][link href="/domma-docs/usage/plugins"]Plugins[/link][/item]
467
+ [item][link href="/domma-docs/usage/users-roles"]Users & Roles[/link][/item]
468
+ [item][link href="/domma-docs/usage/views"]Views[/link][/item]
469
+ [item][link href="/domma-docs/usage/actions"]Actions[/link][/item]
470
+ [item][link href="/domma-docs/usage/cta-shortcode"]CTA Shortcode[/link][/item]
471
+ [/listgroup]
472
+ ```
473
+
474
+ `content/pages/domma-docs/tutorials/index.md`:
475
+
476
+ ```markdown
477
+ ---
478
+ title: Tutorials
479
+ layout: default
480
+ visibility: public
481
+ project: domma-docs
482
+ ---
483
+
484
+ # Tutorials
485
+
486
+ [listgroup]
487
+ [item][link href="/domma-docs/tutorials/crud"]Building a CRUD App[/link][/item]
488
+ [item][link href="/domma-docs/tutorials/plugin"]Writing a Plugin[/link][/item]
489
+ [item][link href="/domma-docs/tutorials/forms"]Form Follow-Up[/link][/item]
490
+ [/listgroup]
491
+ ```
492
+
493
+ `content/pages/domma-docs/api/index.md`:
494
+
495
+ ```markdown
496
+ ---
497
+ title: API Reference
498
+ layout: default
499
+ visibility: public
500
+ project: domma-docs
501
+ ---
502
+
503
+ # API Reference
504
+
505
+ [listgroup]
506
+ [item][link href="/domma-docs/api/authentication"]Authentication[/link][/item]
507
+ [item][link href="/domma-docs/api/pages"]Pages[/link][/item]
508
+ [item][link href="/domma-docs/api/settings"]Settings[/link][/item]
509
+ [item][link href="/domma-docs/api/layouts"]Layouts[/link][/item]
510
+ [item][link href="/domma-docs/api/navigation"]Navigation[/link][/item]
511
+ [item][link href="/domma-docs/api/media"]Media[/link][/item]
512
+ [item][link href="/domma-docs/api/users"]Users[/link][/item]
513
+ [item][link href="/domma-docs/api/plugins"]Plugins[/link][/item]
514
+ [item][link href="/domma-docs/api/collections"]Collections[/link][/item]
515
+ [item][link href="/domma-docs/api/views"]Views[/link][/item]
516
+ [item][link href="/domma-docs/api/actions"]Actions[/link][/item]
517
+ [/listgroup]
518
+ ```
519
+
520
+ - [ ] **Step 3: Checkpoint** - pause for review.
521
+
522
+ ---
523
+
524
+ ### Task 7: Live end-to-end verification of the toggle + pilot pages
525
+
526
+ **Files:** none (verification only). Requires the owner's running server to have picked up the Phase 0 boot change. If it has not restarted, ask the owner to confirm a restart before this task - do not restart it yourself.
527
+
528
+ - [ ] **Step 1: Confirm disabled ⇒ 404 (kill-switch)**
529
+
530
+ With Domma Docs disabled (default), run:
531
+ ```
532
+ curl -s -o /dev/null -w "%{http_code}\n" localhost:4096/domma-docs
533
+ curl -s -o /dev/null -w "%{http_code}\n" localhost:4096/domma-docs/usage/shortcodes
534
+ ```
535
+ Expected: `404` for both.
536
+
537
+ - [ ] **Step 2: Enable the project**
538
+
539
+ Via the admin Projects UI (or API): set `domma-docs` `enabled: true`. Confirm:
540
+ `curl -s localhost:4096/api/projects | grep -o '"domma-docs"[^}]*"enabled":true' | head -1` → a match.
541
+
542
+ - [ ] **Step 3: Confirm enabled ⇒ 200 + content**
543
+
544
+ ```
545
+ curl -s -o /dev/null -w "%{http_code}\n" localhost:4096/domma-docs
546
+ curl -s localhost:4096/domma-docs/usage/shortcodes | grep -c "docs-body"
547
+ ```
548
+ Expected: `200`, and the shortcodes page contains `docs-body`.
549
+
550
+ - [ ] **Step 4: Browser fidelity check (pilot pages)** - load in a real browser at `http://192.168.1.4:4096`:
551
+ - `/domma-docs` - landing card grid renders.
552
+ - `/domma-docs/usage/shortcodes` - **critical**: confirm the `[card]`/`[grid]` examples render as *literal code*, not expanded components (validates `scrubCodeRegions` over the ported `<pre>` blocks). Confirm code blocks are syntax-highlighted and icons render.
553
+ - `/domma-docs/api/collections` - tables render with `.docs-body` styling.
554
+
555
+ Report findings explicitly. If any shortcode example is wrongly expanded, fix by escaping brackets within `<pre>` in `scripts/migrate-docs.js` (replace `[` → `&#91;` inside `<pre>…</pre>` regions) and re-run the script.
556
+
557
+ - [ ] **Step 5: Confirm disabling re-hides**
558
+
559
+ Set `domma-docs` `enabled: false`, then repeat Step 1 → expect `404`. Leave it disabled (the default).
560
+
561
+ - [ ] **Step 6: Checkpoint** - report the full enabled/disabled matrix; pause for review.
562
+
563
+ ---
564
+
565
+ ## Phase 3 - Retire the admin documentation screens
566
+
567
+ > Only after Task 7 passes - the migration script reads the admin templates, so they must survive until the pages are generated and verified.
568
+
569
+ ### Task 8: Remove the admin Documentation menu section
570
+
571
+ **Files:**
572
+ - Modify: `config/menus/admin-sidebar.json`
573
+ - Modify: `server/services/sidebar-migration.js`
574
+ - Test: `tests/services/sidebar-migration.test.js`
575
+
576
+ - [ ] **Step 1: Write the failing test**
577
+
578
+ In `tests/services/sidebar-migration.test.js`, add a test that the migration removes a top-level item whose `text` is `Documentation`. Mirror the file's existing helper for building a menu fixture and invoking the migration. Example shape:
579
+
580
+ ```js
581
+ test('migration strips the legacy Documentation section', async () => {
582
+ const menu = {slug: 'admin-sidebar', items: [
583
+ {text: 'Overview', items: []},
584
+ {text: 'Documentation', items: [{text: 'Usage', items: []}]}
585
+ ]};
586
+ const out = stripDocumentationSection(menu); // new export
587
+ assert.equal(out.items.some(i => i.text === 'Documentation'), false);
588
+ // idempotent
589
+ assert.deepEqual(stripDocumentationSection(out), out);
590
+ });
591
+ ```
592
+
593
+ - [ ] **Step 2: Run test to verify it fails**
594
+
595
+ Run: `npm test -- tests/services/sidebar-migration.test.js`
596
+ Expected: FAIL - `stripDocumentationSection is not exported`.
597
+
598
+ - [ ] **Step 3: Implement + wire**
599
+
600
+ In `server/services/sidebar-migration.js` add and export:
601
+
602
+ ```js
603
+ /**
604
+ * Remove the legacy top-level "Documentation" section from a persisted
605
+ * admin-sidebar menu. The docs now live as the Domma Docs project's pages.
606
+ * Idempotent; returns the (possibly unchanged) menu object.
607
+ *
608
+ * @param {{items?: Array<object>}} menu
609
+ * @returns {object}
610
+ */
611
+ export function stripDocumentationSection(menu) {
612
+ if (!menu || !Array.isArray(menu.items)) return menu;
613
+ const items = menu.items.filter(i => (i?.text || '') !== 'Documentation');
614
+ return items.length === menu.items.length ? menu : {...menu, items};
615
+ }
616
+ ```
617
+
618
+ Call it in the same place the existing sidebar migrations run at boot (follow the pattern of `ensureSidebarItem`/the migration entry point already in this file): load the `admin-sidebar` menu, apply `stripDocumentationSection`, and persist only if changed.
619
+
620
+ Then remove the `Documentation` object from `config/menus/admin-sidebar.json` (this install's live menu) so fresh boots never seed it.
621
+
622
+ - [ ] **Step 4: Run test to verify it passes**
623
+
624
+ Run: `npm test -- tests/services/sidebar-migration.test.js`
625
+ Expected: PASS.
626
+
627
+ - [ ] **Step 5: Browser check** - load the admin at `http://192.168.1.4:4096/admin` in a real browser; confirm the **Documentation** section is gone from the sidebar and the **Projects** section still renders. Report explicitly.
628
+
629
+ - [ ] **Step 6: Checkpoint** - pause for review.
630
+
631
+ ---
632
+
633
+ ### Task 9: Remove the admin doc routes + views
634
+
635
+ **Files:**
636
+ - Modify: `admin/js/app.js` (remove lines 104-136 - the `/documentation`, `/tutorials*`, `/docs/*` route entries)
637
+ - Modify: `admin/js/views/index.js` (remove imports at lines 21-23 and registrations `documentation:`, `tutorials:`, `...docViews`)
638
+ - Delete: `admin/js/views/documentation.js`, `admin/js/views/tutorials.js`, `admin/js/views/doc-pages.js`
639
+ - Delete: `admin/js/templates/docs/*.html`
640
+
641
+ - [ ] **Step 1: Remove the route block** in `admin/js/app.js` - delete every route object from `{path: '/documentation', ...}` through `{path: '/docs/api/actions', ...}` inclusive. Confirm none remain:
642
+
643
+ Run: `grep -cE "path: '/(docs|tutorials|documentation)" admin/js/app.js`
644
+ Expected: `0`
645
+
646
+ - [ ] **Step 2: Remove the view registrations** in `admin/js/views/index.js` - delete the three imports (`documentationView`, `tutorialsView`, `docViews`) and the three registry lines (`documentation:`, `tutorials:`, `...docViews`). Confirm:
647
+
648
+ Run: `grep -cE "documentationView|tutorialsView|docViews" admin/js/views/index.js`
649
+ Expected: `0`
650
+
651
+ - [ ] **Step 3: Delete the now-orphaned files**
652
+
653
+ Run:
654
+ ```
655
+ rm admin/js/views/documentation.js admin/js/views/tutorials.js admin/js/views/doc-pages.js
656
+ rm -r admin/js/templates/docs
657
+ ```
658
+
659
+ - [ ] **Step 4: Verify no dangling references**
660
+
661
+ Run: `grep -rnE "doc-pages|templates/docs|tutorialsView|documentationView|docViews|crud-tutorial" admin/js | grep -v "node_modules"`
662
+ Expected: no matches (the `crud-tutorial.js` lib import lived only in `doc-pages.js`; if `crud-tutorial.js` is now unreferenced, delete it too and re-run).
663
+
664
+ - [ ] **Step 5: Browser smoke test** - load `http://192.168.1.4:4096/admin`, navigate a few sections (Dashboard, Pages, Projects). Confirm no console errors from the removed views and that visiting an old hash like `#/docs/usage/pages` lands on the SPA's not-found/route-miss handling rather than throwing. Report explicitly.
665
+
666
+ - [ ] **Step 6: Checkpoint** - pause for review.
667
+
668
+ ---
669
+
670
+ ## Final verification
671
+
672
+ - [ ] **Full test suite**
673
+
674
+ Run: `npm test`
675
+ Expected: all pass (note any pre-existing unrelated failures separately).
676
+
677
+ - [ ] **End-to-end matrix (browser, live `http://192.168.1.4:4096`)** - report each explicitly:
678
+ - Domma Docs **disabled** (default): `/domma-docs/*` → 404; admin sidebar has **no** Documentation section; Projects section present.
679
+ - Domma Docs **enabled**: `/domma-docs` + a usage/api/tutorial page render with styling, highlighting, icons; shortcode examples on the shortcodes page are literal; admin Projects sidebar shows **Domma Docs** with a `Pages (N)` child; the project's delete control is disabled.
680
+ - Toggle back to **disabled**: pages 404 again.
681
+
682
+ - [ ] **Hand off to owner for commit** (this repo's owner commits).
683
+
684
+ ---
685
+
686
+ ## Spec coverage check
687
+
688
+ - Seed project (slug/name/icon/rootUrl/sortOrder/disabled/protected) → Task 1 ✓
689
+ - Undeletable, toggleable, rootUrl editable (not Core's locks) → Tasks 2-3 ✓
690
+ - Docs as public pages tagged `project: domma-docs`, bodies lifted from templates → Tasks 5-6 ✓
691
+ - Public `.docs-body` CSS + code highlighting → Task 4 ✓
692
+ - Kill-switch (disabled ⇒ 404) reused, not rebuilt → verified Task 7 ✓
693
+ - Retire admin Documentation section/routes/views/templates → Tasks 8-9 ✓
694
+ - Caveat: interactive CRUD scaffolder degrades to static → inherent in Task 5 (no scaffolder mount on public page); confirmed at Task 7 Step 4 ✓
695
+ - Caveat: shortcode examples must stay literal → Task 7 Step 4 explicit check + escape fallback ✓