domma-cms 1.6.59 → 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.
- package/CHANGELOG.md +4344 -0
- package/README.md +1 -1
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/api.js +1 -1
- package/admin/js/app.js +2 -2
- package/admin/js/lib/draft-owner.js +1 -0
- package/admin/js/lib/effect-defs.js +1 -1
- package/admin/js/lib/effects-builder.js +2 -2
- package/admin/js/lib/page-editor-reference.js +5 -5
- package/admin/js/lib/shortcode-tags.js +1 -1
- package/admin/js/templates/docs/usage-shortcodes.html +39 -0
- package/admin/js/views/user-editor.js +1 -1
- package/docs/DommaFeatures.md +3005 -0
- package/docs/MultiTenant.md +176 -0
- package/docs/api-reference.md +1667 -0
- package/docs/cache.md +96 -0
- package/docs/configuration.md +1811 -0
- package/docs/editions-and-licences.md +190 -0
- package/docs/getting-started.md +201 -0
- package/docs/guides/form-triggers.md +260 -0
- package/docs/guides/mongodb-storage-adapter.md +282 -0
- package/docs/guides/multi-page-forms.md +121 -0
- package/docs/images/scribe-composer.png +0 -0
- package/docs/markdown-shortcodes.md +1738 -0
- package/docs/pentest-report-2026-04-03.md +181 -0
- package/docs/plans/2026-02-28-admin-topbar-login-forms.md +349 -0
- package/docs/plans/2026-02-28-domma-ui-polish.md +286 -0
- package/docs/plans/2026-02-28-reset-seed-fresh-commands.md +520 -0
- package/docs/plans/2026-03-02-cookie-consent-design.md +86 -0
- package/docs/plans/2026-03-02-cookie-consent-plugin.md +453 -0
- package/docs/plans/2026-03-02-tooltip-sweep-and-back-to-top-plugin-design.md +126 -0
- package/docs/plans/2026-03-02-tooltip-sweep-back-to-top.md +427 -0
- package/docs/plans/2026-03-05-cookie-consent-btt-core.md +115 -0
- package/docs/plans/2026-03-05-custom-css-migrate-to-core-design.md +35 -0
- package/docs/plans/2026-03-05-custom-css-migrate-to-core.md +352 -0
- package/docs/plans/2026-03-05-scribe-script-shortcode.md +373 -0
- package/docs/plans/2026-03-05-user-types-design.md +139 -0
- package/docs/plans/2026-03-05-user-types-implementation.md +1035 -0
- package/docs/plans/2026-03-09-page-editor-view-cta-inserts-design.md +78 -0
- package/docs/plans/2026-03-09-page-editor-view-cta-inserts.md +388 -0
- package/docs/plans/2026-03-09-pro-documentation-design.md +80 -0
- package/docs/plans/2026-03-09-pro-documentation.md +1017 -0
- package/docs/plans/2026-06-10-core-project-design.md +240 -0
- package/docs/plugin-development.md +827 -0
- package/docs/recruitment-recipe.md +501 -0
- package/docs/scaffolding.md +312 -0
- package/docs/screenshots/calculator-floating.png +0 -0
- package/docs/screenshots/calculator-standard.png +0 -0
- package/docs/screenshots/email-settings.png +0 -0
- package/docs/screenshots/messages-count.png +0 -0
- package/docs/structure/user-types.md +107 -0
- package/docs/superpowers/plans/2026-05-25-admin-sidebar-via-menus.md +1112 -0
- package/docs/superpowers/plans/2026-06-08-navbar-mobile-drawer-and-menu-enrichment.md +1161 -0
- package/docs/superpowers/plans/2026-06-25-domma-docs-project.md +695 -0
- package/docs/superpowers/plans/2026-06-26-surveys-HANDOFF.md +68 -0
- package/docs/superpowers/plans/2026-06-26-surveys-pro-plugin.md +1902 -0
- package/docs/superpowers/plans/2026-08-19-collection-context-sort-export.md +1577 -0
- package/docs/superpowers/plans/2026-10-09-content-api.md +1192 -0
- package/docs/superpowers/specs/2026-03-12-form-followup-actions-design.md +152 -0
- package/docs/superpowers/specs/2026-03-12-page-editor-form-insert-and-per-page-theme-design.md +115 -0
- package/docs/superpowers/specs/2026-03-17-nav-hidden-items-design.md +85 -0
- package/docs/superpowers/specs/2026-03-19-accordion-insert-design.md +175 -0
- package/docs/superpowers/specs/2026-03-25-static-block-shortcode-design.md +153 -0
- package/docs/superpowers/specs/2026-03-25-text-shortcode-design.md +168 -0
- package/docs/superpowers/specs/2026-04-05-card-builder-design.md +315 -0
- package/docs/superpowers/specs/2026-04-06-insert-builders-design.md +278 -0
- package/docs/superpowers/specs/2026-04-06-layouts-design.md +226 -0
- package/docs/superpowers/specs/2026-04-07-blog-plugin-design.md +300 -0
- package/docs/superpowers/specs/2026-04-07-calendar-plugin-design.md +538 -0
- package/docs/superpowers/specs/2026-04-17-components-area-design.md +368 -0
- package/docs/superpowers/specs/2026-05-02-effects-slideover-overhaul-design.md +193 -0
- package/docs/superpowers/specs/2026-05-06-dashboard-rebuild-design.md +257 -0
- package/docs/superpowers/specs/2026-05-24-menus-design.md +466 -0
- package/docs/superpowers/specs/2026-05-25-admin-sidebar-via-menus-design.md +358 -0
- package/docs/superpowers/specs/2026-05-25-projects-design.md +380 -0
- package/docs/superpowers/specs/2026-06-08-navbar-mobile-drawer-and-menu-enrichment-design.md +292 -0
- package/docs/superpowers/specs/2026-06-08-sidebar-tools-plugins-folders-design.md +59 -0
- package/docs/superpowers/specs/2026-06-25-domma-docs-project-design.md +192 -0
- package/docs/superpowers/specs/2026-06-26-domma-charts-upstream-proposal.md +44 -0
- package/docs/superpowers/specs/2026-06-26-manager-scheduler-upstream-proposal.md +46 -0
- package/docs/superpowers/specs/2026-06-26-surveys-pro-plugin-design.md +206 -0
- package/docs/superpowers/specs/2026-07-02-shortcode-context-menu-design.md +96 -0
- package/docs/superpowers/specs/2026-08-19-collection-context-sort-export-design.md +330 -0
- package/docs/superpowers/specs/2026-09-22-dcms-marketplace-design.md +1150 -0
- package/docs/superpowers/specs/2026-10-09-content-api-design.md +325 -0
- package/docs/superpowers/specs/plans/2026-03-12-form-followup-actions.md +624 -0
- package/docs/superpowers/specs/plans/2026-03-12-page-editor-form-insert-and-per-page-theme.md +125 -0
- package/docs/superpowers/specs/plans/2026-03-17-nav-hidden-items.md +578 -0
- package/docs/superpowers/specs/plans/2026-03-19-accordion-insert.md +635 -0
- package/docs/superpowers/specs/plans/2026-03-25-text-shortcode.md +545 -0
- package/docs/superpowers/specs/plans/2026-04-05-card-builder.md +1970 -0
- package/docs/superpowers/specs/plans/2026-04-06-insert-builders.md +1049 -0
- package/docs/superpowers/specs/plans/2026-04-06-layouts.md +1015 -0
- package/docs/superpowers/specs/plans/2026-04-07-calendar-plugin.md +1526 -0
- package/docs/superpowers/specs/plans/2026-04-08-plugin-monetisation-split.md +242 -0
- package/docs/superpowers/specs/plans/2026-04-17-components-area.md +2038 -0
- package/docs/superpowers/specs/plans/2026-05-02-effects-slideover-overhaul.md +2335 -0
- package/docs/superpowers/specs/plans/2026-05-06-dashboard-rebuild.md +1999 -0
- package/docs/superpowers/specs/plans/2026-05-17-cache-system.md +1436 -0
- package/docs/superpowers/specs/plans/2026-05-24-menus.md +3096 -0
- package/docs/superpowers/specs/plans/2026-05-25-projects.md +2869 -0
- package/docs/superpowers/specs/plans/2026-09-22-marketplace-step2-consolidation.md +505 -0
- package/docs/superpowers/specs/plans/2026-09-22-marketplace-step3-catalogue-and-key.md +154 -0
- package/docs/superpowers/specs/plans/2026-09-22-tools-promotion.md +260 -0
- package/docs/theming.md +320 -0
- package/package.json +2 -2
- package/plugins/_lib/admin/mail/keyboard.js +49 -0
- package/plugins/_lib/admin/mail/reader-view.js +4 -2
- package/public/js/celebrations/core/canvas.js +1 -1
- package/public/js/celebrations/index.js +1 -1
- package/public/js/celebrations/licenses/ABeeZee-Regular-OFL.txt +93 -0
- package/public/js/celebrations/licenses/Allura-Regular-OFL.txt +93 -0
- package/public/js/celebrations/message-attrs.mjs +1 -0
- package/public/js/celebrations/themes/anniversary.js +1 -0
- package/public/js/celebrations/themes/autumn.js +1 -0
- package/public/js/celebrations/themes/birthday.js +1 -0
- package/public/js/celebrations/themes/butterfly-flight.js +1 -0
- package/public/js/celebrations/themes/calendar.js +1 -0
- package/public/js/celebrations/themes/chinese-new-year-dates.js +1 -0
- package/public/js/celebrations/themes/chinese-new-year.js +1 -0
- package/public/js/celebrations/themes/christmas.js +1 -1
- package/public/js/celebrations/themes/easter.js +1 -0
- package/public/js/celebrations/themes/garden-drawing.js +1 -0
- package/public/js/celebrations/themes/garden.js +1 -0
- package/public/js/celebrations/themes/graduation.js +1 -0
- package/public/js/celebrations/themes/greeting.js +1 -0
- package/public/js/celebrations/themes/make-it-rain.js +1 -0
- package/public/js/celebrations/themes/money-rain.js +1 -0
- package/public/js/celebrations/themes/new-year.js +1 -0
- package/public/js/celebrations/themes/occasion-drawing.js +1 -0
- package/public/js/celebrations/themes/occasions.js +1 -0
- package/public/js/celebrations/themes/rain-control.js +1 -0
- package/public/js/celebrations/themes/sparkler-font-data.js +1 -0
- package/public/js/celebrations/themes/sparkler-font.js +1 -0
- package/public/js/celebrations/themes/sparkler-writing.js +1 -0
- package/public/js/celebrations/themes/summer-garden.js +1 -0
- package/public/js/celebrations/themes/winter.js +1 -0
- package/public/js/celebrations/trait-data.mjs +1 -1
- package/public/js/effects.js +1 -1
- package/scripts/build.js +4 -0
- package/server/services/markdown.js +5 -2
- package/server/templates/page.html +1 -1
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# Scribe Script Shortcode Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Expose Domma's Scribe `actions` array in CMS Markdown via nested `[render]`, `[wait]`, and `[undo]` shortcodes
|
|
6
|
+
inside `[scribe]`, so content editors can write multi-step typewriter scripts without touching JavaScript.
|
|
7
|
+
|
|
8
|
+
**Architecture:** The `[scribe]` shortcode handler in `plugin.js` gains a dedicated parser that scans the body for inner
|
|
9
|
+
action shortcodes and serialises them to JSON stored in a `data-fx-actions` attribute. `inject-body.html` reads that
|
|
10
|
+
attribute on the client side and passes the parsed array directly to `E.scribe()`. Backward compat is preserved: a
|
|
11
|
+
plain-text body continues to produce a single `{render: text}` action as before.
|
|
12
|
+
|
|
13
|
+
**Tech Stack:** Fastify plugin hooks, `parseShortcodeAttrs` / `escapeAttr` from markdown.js, `E.scribe()` from Domma JS,
|
|
14
|
+
vanilla JS in inject-body.html.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
### Task 1: Export `escapeAttr` from markdown.js and add to plugin context
|
|
19
|
+
|
|
20
|
+
**Files:**
|
|
21
|
+
|
|
22
|
+
- Modify: `server/services/markdown.js`
|
|
23
|
+
|
|
24
|
+
**Step 1: Change `escapeAttr` declaration to `export function`**
|
|
25
|
+
|
|
26
|
+
In `server/services/markdown.js` (around line 154), change:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
function escapeAttr(str) {
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
to:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
export function escapeAttr(str) {
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Step 2: Add `escapeAttr` to the plugin shortcode context**
|
|
39
|
+
|
|
40
|
+
In `processPluginShortcodes` (around line 68), change:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
const context = {parseShortcodeAttrs, scrubCodeRegions, marked, processCardBlocks, processGridBlocks};
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
to:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
const context = {parseShortcodeAttrs, scrubCodeRegions, marked, processCardBlocks, processGridBlocks, escapeAttr};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Step 3: Verify export**
|
|
53
|
+
|
|
54
|
+
Run: `node -e "import('./server/services/markdown.js').then(m => console.log('ok:', typeof m.escapeAttr))"`
|
|
55
|
+
Expected: `ok: function`
|
|
56
|
+
|
|
57
|
+
**Step 4: Commit**
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
git add server/services/markdown.js
|
|
61
|
+
git commit -m "feat: export escapeAttr from markdown.js and expose in plugin shortcode context"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### Task 2: Dedicated `[scribe]` shortcode handler with action parsing
|
|
67
|
+
|
|
68
|
+
**Files:**
|
|
69
|
+
|
|
70
|
+
- Modify: `plugins/domma-effects/plugin.js`
|
|
71
|
+
|
|
72
|
+
**Step 1: Remove `'scribe'` from the generic loop**
|
|
73
|
+
|
|
74
|
+
Change line ~66 from:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
for (const name of ['reveal', 'breathe', 'pulse', 'shake', 'scribe', 'scramble', 'ripple', 'twinkle']) {
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
to:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
for (const name of ['reveal', 'breathe', 'pulse', 'shake', 'scramble', 'ripple', 'twinkle']) {
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Step 2: Insert dedicated scribe handler before the generic loop**
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
registerShortcode('scribe', (attrStr, body, ctx) => {
|
|
90
|
+
if (body === null) return '';
|
|
91
|
+
const attrs = ctx.parseShortcodeAttrs(attrStr);
|
|
92
|
+
const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${v}"`).join('');
|
|
93
|
+
|
|
94
|
+
// Script mode: body contains at least one [render], [wait], or [undo] action shortcode
|
|
95
|
+
const hasActions = /\[\s*(render|wait|undo)\b/.test(body);
|
|
96
|
+
|
|
97
|
+
if (!hasActions) {
|
|
98
|
+
// Legacy path - single render from body text (backward compat)
|
|
99
|
+
const innerHtml = ctx.marked.parse(ctx.processCardBlocks(ctx.processGridBlocks(body.trim())));
|
|
100
|
+
return `<div class="dm-fx-scribe"${dataAttrs}>${innerHtml}</div>\n`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Script path - parse inner action shortcodes in document order
|
|
104
|
+
const actions = [];
|
|
105
|
+
const actionRe = /\[render([^\]]*)\]([\s\S]*?)\[\/render\]|\[wait\]([\s\S]*?)\[\/wait\]|\[undo([^\]]*?)\/\]/gi;
|
|
106
|
+
let match;
|
|
107
|
+
while ((match = actionRe.exec(body)) !== null) {
|
|
108
|
+
if (match[0].startsWith('[render')) {
|
|
109
|
+
const rAttrs = ctx.parseShortcodeAttrs(match[1] || '');
|
|
110
|
+
const action = {render: match[2].trim()};
|
|
111
|
+
if (rAttrs.effect) action.effect = rAttrs.effect;
|
|
112
|
+
actions.push(action);
|
|
113
|
+
} else if (match[0].startsWith('[wait')) {
|
|
114
|
+
const val = match[3].trim();
|
|
115
|
+
actions.push({wait: isNaN(val) ? val : parseInt(val, 10)});
|
|
116
|
+
} else {
|
|
117
|
+
const uAttrs = ctx.parseShortcodeAttrs(match[4] || '');
|
|
118
|
+
if (uAttrs.all === 'true') {
|
|
119
|
+
actions.push({undoRender: 'all'});
|
|
120
|
+
} else if (uAttrs.count) {
|
|
121
|
+
actions.push({undoRender: parseInt(uAttrs.count, 10)});
|
|
122
|
+
} else {
|
|
123
|
+
actions.push({undoRender: true});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!actions.length) return '';
|
|
129
|
+
|
|
130
|
+
const actionsJson = ctx.escapeAttr(JSON.stringify(actions));
|
|
131
|
+
return `<span class="dm-fx-scribe"${dataAttrs} data-fx-actions="${actionsJson}"></span>\n`;
|
|
132
|
+
}, {priority: 5});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Step 3: Smoke-test script mode**
|
|
136
|
+
|
|
137
|
+
Run:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
node -e "import('./server/services/markdown.js').then(({parseMarkdown}) => { const {html} = parseMarkdown('---\ntitle: T\n---\n[scribe loop=\"true\"]\n[render effect=\"fade\"]Hello[/render]\n[wait]1s[/wait]\n[undo /]\n[render]World[/render]\n[/scribe]'); console.log('span:', html.includes('dm-fx-scribe')); console.log('actions:', html.includes('data-fx-actions')); console.log('loop:', html.includes('data-fx-loop')); })"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Expected: `span: true`, `actions: true`, `loop: true`
|
|
144
|
+
|
|
145
|
+
**Step 4: Smoke-test legacy mode is unchanged**
|
|
146
|
+
|
|
147
|
+
Run:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
node -e "import('./server/services/markdown.js').then(({parseMarkdown}) => { const {html} = parseMarkdown('---\ntitle: T\n---\n[scribe speed=\"50\"]Hello World[/scribe]'); console.log('div:', html.includes('<div class=\"dm-fx-scribe\"')); console.log('text:', html.includes('Hello World')); })"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Expected: `div: true`, `text: true`
|
|
154
|
+
|
|
155
|
+
**Step 5: Commit**
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
git add plugins/domma-effects/plugin.js
|
|
159
|
+
git commit -m "feat: dedicated [scribe] handler with [render]/[wait]/[undo] action script support"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### Task 3: Update `inject-body.html` to read `data-fx-actions` and wire new options
|
|
165
|
+
|
|
166
|
+
**Files:**
|
|
167
|
+
|
|
168
|
+
- Modify: `plugins/domma-effects/public/inject-body.html`
|
|
169
|
+
|
|
170
|
+
**Step 1: Replace the `.dm-fx-scribe` handler block**
|
|
171
|
+
|
|
172
|
+
Find the existing block (lines ~218-229):
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
body.querySelectorAll('.dm-fx-scribe').forEach(function (el) {
|
|
176
|
+
var opts = {};
|
|
177
|
+
if (attr(el, 'speed')) opts.speed = numAttr(el, 'speed', 50);
|
|
178
|
+
if (attr(el, 'cursor')) opts.cursor = boolAttr(el, 'cursor', true);
|
|
179
|
+
if (attr(el, 'mode')) opts.mode = attr(el, 'mode');
|
|
180
|
+
if (attr(el, 'loop')) opts.loop = boolAttr(el, 'loop', false);
|
|
181
|
+
var text = el.textContent.trim();
|
|
182
|
+
if (!text) return;
|
|
183
|
+
el.textContent = '';
|
|
184
|
+
opts.actions = [{render: text}];
|
|
185
|
+
E.scribe(el, opts);
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Replace with:
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
body.querySelectorAll('.dm-fx-scribe').forEach(function (el) {
|
|
193
|
+
var opts = {};
|
|
194
|
+
if (attr(el, 'speed')) opts.speed = numAttr(el, 'speed', 50);
|
|
195
|
+
if (attr(el, 'delete-speed')) opts.deleteSpeed = numAttr(el, 'delete-speed', 30);
|
|
196
|
+
if (attr(el, 'cursor')) opts.cursor = boolAttr(el, 'cursor', true);
|
|
197
|
+
if (attr(el, 'cursor-char')) opts.cursorChar = attr(el, 'cursor-char');
|
|
198
|
+
if (attr(el, 'cursor-type')) opts.cursorType = attr(el, 'cursor-type');
|
|
199
|
+
if (attr(el, 'mode')) opts.mode = attr(el, 'mode');
|
|
200
|
+
if (attr(el, 'loop')) opts.loop = boolAttr(el, 'loop', false);
|
|
201
|
+
if (attr(el, 'loop-delay')) opts.loopDelay = numAttr(el, 'loop-delay', 1000);
|
|
202
|
+
if (attr(el, 'pause-on-hover')) opts.pauseOnHover = boolAttr(el, 'pause-on-hover', false);
|
|
203
|
+
|
|
204
|
+
var actionsAttr = attr(el, 'actions');
|
|
205
|
+
if (actionsAttr) {
|
|
206
|
+
try { opts.actions = JSON.parse(actionsAttr); } catch (e) { return; }
|
|
207
|
+
el.textContent = '';
|
|
208
|
+
} else {
|
|
209
|
+
var text = el.textContent.trim();
|
|
210
|
+
if (!text) return;
|
|
211
|
+
el.textContent = '';
|
|
212
|
+
opts.actions = [{render: text}];
|
|
213
|
+
}
|
|
214
|
+
E.scribe(el, opts);
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Step 2: Commit**
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
git add plugins/domma-effects/public/inject-body.html
|
|
222
|
+
git commit -m "feat: read data-fx-actions in scribe client init; expose deleteSpeed, cursorChar, cursorType, loopDelay, pauseOnHover"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
### Task 4: Update Domma Effects admin docs
|
|
228
|
+
|
|
229
|
+
**Files:**
|
|
230
|
+
|
|
231
|
+
- Modify: `plugins/domma-effects/admin/templates/domma-effects.html`
|
|
232
|
+
|
|
233
|
+
**Step 1: Replace the Scribe section (lines ~178-183)**
|
|
234
|
+
|
|
235
|
+
Find:
|
|
236
|
+
|
|
237
|
+
```html
|
|
238
|
+
<h3 class="mb-2">Typewriter (Scribe)</h3>
|
|
239
|
+
<p class="text-muted mb-2">Types text character by character. Perfect for hero taglines or code
|
|
240
|
+
demonstrations.</p>
|
|
241
|
+
<pre class="code-block mb-3">[scribe speed="50" cursor="true" loop="false"]
|
|
242
|
+
Your text appears letter by letter.
|
|
243
|
+
[/scribe]</pre>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Replace with:
|
|
247
|
+
|
|
248
|
+
```html
|
|
249
|
+
<h3 class="mb-2">Typewriter (Scribe)</h3>
|
|
250
|
+
<p class="text-muted mb-2">Types text character by character. Supports a multi-step <strong>script</strong> of render, wait, and undo actions for looping typewriter sequences.</p>
|
|
251
|
+
|
|
252
|
+
<p class="text-muted mb-1" style="font-size:.85rem;"><strong>Simple (single phrase):</strong></p>
|
|
253
|
+
<pre class="code-block mb-3">[scribe speed="50" cursor="true" loop="false"]
|
|
254
|
+
Your text appears letter by letter.
|
|
255
|
+
[/scribe]</pre>
|
|
256
|
+
|
|
257
|
+
<p class="text-muted mb-1" style="font-size:.85rem;"><strong>Script (multi-step sequence):</strong></p>
|
|
258
|
+
<pre class="code-block mb-2">[scribe loop="true" loop-delay="1000" delete-speed="30"]
|
|
259
|
+
[render effect="fade"]Hello, World![/render]
|
|
260
|
+
[wait]1.5s[/wait]
|
|
261
|
+
[undo /]
|
|
262
|
+
[render]Something else entirely.[/render]
|
|
263
|
+
[wait]2s[/wait]
|
|
264
|
+
[undo all="true" /]
|
|
265
|
+
[/scribe]</pre>
|
|
266
|
+
|
|
267
|
+
<table class="table mb-2" style="font-size:.85rem;">
|
|
268
|
+
<thead><tr><th>Action</th><th>Description</th></tr></thead>
|
|
269
|
+
<tbody>
|
|
270
|
+
<tr><td><code>[render effect="fade"]text[/render]</code></td><td>Type text. effect: none, fade, bounce, glow</td></tr>
|
|
271
|
+
<tr><td><code>[wait]1.5s[/wait]</code></td><td>Pause. Accepts ms, s, or bare number (ms)</td></tr>
|
|
272
|
+
<tr><td><code>[undo /]</code></td><td>Delete the last render</td></tr>
|
|
273
|
+
<tr><td><code>[undo all="true" /]</code></td><td>Delete everything typed so far</td></tr>
|
|
274
|
+
<tr><td><code>[undo count="5" /]</code></td><td>Delete exactly N characters</td></tr>
|
|
275
|
+
</tbody>
|
|
276
|
+
</table>
|
|
277
|
+
|
|
278
|
+
<table class="table mb-3" style="font-size:.85rem;">
|
|
279
|
+
<thead><tr><th>Attribute</th><th>Default</th><th>Description</th></tr></thead>
|
|
280
|
+
<tbody>
|
|
281
|
+
<tr><td><code>speed</code></td><td>50</td><td>Typing speed (ms/char)</td></tr>
|
|
282
|
+
<tr><td><code>delete-speed</code></td><td>30</td><td>Deletion speed (ms/char)</td></tr>
|
|
283
|
+
<tr><td><code>cursor</code></td><td>true</td><td>Show cursor</td></tr>
|
|
284
|
+
<tr><td><code>cursor-type</code></td><td>caret</td><td>caret, block, underline</td></tr>
|
|
285
|
+
<tr><td><code>cursor-char</code></td><td>|</td><td>Custom cursor character</td></tr>
|
|
286
|
+
<tr><td><code>mode</code></td><td>typewriter</td><td>typewriter, word, sentence</td></tr>
|
|
287
|
+
<tr><td><code>loop</code></td><td>false</td><td>true to loop, or a repeat count</td></tr>
|
|
288
|
+
<tr><td><code>loop-delay</code></td><td>1000</td><td>Pause between loops (ms)</td></tr>
|
|
289
|
+
<tr><td><code>pause-on-hover</code></td><td>false</td><td>Pause typing while hovered</td></tr>
|
|
290
|
+
</tbody>
|
|
291
|
+
</table>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Step 2: Commit**
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
git add plugins/domma-effects/admin/templates/domma-effects.html
|
|
298
|
+
git commit -m "docs: add Scribe script syntax and full attribute table to effects admin"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
### Task 5: Update page editor shortcode help
|
|
304
|
+
|
|
305
|
+
**Files:**
|
|
306
|
+
|
|
307
|
+
- Modify: `admin/js/views/page-editor.js`
|
|
308
|
+
|
|
309
|
+
**Step 1: Update the scribe row description (around line 284)**
|
|
310
|
+
|
|
311
|
+
Change:
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
['[scribe speed="50"]...[/scribe]', 'Typewriter character-by-character'],
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
to:
|
|
318
|
+
|
|
319
|
+
```js
|
|
320
|
+
['[scribe speed="50"]...[/scribe]', 'Typewriter - plain text or multi-step script'],
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Step 2: Add Scribe script snippet after `section5.appendChild(effectsTable)` (around line 310)**
|
|
324
|
+
|
|
325
|
+
```js
|
|
326
|
+
section5.appendChild(makeSnippet(
|
|
327
|
+
'Scribe - multi-step script',
|
|
328
|
+
'[scribe loop="true" loop-delay="1000"]\n[render effect="fade"]Hello, World![/render]\n[wait]1.5s[/wait]\n[undo /]\n[render]Something else.[/render]\n[wait]2s[/wait]\n[undo all="true" /]\n[/scribe]',
|
|
329
|
+
'Actions: [render effect="fade|bounce|glow"]text[/render] · [wait]1.5s[/wait] · [undo /] · [undo all="true" /] · [undo count="5" /]'
|
|
330
|
+
));
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Step 3: Commit**
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
git add admin/js/views/page-editor.js
|
|
337
|
+
git commit -m "docs: add Scribe multi-step script snippet to page editor help"
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Final Verification
|
|
343
|
+
|
|
344
|
+
```
|
|
345
|
+
node -e "
|
|
346
|
+
import('./server/services/markdown.js').then(({parseMarkdown}) => {
|
|
347
|
+
const src = \`---
|
|
348
|
+
title: T
|
|
349
|
+
---
|
|
350
|
+
[scribe loop=\"true\" delete-speed=\"25\" cursor-type=\"block\"]
|
|
351
|
+
[render effect=\"bounce\"]Step one[/render]
|
|
352
|
+
[wait]500ms[/wait]
|
|
353
|
+
[undo /]
|
|
354
|
+
[render]Step two[/render]
|
|
355
|
+
[wait]1s[/wait]
|
|
356
|
+
[undo count=\"4\" /]
|
|
357
|
+
[render]Done.[/render]
|
|
358
|
+
[/scribe]\`;
|
|
359
|
+
const {html} = parseMarkdown(src);
|
|
360
|
+
const m = html.match(/data-fx-actions=\\\"([^\\\"]*)\\\"/);
|
|
361
|
+
if (!m) { console.error('FAIL: no data-fx-actions'); process.exit(1); }
|
|
362
|
+
const decoded = m[1].replace(/"/g,'\"').replace(/&/g,'&');
|
|
363
|
+
const actions = JSON.parse(decoded);
|
|
364
|
+
console.log('actions.length === 6:', actions.length === 6);
|
|
365
|
+
console.log('render effect bounce:', actions[0].effect === 'bounce');
|
|
366
|
+
console.log('wait 500ms:', actions[1].wait === '500ms');
|
|
367
|
+
console.log('undoRender true:', actions[2].undoRender === true);
|
|
368
|
+
console.log('undoRender count 4:', actions[5].undoRender === 4);
|
|
369
|
+
console.log('loop attr:', html.includes('data-fx-loop=\"true\"'));
|
|
370
|
+
console.log('PASS');
|
|
371
|
+
}).catch(e => console.error('ERROR:', e.message));
|
|
372
|
+
"
|
|
373
|
+
```
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# User Types - Design Document
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-03-05
|
|
4
|
+
**Status:** Approved
|
|
5
|
+
**Approach:** Option A - Preset Collection (full dynamic roles)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The Users admin view gains a second tab - **User Types** - backed by a preset Collection. Roles become runtime-dynamic: stored in `content/collections/user-types/data.json`, read at startup, cached in memory, and invalidated on CRUD. `config/auth.json` is stripped back to JWT/bcrypt config only.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Data Model
|
|
16
|
+
|
|
17
|
+
### Preset Collection: `user-types`
|
|
18
|
+
|
|
19
|
+
**Location:** `content/collections/user-types/`
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
schema.json - field definitions (locked - preset: true)
|
|
23
|
+
data.json - role entries (fully editable via admin)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Schema fields:**
|
|
27
|
+
|
|
28
|
+
| Field | Type | Notes |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `name` | text | Slug-style key (`editor`). Unique. |
|
|
31
|
+
| `label` | text | Display name (`Editor`) |
|
|
32
|
+
| `level` | number | Access hierarchy - lower = more privileged |
|
|
33
|
+
| `permissions` | multi-select | Which resources this role can access |
|
|
34
|
+
| `badgeClass` | select | Badge CSS class (`badge-info` etc.) |
|
|
35
|
+
|
|
36
|
+
**`config/auth.json` after migration:**
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"accessTokenExpiry": "15m",
|
|
41
|
+
"refreshTokenExpiry": "7d",
|
|
42
|
+
"bcryptRounds": 10
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Roles and permissions blocks are removed entirely from `auth.json`.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Auth Middleware
|
|
51
|
+
|
|
52
|
+
### Startup
|
|
53
|
+
|
|
54
|
+
`server.js` calls `loadUserTypes()` before registering routes. This reads `data.json` and builds two in-memory maps:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
roleMap: { admin: { label, level }, editor: { label, level }, ... }
|
|
58
|
+
permissionsMap: { pages: ['admin','manager','editor'], ... } // inverted from role-centric entries
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Cache Strategy
|
|
62
|
+
|
|
63
|
+
- Loaded once at startup
|
|
64
|
+
- Invalidated and reloaded on User Type create/update/delete (event-driven, no TTL)
|
|
65
|
+
- File is small - synchronous-equivalent performance
|
|
66
|
+
|
|
67
|
+
### Bootstrap Protection
|
|
68
|
+
|
|
69
|
+
- `admin` role (level 0) cannot be deleted
|
|
70
|
+
- Delete route returns 403 if `entry.data.level === 0`
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Seeding
|
|
75
|
+
|
|
76
|
+
`seedPresetCollections()` runs at startup before routes register:
|
|
77
|
+
|
|
78
|
+
1. Check if `content/collections/user-types/data.json` exists
|
|
79
|
+
2. If **not** → create directory, write `schema.json` + seed `data.json`
|
|
80
|
+
3. If **yes** → skip (user data preserved)
|
|
81
|
+
|
|
82
|
+
**Seed roles:**
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
[
|
|
86
|
+
{ "data": { "name": "admin", "label": "Admin", "level": 0, "permissions": ["pages","settings","navigation","layouts","media","users","plugins","collections"], "badgeClass": "badge-danger" } },
|
|
87
|
+
{ "data": { "name": "manager", "label": "Manager", "level": 1, "permissions": ["pages","settings","navigation","layouts","media","users","collections"], "badgeClass": "badge-warning" } },
|
|
88
|
+
{ "data": { "name": "editor", "label": "Editor", "level": 2, "permissions": ["pages","media"], "badgeClass": "badge-info" } },
|
|
89
|
+
{ "data": { "name": "subscriber", "label": "Subscriber", "level": 3, "permissions": [], "badgeClass": "badge-secondary" } }
|
|
90
|
+
]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Admin UI
|
|
96
|
+
|
|
97
|
+
**`admin/js/views/users.js`** - wrapped in `E.tabs()`:
|
|
98
|
+
|
|
99
|
+
- **Tab 1: Users** - existing `T.create()` table, unchanged
|
|
100
|
+
- **Tab 2: User Types** - own `T.create()` table, reads `/api/collections/user-types/entries`
|
|
101
|
+
|
|
102
|
+
User Types table features:
|
|
103
|
+
- Add button → modal form (name, label, level, permissions checkboxes, badge colour)
|
|
104
|
+
- Edit → same modal pre-populated
|
|
105
|
+
- Delete → `E.confirm()` guard, disabled for admin (level 0)
|
|
106
|
+
- Badge colour rendered live in table rows
|
|
107
|
+
|
|
108
|
+
**`admin/js/templates/users.html`** structure:
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<div class="view-header">...</div>
|
|
112
|
+
<div class="tabs">
|
|
113
|
+
<div class="tab-list">
|
|
114
|
+
<button class="tab-item active">Users</button>
|
|
115
|
+
<button class="tab-item">User Types</button>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="tab-content">
|
|
118
|
+
<div class="tab-panel active" id="panel-users">...</div>
|
|
119
|
+
<div class="tab-panel" id="panel-user-types">...</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Files Changed
|
|
127
|
+
|
|
128
|
+
| File | Action |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `server/server.js` | Call `seedPresetCollections()` + `loadUserTypes()` at startup |
|
|
131
|
+
| `server/services/userTypes.js` | New - seed, load, cache, invalidate, invert permissions |
|
|
132
|
+
| `server/routes/api/users.js` | Middleware reads from `userTypes` cache instead of `auth.json` |
|
|
133
|
+
| `server/routes/api/collections.js` | CRUD routes call `userTypes.invalidate()` for `user-types` slug |
|
|
134
|
+
| `config/auth.json` | Remove `roles` + `permissions` blocks |
|
|
135
|
+
| `content/collections/user-types/schema.json` | Seeded at startup |
|
|
136
|
+
| `content/collections/user-types/data.json` | Seeded at startup |
|
|
137
|
+
| `admin/js/views/users.js` | Add tabs + User Types table + modal |
|
|
138
|
+
| `admin/js/templates/users.html` | Add tabs structure |
|
|
139
|
+
| `docs/structure/user-types.md` | New - structure documentation |
|