domma-cms 1.6.60 → 1.7.1
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 +4349 -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/lib/effect-defs.js +1 -1
- package/admin/js/lib/effects-builder.js +2 -2
- package/admin/js/lib/mail-compose.js +1 -0
- package/admin/js/lib/page-editor-reference.js +5 -5
- package/admin/js/lib/shortcode-tags.js +1 -1
- package/admin/js/templates/call-new.html +1 -1
- package/admin/js/templates/docs/usage-shortcodes.html +39 -0
- package/admin/js/views/call-new.js +1 -1
- package/admin/js/views/contacts.js +15 -15
- package/admin/js/views/form-submissions.js +2 -2
- 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/compose-window.js +3 -1
- package/plugins/_lib/admin/mail/identity.js +18 -3
- package/plugins/_lib/admin/mail/reader-view.js +32 -3
- package/plugins/blog/CLAUDE.md +4 -0
- package/plugins/blog/admin/lib/email.js +191 -0
- package/plugins/blog/admin/views/comments.js +3 -1
- package/plugins/blog/plugin.json +2 -2
- package/plugins/blog/plugin.public.js +1 -1
- package/plugins/blog/tests/email.test.js +69 -0
- package/plugins/booking/CLAUDE.md +4 -0
- package/plugins/booking/admin/lib/email.js +191 -0
- package/plugins/booking/admin/views/panels.js +7 -3
- package/plugins/booking/plugin.json +1 -1
- package/plugins/booking/tests/email.test.js +69 -0
- package/plugins/free-tier.lock.json +27 -19
- package/plugins/shopping-cart/CLAUDE.md +4 -0
- package/plugins/shopping-cart/admin/lib/email.js +191 -0
- package/plugins/shopping-cart/admin/views/orders.js +11 -3
- package/plugins/shopping-cart/plugin.json +2 -2
- package/plugins/shopping-cart/tests/email.test.js +69 -0
- package/plugins/support/CLAUDE.md +4 -0
- package/plugins/support/admin/lib/email.js +191 -0
- package/plugins/support/admin/views/desk.js +4 -1
- package/plugins/support/admin/views/inbox.js +6 -2
- package/plugins/support/plugin.json +2 -2
- package/plugins/support/tests/email.test.js +69 -0
- 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,278 @@
|
|
|
1
|
+
# Insert Builders - Tabs, Hero, Banner
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-04-06
|
|
4
|
+
**Status:** Approved
|
|
5
|
+
**Scope:** Admin page editor - Insert dropdown enhancements
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Three improvements to the Page Editor Insert toolbar:
|
|
12
|
+
|
|
13
|
+
1. **Tabs** - upgrade the existing basic modal to a richer, field-organised dialog with live shortcode preview
|
|
14
|
+
2. **Hero** - wire up the currently dead `onHero` handler with a full-featured insert dialog
|
|
15
|
+
3. **Banner** - new `[banner]` shortcode (server-side + CSS + insert dialog + toolbar item)
|
|
16
|
+
|
|
17
|
+
A shared utility module (`shortcode-modal.js`) provides UI primitives for the new and upgraded handlers. Each of
|
|
18
|
+
`onTabs` (upgraded), `onHero` (new), and `onBanner` (new) in `page-editor.js` will import from it. Note:
|
|
19
|
+
`markdown-toolbar.js` also has its own duplicated helpers inside its action-handler closures (for text, timeline,
|
|
20
|
+
etc.) - those are a separate concern and not in scope here.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
### Files
|
|
27
|
+
|
|
28
|
+
| File | Change |
|
|
29
|
+
|------------------------------------|--------------------------------------------------------------------|
|
|
30
|
+
| `admin/js/lib/shortcode-modal.js` | **New** - shared UI primitives |
|
|
31
|
+
| `admin/js/views/page-editor.js` | Upgrade `onTabs`, add `onHero`, add `onBanner` |
|
|
32
|
+
| `admin/js/lib/markdown-toolbar.js` | Add `banner` insert item, `onBanner` handler, `banner-insert` icon |
|
|
33
|
+
| `server/services/markdown.js` | Add `processBannerBlocks()` |
|
|
34
|
+
| `public/css/site.css` | Add `.dm-banner` styles and type variants |
|
|
35
|
+
|
|
36
|
+
### Shared utility: `shortcode-modal.js`
|
|
37
|
+
|
|
38
|
+
Exports the following helpers. Imported by `page-editor.js` for the upgraded `onTabs` and new `onHero`/`onBanner`
|
|
39
|
+
handlers:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
export function makeField(label, control) // label + control wrapper div
|
|
43
|
+
export function makeSelect(items) // styled <select>
|
|
44
|
+
export function makeTextInput(placeholder, val) // styled <input type="text">
|
|
45
|
+
export function makeCheckbox(label) // styled checkbox row
|
|
46
|
+
export function makeLivePreview() // returns { el, update(str) }
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`makeLivePreview()` returns a `<pre>` element that displays the current shortcode string and a helper `update(str)`
|
|
50
|
+
function to refresh it. Wired to field `input`/`change` events in each handler.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Section 1: Tabs Builder
|
|
55
|
+
|
|
56
|
+
### Handler: `onTabs` in `page-editor.js`
|
|
57
|
+
|
|
58
|
+
Replaces the existing basic handler. Opens an `E.modal({ title: 'Insert Tabs', size: 'sm' })`.
|
|
59
|
+
|
|
60
|
+
### Fields
|
|
61
|
+
|
|
62
|
+
| Field | Control | Shortcode attribute |
|
|
63
|
+
|----------------|------------------------------------|--------------------------------------|
|
|
64
|
+
| Style | Select: Default / Pills / Bordered | `style="pills"` / `style="bordered"` |
|
|
65
|
+
| Alignment | Select: Left / Centre / Right | `align="center"` / `align="right"` |
|
|
66
|
+
| Animation | Select: None / Fade | `fade` (boolean flag) |
|
|
67
|
+
| Multiple open | Checkbox | `multiple="true"` |
|
|
68
|
+
| Tab list | Dynamic rows (title + icon inputs) | `[tab title="..." icon="..."]` |
|
|
69
|
+
| Add Tab button | - | max 10 tabs, min 2 |
|
|
70
|
+
| Live preview | `<pre>` box | - |
|
|
71
|
+
|
|
72
|
+
### Tab rows
|
|
73
|
+
|
|
74
|
+
Each tab row contains:
|
|
75
|
+
|
|
76
|
+
- Text input: tab title (required, placeholder "Tab title")
|
|
77
|
+
- Text input: icon name (optional, placeholder "icon (optional)")
|
|
78
|
+
- Remove button (disabled when ≤2 tabs)
|
|
79
|
+
|
|
80
|
+
Default state: 2 tabs pre-populated ("Overview", "Details").
|
|
81
|
+
|
|
82
|
+
### Shortcode output
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
[tabs style="pills" align="center" fade]
|
|
86
|
+
[tab title="Overview" icon="home"]Tab content here.[/tab]
|
|
87
|
+
[tab title="Details"]Tab content here.[/tab]
|
|
88
|
+
[/tabs]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Omit attributes that are default/empty (e.g. no `style` attribute if "Default" selected, no `icon` if blank).
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Section 2: Hero Builder
|
|
96
|
+
|
|
97
|
+
### Handler: `onHero` in `page-editor.js`
|
|
98
|
+
|
|
99
|
+
The toolbar item `{label: 'Hero', icon: 'hero', action: 'hero'}` **already exists** in the `markdown-toolbar.js` items
|
|
100
|
+
array, and the dispatch `if (handlers.hero) handlers.hero(ta)` already exists in the action switch. What is missing is
|
|
101
|
+
the handler registration in `page-editor.js` - so clicking Insert → Hero currently does nothing. This task adds only the
|
|
102
|
+
`page-editor.js` handler; no changes to `markdown-toolbar.js` are required for Hero.
|
|
103
|
+
|
|
104
|
+
Opens `E.modal({ title: 'Insert Hero', size: 'sm' })`.
|
|
105
|
+
|
|
106
|
+
### Fields
|
|
107
|
+
|
|
108
|
+
| Field | Control | Shortcode attribute |
|
|
109
|
+
|--------------|---------------------------------------|-------------------------------------|
|
|
110
|
+
| Title | Text input (required) | `title="..."` |
|
|
111
|
+
| Tagline | Text input (optional) | `tagline="..."` |
|
|
112
|
+
| Size | Select: Small / Medium / Large / Full | `size="sm/md/lg/full"` |
|
|
113
|
+
| Alignment | Select: Left / Centre / Right | `align="center"` |
|
|
114
|
+
| Background | Radio: None / Colour / Image | - |
|
|
115
|
+
| → Colour | Text input (hex/CSS) | `color="..."` - shown when Colour |
|
|
116
|
+
| → Image path | Text input | `image="..."` - shown when Image |
|
|
117
|
+
| → Overlay | Select: None / Dark / Light | `overlay="dark"` - shown when Image |
|
|
118
|
+
| Min height | Text input (optional, e.g. `400px`) | `min-height="..."` |
|
|
119
|
+
| Live preview | `<pre>` box | - |
|
|
120
|
+
|
|
121
|
+
The Background radio group conditionally shows/hides Colour and Image fields. When "None" is selected, both are hidden.
|
|
122
|
+
|
|
123
|
+
### Shortcode output
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
[hero title="Our Story" tagline="Building something great" size="lg" align="center" image="/media/hero.jpg" overlay="dark"]
|
|
127
|
+
Content here.
|
|
128
|
+
[/hero]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Omit attributes that are default/empty.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Section 3: Banner Shortcode
|
|
136
|
+
|
|
137
|
+
### 3a. Server side - `markdown.js`
|
|
138
|
+
|
|
139
|
+
Add `processBannerBlocks(content)` function. The existing pipeline order in `parseMarkdown` is:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
processCollectionBlocks → processViewBlocks → processStaticBlocks
|
|
143
|
+
→ processDConfigBlocks → processGridBlocks → processCardBlocks
|
|
144
|
+
→ processSlideoverBlocks
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Insert `processBannerBlocks` **after `processCardBlocks` and before `processSlideoverBlocks`** - this ensures banners
|
|
148
|
+
render inside grid columns but can also sit inside a slideover body. Nesting banners inside card bodies is out of scope
|
|
149
|
+
for the initial implementation (card body rendering would need a separate call to `processBannerBlocks` on the inner
|
|
150
|
+
content).
|
|
151
|
+
|
|
152
|
+
**Syntax:**
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
[banner type="info" title="Note" icon="info" dismissible]
|
|
156
|
+
Read this message.
|
|
157
|
+
[/banner]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Attributes:**
|
|
161
|
+
|
|
162
|
+
| Attribute | Values | Default |
|
|
163
|
+
|---------------|-------------------------------------------------------|---------|
|
|
164
|
+
| `type` | `info` / `success` / `warning` / `danger` / `neutral` | `info` |
|
|
165
|
+
| `title` | String | - |
|
|
166
|
+
| `icon` | Domma icon name | - |
|
|
167
|
+
| `dismissible` | Boolean flag | false |
|
|
168
|
+
| `class` | Extra CSS class | - |
|
|
169
|
+
|
|
170
|
+
**Rendered HTML:**
|
|
171
|
+
|
|
172
|
+
```html
|
|
173
|
+
<div class="dm-banner dm-banner--info [extra-class]">
|
|
174
|
+
<span class="dm-banner__icon" data-icon="info"></span>
|
|
175
|
+
<div class="dm-banner__body">
|
|
176
|
+
<strong class="dm-banner__title">Note</strong>
|
|
177
|
+
<p><!-- body content (Markdown parsed) --></p>
|
|
178
|
+
</div>
|
|
179
|
+
<button class="dm-banner__dismiss" aria-label="Dismiss">×</button>
|
|
180
|
+
</div>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- `dm-banner__icon` omitted if no `icon` attribute
|
|
184
|
+
- `dm-banner__title` omitted if no `title` attribute
|
|
185
|
+
- `dm-banner__dismiss` omitted if `dismissible` not set
|
|
186
|
+
|
|
187
|
+
### 3b. CSS - `public/css/site.css`
|
|
188
|
+
|
|
189
|
+
Five type variants using Domma CSS custom properties. Base styles cover layout (flex row, aligned icon + body),
|
|
190
|
+
border-left accent, padding, border-radius. Each `--info/success/warning/danger/neutral` variant sets background tint
|
|
191
|
+
and accent colour.
|
|
192
|
+
|
|
193
|
+
`.dm-banner` base style must include `position: relative` for the dismiss button's absolute positioning to work. Dismiss
|
|
194
|
+
button: `position: absolute; top: 0.5rem; right: 0.5rem;` - hides banner on click via `site.js`.
|
|
195
|
+
|
|
196
|
+
### 3c. Client dismiss handler - `public/js/site.js`
|
|
197
|
+
|
|
198
|
+
Add event delegation for `.dm-banner__dismiss` clicks:
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
$(document).on('click', '.dm-banner__dismiss', function() {
|
|
202
|
+
$(this).closest('.dm-banner').remove();
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Note: `.closest()` is a standard Domma (jQuery-compatible) method - confirmed available.
|
|
207
|
+
|
|
208
|
+
### 3d. Toolbar - `markdown-toolbar.js`
|
|
209
|
+
|
|
210
|
+
Three changes:
|
|
211
|
+
|
|
212
|
+
1. **Register icon** - new `banner-insert` SVG (horizontal strip with lines, consistent with toolbar icon style)
|
|
213
|
+
2. **Add insert item** to the items array (alphabetical order between `Accordion` and `Badge`):
|
|
214
|
+
```js
|
|
215
|
+
{label: 'Banner', icon: 'banner-insert', action: 'banner'},
|
|
216
|
+
```
|
|
217
|
+
3. **Add dispatch** in the action switch:
|
|
218
|
+
```js
|
|
219
|
+
} else if (action === 'banner') {
|
|
220
|
+
if (handlers.banner) handlers.banner(ta);
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
4. **Add `onBanner` to the returned handler chain and JSDoc**
|
|
224
|
+
|
|
225
|
+
### 3e. Insert dialog - `onBanner` in `page-editor.js`
|
|
226
|
+
|
|
227
|
+
Opens `E.modal({ title: 'Insert Banner', size: 'sm' })`.
|
|
228
|
+
|
|
229
|
+
| Field | Control |
|
|
230
|
+
|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
231
|
+
| Type | Segmented button group (5 buttons, each with a coloured dot + label) - not a `<select>`, since `<option>` colour styling is browser-inconsistent. Active button gets `.active` class. |
|
|
232
|
+
| Title | Text input (optional) |
|
|
233
|
+
| Icon | Text input (optional, Domma icon name) |
|
|
234
|
+
| Dismissible | Checkbox |
|
|
235
|
+
| Live preview | `<pre>` box |
|
|
236
|
+
|
|
237
|
+
**Shortcode output:**
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
[banner type="success" title="All done" icon="check-circle" dismissible]
|
|
241
|
+
Banner content here.
|
|
242
|
+
[/banner]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Data Flow
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
User clicks Insert → Banner
|
|
251
|
+
→ onBanner(ta) fires
|
|
252
|
+
→ E.modal opens with fields
|
|
253
|
+
→ fields change → live preview updates
|
|
254
|
+
→ user clicks Insert Banner
|
|
255
|
+
→ shortcode inserted at cursor in textarea
|
|
256
|
+
→ preview re-renders via existing debounced preview call
|
|
257
|
+
→ server processBannerBlocks() → HTML → CSS styles applied
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Out of Scope
|
|
263
|
+
|
|
264
|
+
- Layout management (separate spec)
|
|
265
|
+
- Banner `action` link attribute (can be added in future)
|
|
266
|
+
- Banner nested inside card bodies (requires `processBannerBlocks` call inside card body render)
|
|
267
|
+
- Tabs per-tab body content editing within the dialog (user fills body content in the editor after insert)
|
|
268
|
+
- Hero video background (future enhancement)
|
|
269
|
+
|
|
270
|
+
## In Scope - Server-side Tabs Rendering
|
|
271
|
+
|
|
272
|
+
The new `[tabs]` attributes (`style="bordered"`, `fade`, per-tab `icon`) must be rendered server-side or the insert
|
|
273
|
+
dialog generates attributes the server silently ignores - a misleading UX. `markdown.js` `processTabsBlocks()` (or
|
|
274
|
+
equivalent) must be updated to:
|
|
275
|
+
|
|
276
|
+
- `style="bordered"` → add `tabs--bordered` class to wrapper
|
|
277
|
+
- `fade` → add `tabs--fade` class to wrapper
|
|
278
|
+
- Per-tab `icon` attribute → render `<span data-icon="...">` before tab title text in the tab button
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Layout Presets - Design Spec
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Extend the existing layout preset system so presets control page width, background colour, background image, and custom
|
|
6
|
+
CSS class - in addition to the existing navbar/footer/sidebar toggles. Add full CRUD in the admin UI (create, edit,
|
|
7
|
+
delete), with three built-in presets (default, landing, blank) that can be edited but not deleted.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Data Model
|
|
12
|
+
|
|
13
|
+
Each layout preset is a keyed object stored in `config/presets.json` (via `getConfig('presets')` /
|
|
14
|
+
`saveConfig('presets', data)`).
|
|
15
|
+
|
|
16
|
+
**Full shape:**
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"default": {
|
|
21
|
+
"key": "default",
|
|
22
|
+
"label": "Default",
|
|
23
|
+
"description": "Standard page with navbar and footer.",
|
|
24
|
+
"builtin": true,
|
|
25
|
+
"navbar": true,
|
|
26
|
+
"footer": true,
|
|
27
|
+
"sidebar": false,
|
|
28
|
+
"width": "normal",
|
|
29
|
+
"bgColor": "",
|
|
30
|
+
"bgImage": "",
|
|
31
|
+
"class": ""
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Fields:**
|
|
37
|
+
|
|
38
|
+
| Field | Type | Description |
|
|
39
|
+
|---------------|---------|---------------------------------------------------------------------------------|
|
|
40
|
+
| `key` | string | Unique identifier (slug). Auto-generated from label on create. |
|
|
41
|
+
| `label` | string | Display name shown in the admin and the page editor dropdown. |
|
|
42
|
+
| `description` | string | Optional short description shown on the preset card. |
|
|
43
|
+
| `builtin` | boolean | If `true`, the preset cannot be deleted via the API. |
|
|
44
|
+
| `navbar` | boolean | Whether the public navbar renders on this layout. |
|
|
45
|
+
| `footer` | boolean | Whether the public footer renders on this layout. |
|
|
46
|
+
| `sidebar` | boolean | Whether a page sidebar region renders. |
|
|
47
|
+
| `width` | string | One of: `narrow`, `normal`, `wide`, `full`. Default: `normal`. |
|
|
48
|
+
| `bgColor` | string | CSS colour value for the page background (e.g. `#f5f5f5`). Empty = no override. |
|
|
49
|
+
| `bgImage` | string | URL for a background image. Empty = no override. |
|
|
50
|
+
| `class` | string | Space-separated extra CSS classes added to `<body>`. |
|
|
51
|
+
|
|
52
|
+
**Built-in presets** - seeded on server startup (see API section):
|
|
53
|
+
|
|
54
|
+
| Key | navbar | footer | sidebar | width |
|
|
55
|
+
|-----------|--------|--------|---------|--------|
|
|
56
|
+
| `default` | true | true | false | normal |
|
|
57
|
+
| `landing` | true | false | false | full |
|
|
58
|
+
| `blank` | false | false | false | normal |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Server - API (`server/routes/api/layouts.js`)
|
|
63
|
+
|
|
64
|
+
**Existing routes kept as-is:**
|
|
65
|
+
|
|
66
|
+
- `GET /api/layouts` - returns all presets
|
|
67
|
+
- `PUT /api/layouts` - bulk-saves presets
|
|
68
|
+
- `GET /api/layouts/options` / `PUT /api/layouts/options` - spacer config (unchanged)
|
|
69
|
+
|
|
70
|
+
**New routes:**
|
|
71
|
+
|
|
72
|
+
| Method | Path | Purpose |
|
|
73
|
+
|----------|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
74
|
+
| `POST` | `/api/layouts` | Create a new preset. Validates: `label` required, `key` auto-generated (slugified from label), key must be unique, `builtin` always forced to `false`. |
|
|
75
|
+
| `PUT` | `/api/layouts/:key` | Update a single preset by key. All fields editable except `builtin`. Returns `404` if the key does not exist. |
|
|
76
|
+
| `DELETE` | `/api/layouts/:key` | Delete a preset. Returns `400` if `builtin: true`. Returns `404` if the key does not exist. |
|
|
77
|
+
|
|
78
|
+
**Key collision on `POST`:** If the slugified label produces a key that already exists, return `409` with
|
|
79
|
+
`{ error: 'A preset with this key already exists' }`. The client shows the error message in a toast and keeps the
|
|
80
|
+
slideover open.
|
|
81
|
+
|
|
82
|
+
**Validation rules:**
|
|
83
|
+
|
|
84
|
+
- `width` must be one of `narrow | normal | wide | full` (default `normal` if missing/invalid).
|
|
85
|
+
- `bgColor`: if non-empty, must match `/^[a-zA-Z0-9#(),.\s%/-]+$/`. This guard is intentionally permissive - it prevents
|
|
86
|
+
CSS injection, not invalid colour syntax.
|
|
87
|
+
- `bgImage`: stored as-is (URL); renderer applies `url(...)` wrapper.
|
|
88
|
+
- `class`: stripped of any characters outside `[a-zA-Z0-9\s_-]`.
|
|
89
|
+
- `label`: max 60 chars.
|
|
90
|
+
- `description`: max 200 chars.
|
|
91
|
+
|
|
92
|
+
**Built-in preset seeding:** `config/presets.json` is committed to the repository with the three built-in presets as its
|
|
93
|
+
initial content. On server startup (in `server/routes/api/layouts.js` route registration), a merge-in check runs: for
|
|
94
|
+
each of the three built-in keys (`default`, `landing`, `blank`), if the key is absent from the loaded config, it is
|
|
95
|
+
written in. This handles upgrades on existing installs without overwriting user edits. Do not duplicate this logic
|
|
96
|
+
elsewhere. `config/presets.json` must not be gitignored.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Server - Renderer (`server/services/renderer.js`)
|
|
101
|
+
|
|
102
|
+
When assembling a public page, look up the page's `layout` frontmatter value against the presets config:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
const presets = getConfig('presets') || {};
|
|
106
|
+
const preset = presets[page.layout] || presets['default'] || {};
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Apply the preset to the rendered HTML:
|
|
110
|
+
|
|
111
|
+
1. **Width class** - add `dm-layout-{width}` to `<body>` classlist (e.g. `dm-layout-normal`). If `width` is missing, use
|
|
112
|
+
`normal`.
|
|
113
|
+
2. **Custom class** - append preset `class` string to `<body>` classlist.
|
|
114
|
+
3. **Background colour** - if `bgColor` is non-empty, add `background-color:{bgColor}` to the inline style of the
|
|
115
|
+
`.page-body` wrapper element.
|
|
116
|
+
4. **Background image** - if `bgImage` is non-empty, append
|
|
117
|
+
`background-image:url('{bgImage}');background-size:cover;background-position:center;` to the `.page-body` inline
|
|
118
|
+
style. Both `bgColor` and `bgImage` may be set simultaneously - the image renders over the colour as per normal CSS
|
|
119
|
+
stacking.
|
|
120
|
+
5. **Navbar / footer** - existing mechanism (already controlled by preset); no change needed.
|
|
121
|
+
|
|
122
|
+
The `data-layout="{{layout}}"` attribute on `<body>` is preserved as-is for CSS targeting.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Public CSS (`public/css/site.css`)
|
|
127
|
+
|
|
128
|
+
Four width utility classes controlling `.page-body` max-width:
|
|
129
|
+
|
|
130
|
+
```css
|
|
131
|
+
body.dm-layout-narrow .page-body {
|
|
132
|
+
max-width: 768px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
body.dm-layout-normal .page-body {
|
|
136
|
+
max-width: 1100px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
body.dm-layout-wide .page-body {
|
|
140
|
+
max-width: 1280px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
body.dm-layout-full .page-body {
|
|
144
|
+
max-width: none;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
All four are centred: `.page-body` already has `margin: 0 auto` (or this is added if missing).
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Admin UI (`admin/js/views/layouts.js`)
|
|
153
|
+
|
|
154
|
+
### Preset Cards Grid
|
|
155
|
+
|
|
156
|
+
Each card shows:
|
|
157
|
+
|
|
158
|
+
- **Label** (bold) + builtin badge (`built-in`) if `builtin: true`
|
|
159
|
+
- **Width** as a small pill (e.g. `normal`)
|
|
160
|
+
- **Toggle indicators**: three small labelled dots or icons - Navbar / Footer / Sidebar - filled if enabled, muted if
|
|
161
|
+
not
|
|
162
|
+
- **Background colour swatch** (small square) if `bgColor` is set
|
|
163
|
+
- Click anywhere on the card → opens the edit slideover
|
|
164
|
+
|
|
165
|
+
**Create button** - top-right of the grid section. Opens a blank edit slideover (all defaults).
|
|
166
|
+
|
|
167
|
+
### Edit Slideover
|
|
168
|
+
|
|
169
|
+
Opened via `E.slideover({ title: 'Edit Layout' / 'New Layout', size: 'md' })` using the light DOM slot pattern.
|
|
170
|
+
|
|
171
|
+
**Form fields (top to bottom):**
|
|
172
|
+
|
|
173
|
+
1. **Label** - text input (required)
|
|
174
|
+
2. **Description** - text input (optional)
|
|
175
|
+
3. **Width** - `<select>`: Narrow (768px) / Normal (1100px) / Wide (1280px) / Full
|
|
176
|
+
4. **Navbar** - checkbox
|
|
177
|
+
5. **Footer** - checkbox
|
|
178
|
+
6. **Sidebar** - checkbox
|
|
179
|
+
7. **Background Colour** - `<input type="color">` paired with a "Clear" button. When the current `bgColor` value is
|
|
180
|
+
`""`, the colour picker is disabled and visually greyed out; the label reads "None". The user must click "Choose" (or
|
|
181
|
+
interact with the picker) to enable it, which sets a default of `#ffffff`. Clicking "Clear" disables the picker again
|
|
182
|
+
and writes `""` back.
|
|
183
|
+
8. **Background Image** - text input (URL). Empty string = no image.
|
|
184
|
+
9. **Custom CSS Class** - text input. Empty string = no extra class.
|
|
185
|
+
|
|
186
|
+
**Action buttons:**
|
|
187
|
+
|
|
188
|
+
- **Save** - calls `POST /api/layouts` (create) or `PUT /api/layouts/:key` (update). On success: closes slideover,
|
|
189
|
+
refreshes card grid, shows success toast. On error: shows error toast with the server's message; slideover remains
|
|
190
|
+
open.
|
|
191
|
+
- **Delete** (custom presets only, not shown for `builtin: true`) - `await E.confirm('Delete this layout?')` →
|
|
192
|
+
`DELETE /api/layouts/:key` → on success: closes slideover, removes card, shows success toast. On error: shows error
|
|
193
|
+
toast; slideover remains open.
|
|
194
|
+
|
|
195
|
+
### Layout Options Tab
|
|
196
|
+
|
|
197
|
+
The existing spacer config tab is unchanged.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Page Editor Integration
|
|
202
|
+
|
|
203
|
+
No changes required. The page editor already populates the layout dropdown from `api.layouts.get()` and saves `layout`
|
|
204
|
+
to frontmatter. The new fields are transparent to the editor - they are applied server-side at render time.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Files to Create / Modify
|
|
209
|
+
|
|
210
|
+
| File | Change |
|
|
211
|
+
|-----------------------------------|---------------------------------------------------------------------------|
|
|
212
|
+
| `config/presets.json` | Update committed file to include new fields on all three built-in presets |
|
|
213
|
+
| `server/routes/api/layouts.js` | Add `POST`, `PUT /:key`, `DELETE /:key` routes; add field validation |
|
|
214
|
+
| `server/services/renderer.js` | Look up preset, apply width class, custom class, bg styles to page output |
|
|
215
|
+
| `admin/js/views/layouts.js` | Full rebuild: CRUD cards + slideover edit form |
|
|
216
|
+
| `admin/js/templates/layouts.html` | Update template if needed for new card structure |
|
|
217
|
+
| `public/css/site.css` | Add four `dm-layout-*` width utility classes |
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Out of Scope
|
|
222
|
+
|
|
223
|
+
- Drag-to-reorder presets
|
|
224
|
+
- Per-column or per-section width overrides
|
|
225
|
+
- Background image upload (URL input only; media browser integration is a future enhancement)
|
|
226
|
+
- Sidebar region implementation (toggle stored in preset but sidebar HTML is not in scope for this spec)
|