domma-cms 0.43.1 → 0.45.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 (251) hide show
  1. package/CLAUDE.md +53 -53
  2. package/README.md +27 -27
  3. package/admin/css/admin.css +1 -1
  4. package/admin/dist/domma/domma-tools.css +3 -3
  5. package/admin/dist/domma/domma-tools.min.js +3 -3
  6. package/admin/index.html +2 -2
  7. package/admin/js/app.js +1 -1
  8. package/admin/js/lib/card-builder.js +2 -2
  9. package/admin/js/lib/crud-tutorial.js +1 -1
  10. package/admin/js/lib/effect-defs.js +1 -1
  11. package/admin/js/lib/effects-builder.js +1 -1
  12. package/admin/js/lib/image-editor.js +1 -1
  13. package/admin/js/lib/markdown-toolbar.js +2 -2
  14. package/admin/js/lib/project-context.js +1 -1
  15. package/admin/js/lib/project-quick-create.js +1 -1
  16. package/admin/js/lib/scribe-composer.js +1 -1
  17. package/admin/js/lib/server-restart.js +1 -1
  18. package/admin/js/lib/shortcode-context-menu.js +2 -2
  19. package/admin/js/lib/simple-editor.js +1 -1
  20. package/admin/js/lib/themes.js +1 -1
  21. package/admin/js/lib/timeline-builder.js +1 -1
  22. package/admin/js/templates/action-editor.html +8 -8
  23. package/admin/js/templates/api-endpoint-editor.html +7 -7
  24. package/admin/js/templates/api-endpoints.html +1 -1
  25. package/admin/js/templates/api-reference.html +1 -1
  26. package/admin/js/templates/block-editor.html +6 -6
  27. package/admin/js/templates/collection-editor.html +1 -1
  28. package/admin/js/templates/component-editor.html +2 -2
  29. package/admin/js/templates/dashboard/cache.html +3 -3
  30. package/admin/js/templates/dashboard/journeys.html +3 -3
  31. package/admin/js/templates/dashboard/kpi-strip.html +3 -3
  32. package/admin/js/templates/dashboard/top-pages.html +1 -1
  33. package/admin/js/templates/dashboard/traffic-chart.html +1 -1
  34. package/admin/js/templates/docs/api-actions.html +1 -1
  35. package/admin/js/templates/docs/api-collections.html +4 -4
  36. package/admin/js/templates/docs/api-pages.html +2 -2
  37. package/admin/js/templates/docs/api-plugins.html +1 -1
  38. package/admin/js/templates/docs/api-settings.html +2 -2
  39. package/admin/js/templates/docs/api-users.html +1 -1
  40. package/admin/js/templates/docs/components-howto.html +4 -4
  41. package/admin/js/templates/docs/components-reference.html +21 -21
  42. package/admin/js/templates/docs/components-rules.html +14 -14
  43. package/admin/js/templates/docs/components-walkthrough.html +13 -13
  44. package/admin/js/templates/docs/tutorial-crud.html +49 -49
  45. package/admin/js/templates/docs/tutorial-forms.html +4 -4
  46. package/admin/js/templates/docs/tutorial-plugin.html +10 -10
  47. package/admin/js/templates/docs/usage-actions.html +6 -6
  48. package/admin/js/templates/docs/usage-cta-shortcode.html +9 -9
  49. package/admin/js/templates/docs/usage-dconfig.html +4 -4
  50. package/admin/js/templates/docs/usage-navigation.html +1 -1
  51. package/admin/js/templates/docs/usage-pages.html +1 -1
  52. package/admin/js/templates/docs/usage-shortcodes.html +24 -24
  53. package/admin/js/templates/docs/usage-users-roles.html +3 -3
  54. package/admin/js/templates/docs/usage-views.html +4 -4
  55. package/admin/js/templates/documentation.html +1 -1
  56. package/admin/js/templates/effects.html +25 -25
  57. package/admin/js/templates/form-editor.html +5 -5
  58. package/admin/js/templates/login.html +5 -5
  59. package/admin/js/templates/menu-editor.html +10 -10
  60. package/admin/js/templates/navigation.html +11 -11
  61. package/admin/js/templates/notifications.html +1 -1
  62. package/admin/js/templates/page-editor.html +4 -4
  63. package/admin/js/templates/pro-docs.html +14 -14
  64. package/admin/js/templates/role-editor.html +1 -1
  65. package/admin/js/templates/settings.html +10 -10
  66. package/admin/js/templates/tutorials.html +3 -3
  67. package/admin/js/templates/view-editor.html +15 -15
  68. package/admin/js/views/action-editor.js +1 -1
  69. package/admin/js/views/actions-list.js +1 -1
  70. package/admin/js/views/api-endpoint-editor.js +2 -2
  71. package/admin/js/views/api-endpoints.js +2 -2
  72. package/admin/js/views/api-tokens.js +3 -3
  73. package/admin/js/views/blocks.js +2 -2
  74. package/admin/js/views/collection-editor.js +1 -1
  75. package/admin/js/views/collection-entries.js +1 -1
  76. package/admin/js/views/collections.js +1 -1
  77. package/admin/js/views/component-editor.js +1 -1
  78. package/admin/js/views/components.js +4 -4
  79. package/admin/js/views/dashboard/widgets/cache.js +1 -1
  80. package/admin/js/views/dashboard/widgets/journeys.js +1 -1
  81. package/admin/js/views/dashboard/widgets/kpi-strip.js +1 -1
  82. package/admin/js/views/form-editor.js +7 -7
  83. package/admin/js/views/form-submissions.js +1 -1
  84. package/admin/js/views/forms.js +1 -1
  85. package/admin/js/views/layouts.js +1 -1
  86. package/admin/js/views/login.js +2 -2
  87. package/admin/js/views/menu-editor.js +3 -3
  88. package/admin/js/views/menu-locations.js +1 -1
  89. package/admin/js/views/menus.js +3 -3
  90. package/admin/js/views/navigation.js +8 -8
  91. package/admin/js/views/page-editor.js +40 -40
  92. package/admin/js/views/pages.js +3 -3
  93. package/admin/js/views/plugin-code.js +2 -2
  94. package/admin/js/views/plugins.js +1 -1
  95. package/admin/js/views/project-settings.js +1 -1
  96. package/admin/js/views/projects.js +1 -1
  97. package/admin/js/views/roles.js +1 -1
  98. package/admin/js/views/user-editor.js +1 -1
  99. package/admin/js/views/users.js +3 -3
  100. package/admin/js/views/view-editor.js +1 -1
  101. package/admin/js/views/view-preview.js +1 -1
  102. package/admin/js/views/views-list.js +1 -1
  103. package/bin/cli.js +8 -8
  104. package/bin/lib/config-merge.js +5 -5
  105. package/bin/update.js +16 -16
  106. package/config/site.json +86 -86
  107. package/package.json +2 -2
  108. package/plugins/_template/admin/views/index.js +1 -1
  109. package/plugins/_template/config.js +1 -1
  110. package/plugins/_template/plugin.js +1 -1
  111. package/plugins/analytics/admin/templates/analytics.html +3 -3
  112. package/plugins/analytics/admin/views/analytics.js +1 -1
  113. package/plugins/analytics/config.js +1 -1
  114. package/plugins/analytics/plugin.js +4 -4
  115. package/plugins/analytics/plugin.json +1 -1
  116. package/plugins/analytics/public/inject-body.html +2 -2
  117. package/plugins/analytics/public/inject-head.html +1 -1
  118. package/plugins/blog/admin/templates/blog.html +4 -4
  119. package/plugins/blog/admin/views/blog.js +3 -3
  120. package/plugins/blog/admin/views/categories.js +2 -2
  121. package/plugins/blog/admin/views/comments.js +4 -4
  122. package/plugins/blog/admin/views/post-editor.js +1 -1
  123. package/plugins/blog/plugin.js +2 -2
  124. package/plugins/blog/plugin.json +4 -4
  125. package/plugins/contacts/admin/views/contacts.js +8 -8
  126. package/plugins/contacts/plugin.js +4 -4
  127. package/plugins/contacts/plugin.json +1 -1
  128. package/plugins/invoice/admin/templates/editor.html +6 -6
  129. package/plugins/invoice/admin/templates/index.html +4 -4
  130. package/plugins/invoice/admin/views/editor.js +3 -3
  131. package/plugins/invoice/admin/views/index.js +4 -4
  132. package/plugins/invoice/admin/views/issuers.js +1 -1
  133. package/plugins/invoice/admin/views/party-view.js +3 -3
  134. package/plugins/invoice/admin/views/receivers.js +1 -1
  135. package/plugins/invoice/config.js +1 -1
  136. package/plugins/invoice/plugin.js +1 -1
  137. package/plugins/invoice/plugin.json +4 -4
  138. package/plugins/notes/admin/views/notes.js +4 -4
  139. package/plugins/notes/plugin.js +1 -1
  140. package/plugins/notes/plugin.json +1 -1
  141. package/plugins/site-search/admin/templates/site-search.html +3 -3
  142. package/plugins/site-search/admin/views/site-search.js +1 -1
  143. package/plugins/site-search/config.js +1 -1
  144. package/plugins/site-search/plugin.js +4 -4
  145. package/plugins/site-search/plugin.json +1 -1
  146. package/plugins/surveys/admin/templates/results.html +1 -1
  147. package/plugins/surveys/admin/templates/survey-editor.html +4 -4
  148. package/plugins/surveys/admin/views/audience.js +6 -6
  149. package/plugins/surveys/admin/views/results.js +3 -3
  150. package/plugins/surveys/admin/views/survey-editor.js +1 -1
  151. package/plugins/surveys/admin/views/surveys.js +2 -2
  152. package/plugins/surveys/plugin.js +1 -1
  153. package/plugins/surveys/plugin.json +4 -4
  154. package/plugins/theme-switcher/admin/views/theme-switcher.js +1 -1
  155. package/plugins/theme-switcher/plugin.json +3 -3
  156. package/plugins/theme-switcher/public/inject-body.html +28 -28
  157. package/plugins/theme-switcher/public/inject-head.html +1 -1
  158. package/plugins/todo/plugin.js +1 -1
  159. package/public/css/forms.css +1 -1
  160. package/public/js/collection-browser.js +1 -1
  161. package/public/js/collection-context.js +1 -1
  162. package/public/js/form-logic-engine.js +1 -1
  163. package/public/js/forms.js +2 -1
  164. package/public/js/site.js +1 -1
  165. package/scripts/build.js +100 -6
  166. package/scripts/create-plugin.js +2 -2
  167. package/scripts/fresh.js +2 -2
  168. package/scripts/gen-instance-secret.js +1 -1
  169. package/scripts/pro.js +12 -12
  170. package/scripts/reset.js +2 -2
  171. package/scripts/setup.js +15 -15
  172. package/scripts/users.js +5 -5
  173. package/scripts/verify-assets.mjs +6 -6
  174. package/server/config.js +1 -1
  175. package/server/middleware/auth.js +11 -11
  176. package/server/middleware/managerAuth.js +3 -3
  177. package/server/routes/api/actions.js +4 -4
  178. package/server/routes/api/api-endpoints.js +7 -7
  179. package/server/routes/api/api-tokens.js +5 -5
  180. package/server/routes/api/auth.js +10 -10
  181. package/server/routes/api/blocks.js +3 -3
  182. package/server/routes/api/collections.js +18 -18
  183. package/server/routes/api/dashboard.js +5 -5
  184. package/server/routes/api/effects.js +2 -2
  185. package/server/routes/api/endpoints-public.js +5 -5
  186. package/server/routes/api/forms.js +47 -47
  187. package/server/routes/api/menu-locations.js +3 -3
  188. package/server/routes/api/menus.js +7 -7
  189. package/server/routes/api/notifications.js +2 -2
  190. package/server/routes/api/pages.js +14 -2
  191. package/server/routes/api/plugin-marketplace.js +3 -3
  192. package/server/routes/api/plugins.js +6 -6
  193. package/server/routes/api/projects.js +9 -9
  194. package/server/routes/api/scaffold.js +7 -7
  195. package/server/routes/api/settings.js +6 -6
  196. package/server/routes/api/sidebar.js +1 -1
  197. package/server/routes/api/users.js +5 -5
  198. package/server/routes/api/versions.js +1 -1
  199. package/server/routes/api/views.js +1 -1
  200. package/server/routes/docs-public.js +3 -3
  201. package/server/routes/public.js +14 -14
  202. package/server/server.js +34 -16
  203. package/server/services/actions.js +19 -19
  204. package/server/services/adapters/FileAdapter.js +2 -2
  205. package/server/services/adapters/MongoAdapter.js +5 -5
  206. package/server/services/apiEndpoints.js +15 -15
  207. package/server/services/apiTokens.js +7 -7
  208. package/server/services/blocks.js +13 -13
  209. package/server/services/cache/drivers/MemoryDriver.js +3 -3
  210. package/server/services/cache/drivers/NoneDriver.js +1 -1
  211. package/server/services/cache/index.js +5 -5
  212. package/server/services/collections.js +14 -14
  213. package/server/services/components.js +9 -9
  214. package/server/services/connectionManager.js +4 -4
  215. package/server/services/content.js +25 -2
  216. package/server/services/docs.js +14 -14
  217. package/server/services/email.js +1 -1
  218. package/server/services/filterEngine.js +6 -6
  219. package/server/services/forms.js +18 -10
  220. package/server/services/health.js +1 -1
  221. package/server/services/hooks.js +7 -7
  222. package/server/services/images.js +4 -4
  223. package/server/services/managerClient.js +1 -1
  224. package/server/services/markdown.js +79 -79
  225. package/server/services/menuRender.js +5 -5
  226. package/server/services/menus-migration.js +3 -3
  227. package/server/services/menus.js +31 -31
  228. package/server/services/permissionRegistry.js +1 -1
  229. package/server/services/pluginFiles.js +5 -5
  230. package/server/services/pluginInstaller.js +17 -17
  231. package/server/services/pluginScaffold.js +1 -1
  232. package/server/services/plugins.js +17 -17
  233. package/server/services/presetCollections.js +2 -2
  234. package/server/services/projects.js +35 -26
  235. package/server/services/recipes/contact-list.json +1 -1
  236. package/server/services/recipes/onboarding.json +8 -8
  237. package/server/services/references.js +8 -8
  238. package/server/services/renderer.js +9 -9
  239. package/server/services/roles.js +9 -9
  240. package/server/services/rowAccess.js +10 -10
  241. package/server/services/scaffolder.js +30 -30
  242. package/server/services/sidebar-migration.js +10 -10
  243. package/server/services/sitemap.js +1 -1
  244. package/server/services/userProfiles.js +4 -4
  245. package/server/services/userRoles.js +7 -7
  246. package/server/services/users.js +5 -5
  247. package/server/services/versions.js +3 -3
  248. package/server/services/viewPipeline.js +6 -6
  249. package/server/services/views.js +11 -11
  250. package/server/templates/page.html +5 -5
  251. package/config/navigation.json.bak +0 -31
package/bin/update.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Domma CMS — Project Update
3
+ * Domma CMS - Project Update
4
4
  * Usage: npx domma-cms update [--yes] [--no-backup] [--no-install] [--dry-run]
5
5
  *
6
6
  * Run from inside an existing Domma CMS project directory.
@@ -126,7 +126,7 @@ export default async function update(_positional, flags) {
126
126
  console.log('');
127
127
  console.log(' ┌──────────────────────────────────────────┐');
128
128
  console.log(' │ │');
129
- console.log(' │ Domma CMS — Project Updater │');
129
+ console.log(' │ Domma CMS - Project Updater │');
130
130
  console.log(' │ │');
131
131
  console.log(' └──────────────────────────────────────────┘');
132
132
  console.log('');
@@ -146,14 +146,14 @@ export default async function update(_positional, flags) {
146
146
  }
147
147
  }
148
148
 
149
- // Scaffolded projects are always private — reject if not
149
+ // Scaffolded projects are always private - reject if not
150
150
  try {
151
151
  const cwdPkg = readJson(path.join(cwd, 'package.json'));
152
152
  if (!cwdPkg.private) {
153
153
  console.error(' ✗ This does not appear to be a scaffolded Domma CMS project (package.json is not private).\n');
154
154
  process.exit(1);
155
155
  }
156
- } catch { /* malformed package.json — let it proceed */
156
+ } catch { /* malformed package.json - let it proceed */
157
157
  }
158
158
 
159
159
  // -------------------------------------------------------------------------
@@ -169,7 +169,7 @@ export default async function update(_positional, flags) {
169
169
  markerData = readJson(markerPath);
170
170
  installedVersion = markerData.cmsVersion ?? 'unknown';
171
171
  } catch {
172
- warn('Could not read .domma marker — treating as unknown version.');
172
+ warn('Could not read .domma marker - treating as unknown version.');
173
173
  }
174
174
  } else {
175
175
  warn('No .domma marker found. This project was scaffolded before version tracking was introduced.');
@@ -191,16 +191,16 @@ export default async function update(_positional, flags) {
191
191
  }
192
192
 
193
193
  // -------------------------------------------------------------------------
194
- // 3. Dry-run mode — preview changes and exit
194
+ // 3. Dry-run mode - preview changes and exit
195
195
  // -------------------------------------------------------------------------
196
196
 
197
197
  if (isDryRun) {
198
- info('Dry run — no files will be modified.\n');
198
+ info('Dry run - no files will be modified.\n');
199
199
 
200
200
  info('Upstream directories that would be replaced:');
201
201
  for (const dir of UPSTREAM_DIRS) {
202
202
  if (dir === 'public') {
203
- info(` → ${dir}/ (upstream entries only — site-added files are preserved)`);
203
+ info(` → ${dir}/ (upstream entries only - site-added files are preserved)`);
204
204
  } else {
205
205
  info(` → ${dir}/`);
206
206
  }
@@ -317,7 +317,7 @@ export default async function update(_positional, flags) {
317
317
  let st = null;
318
318
  try { st = lstatSync(destEntry); } catch { /* missing */ }
319
319
  // A symlinked entry (e.g. a manager-shared public/js) is owned by
320
- // whatever it points at — replacing it would silently fork it.
320
+ // whatever it points at - replacing it would silently fork it.
321
321
  if (st && st.isSymbolicLink()) continue;
322
322
  if (st) rmSync(destEntry, {recursive: true, force: true});
323
323
  cpSync(path.join(src, entry), destEntry, {recursive: true});
@@ -355,7 +355,7 @@ export default async function update(_positional, flags) {
355
355
  const isNew = !existsSync(dstPlugin);
356
356
 
357
357
  if (isNew) {
358
- // Brand-new plugin — copy wholesale, apply scaffold.reset
358
+ // Brand-new plugin - copy wholesale, apply scaffold.reset
359
359
  cpSync(srcPlugin, dstPlugin, {recursive: true});
360
360
  try {
361
361
  const manifest = readJson(path.join(dstPlugin, 'plugin.json'));
@@ -364,11 +364,11 @@ export default async function update(_positional, flags) {
364
364
  mkdirSync(path.dirname(absPath), {recursive: true});
365
365
  writeFileSync(absPath, content + '\n', 'utf8');
366
366
  }
367
- } catch { /* no manifest — skip reset */
367
+ } catch { /* no manifest - skip reset */
368
368
  }
369
369
  newPlugins.push(name);
370
370
  } else {
371
- // Existing plugin — identify paths to preserve
371
+ // Existing plugin - identify paths to preserve
372
372
  let preservePaths = new Set(['data']);
373
373
  try {
374
374
  const manifest = readJson(path.join(srcPlugin, 'plugin.json'));
@@ -390,7 +390,7 @@ export default async function update(_positional, flags) {
390
390
  preserved.set(f, readFileSync(path.join(dstPlugin, f)));
391
391
  }
392
392
  } else {
393
- // scaffold.reset entry — preserve the single file
393
+ // scaffold.reset entry - preserve the single file
394
394
  preserved.set(rel, readFileSync(abs));
395
395
  }
396
396
  }
@@ -448,7 +448,7 @@ export default async function update(_positional, flags) {
448
448
  writeJson(dstPath, merged);
449
449
  mergedKeys[cfgFile] = added;
450
450
  }
451
- } catch { /* malformed JSON — skip */
451
+ } catch { /* malformed JSON - skip */
452
452
  }
453
453
  }
454
454
  done();
@@ -483,13 +483,13 @@ export default async function update(_positional, flags) {
483
483
  projectPkg.scripts = newScripts;
484
484
 
485
485
  writeFileSync(projectPkgPath, JSON.stringify(projectPkg, null, 2) + '\n', 'utf8');
486
- } catch { /* malformed package.json — skip */
486
+ } catch { /* malformed package.json - skip */
487
487
  }
488
488
  done();
489
489
 
490
490
  // -------------------------------------------------------------------------
491
491
  // 11. Ensure runtime state is in .gitignore (self-heal existing installs)
492
- // .domma-backups/ + analytics counters — tracking the latter resets
492
+ // .domma-backups/ + analytics counters - tracking the latter resets
493
493
  // live analytics on every checkout/pull/update (recurring bug).
494
494
  // -------------------------------------------------------------------------
495
495
 
package/config/site.json CHANGED
@@ -1,88 +1,88 @@
1
1
  {
2
- "title": "My CMS Site",
3
- "url": "http://domma-cms.local:4096",
4
- "tagline": "This is the Domma CMS",
5
- "theme": "silver-dark",
6
- "seo": {
7
- "defaultTitle": "My Bloggy Thing",
8
- "titleSeparator": " | ",
9
- "defaultDescription": "A site built with Domma CMS"
10
- },
11
- "footer": {
12
- "copyright": "© My Site. All rights reserved.",
13
- "links": []
14
- },
15
- "social": {
16
- "twitter": "",
17
- "facebook": "",
18
- "instagram": "",
19
- "linkedin": "",
20
- "github": "",
21
- "youtube": ""
22
- },
23
- "smtp": {
24
- "host": "192.168.1.27",
25
- "port": 1025,
26
- "user": "",
27
- "pass": "",
28
- "secure": false,
29
- "fromAddress": "",
30
- "fromName": ""
31
- },
32
- "adminBrand": {
33
- "title": "Domma CMS"
34
- },
35
- "fontFamily": "Nunito",
36
- "fontSize": 18,
37
- "autoTheme": {
38
- "enabled": false,
39
- "dayTheme": "charcoal-light",
40
- "nightTheme": "charcoal-dark",
41
- "dayStart": "07:00",
42
- "nightStart": "19:00"
43
- },
44
- "adminTheme": "silver-dark",
45
- "layoutOptions": {
46
- "spacerSize": 40
47
- },
48
- "backToTop": {
49
- "enabled": true,
50
- "scrollThreshold": 300,
51
- "position": "bottom-right",
52
- "offset": 32,
53
- "bottomOffset": 32,
54
- "label": "",
55
- "smooth": true
56
- },
57
- "cookieConsent": {
58
- "enabled": true,
59
- "message": "",
60
- "acceptAllText": "Accept All",
61
- "rejectAllText": "Reject All",
62
- "customizeText": "Customize",
63
- "savePreferencesText": "Save Preferences",
64
- "privacyPolicyText": "Privacy Policy",
65
- "privacyPolicyUrl": "",
66
- "cookiePolicyText": "Cookie Policy",
67
- "cookiePolicyUrl": "",
68
- "position": "bottom",
69
- "layout": "bar",
70
- "theme": "dark",
71
- "showFunctional": true,
72
- "showAnalytics": true,
73
- "showMarketing": true,
74
- "consentVersion": "1.0"
75
- },
76
- "breadcrumbs": {
77
- "enabled": false,
78
- "homeLabel": "Home",
79
- "position": "TR",
80
- "offsetX": 64,
81
- "offsetY": 64
82
- },
83
- "brand": {
84
- "text": "My Bloggy Thing",
85
- "logo": null,
86
- "url": "/"
87
- }
2
+ "title": "My CMS Site",
3
+ "url": "http://localhost:4096",
4
+ "tagline": "This is the Domma CMS",
5
+ "theme": "silver-dark",
6
+ "seo": {
7
+ "defaultTitle": "My Bloggy Thing",
8
+ "titleSeparator": " | ",
9
+ "defaultDescription": "A site built with Domma CMS"
10
+ },
11
+ "footer": {
12
+ "copyright": "© My Site. All rights reserved.",
13
+ "links": []
14
+ },
15
+ "social": {
16
+ "twitter": "",
17
+ "facebook": "",
18
+ "instagram": "",
19
+ "linkedin": "",
20
+ "github": "",
21
+ "youtube": ""
22
+ },
23
+ "smtp": {
24
+ "host": "",
25
+ "port": 587,
26
+ "user": "",
27
+ "pass": "",
28
+ "secure": false,
29
+ "fromAddress": "",
30
+ "fromName": ""
31
+ },
32
+ "adminBrand": {
33
+ "title": "Domma CMS"
34
+ },
35
+ "fontFamily": "Nunito",
36
+ "fontSize": 18,
37
+ "autoTheme": {
38
+ "enabled": false,
39
+ "dayTheme": "charcoal-light",
40
+ "nightTheme": "charcoal-dark",
41
+ "dayStart": "07:00",
42
+ "nightStart": "19:00"
43
+ },
44
+ "adminTheme": "silver-dark",
45
+ "layoutOptions": {
46
+ "spacerSize": 40
47
+ },
48
+ "backToTop": {
49
+ "enabled": true,
50
+ "scrollThreshold": 300,
51
+ "position": "bottom-right",
52
+ "offset": 32,
53
+ "bottomOffset": 32,
54
+ "label": "",
55
+ "smooth": true
56
+ },
57
+ "cookieConsent": {
58
+ "enabled": true,
59
+ "message": "",
60
+ "acceptAllText": "Accept All",
61
+ "rejectAllText": "Reject All",
62
+ "customizeText": "Customize",
63
+ "savePreferencesText": "Save Preferences",
64
+ "privacyPolicyText": "Privacy Policy",
65
+ "privacyPolicyUrl": "",
66
+ "cookiePolicyText": "Cookie Policy",
67
+ "cookiePolicyUrl": "",
68
+ "position": "bottom",
69
+ "layout": "bar",
70
+ "theme": "dark",
71
+ "showFunctional": true,
72
+ "showAnalytics": true,
73
+ "showMarketing": true,
74
+ "consentVersion": "1.0"
75
+ },
76
+ "breadcrumbs": {
77
+ "enabled": false,
78
+ "homeLabel": "Home",
79
+ "position": "TR",
80
+ "offsetX": 64,
81
+ "offsetY": 64
82
+ },
83
+ "brand": {
84
+ "text": "My Bloggy Thing",
85
+ "logo": null,
86
+ "url": "/"
87
+ }
88
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.43.1",
3
+ "version": "0.45.0",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
@@ -81,7 +81,7 @@
81
81
  "@fastify/rate-limit": "^10.3.0",
82
82
  "@fastify/static": "9.1.1",
83
83
  "bcryptjs": "^3.0.3",
84
- "domma-js": "0.39.0",
84
+ "domma-js": "0.39.1",
85
85
  "dotenv": "^17.2.3",
86
86
  "fastify": "5.8.5",
87
87
  "gray-matter": "^4.0.3",
@@ -1,5 +1,5 @@
1
1
  /**
2
- * PLUGIN_DISPLAY_NAME — admin view.
2
+ * PLUGIN_DISPLAY_NAME - admin view.
3
3
  * Route: #/plugins/PLUGIN_SLUG
4
4
  */
5
5
  export const indexView = {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * PLUGIN_DISPLAY_NAME plugin — default configuration.
2
+ * PLUGIN_DISPLAY_NAME plugin - default configuration.
3
3
  * Merged with user overrides from config/plugins.json.
4
4
  */
5
5
  export default {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * PLUGIN_DISPLAY_NAME plugin — server-side routes and lifecycle hooks.
2
+ * PLUGIN_DISPLAY_NAME plugin - server-side routes and lifecycle hooks.
3
3
  */
4
4
 
5
5
  export default async function plugin(fastify, options) {
@@ -26,19 +26,19 @@
26
26
  <div class="card">
27
27
  <div class="card-body">
28
28
  <div class="text-muted text-sm">Hits in range</div>
29
- <div class="h2 mb-0" id="kpi-range">—</div>
29
+ <div class="h2 mb-0" id="kpi-range">-</div>
30
30
  </div>
31
31
  </div>
32
32
  <div class="card">
33
33
  <div class="card-body">
34
34
  <div class="text-muted text-sm">Unique pages</div>
35
- <div class="h2 mb-0" id="kpi-pages">—</div>
35
+ <div class="h2 mb-0" id="kpi-pages">-</div>
36
36
  </div>
37
37
  </div>
38
38
  <div class="card">
39
39
  <div class="card-body">
40
40
  <div class="text-muted text-sm">Lifetime hits</div>
41
- <div class="h2 mb-0" id="kpi-lifetime">—</div>
41
+ <div class="h2 mb-0" id="kpi-lifetime">-</div>
42
42
  </div>
43
43
  </div>
44
44
  </div>
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Analytics Plugin — Admin View
2
+ * Analytics Plugin - Admin View
3
3
  * KPI cards, trend chart (Chart.js via CDN), filterable top-pages table, CSV export.
4
4
  */
5
5
  const CHART_CDN = 'https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Analytics Plugin — Default Configuration
2
+ * Analytics Plugin - Default Configuration
3
3
  *
4
4
  * excludeAdmin strip /admin pageviews before recording
5
5
  * excludePaths array of path prefixes to ignore (e.g. ['/api', '/healthz'])
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Analytics Plugin — Server
2
+ * Analytics Plugin - Server
3
3
  * Tracks page hits with per-day buckets so we can chart trends and
4
4
  * filter by time range.
5
5
  *
@@ -20,7 +20,7 @@ import {fileURLToPath} from 'url';
20
20
 
21
21
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
22
 
23
- // Runtime counters live under data/ — the carve-out that BOTH update paths
23
+ // Runtime counters live under data/ - the carve-out that BOTH update paths
24
24
  // preserve (siteCmsUpdater's copyPlugin backs up data/; `domma-cms update`
25
25
  // keeps 'data' in its preserve set). Pre-2.1 they sat in the plugin root and
26
26
  // were overwritten by the manager's copy on every plugin push. On first boot
@@ -56,7 +56,7 @@ async function migrateToDataDir() {
56
56
  await fs.mkdir(DATA_DIR, { recursive: true });
57
57
  for (const name of ['lifetime.json', 'daily.json', 'journeys.json', 'stats.json']) {
58
58
  const dest = path.join(DATA_DIR, name);
59
- try { await fs.access(dest); continue; } catch { /* dest absent — maybe move */ }
59
+ try { await fs.access(dest); continue; } catch { /* dest absent - maybe move */ }
60
60
  const oldPath = path.join(__dirname, name);
61
61
  try {
62
62
  await fs.access(oldPath);
@@ -355,7 +355,7 @@ export default async function analyticsPlugin(fastify, options) {
355
355
  if (!daily[today]) daily[today] = {};
356
356
  daily[today][normalised] = (daily[today][normalised] || 0) + 1;
357
357
 
358
- // Journey event — only if sid is a string of reasonable shape, and under cap
358
+ // Journey event - only if sid is a string of reasonable shape, and under cap
359
359
  const safeSid = typeof sid === 'string' && sid.length > 0 && sid.length <= 64 ? sid : null;
360
360
  const safeRef = typeof ref === 'string' && ref.length <= 512 ? ref : '';
361
361
  if (safeSid) {
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "views": {
27
27
  "plugin-analytics": {
28
- "entry": "analytics/admin/views/analytics.js?v=2c26480d",
28
+ "entry": "analytics/admin/views/analytics.js?v=28fcef99",
29
29
  "exportName": "analyticsView"
30
30
  }
31
31
  }
@@ -10,7 +10,7 @@
10
10
 
11
11
  var url = window.location.pathname;
12
12
 
13
- // Session-scoped anonymous ID — clears with the tab. Cookie-less.
13
+ // Session-scoped anonymous ID - clears with the tab. Cookie-less.
14
14
  var sid = '';
15
15
  try {
16
16
  sid = sessionStorage.getItem('dm_sid') || '';
@@ -21,7 +21,7 @@
21
21
  sessionStorage.setItem('dm_sid', sid);
22
22
  }
23
23
  } catch (e) {
24
- // sessionStorage unavailable — proceed without sid
24
+ // sessionStorage unavailable - proceed without sid
25
25
  }
26
26
 
27
27
  // Session dedup (one hit per URL per session)
@@ -1 +1 @@
1
- <!-- analytics: head injection (empty — tracking is done via body script) -->
1
+ <!-- analytics: head injection (empty - tracking is done via body script) -->
@@ -8,19 +8,19 @@
8
8
  <div class="grid grid-cols-4 gap-3 mb-4">
9
9
  <div class="card"><div class="card-body">
10
10
  <div class="text-muted small">Total</div>
11
- <div class="h3 mb-0" id="kpi-total">—</div>
11
+ <div class="h3 mb-0" id="kpi-total">-</div>
12
12
  </div></div>
13
13
  <div class="card"><div class="card-body">
14
14
  <div class="text-muted small">Published</div>
15
- <div class="h3 mb-0" id="kpi-published">—</div>
15
+ <div class="h3 mb-0" id="kpi-published">-</div>
16
16
  </div></div>
17
17
  <div class="card"><div class="card-body">
18
18
  <div class="text-muted small">Drafts</div>
19
- <div class="h3 mb-0" id="kpi-drafts">—</div>
19
+ <div class="h3 mb-0" id="kpi-drafts">-</div>
20
20
  </div></div>
21
21
  <div class="card"><div class="card-body">
22
22
  <div class="text-muted small">Scheduled</div>
23
- <div class="h3 mb-0" id="kpi-scheduled">—</div>
23
+ <div class="h3 mb-0" id="kpi-scheduled">-</div>
24
24
  </div></div>
25
25
  </div>
26
26
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Blog posts overview — KPI cards + filterable, sortable post table.
2
+ * Blog posts overview - KPI cards + filterable, sortable post table.
3
3
  *
4
4
  * @module blog/admin/views/blog
5
5
  */
@@ -61,7 +61,7 @@ export const blogView = {
61
61
  sortable: false,
62
62
  render: (val) => Array.isArray(val) && val.length
63
63
  ? val.map((id) => `<span class="badge badge-secondary mr-1">${escapeHtml(catNameById(id) || id)}</span>`).join('')
64
- : '<span style="opacity:0.4;">—</span>'
64
+ : '<span style="opacity:0.4;">-</span>'
65
65
  },
66
66
  {
67
67
  key: 'publishedAt',
@@ -69,7 +69,7 @@ export const blogView = {
69
69
  sortable: true,
70
70
  render: (v) => v
71
71
  ? `<span title="${escapeHtml(D(v).format('DD MMM YYYY HH:mm'))}">${escapeHtml(D(v).format('DD MMM YYYY'))}</span>`
72
- : '<span style="opacity:0.4;">—</span>'
72
+ : '<span style="opacity:0.4;">-</span>'
73
73
  },
74
74
  {
75
75
  key: 'id',
@@ -28,7 +28,7 @@ export const categoriesView = {
28
28
  let descInput = null;
29
29
 
30
30
  // ---------------------------------------------------------------
31
- // Modal setup (built with DOM APIs — not innerHTML)
31
+ // Modal setup (built with DOM APIs - not innerHTML)
32
32
  // ---------------------------------------------------------------
33
33
 
34
34
  function buildModal() {
@@ -159,7 +159,7 @@ export const categoriesView = {
159
159
  key: 'description',
160
160
  title: 'Description',
161
161
  sortable: false,
162
- render: (v) => escapeHtml(v) || '<span style="opacity:0.4;">—</span>'
162
+ render: (v) => escapeHtml(v) || '<span style="opacity:0.4;">-</span>'
163
163
  },
164
164
  {
165
165
  key: 'id',
@@ -65,7 +65,7 @@ export const commentsView = {
65
65
  sortable: false,
66
66
  render: (v) => v
67
67
  ? `<a href="mailto:${escapeHtml(v)}">${escapeHtml(v)}</a>`
68
- : '<span style="opacity:0.4;">—</span>'
68
+ : '<span style="opacity:0.4;">-</span>'
69
69
  },
70
70
  {
71
71
  key: 'body',
@@ -83,7 +83,7 @@ export const commentsView = {
83
83
  sortable: false,
84
84
  render: (v) => v
85
85
  ? `<a href="#/plugins/blog/posts/${escapeHtml(v)}">${escapeHtml(v)}</a>`
86
- : '<span style="opacity:0.4;">—</span>'
86
+ : '<span style="opacity:0.4;">-</span>'
87
87
  },
88
88
  {
89
89
  key: 'createdAt',
@@ -91,7 +91,7 @@ export const commentsView = {
91
91
  sortable: true,
92
92
  render: (v) => v
93
93
  ? `<span title="${escapeHtml(D(v).format('DD MMM YYYY HH:mm'))}">${escapeHtml(D(v).fromNow())}</span>`
94
- : '<span style="opacity:0.4;">—</span>'
94
+ : '<span style="opacity:0.4;">-</span>'
95
95
  },
96
96
  {
97
97
  key: 'id',
@@ -117,7 +117,7 @@ export const commentsView = {
117
117
  emptyMessage: `No ${currentTab} comments.`
118
118
  });
119
119
 
120
- // Event delegation — attach once
120
+ // Event delegation - attach once
121
121
  if (!tableEl.dataset.listenerAttached) {
122
122
  tableEl.dataset.listenerAttached = '1';
123
123
  tableEl.addEventListener('click', async (e) => {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Blog post editor view — create and edit posts.
2
+ * Blog post editor view - create and edit posts.
3
3
  *
4
4
  * @module blog/admin/views/post-editor
5
5
  */
@@ -52,7 +52,7 @@ function toComment(entry) {
52
52
  }
53
53
 
54
54
  // ---------------------------------------------------------------------------
55
- // Embed shortcodes — register `[blog-list]`, `[blog-grid]`, `[blog-featured]`,
55
+ // Embed shortcodes - register `[blog-list]`, `[blog-grid]`, `[blog-featured]`,
56
56
  // `[blog-categories]` so users can drop blog content into any page.
57
57
  // ---------------------------------------------------------------------------
58
58
 
@@ -401,7 +401,7 @@ export default async function blogPlugin(fastify, options) {
401
401
  return reply.send(comments);
402
402
  });
403
403
 
404
- /** POST /api/plugins/blog/comments — public, no authenticate */
404
+ /** POST /api/plugins/blog/comments - public, no authenticate */
405
405
  fastify.post('/comments', async (request, reply) => {
406
406
  const body = request.body ?? {};
407
407
 
@@ -72,19 +72,19 @@
72
72
  ],
73
73
  "views": {
74
74
  "plugin-blog-posts": {
75
- "entry": "blog/admin/views/blog.js?v=2722ac2e",
75
+ "entry": "blog/admin/views/blog.js?v=da5c1a46",
76
76
  "exportName": "blogView"
77
77
  },
78
78
  "plugin-blog-post-editor": {
79
- "entry": "blog/admin/views/post-editor.js?v=d3f2c327",
79
+ "entry": "blog/admin/views/post-editor.js?v=d4d7a514",
80
80
  "exportName": "postEditorView"
81
81
  },
82
82
  "plugin-blog-categories": {
83
- "entry": "blog/admin/views/categories.js?v=489c8dfe",
83
+ "entry": "blog/admin/views/categories.js?v=7e3946ca",
84
84
  "exportName": "categoriesView"
85
85
  },
86
86
  "plugin-blog-comments": {
87
- "entry": "blog/admin/views/comments.js?v=47c59ee8",
87
+ "entry": "blog/admin/views/comments.js?v=ee2146e9",
88
88
  "exportName": "commentsView"
89
89
  },
90
90
  "plugin-blog-settings": {