domma-cms 0.1.0 → 0.2.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/admin/css/admin.css +78 -1
- package/admin/js/api.js +32 -0
- package/admin/js/app.js +24 -7
- package/admin/js/config/sidebar-config.js +8 -0
- package/admin/js/templates/collection-editor.html +80 -0
- package/admin/js/templates/collection-entries.html +36 -0
- package/admin/js/templates/collections.html +12 -0
- package/admin/js/templates/documentation.html +136 -0
- package/admin/js/templates/navigation.html +26 -4
- package/admin/js/templates/page-editor.html +91 -85
- package/admin/js/templates/settings.html +433 -172
- package/admin/js/views/collection-editor.js +487 -0
- package/admin/js/views/collection-entries.js +484 -0
- package/admin/js/views/collections.js +153 -0
- package/admin/js/views/dashboard.js +14 -6
- package/admin/js/views/index.js +9 -3
- package/admin/js/views/login.js +3 -2
- package/admin/js/views/navigation.js +77 -11
- package/admin/js/views/page-editor.js +207 -25
- package/admin/js/views/pages.js +14 -6
- package/admin/js/views/settings.js +137 -2
- package/admin/js/views/users.js +10 -7
- package/bin/cli.js +37 -10
- package/config/auth.json +2 -1
- package/config/content.json +1 -0
- package/config/navigation.json +14 -4
- package/config/plugins.json +0 -18
- package/config/presets.json +4 -8
- package/config/site.json +44 -3
- package/package.json +6 -2
- package/plugins/domma-effects/admin/templates/domma-effects.html +92 -3
- package/plugins/domma-effects/plugin.js +125 -0
- package/plugins/domma-effects/public/inject-body.html +19 -0
- package/plugins/example-analytics/admin/views/analytics.js +2 -2
- package/plugins/example-analytics/plugin.json +8 -0
- package/plugins/example-analytics/stats.json +15 -1
- package/plugins/form-builder/admin/templates/form-editor.html +19 -6
- package/plugins/form-builder/admin/views/form-editor.js +634 -9
- package/plugins/form-builder/admin/views/form-submissions.js +4 -4
- package/plugins/form-builder/admin/views/forms-list.js +5 -5
- package/plugins/form-builder/data/forms/consent.json +104 -0
- package/plugins/form-builder/data/forms/contacts.json +66 -0
- package/plugins/form-builder/data/submissions/consent.json +13 -0
- package/plugins/form-builder/data/submissions/contacts.json +26 -0
- package/plugins/form-builder/plugin.js +62 -11
- package/plugins/form-builder/plugin.json +12 -16
- package/plugins/form-builder/public/form-logic-engine.js +568 -0
- package/plugins/form-builder/public/inject-body.html +88 -6
- package/plugins/form-builder/public/inject-head.html +16 -0
- package/plugins/form-builder/public/package.json +1 -0
- package/public/css/site.css +113 -0
- package/public/js/btt.js +90 -0
- package/public/js/cookie-consent.js +61 -0
- package/public/js/site.js +129 -34
- package/scripts/build.js +129 -0
- package/scripts/seed.js +517 -7
- package/server/routes/api/collections.js +301 -0
- package/server/routes/api/settings.js +66 -2
- package/server/server.js +19 -15
- package/server/services/collections.js +430 -0
- package/server/services/content.js +11 -2
- package/server/services/hooks.js +109 -0
- package/server/services/markdown.js +500 -149
- package/server/services/plugins.js +6 -1
- package/server/services/renderer.js +73 -7
- package/server/templates/page.html +38 -3
- package/plugins/back-to-top/admin/templates/back-to-top-settings.html +0 -55
- package/plugins/back-to-top/admin/views/back-to-top-settings.js +0 -44
- package/plugins/back-to-top/config.js +0 -10
- package/plugins/back-to-top/plugin.js +0 -24
- package/plugins/back-to-top/plugin.json +0 -36
- package/plugins/back-to-top/public/inject-body.html +0 -105
- package/plugins/cookie-consent/admin/templates/cookie-consent-settings.html +0 -113
- package/plugins/cookie-consent/admin/views/cookie-consent-settings.js +0 -73
- package/plugins/cookie-consent/config.js +0 -30
- package/plugins/cookie-consent/plugin.js +0 -24
- package/plugins/cookie-consent/plugin.json +0 -36
- package/plugins/cookie-consent/public/inject-body.html +0 -69
- package/plugins/custom-css/admin/templates/custom-css.html +0 -17
- package/plugins/custom-css/admin/views/custom-css.js +0 -35
- package/plugins/custom-css/config.js +0 -1
- package/plugins/custom-css/data/custom.css +0 -0
- package/plugins/custom-css/plugin.js +0 -63
- package/plugins/custom-css/plugin.json +0 -32
- package/plugins/custom-css/public/inject-head.html +0 -1
- package/plugins/form-builder/data/forms/contact.json +0 -52
- package/plugins/form-builder/data/submissions/contact.json +0 -14
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Domma Effects Plugin — Server
|
|
3
3
|
*
|
|
4
|
+
* Registers all [effect] shortcodes via the hooks API at startup.
|
|
5
|
+
* Shortcodes are only active when this plugin is enabled — disabling the plugin
|
|
6
|
+
* means [reveal], [counter], etc. pass through as plain text.
|
|
7
|
+
*
|
|
4
8
|
* Endpoints (prefix: /api/plugins/domma-effects):
|
|
5
9
|
* GET /settings — public (called from public site IIFE)
|
|
6
10
|
* PUT /settings — admin-auth — saves user overrides
|
|
@@ -9,6 +13,127 @@ import {getPluginSettings, savePluginState} from '../../server/services/plugins.
|
|
|
9
13
|
|
|
10
14
|
export default async function dommaEffectsPlugin(fastify, options) {
|
|
11
15
|
const {authenticate, requireAdmin} = options.auth;
|
|
16
|
+
const {registerShortcode} = options.hooks;
|
|
17
|
+
|
|
18
|
+
// -------------------------------------------------------------------------
|
|
19
|
+
// Shortcode registrations
|
|
20
|
+
// -------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
// Self-closing: [counter to="100" from="0" prefix="$" suffix="+" duration="2000" /]
|
|
23
|
+
registerShortcode('counter', (attrStr, _body, ctx) => {
|
|
24
|
+
const attrs = ctx.parseShortcodeAttrs(attrStr);
|
|
25
|
+
const to = attrs.to || '0';
|
|
26
|
+
const prefix = attrs.prefix || '';
|
|
27
|
+
const suffix = attrs.suffix || '';
|
|
28
|
+
const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${v}"`).join('');
|
|
29
|
+
return `<span class="dm-fx-counter"${dataAttrs}>${prefix}${to}${suffix}</span>`;
|
|
30
|
+
}, {priority: 5});
|
|
31
|
+
|
|
32
|
+
// Self-closing: [celebrate theme="christmas" intensity="medium" /]
|
|
33
|
+
registerShortcode('celebrate', (attrStr, _body, ctx) => {
|
|
34
|
+
const attrs = ctx.parseShortcodeAttrs(attrStr);
|
|
35
|
+
const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${v}"`).join('');
|
|
36
|
+
return `<div class="dm-fx-celebrate"${dataAttrs}></div>`;
|
|
37
|
+
}, {priority: 5});
|
|
38
|
+
|
|
39
|
+
// [firework] — self-closing and wrapping forms.
|
|
40
|
+
// Self-closing: [firework type="burst" colour="rainbow" size="lg" continuous="true" /]
|
|
41
|
+
// Wrapping: [firework type="sparkle" colour="info"]Click me[/firework]
|
|
42
|
+
// Note: [fireworks] (plural) is registered separately below.
|
|
43
|
+
registerShortcode('firework', (attrStr, body, ctx) => {
|
|
44
|
+
const attrs = ctx.parseShortcodeAttrs(attrStr);
|
|
45
|
+
const classes = ['firework'];
|
|
46
|
+
if (attrs.type) classes.push(`firework-${attrs.type}`);
|
|
47
|
+
if (attrs.colour) classes.push(`firework-${attrs.colour}`);
|
|
48
|
+
if (attrs.size) classes.push(`firework-${attrs.size}`);
|
|
49
|
+
if (attrs.continuous === 'true') classes.push('firework-continuous');
|
|
50
|
+
if (attrs.hover === 'true') classes.push('firework-on-hover');
|
|
51
|
+
if (body === null) {
|
|
52
|
+
return `<div class="${classes.join(' ')}"></div>`;
|
|
53
|
+
}
|
|
54
|
+
const innerHtml = ctx.marked.parse(ctx.processCardBlocks(ctx.processGridBlocks(body.trim())));
|
|
55
|
+
return `<div class="${classes.join(' ')}">${innerHtml}</div>\n`;
|
|
56
|
+
}, {priority: 5});
|
|
57
|
+
|
|
58
|
+
// Wrapping container: [fireworks]...[/fireworks]
|
|
59
|
+
// Body contains already-processed firework HTML from the firework handler above.
|
|
60
|
+
registerShortcode('fireworks', (attrStr, body, _ctx) => {
|
|
61
|
+
if (body === null) return '';
|
|
62
|
+
return `<div class="fireworks-container">${body.trim()}</div>\n`;
|
|
63
|
+
}, {priority: 10});
|
|
64
|
+
|
|
65
|
+
// Dedicated scribe handler — supports both simple wrapping and action-script syntax.
|
|
66
|
+
// Simple: [scribe speed="fast"]Hello world[/scribe]
|
|
67
|
+
// Script: [scribe][render effect="fade"]Hello[/render][wait]1s[/wait][undo /][/scribe]
|
|
68
|
+
registerShortcode('scribe', (attrStr, body, ctx) => {
|
|
69
|
+
if (body === null) return '';
|
|
70
|
+
const attrs = ctx.parseShortcodeAttrs(attrStr);
|
|
71
|
+
const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${ctx.escapeAttr(v)}"`).join('');
|
|
72
|
+
const hasActions = /\[\s*(render|wait|undo)\b/.test(body);
|
|
73
|
+
if (!hasActions) {
|
|
74
|
+
const innerHtml = ctx.marked.parse(ctx.processCardBlocks(ctx.processGridBlocks(body.trim())));
|
|
75
|
+
return `<div class="dm-fx-scribe"${dataAttrs}>${innerHtml}</div>\n`;
|
|
76
|
+
}
|
|
77
|
+
const actions = [];
|
|
78
|
+
const actionRe = /\[render([^\]]*)\]([\s\S]*?)\[\/render\]|\[wait\]([\s\S]*?)\[\/wait\]|\[undo([^\]]*?)\/\]/gi;
|
|
79
|
+
for (const match of body.matchAll(actionRe)) {
|
|
80
|
+
if (match[0].startsWith('[render')) {
|
|
81
|
+
const rAttrs = ctx.parseShortcodeAttrs(match[1] || '');
|
|
82
|
+
const action = {render: match[2].trim()};
|
|
83
|
+
if (rAttrs.effect) action.effect = rAttrs.effect;
|
|
84
|
+
actions.push(action);
|
|
85
|
+
} else if (match[0].startsWith('[wait')) {
|
|
86
|
+
const val = match[3].trim();
|
|
87
|
+
actions.push({wait: isNaN(val) ? val : parseInt(val, 10)});
|
|
88
|
+
} else {
|
|
89
|
+
const uAttrs = ctx.parseShortcodeAttrs(match[4] || '');
|
|
90
|
+
if (uAttrs.all === 'true') actions.push({undoRender: 'all'});
|
|
91
|
+
else if (uAttrs.count) actions.push({undoRender: parseInt(uAttrs.count, 10)});
|
|
92
|
+
else actions.push({undoRender: true});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!actions.length) return '';
|
|
96
|
+
const actionsJson = ctx.escapeAttr(JSON.stringify(actions));
|
|
97
|
+
return `<span class="dm-fx-scribe"${dataAttrs} data-fx-actions="${actionsJson}"></span>\n`;
|
|
98
|
+
}, {priority: 5});
|
|
99
|
+
|
|
100
|
+
// Wrapping animation effects — all follow the same dm-fx-{name} pattern.
|
|
101
|
+
for (const name of ['reveal', 'breathe', 'pulse', 'shake', 'scramble', 'ripple', 'twinkle']) {
|
|
102
|
+
registerShortcode(name, (attrStr, body, ctx) => {
|
|
103
|
+
if (body === null) return '';
|
|
104
|
+
const attrs = ctx.parseShortcodeAttrs(attrStr);
|
|
105
|
+
const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${v}"`).join('');
|
|
106
|
+
const innerHtml = ctx.marked.parse(ctx.processCardBlocks(ctx.processGridBlocks(body.trim())));
|
|
107
|
+
return `<div class="dm-fx-${name}"${dataAttrs}>${innerHtml}</div>\n`;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// CSS-only: [animate type="fade-in-up" duration="slow" delay="200ms" repeat="infinite"]...[/animate]
|
|
112
|
+
registerShortcode('animate', (attrStr, body, ctx) => {
|
|
113
|
+
if (body === null) return '';
|
|
114
|
+
const attrs = ctx.parseShortcodeAttrs(attrStr);
|
|
115
|
+
const classes = [];
|
|
116
|
+
if (attrs.type) classes.push(`animate-${attrs.type}`);
|
|
117
|
+
if (attrs.duration) classes.push(`animate-duration-${attrs.duration}`);
|
|
118
|
+
if (attrs.delay) classes.push(`animate-delay-${attrs.delay}`);
|
|
119
|
+
if (attrs.repeat) classes.push(`animate-${attrs.repeat}`);
|
|
120
|
+
return `<div class="${classes.join(' ')}">${ctx.marked.parse(ctx.processCardBlocks(ctx.processGridBlocks(body.trim())))}</div>\n`;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// CSS-only: [ambient type="float-blobs" speed="slow" intensity="subtle"]...[/ambient]
|
|
124
|
+
registerShortcode('ambient', (attrStr, body, ctx) => {
|
|
125
|
+
if (body === null) return '';
|
|
126
|
+
const attrs = ctx.parseShortcodeAttrs(attrStr);
|
|
127
|
+
const classes = [];
|
|
128
|
+
if (attrs.type) classes.push(`bg-ambient-${attrs.type}`);
|
|
129
|
+
if (attrs.speed) classes.push(`bg-ambient-${attrs.speed}`);
|
|
130
|
+
if (attrs.intensity) classes.push(`bg-ambient-${attrs.intensity}`);
|
|
131
|
+
return `<div class="${classes.join(' ')}">${ctx.marked.parse(ctx.processCardBlocks(ctx.processGridBlocks(body.trim())))}</div>\n`;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// -------------------------------------------------------------------------
|
|
135
|
+
// Settings routes
|
|
136
|
+
// -------------------------------------------------------------------------
|
|
12
137
|
|
|
13
138
|
fastify.get('/settings', async () => {
|
|
14
139
|
return getPluginSettings('domma-effects');
|
|
@@ -218,9 +218,28 @@
|
|
|
218
218
|
body.querySelectorAll('.dm-fx-scribe').forEach(function (el) {
|
|
219
219
|
var opts = {};
|
|
220
220
|
if (attr(el, 'speed')) opts.speed = numAttr(el, 'speed', 50);
|
|
221
|
+
if (attr(el, 'delete-speed')) opts.deleteSpeed = numAttr(el, 'delete-speed', 30);
|
|
221
222
|
if (attr(el, 'cursor')) opts.cursor = boolAttr(el, 'cursor', true);
|
|
223
|
+
if (attr(el, 'cursor-char')) opts.cursorChar = attr(el, 'cursor-char');
|
|
224
|
+
if (attr(el, 'cursor-type')) opts.cursorType = attr(el, 'cursor-type');
|
|
222
225
|
if (attr(el, 'mode')) opts.mode = attr(el, 'mode');
|
|
223
226
|
if (attr(el, 'loop')) opts.loop = boolAttr(el, 'loop', false);
|
|
227
|
+
if (attr(el, 'loop-delay')) opts.loopDelay = numAttr(el, 'loop-delay', 1000);
|
|
228
|
+
if (attr(el, 'pause-on-hover')) opts.pauseOnHover = boolAttr(el, 'pause-on-hover', false);
|
|
229
|
+
// Script mode: data-fx-actions contains JSON action array
|
|
230
|
+
var actionsRaw = attr(el, 'actions');
|
|
231
|
+
if (actionsRaw) {
|
|
232
|
+
try {
|
|
233
|
+
opts.actions = JSON.parse(actionsRaw);
|
|
234
|
+
} catch (e) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
var text = el.textContent.trim();
|
|
239
|
+
if (!text) return;
|
|
240
|
+
el.textContent = '';
|
|
241
|
+
opts.actions = [{render: text}];
|
|
242
|
+
}
|
|
224
243
|
E.scribe(el, opts);
|
|
225
244
|
});
|
|
226
245
|
|
|
@@ -43,8 +43,8 @@ async function loadStats($container) {
|
|
|
43
43
|
T.create('#analytics-table', {
|
|
44
44
|
data: stats,
|
|
45
45
|
columns: [
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
{key: 'url', title: 'Page URL', render: (val) => `<code>${val}</code>`},
|
|
47
|
+
{key: 'hits', title: 'Page views'}
|
|
48
48
|
],
|
|
49
49
|
emptyMessage: 'No page views recorded yet.'
|
|
50
50
|
});
|
|
@@ -1 +1,15 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"/": 30,
|
|
3
|
+
"/about": 28,
|
|
4
|
+
"/blog": 11,
|
|
5
|
+
"/contact": 10,
|
|
6
|
+
"/resources/typography": 3,
|
|
7
|
+
"/resources": 2,
|
|
8
|
+
"/resources/shortcodes": 5,
|
|
9
|
+
"/resources/cards": 1,
|
|
10
|
+
"/resources/interactive": 12,
|
|
11
|
+
"/resources/grid": 4,
|
|
12
|
+
"/forms": 14,
|
|
13
|
+
"/resources/effects": 3,
|
|
14
|
+
"/blog/hello-world": 14
|
|
15
|
+
}
|
|
@@ -42,13 +42,26 @@
|
|
|
42
42
|
<div class="card mb-4">
|
|
43
43
|
<div class="card-header" style="display:flex;justify-content:space-between;align-items:center;">
|
|
44
44
|
<h2>Fields</h2>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<button id="add-page-break-btn" class="btn btn-ghost btn-sm" title="Add a wizard step separator">
|
|
50
|
-
<span data-icon="minus"></span> Add Page Break
|
|
45
|
+
<div id="add-element-dropdown" style="position:relative;">
|
|
46
|
+
<button id="add-element-btn" class="btn btn-ghost btn-sm" type="button">
|
|
47
|
+
<span data-icon="plus"></span> Add <span
|
|
48
|
+
style="font-size:.65rem;opacity:.6;margin-left:.1rem;">▾</span>
|
|
51
49
|
</button>
|
|
50
|
+
<div id="add-element-menu"
|
|
51
|
+
style="display:none;position:absolute;right:0;top:calc(100% + .2rem);background:var(--card-bg,#1e1e2e);border:1px solid var(--border-color,#333);border-radius:6px;min-width:140px;z-index:100;box-shadow:0 4px 16px rgba(0,0,0,.3);overflow:hidden;">
|
|
52
|
+
<button id="add-field-btn" type="button"
|
|
53
|
+
style="display:block;width:100%;text-align:left;padding:.5rem .85rem;background:none;border:none;color:inherit;cursor:pointer;font-size:.875rem;border-bottom:1px solid var(--border-color,#333);">
|
|
54
|
+
Field
|
|
55
|
+
</button>
|
|
56
|
+
<button id="add-spacer-btn" type="button"
|
|
57
|
+
style="display:block;width:100%;text-align:left;padding:.5rem .85rem;background:none;border:none;color:inherit;cursor:pointer;font-size:.875rem;border-bottom:1px solid var(--border-color,#333);">
|
|
58
|
+
Spacer
|
|
59
|
+
</button>
|
|
60
|
+
<button id="add-page-break-btn" type="button"
|
|
61
|
+
style="display:block;width:100%;text-align:left;padding:.5rem .85rem;background:none;border:none;color:inherit;cursor:pointer;font-size:.875rem;">
|
|
62
|
+
Page Break
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
52
65
|
</div>
|
|
53
66
|
</div>
|
|
54
67
|
<div class="card-body" style="padding:0;">
|