domma-cms 0.6.4 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md ADDED
@@ -0,0 +1,159 @@
1
+ # Domma CMS — AI Assistant Guide
2
+
3
+ ## Project Overview
4
+
5
+ This is a **Domma CMS** project. Stack: Fastify 5 (ESM) backend, Domma SPA admin, SSR public site.
6
+ Content is Markdown + YAML frontmatter in `content/pages/`. Config is JSON in `config/`.
7
+ Server runs on port **4096** by default (`config/server.json`).
8
+
9
+ ## Domma Framework — Use These, Not Vanilla JS
10
+
11
+ Domma provides built-in solutions. Never use vanilla JS equivalents.
12
+
13
+ | Alias | Purpose | NOT this |
14
+ |--------------------------------------------------|------------------------------------------|------------------------------------|
15
+ | `$('#el')` | DOM selection + manipulation | `document.querySelector()` |
16
+ | `S.set/get` | Storage | `localStorage.setItem/getItem` |
17
+ | `H.get/post/put/delete` | HTTP | `fetch()`, `XMLHttpRequest` |
18
+ | `D()` | Date manipulation (Moment-style) | Manual `Date` arithmetic |
19
+ | `_` | Array/object utils (`_.map`, `_.filter`) | Always evaluate if native suffices |
20
+ | `M.create(blueprint)` | Reactive data models | Manual state management |
21
+ | `F.create(selector, {blueprint})` | Form generation | Manual `<form>` HTML |
22
+ | `E.toast/confirm/modal/tabs/accordion/slideover` | UI components | Manual HTML/CSS/JS components |
23
+ | `I.scan()` / `<span data-icon="name">` | Icons | Manual SVG or icon fonts |
24
+ | `T.create(selector, {data, columns})` | Tables | Manual `<table>` generation |
25
+
26
+ ## Architecture
27
+
28
+ ```
29
+ server/ Fastify 5 API + SSR (DO NOT modify — upstream-replaced on update)
30
+ admin/ Domma SPA admin panel (DO NOT modify — upstream-replaced on update)
31
+ public/js/ Public site JS (site.js + component init)
32
+ public/css/ Site stylesheet (site.css is yours to edit)
33
+ content/pages/ Markdown pages (URL → file mapping below)
34
+ content/media/ Uploaded media
35
+ content/users/ User accounts (JSON)
36
+ content/collections/ Collection entries
37
+ config/ All configuration JSON (see Config section)
38
+ plugins/ CMS plugins (yours to create/modify)
39
+ docs/ Reference documentation
40
+ ```
41
+
42
+ **URL → file mapping:**
43
+
44
+ - `/` → `content/pages/index.md`
45
+ - `/about` → `content/pages/about.md`
46
+ - `/services` → `content/pages/services/index.md`
47
+
48
+ ## Content Pages
49
+
50
+ Pages are Markdown files with YAML frontmatter:
51
+
52
+ ```markdown
53
+ ---
54
+ title: Page Title
55
+ layout: default # default | landing | blank
56
+ description: SEO desc
57
+ ---
58
+
59
+ Page content here. Shortcodes work anywhere in the body.
60
+ ```
61
+
62
+ ### Shortcodes (18 types — full syntax in `docs/markdown-shortcodes.md`)
63
+
64
+ | Shortcode | Purpose |
65
+ |-----------------------------------------|----------------------------------------------------|
66
+ | `[card]` | Card container (optional title, collapsible, icon) |
67
+ | `[grid cols="N"]` / `[col]` | CSS Grid layout (Domma Grid — NOT `.col` class) |
68
+ | `[row]` / `[col]` | Row/column layout |
69
+ | `[slideover trigger="..." title="..."]` | Slide-over panel |
70
+ | `[dconfig]{...}[/dconfig]` | Declarative page config (JSON) |
71
+ | `[tabs]` / `[tab title="..."]` | Tab panels |
72
+ | `[accordion]` / `[item title="..."]` | Accordion sections |
73
+ | `[carousel]` / `[slide]` | Image/content carousel |
74
+ | `[hero title="..." tagline="..."]` | Hero section |
75
+ | `[table]` | Data table (Markdown table inside) |
76
+ | `[badge variant="..."]` | Badge/label |
77
+ | `[countdown to="..." /]` | Countdown timer |
78
+ | `[timeline]` / `[event]` | Timeline component |
79
+ | `[spacer /]` | Vertical spacer |
80
+ | `[center]` | Centre-align content |
81
+ | `[icon name="..." /]` | Inline icon |
82
+ | `[cta action="..."]` | Collection action trigger (admin) |
83
+
84
+ ### Shortcode Nesting Rules
85
+
86
+ Processing order (innermost first):
87
+
88
+ 1. `[dconfig]` — always first
89
+ 2. `[grid]` / `[row]` / `[col]`
90
+ 3. `[card]`
91
+ 4. `[tabs]`, `[accordion]`, `[carousel]`, `[hero]`, `[table]`, `[badge]`, `[countdown]`, `[timeline]`, `[spacer]`,
92
+ `[center]`, `[icon]`, `[cta]`
93
+ 5. `[slideover]` — always last (can contain anything above)
94
+
95
+ Safe nesting: `[card]` inside `[slideover]`, `[grid]` inside `[slideover]`, `[col]` inside `[grid]`.
96
+ Do NOT nest `[slideover]` inside another `[slideover]`.
97
+
98
+ ## Public Site Patterns
99
+
100
+ The server injects these globals on every public page:
101
+
102
+ ```js
103
+ window.__CMS_NAV__ // Navigation config (brand, items)
104
+ window.__CMS_SITE__ // Site config (title, theme, footer, smtp, etc.)
105
+ window.__CMS_DCONFIG__ // Page-level declarative config (merged with inline [dconfig])
106
+ ```
107
+
108
+ `public/js/site.js` reads these to initialise the Domma navbar, footer, and page components.
109
+ Domma components (tabs, accordion, carousel, etc.) in `.page-body` are auto-initialised by `site.js`.
110
+ Add custom public-site JS to `public/js/site.js` or new files loaded from `public/js/`.
111
+
112
+ ## Plugin Development
113
+
114
+ Each plugin needs exactly 3 files:
115
+
116
+ ```
117
+ plugins/my-plugin/
118
+ plugin.json Required manifest (name, displayName, version, description, author, date, icon)
119
+ plugin.js Default export: Fastify plugin function
120
+ config.js Default export: plain object of config defaults
121
+ ```
122
+
123
+ See `docs/plugin-development.md` for full plugin API, hooks, and injection points.
124
+
125
+ ## Config Reference
126
+
127
+ | File | Owner | Purpose |
128
+ |--------------------------|-----------------------|------------------------------------|
129
+ | `config/server.json` | CMS (merge on update) | Port, host, CORS, uploads |
130
+ | `config/auth.json` | CMS (merge on update) | JWT expiry, bcrypt rounds |
131
+ | `config/content.json` | CMS (merge on update) | Content dirs, page defaults |
132
+ | `config/presets.json` | CMS (merge on update) | Preset collection schemas |
133
+ | `config/site.json` | Yours | Site title, theme, footer, SMTP |
134
+ | `config/navigation.json` | Yours | Navbar brand + items |
135
+ | `config/plugins.json` | Yours | Plugin enabled/disabled + settings |
136
+
137
+ See `docs/configuration.md` for full schema reference.
138
+
139
+ ## Key Gotchas
140
+
141
+ 1. **Navigation sub-items**: use `items` key, NOT `children` — Domma navbar reads `items`.
142
+ 2. **Domma collections**: use `.get(0)` NOT `[0]` to get native DOM elements.
143
+ 3. **`.html()` strips interactive elements**: `E.modal().setContent(x)` also strips HTML — use light DOM slot
144
+ projection (`modal.element.appendChild(myDomElement)`) for buttons/inputs.
145
+ 4. **Landing layout**: `layout: landing` removes the standard page wrapper — add your own container CSS.
146
+ 5. **Grid shortcode**: uses Domma Grid (`grid`, `grid-cols-N`) — NOT the `.col` compatibility class.
147
+ 6. **Event delegation namespaces**: `$(doc).on('click.ns', '.sel', cb)` silently fails — use direct `addEventListener`
148
+ or unnamespaced `$(el).on('click', cb)`.
149
+ 7. **Tabs component classes**: wrapper `.tabs`, list `.tab-list`, trigger `button.tab-item`, panel `.tab-panel` (not
150
+ `.tabs-nav`, `.tabs-content`, `.tabs-pane`).
151
+
152
+ ## Docs
153
+
154
+ - `docs/getting-started.md` — Installation, first run, setup wizard
155
+ - `docs/markdown-shortcodes.md` — Full shortcode syntax and examples
156
+ - `docs/configuration.md` — Complete config schema for all JSON files
157
+ - `docs/plugin-development.md` — Plugin API, lifecycle hooks, injection points
158
+ - `docs/theming.md` — Available themes, CSS variables, customisation
159
+ - `docs/api-reference.md` — REST API endpoints (for headless / external integrations)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
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",
@@ -29,7 +29,10 @@
29
29
  "build": "node scripts/build.js",
30
30
  "delete-users": "node -e \"import('fs').then(({readdirSync,rmSync})=>{const d='content/users';readdirSync(d).filter(f=>f.endsWith('.json')).forEach(f=>{rmSync(d+'/'+f);console.log('deleted',f)})})\"",
31
31
  "start": "node server/server.js",
32
- "start:cluster": "pm2 start server/server.js -i max --name domma-cms",
32
+ "pm2:start": "pm2 start ecosystem.config.cjs --env production",
33
+ "pm2:stop": "pm2 stop ecosystem.config.cjs",
34
+ "pm2:restart": "pm2 reload ecosystem.config.cjs",
35
+ "pm2:logs": "pm2 logs domma-cms",
33
36
  "dev": "node --watch server/server.js",
34
37
  "prod": "node server/server.js",
35
38
  "setup": "node scripts/setup.js",
@@ -6,7 +6,7 @@
6
6
  "/resources/typography": 4,
7
7
  "/resources": 13,
8
8
  "/resources/shortcodes": 14,
9
- "/resources/cards": 14,
9
+ "/resources/cards": 15,
10
10
  "/resources/interactive": 13,
11
11
  "/resources/grid": 6,
12
12
  "/forms": 14,
package/scripts/build.js CHANGED
@@ -20,6 +20,7 @@ const COPY_AS_IS = [
20
20
  'scripts',
21
21
  'package.json',
22
22
  'README.md',
23
+ 'CLAUDE.md',
23
24
  'LICENSE',
24
25
  '.npmignore',
25
26
  ];
@@ -64,7 +64,7 @@ async function loadBlockTemplate(blockName) {
64
64
  * @param {object|null} ctaOpts - Optional CTA button options
65
65
  * @returns {string}
66
66
  */
67
- function renderCollectionBlocks(entries, blockTemplate, emptyMsg, ctaOpts) {
67
+ function renderCollectionBlocks(entries, blockTemplate, emptyMsg, ctaOpts, cols) {
68
68
  if (!entries.length) {
69
69
  return `<div class="dm-collection-display dm-collection-empty"><p>${escapeHtmlText(emptyMsg)}</p></div>`;
70
70
  }
@@ -88,7 +88,12 @@ function renderCollectionBlocks(entries, blockTemplate, emptyMsg, ctaOpts) {
88
88
  return html;
89
89
  });
90
90
 
91
- return `<div class="dm-collection-display dm-collection-blocks">\n${items.join('\n')}\n</div>`;
91
+ const validCols = ['2', '3', '4', '5', '6'].includes(String(cols)) ? cols : '';
92
+ const wrapperClass = validCols
93
+ ? `dm-collection-display dm-collection-blocks grid grid-cols-${validCols} gap-4`
94
+ : 'dm-collection-display dm-collection-blocks';
95
+
96
+ return `<div class="${wrapperClass}">\n${items.join('\n')}\n</div>`;
92
97
  }
93
98
 
94
99
  function renderCollectionTable(slug, entries, visibleFields, attrs, ctaOpts) {
@@ -333,7 +338,8 @@ async function processCollectionBlocks(markdown) {
333
338
  if (blockName) {
334
339
  try {
335
340
  const tpl = await loadBlockTemplate(blockName);
336
- replacement = renderCollectionBlocks(entries, tpl, emptyMsg, ctaOpts);
341
+ const cols = attrs.cols || '';
342
+ replacement = renderCollectionBlocks(entries, tpl, emptyMsg, ctaOpts, cols);
337
343
  } catch {
338
344
  replacement = `<div class="dm-collection-display dm-collection-empty"><p>Block template &ldquo;${escapeHtmlText(blockName)}&rdquo; not found.</p></div>`;
339
345
  }