domma-cms 0.75.0 → 0.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/CLAUDE.md +1 -1
  2. package/admin/js/api.js +1 -1
  3. package/admin/js/app.js +4 -4
  4. package/admin/js/lib/action-editor-arrange.js +1 -0
  5. package/admin/js/lib/api-endpoint-editor-arrange.js +2 -0
  6. package/admin/js/lib/api-tokens-arrange.js +3 -0
  7. package/admin/js/lib/block-editor-arrange.js +2 -0
  8. package/admin/js/lib/collection-editor-arrange.js +3 -0
  9. package/admin/js/lib/collection-entries-arrange.js +1 -0
  10. package/admin/js/lib/component-editor-arrange.js +27 -0
  11. package/admin/js/lib/context-menu-editor-arrange.js +1 -0
  12. package/admin/js/lib/dashboard-arrange.js +1 -0
  13. package/admin/js/lib/effects-arrange.js +3 -0
  14. package/admin/js/lib/form-editor-arrange.js +1 -0
  15. package/admin/js/lib/form-submissions-arrange.js +2 -0
  16. package/admin/js/lib/layouts-arrange.js +1 -0
  17. package/admin/js/lib/menu-editor-arrange.js +2 -0
  18. package/admin/js/lib/my-profile-arrange.js +1 -0
  19. package/admin/js/lib/notifications-arrange.js +1 -0
  20. package/admin/js/lib/page-editor-arrange.js +1 -0
  21. package/admin/js/lib/plugin-code-arrange.js +1 -0
  22. package/admin/js/lib/plugins-arrange.js +2 -0
  23. package/admin/js/lib/project-page-arrange.js +1 -0
  24. package/admin/js/lib/role-editor-arrange.js +3 -0
  25. package/admin/js/lib/roles-arrange.js +1 -0
  26. package/admin/js/lib/search-settings-arrange.js +1 -0
  27. package/admin/js/lib/sidebar-renderer.js +2 -2
  28. package/admin/js/lib/site-settings-arrange.js +1 -0
  29. package/admin/js/lib/user-editor-arrange.js +1 -0
  30. package/admin/js/lib/users-arrange.js +1 -0
  31. package/admin/js/lib/view-editor-arrange.js +14 -0
  32. package/admin/js/templates/action-editor.html +223 -147
  33. package/admin/js/templates/api-endpoint-editor.html +172 -117
  34. package/admin/js/templates/api-tokens.html +83 -10
  35. package/admin/js/templates/block-editor.html +194 -203
  36. package/admin/js/templates/collection-editor.html +191 -113
  37. package/admin/js/templates/collection-entries.html +99 -36
  38. package/admin/js/templates/component-editor.html +128 -126
  39. package/admin/js/templates/context-menu-editor.html +208 -148
  40. package/admin/js/templates/dashboard.html +184 -22
  41. package/admin/js/templates/effects.html +155 -72
  42. package/admin/js/templates/form-editor.html +41 -26
  43. package/admin/js/templates/form-submissions.html +78 -26
  44. package/admin/js/templates/layouts.html +126 -43
  45. package/admin/js/templates/menu-editor.html +57 -17
  46. package/admin/js/templates/menu-locations.html +54 -11
  47. package/admin/js/templates/my-profile.html +100 -12
  48. package/admin/js/templates/notifications.html +70 -10
  49. package/admin/js/templates/page-editor.html +47 -26
  50. package/admin/js/templates/plugin-code.html +91 -73
  51. package/admin/js/templates/plugins.html +100 -28
  52. package/admin/js/templates/project-detail.html +83 -145
  53. package/admin/js/templates/project-settings.html +88 -53
  54. package/admin/js/templates/role-editor.html +158 -83
  55. package/admin/js/templates/roles.html +78 -7
  56. package/admin/js/templates/search.html +193 -216
  57. package/admin/js/templates/settings.html +404 -472
  58. package/admin/js/templates/user-editor.html +150 -14
  59. package/admin/js/templates/users.html +69 -9
  60. package/admin/js/templates/view-editor.html +203 -180
  61. package/admin/js/templates/view-preview.html +45 -45
  62. package/admin/js/views/action-editor.js +7 -1
  63. package/admin/js/views/api-endpoint-editor.js +2 -3
  64. package/admin/js/views/api-tokens.js +62 -8
  65. package/admin/js/views/block-editor.js +14 -8
  66. package/admin/js/views/collection-editor.js +64 -5
  67. package/admin/js/views/collection-entries.js +50 -1
  68. package/admin/js/views/component-editor.js +2 -28
  69. package/admin/js/views/context-menu-editor.js +54 -55
  70. package/admin/js/views/dashboard.js +7 -1
  71. package/admin/js/views/effects.js +31 -1
  72. package/admin/js/views/form-editor.js +7 -7
  73. package/admin/js/views/form-submissions.js +11 -1
  74. package/admin/js/views/index.js +1 -1
  75. package/admin/js/views/layouts.js +81 -1
  76. package/admin/js/views/login.js +2 -2
  77. package/admin/js/views/menu-editor.js +21 -43
  78. package/admin/js/views/menu-locations.js +16 -1
  79. package/admin/js/views/my-profile.js +1 -1
  80. package/admin/js/views/notifications.js +18 -1
  81. package/admin/js/views/page-editor.js +26 -26
  82. package/admin/js/views/plugin-code.js +23 -4
  83. package/admin/js/views/plugins.js +97 -39
  84. package/admin/js/views/project-detail.js +9 -1
  85. package/admin/js/views/project-settings.js +1 -1
  86. package/admin/js/views/role-editor.js +11 -3
  87. package/admin/js/views/roles.js +49 -4
  88. package/admin/js/views/search.js +7 -1
  89. package/admin/js/views/settings.js +13 -3
  90. package/admin/js/views/user-editor.js +1 -1
  91. package/admin/js/views/users.js +13 -4
  92. package/admin/js/views/view-editor.js +1 -1
  93. package/admin/js/views/view-preview.js +1 -1
  94. package/package.json +1 -1
  95. package/plugins/_lib/admin/mail/compose-window.js +97 -40
  96. package/plugins/_lib/admin/mail/contacts.js +4 -1
  97. package/plugins/_lib/admin/mail/diagnostics-section.js +4 -1
  98. package/plugins/_lib/admin/mail/folder-tree.js +1 -1
  99. package/plugins/_lib/admin/mail/image-senders-section.js +69 -90
  100. package/plugins/_lib/admin/mail/mail.css +1 -1
  101. package/plugins/_lib/admin/mail/mail.html +1 -1
  102. package/plugins/_lib/admin/mail/reader-view.js +303 -44
  103. package/plugins/_lib/admin/mail/when-closed.js +26 -0
  104. package/plugins/_lib/mail/readRoutes.js +43 -1
  105. package/plugins/_lib/mail/unreadBadge.js +42 -0
  106. package/plugins/free-tier.lock.json +5 -5
  107. package/plugins/mail-reader/CLAUDE.md +1 -0
  108. package/plugins/mail-reader/plugin.js +3 -1
  109. package/plugins/mail-reader/plugin.json +5 -4
  110. package/server/routes/api/api-tokens.js +56 -0
  111. package/server/routes/api/auth.js +7 -1
  112. package/server/routes/api/collections.js +13 -1
  113. package/server/routes/api/dashboard.js +32 -4
  114. package/server/routes/api/effects.js +68 -13
  115. package/server/routes/api/forms.js +104 -13
  116. package/server/routes/api/layouts.js +93 -2
  117. package/server/routes/api/notifications.js +140 -14
  118. package/server/routes/api/plugins.js +135 -4
  119. package/server/routes/api/roles.js +111 -0
  120. package/server/routes/api/search.js +38 -4
  121. package/server/routes/api/settings.js +52 -0
  122. package/server/routes/api/users.js +65 -3
  123. package/server/routes/api/views.js +55 -1
  124. package/server/server.js +12 -0
  125. package/server/services/components.js +9 -0
  126. package/server/services/plugins.js +7 -0
  127. package/server/services/roles.js +3 -0
  128. package/server/services/search.js +58 -0
  129. package/server/services/users.js +16 -2
  130. package/server/services/views.js +14 -0
  131. package/admin/js/templates/dashboard/activity-feed.html +0 -3
  132. package/admin/js/templates/dashboard/cache.html +0 -32
  133. package/admin/js/templates/dashboard/health-detail.html +0 -2
  134. package/admin/js/templates/dashboard/journeys.html +0 -17
  135. package/admin/js/templates/dashboard/kpi-strip.html +0 -34
  136. package/admin/js/templates/dashboard/spike-feed.html +0 -3
  137. package/admin/js/templates/dashboard/top-pages.html +0 -3
  138. package/admin/js/templates/dashboard/traffic-chart.html +0 -3
  139. package/admin/js/templates/plugin-marketplace.html +0 -29
  140. package/admin/js/views/block-editor-enhance.js +0 -1
  141. package/admin/js/views/dashboard/lib/escape.js +0 -1
  142. package/admin/js/views/dashboard/widgets/activity-feed.js +0 -1
  143. package/admin/js/views/dashboard/widgets/cache.js +0 -1
  144. package/admin/js/views/dashboard/widgets/health-detail.js +0 -1
  145. package/admin/js/views/dashboard/widgets/journeys.js +0 -1
  146. package/admin/js/views/dashboard/widgets/kpi-strip.js +0 -1
  147. package/admin/js/views/dashboard/widgets/spike-feed.js +0 -6
  148. package/admin/js/views/dashboard/widgets/top-pages.js +0 -1
  149. package/admin/js/views/dashboard/widgets/traffic-chart.js +0 -1
  150. package/admin/js/views/plugin-marketplace.js +0 -1
@@ -1,228 +1,219 @@
1
- <div class="view-header">
2
- <h1><span data-icon="box"></span> <span id="block-editor-title">New Block</span></h1>
3
- <div style="display:flex;gap:.5rem;align-items:center;">
4
- <select id="block-quick-switch" class="form-input form-input--sm"
5
- title="Switch to another block"
6
- style="display:none;max-width:260px;">
7
- <option value="">Switch to…</option>
1
+ <!-- Block editor. Bound with M.applyBindings - see admin/js/views/block-editor.js, which also inserts the
2
+ sample-data row template (the router renders a templateUrl through _.render, eating any {{…}} - so the
3
+ placeholder text below is written as &#123;&#123;…&#125;&#125;). No page heading: the Tool banner above
4
+ is the heading ("Blocks › <name>"), and All blocks, Copy shortcode and Save are banner actions.
5
+ #block-editor-body stays: admin.css lifts the view-width cap on it. -->
6
+ <div class="bex-page">
7
+ <div class="bex">
8
+ <div class="bex-bar">
9
+ <div class="bex-ident">
10
+ <strong data-bind-text="titleText"></strong>
11
+ <small data-bind-text="metaText.value"></small>
12
+ <span class="bex-chip is-warn" data-bind-hidden="!dirty.value" title="Changes not saved yet">Unsaved</span>
13
+ </div>
14
+ <span class="bex-spacer"></span>
15
+ <select id="block-quick-switch" class="form-input form-input--sm bex-switch" aria-label="Open another block" title="Open another block" data-bind-hidden="isNew">
16
+ <option value="">Open another block…</option>
8
17
  </select>
9
- <a href="#/blocks" class="btn btn-ghost btn-sm">
10
- <span data-icon="arrow-left"></span> All Blocks
11
- </a>
12
- <button id="save-block-btn" class="btn btn-primary">
13
- <span data-icon="save"></span> Save Block
14
- </button>
15
18
  </div>
16
- </div>
17
19
 
18
- <!-- Block Name -->
19
- <div class="card card-collapsible mb-3">
20
- <div class="card-header">
21
- <h2>Block Name</h2>
22
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
20
+ <div class="bex-unsaved" data-bind-hidden="!dirty.value">
21
+ <span data-icon="alert-circle" data-icon-size="15"></span>
22
+ <span>You have unsaved changes.</span>
23
+ <span class="bex-spacer"></span>
24
+ <button type="button" class="btn btn-sm btn-ghost" data-on-click="discard">Discard</button>
25
+ <button type="button" class="btn btn-sm btn-primary" data-on-click="save" data-bind-disabled="cannotSave.value">
26
+ <span data-icon="save" data-icon-size="13"></span> Save</button>
23
27
  </div>
24
- <div class="card-body" style="max-width:420px;">
25
- <label class="form-label">Name <span style="color:var(--dm-danger,#f87171);">*</span></label>
26
- <input id="block-name" type="text" class="form-input" placeholder="e.g. feedback-card">
27
- <small class="text-muted">Lowercase letters, digits, and hyphens only. Cannot be changed after creation.</small>
28
- <div class="mt-3">
29
- <label class="form-label">Project</label>
30
- <select id="block-project" class="form-input">
31
- <option value="">- none -</option>
32
- </select>
33
- <small class="text-muted">Tag this block to a project for grouping.</small>
34
- </div>
35
- <div class="mt-3">
36
- <label class="form-check-label" title="Included in fresh installs via the seed script">
37
- <input id="block-bundled" type="checkbox" class="form-check"> Bundled
38
- </label>
39
- </div>
40
- </div>
41
- </div>
42
28
 
43
- <!-- HTML Editor + Preview -->
44
- <div class="card card-collapsible editor-card mb-3">
45
- <div class="card-header">
46
- <h2>Template HTML</h2>
47
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
48
- </div>
49
- <div class="card-body p-0">
50
-
51
- <!-- Toolbar -->
52
- <div id="block-editor-toolbar" class="editor-toolbar">
53
- <button type="button" class="btn btn-ghost btn-sm" data-action="undo" title="Undo (Ctrl+Z)">
54
- <span data-icon="corner-up-left"></span>
55
- </button>
56
- <button type="button" class="btn btn-ghost btn-sm" data-action="redo" title="Redo (Ctrl+Y)">
57
- <span data-icon="corner-up-right"></span>
58
- </button>
59
- <span class="editor-toolbar-sep"></span>
60
- <button type="button" class="btn btn-ghost btn-sm" data-action="cut" title="Cut (Ctrl+X)">
61
- <span data-icon="scissors"></span>
62
- </button>
63
- <button type="button" class="btn btn-ghost btn-sm" data-action="copy" title="Copy (Ctrl+C)">
64
- <span data-icon="copy"></span>
65
- </button>
66
- <button type="button" class="btn btn-ghost btn-sm" data-action="paste" title="Paste (Ctrl+V)">
67
- <span data-icon="clipboard"></span>
68
- </button>
69
- <span class="editor-toolbar-sep"></span>
70
- <button type="button" class="btn btn-ghost btn-sm" data-action="select-all" title="Select All (Ctrl+A)">
71
- <span data-icon="maximise-2"></span>
72
- </button>
73
- <button type="button" class="btn btn-ghost btn-sm" data-action="format" title="Format HTML">
74
- <span data-icon="code"></span> Format
75
- </button>
76
-
77
- <!-- View mode toggles (right side) -->
78
- <div class="editor-toolbar-right">
79
- <span id="block-cursor-pos"
80
- style="font-size:.75rem;color:var(--dm-text-muted,#888);margin-right:.5rem;">Ln 1, Col 1</span>
81
- <span class="editor-toolbar-sep"></span>
82
- <button type="button" class="editor-view-btn" data-mode="write" title="Edit only">
83
- <span data-icon="edit-2"></span>
84
- </button>
85
- <button type="button" class="editor-view-btn active" data-mode="split" title="Split view">
86
- <span data-icon="columns"></span>
87
- </button>
88
- <button type="button" class="editor-view-btn" data-mode="preview" title="Preview only">
89
- <span data-icon="eye"></span>
90
- </button>
29
+ <!-- Details -->
30
+ <div class="bex-card">
31
+ <div class="bex-card-title">Details</div>
32
+ <div class="bex-grid">
33
+ <div>
34
+ <label class="form-label" for="block-name">Name <span class="text-danger">*</span>
35
+ <span data-help-title="Name" data-help="The block's id - what a shortcode names (block=&quot;…&quot;) and what its CSS is scoped to. Lowercase letters, digits and hyphens; it cannot be changed once the block exists, and a new block cannot take a name already in use."></span></label>
36
+ <input id="block-name" type="text" class="form-input font-mono" maxlength="80" placeholder="e.g. feedback-card"
37
+ data-model="name.value" data-bind-disabled="notNew" autocomplete="off">
38
+ <small class="bex-err" data-bind-hidden="!nameErr.value" data-bind-text="nameErr.value"></small>
39
+ </div>
40
+ <div>
41
+ <label class="form-label" for="block-project">Project
42
+ <span data-help="Files the block under a project - for the project's sidebar section and access scope. Optional."></span></label>
43
+ <select id="block-project" class="form-input" data-model="project.value">
44
+ <option value="">- none -</option>
45
+ </select>
46
+ </div>
47
+ <div class="bex-setting">
48
+ <label class="form-label" for="block-bundled">Bundled
49
+ <span data-help="Shipped with fresh installs by the seed script. Leave it off for a block that belongs to this site only."></span></label>
50
+ <input id="block-bundled" type="checkbox" class="form-switch" data-model="bundled.value">
91
51
  </div>
92
52
  </div>
53
+ </div>
93
54
 
94
- <!-- Editor body - split pane -->
95
- <div id="block-editor-body" class="editor-body editor-mode-split" style="min-height:460px;">
96
-
97
- <!-- Write pane -->
98
- <div class="editor-pane editor-pane--write" style="flex-direction:row;overflow:hidden;">
99
- <div id="block-line-numbers" class="editor-line-numbers">1</div>
100
- <textarea id="block-content" class="editor-textarea"
101
- spellcheck="false"
102
- placeholder="<div class=&quot;card mb-3&quot;>&#10; <div class=&quot;card-body&quot;>&#10; <h3>&#123;&#123;title&#125;&#125;</h3>&#10; <p>&#123;&#123;message&#125;&#125;</p>&#10; </div>&#10;</div>"></textarea>
55
+ <!-- Template HTML + preview -->
56
+ <div class="bex-card bex-editor-card">
57
+ <div class="bex-card-title">Template HTML
58
+ <span data-help-title="Template" data-help="The block's HTML. &#123;&#123;field&#125;&#125; inserts an entry's value (HTML-escaped); &#123;&#123;field:DD MMM YYYY&#125;&#125; formats it as a date. The preview fills each placeholder from the sample values beside it. HTML comments are stripped when the block is rendered."></span>
59
+ </div>
60
+ <div class="bex-toolbar" role="toolbar" aria-label="Template tools">
61
+ <button type="button" class="btn btn-ghost btn-sm" data-action="undo" title="Undo (Ctrl+Z)" aria-label="Undo"><span data-icon="corner-up-left" data-icon-size="14"></span></button>
62
+ <button type="button" class="btn btn-ghost btn-sm" data-action="redo" title="Redo (Ctrl+Y)" aria-label="Redo"><span data-icon="corner-up-right" data-icon-size="14"></span></button>
63
+ <span class="bex-sep"></span>
64
+ <button type="button" class="btn btn-ghost btn-sm" data-action="cut" title="Cut (Ctrl+X)" aria-label="Cut"><span data-icon="scissors" data-icon-size="14"></span></button>
65
+ <button type="button" class="btn btn-ghost btn-sm" data-action="copy" title="Copy (Ctrl+C)" aria-label="Copy"><span data-icon="copy" data-icon-size="14"></span></button>
66
+ <button type="button" class="btn btn-ghost btn-sm" data-action="paste" title="Paste (Ctrl+V)" aria-label="Paste"><span data-icon="clipboard" data-icon-size="14"></span></button>
67
+ <span class="bex-sep"></span>
68
+ <button type="button" class="btn btn-ghost btn-sm" data-action="select-all" title="Select all (Ctrl+A)" aria-label="Select all"><span data-icon="maximize-2" data-icon-size="14"></span></button>
69
+ <button type="button" class="btn btn-ghost btn-sm" data-action="format" title="Indent the HTML by nesting"><span data-icon="code" data-icon-size="14"></span> Format</button>
70
+ <span class="bex-spacer"></span>
71
+ <small class="bex-cursor" data-bind-text="cursor.value"></small>
72
+ <div class="bex-seg" role="group" aria-label="Show">
73
+ <button type="button" data-on-click="setWrite" data-bind-class="mode.value === 'write' && 'is-on'" title="Template only">Edit</button>
74
+ <button type="button" data-on-click="setSplit" data-bind-class="mode.value === 'split' && 'is-on'" title="Template and preview">Split</button>
75
+ <button type="button" data-on-click="setPreview" data-bind-class="mode.value === 'preview' && 'is-on'" title="Preview only">Preview</button>
103
76
  </div>
104
-
105
- <div class="editor-divider"></div>
106
-
107
- <!-- Preview pane -->
108
- <div class="editor-pane editor-pane--preview" style="flex-direction:column;overflow:hidden;">
109
- <!-- Sample data inputs - auto-populated by JS -->
110
- <div id="block-sample-data" style="
111
- border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));
112
- padding:.6rem .75rem;
113
- background:var(--dm-background-alt,rgba(0,0,0,.1));
114
- flex-shrink:0;">
115
- <p class="text-muted" style="font-size:.8rem;margin:0;">
116
- Write some template HTML to populate sample data fields.
117
- </p>
77
+ </div>
78
+ <div id="block-editor-body" data-bind-class="modeCls.value">
79
+ <div class="bex-pane bex-write"></div>
80
+ <div class="bex-pane bex-preview">
81
+ <div class="bex-samples-wrap">
82
+ <div class="bex-samples-title">Sample values
83
+ <span data-help-title="Sample values" data-help="One per placeholder the template uses, so the preview shows something real. They are only for the preview - nothing here is saved. Right-click one (or ⋮, or Shift+F10) to insert its placeholder, copy it or reset the sample."></span></div>
84
+ <p class="bex-note" data-bind-hidden="!noSamples.value" data-bind-text="noSamplesText"></p>
85
+ <div class="bex-samples" data-each="sampleRows.value key=key"></div>
118
86
  </div>
119
- <!-- Rendered output -->
120
- <div id="block-preview-output" style="flex:1;overflow-y:auto;padding:1rem;"></div>
87
+ <div id="block-preview-output" class="bex-output" aria-label="Preview"></div>
121
88
  </div>
122
-
123
89
  </div>
124
90
  </div>
125
- </div>
126
91
 
127
- <!-- Custom CSS -->
128
- <div class="card card-collapsible mb-3">
129
- <div class="card-header">
130
- <h2>Custom CSS</h2>
131
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
132
- </div>
133
- <div class="card-body">
134
- <p style="font-size:.85rem;color:var(--dm-text-muted,#888);margin:0 0 .5rem;line-height:1.5;">
135
- Your CSS is automatically scoped to
136
- <code class="dm-code-inline" id="block-css-scope-hint" style="font-size:.9em;">[data-block="…"]</code>.
137
- Use CSS nesting for child rules.
138
- <strong>Note:</strong>
139
- <code class="dm-code-inline" style="font-size:.9em;">@keyframes</code>,
140
- <code class="dm-code-inline" style="font-size:.9em;">@font-face</code>, and
141
- <code class="dm-code-inline" style="font-size:.9em;">@import</code>
142
- cannot be scoped - use site-wide Custom CSS in Settings for those.
143
- </p>
144
- <textarea id="block-css"
145
- class="editor-textarea"
146
- spellcheck="false"
147
- style="font-family:var(--dm-font-mono,monospace);min-height:240px;width:100%;"
148
- placeholder="/* Example:
149
- color: var(--dm-primary);
150
- h2 { font-size: 1.5rem; }
151
- .highlight { background: yellow; } */"></textarea>
92
+ <!-- Custom CSS -->
93
+ <div class="bex-card">
94
+ <div class="bex-card-title">Custom CSS
95
+ <span data-help-title="Custom CSS" data-help="Scoped to this block automatically - the rules below are wrapped in [data-block=&quot;name&quot;] { … }, so use nesting for child rules. @keyframes, @font-face and @import cannot be scoped: put those in Site Settings › Custom CSS. Up to 50 KB."></span>
96
+ </div>
97
+ <p class="bex-note">Scoped to <code class="dm-code-inline" id="block-css-scope-hint" data-bind-text="scopeHint.value"></code>.</p>
98
+ <div class="bex-css"></div>
99
+ <small class="bex-err" data-bind-hidden="!cssErr.value" data-bind-text="cssErr.value"></small>
152
100
  </div>
101
+
153
102
  </div>
154
103
 
155
- <!-- Placeholder Reference -->
156
- <div class="card card-collapsible mb-3 card-collapsed">
157
- <div class="card-header">
158
- <h2>Placeholder Reference</h2>
159
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
160
- </div>
161
- <div class="card-body" style="font-size:.85rem;line-height:1.8;">
162
- <p>Use <code class="dm-code-inline">&#123;&#123;fieldName&#125;&#125;</code> to insert entry field values. Values are HTML-escaped
163
- automatically to prevent XSS.</p>
164
- <table class="table" style="max-width:520px;margin:.75rem 0;">
165
- <thead>
166
- <tr>
167
- <th style="width:180px;">Placeholder</th>
168
- <th>Value</th>
169
- </tr>
170
- </thead>
171
- <tbody>
172
- <tr>
173
- <td><code class="dm-code-inline">&#123;&#123;fieldName&#125;&#125;</code></td>
174
- <td>Any entry field - e.g. <code class="dm-code-inline">&#123;&#123;title&#125;&#125;</code>, <code class="dm-code-inline">&#123;&#123;rating&#125;&#125;</code>,
175
- <code class="dm-code-inline">&#123;&#123;message&#125;&#125;</code></td>
176
- </tr>
177
- <tr>
178
- <td><code class="dm-code-inline">&#123;&#123;_id&#125;&#125;</code></td>
179
- <td>Entry UUID</td>
180
- </tr>
181
- <tr>
182
- <td><code class="dm-code-inline">&#123;&#123;_createdAt&#125;&#125;</code></td>
183
- <td>Creation timestamp (ISO 8601 by default)</td>
184
- </tr>
185
- <tr>
186
- <td><code class="dm-code-inline">&#123;&#123;_updatedAt&#125;&#125;</code></td>
187
- <td>Last-updated timestamp (ISO 8601 by default)</td>
188
- </tr>
189
- <tr>
190
- <td><code class="dm-code-inline">&#123;&#123;field:FORMAT&#125;&#125;</code></td>
191
- <td>Any placeholder with a <code class="dm-code-inline">:FORMAT</code> suffix
192
- applies moment-style date formatting when the value parses as a date.
193
- E.g. <code class="dm-code-inline">&#123;&#123;_createdAt:DD MMM YYYY&#125;&#125;</code> →
194
- <em>05 Jan 2026</em>, or
195
- <code class="dm-code-inline">&#123;&#123;_createdAt:HH:mm&#125;&#125;</code> →
196
- <em>14:30</em>.
197
- </td>
198
- </tr>
199
- </tbody>
200
- </table>
201
- <p style="margin-top:.5rem;"><strong>Format tokens:</strong>
202
- <code class="dm-code-inline">YYYY</code> <code class="dm-code-inline">YY</code>
203
- <code class="dm-code-inline">MMMM</code> <code class="dm-code-inline">MMM</code>
204
- <code class="dm-code-inline">MM</code> <code class="dm-code-inline">M</code>
205
- <code class="dm-code-inline">DD</code> <code class="dm-code-inline">D</code>
206
- <code class="dm-code-inline">dddd</code> <code class="dm-code-inline">ddd</code>
207
- <code class="dm-code-inline">HH</code> <code class="dm-code-inline">H</code>
208
- <code class="dm-code-inline">hh</code> <code class="dm-code-inline">h</code>
209
- <code class="dm-code-inline">mm</code> <code class="dm-code-inline">m</code>
210
- <code class="dm-code-inline">ss</code> <code class="dm-code-inline">s</code>
211
- <code class="dm-code-inline">A</code> <code class="dm-code-inline">a</code>
212
- - any literal character that isn't one of these tokens passes through unchanged
213
- (so <code class="dm-code-inline">DD/MM/YYYY</code>, <code class="dm-code-inline">HH:mm</code>,
214
- and <code class="dm-code-inline">dddd D MMMM YYYY</code> all work).
215
- </p>
216
- <p><strong>Use in a shortcode:</strong></p>
217
- <pre class="dm-code-block">[collection slug="feedback" display="block" block="feedback-card" /]
104
+ <!-- Outside the bound root (.bex): the reference is full of literal placeholders, which the binder would flag as un-interpolated. -->
105
+ <!-- Placeholder reference -->
106
+ <details class="bex-card bex-ref">
107
+ <summary class="bex-card-title">Placeholder reference</summary>
108
+ <div class="bex-ref-body">
109
+ <p>Use <code class="dm-code-inline">&#123;&#123;fieldName&#125;&#125;</code> to insert entry field values. Values are HTML-escaped
110
+ automatically to prevent XSS.</p>
111
+ <table class="table bex-ref-table">
112
+ <thead><tr><th>Placeholder</th><th>Value</th></tr></thead>
113
+ <tbody>
114
+ <tr><td><code class="dm-code-inline">&#123;&#123;fieldName&#125;&#125;</code></td>
115
+ <td>Any entry field - e.g. <code class="dm-code-inline">&#123;&#123;title&#125;&#125;</code>, <code class="dm-code-inline">&#123;&#123;rating&#125;&#125;</code>,
116
+ <code class="dm-code-inline">&#123;&#123;message&#125;&#125;</code></td></tr>
117
+ <tr><td><code class="dm-code-inline">&#123;&#123;_id&#125;&#125;</code></td><td>Entry UUID</td></tr>
118
+ <tr><td><code class="dm-code-inline">&#123;&#123;_createdAt&#125;&#125;</code></td><td>Creation timestamp (ISO 8601 by default)</td></tr>
119
+ <tr><td><code class="dm-code-inline">&#123;&#123;_updatedAt&#125;&#125;</code></td><td>Last-updated timestamp (ISO 8601 by default)</td></tr>
120
+ <tr><td><code class="dm-code-inline">&#123;&#123;field:FORMAT&#125;&#125;</code></td>
121
+ <td>Any placeholder with a <code class="dm-code-inline">:FORMAT</code> suffix applies moment-style date formatting when
122
+ the value parses as a date. E.g. <code class="dm-code-inline">&#123;&#123;_createdAt:DD MMM YYYY&#125;&#125;</code> →
123
+ <em>05 Jan 2026</em>, or <code class="dm-code-inline">&#123;&#123;_createdAt:HH:mm&#125;&#125;</code> → <em>14:30</em>.</td></tr>
124
+ </tbody>
125
+ </table>
126
+ <p><strong>Format tokens:</strong>
127
+ <code class="dm-code-inline">YYYY</code> <code class="dm-code-inline">YY</code>
128
+ <code class="dm-code-inline">MMMM</code> <code class="dm-code-inline">MMM</code>
129
+ <code class="dm-code-inline">MM</code> <code class="dm-code-inline">M</code>
130
+ <code class="dm-code-inline">DD</code> <code class="dm-code-inline">D</code>
131
+ <code class="dm-code-inline">dddd</code> <code class="dm-code-inline">ddd</code>
132
+ <code class="dm-code-inline">HH</code> <code class="dm-code-inline">H</code>
133
+ <code class="dm-code-inline">hh</code> <code class="dm-code-inline">h</code>
134
+ <code class="dm-code-inline">mm</code> <code class="dm-code-inline">m</code>
135
+ <code class="dm-code-inline">ss</code> <code class="dm-code-inline">s</code>
136
+ <code class="dm-code-inline">A</code> <code class="dm-code-inline">a</code>
137
+ - any other character passes through unchanged (so <code class="dm-code-inline">DD/MM/YYYY</code>,
138
+ <code class="dm-code-inline">HH:mm</code> and <code class="dm-code-inline">dddd D MMMM YYYY</code> all work).</p>
139
+ <p><strong>Use in a shortcode:</strong></p>
140
+ <pre class="dm-code-block">[collection slug="feedback" display="block" block="feedback-card" /]
218
141
  [view slug="my-view" display="block" block="feedback-card" /]</pre>
219
- <p style="margin-top:.75rem;"><strong>Example block template:</strong></p>
220
- <pre class="dm-code-block">&lt;div class="card mb-3"&gt;
142
+ <p><strong>Example block template:</strong></p>
143
+ <pre class="dm-code-block">&lt;div class="card mb-3"&gt;
221
144
  &lt;div class="card-body"&gt;
222
145
  &lt;h3&gt;&#123;&#123;name&#125;&#125;&lt;/h3&gt;
223
146
  &lt;p&gt;&#123;&#123;message&#125;&#125;&lt;/p&gt;
224
147
  &lt;small&gt;Rating: &#123;&#123;rating&#125;&#125; &amp;mdash; &#123;&#123;_createdAt&#125;&#125;&lt;/small&gt;
225
148
  &lt;/div&gt;
226
149
  &lt;/div&gt;</pre>
227
- </div>
150
+ </div>
151
+ </details>
228
152
  </div>
153
+ <style>
154
+ .bex-page { display: flex; flex-direction: column; gap: .75rem; }
155
+ .bex { display: flex; flex-direction: column; gap: .75rem; }
156
+ .bex [hidden] { display: none !important; }
157
+ .bex-bar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
158
+ .bex-ident { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; min-width: 0; }
159
+ .bex-ident strong { font-family: var(--dm-font-mono, monospace); }
160
+ .bex-ident small, .bex-note, .bex-cursor { color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
161
+ .bex-note { margin: 0; }
162
+ .bex-cursor { font-size: var(--dm-text-xs); font-variant-numeric: tabular-nums; }
163
+ .bex-spacer { flex: 1; }
164
+ .bex-switch { width: auto; max-width: 16rem; }
165
+ .bex-chip { display: inline-flex; align-items: center; font-size: var(--dm-text-xs); font-weight: 600; padding: .1rem .5rem; border-radius: 999px; white-space: nowrap;
166
+ background: color-mix(in srgb, var(--dm-text) 10%, transparent); color: var(--dm-text-muted); }
167
+ .bex-chip.is-warn { background: color-mix(in srgb, var(--dm-warning) 18%, transparent); color: var(--dm-warning); }
168
+ .bex-unsaved { display: flex; align-items: center; gap: .5rem; padding: .5rem .75rem; border-radius: var(--dm-radius-md);
169
+ background: color-mix(in srgb, var(--dm-warning) 12%, transparent); border: 1px solid color-mix(in srgb, var(--dm-warning) 35%, transparent);
170
+ position: sticky; top: 0; z-index: 5; }
171
+ .bex-unsaved .btn { display: inline-flex; align-items: center; gap: .3rem; }
172
+ .bex-card { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); padding: .75rem .9rem; display: flex; flex-direction: column; gap: .6rem;
173
+ background: var(--dm-surface); }
174
+ .bex-card-title { display: flex; align-items: center; gap: .3rem; font-weight: 600; }
175
+ .bex-grid { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 2fr) minmax(0, 1fr); gap: .85rem; align-items: start; }
176
+ .bex-grid .form-label { display: flex; align-items: center; gap: .3rem; }
177
+ .bex-setting { display: flex; flex-direction: column; gap: .35rem; }
178
+ .bex-err { display: block; color: var(--dm-danger); font-size: var(--dm-text-xs); margin-top: .25rem; }
179
+ .bex-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: .15rem; padding-bottom: .5rem; border-bottom: 1px solid var(--dm-border); }
180
+ .bex-toolbar .btn { display: inline-flex; align-items: center; gap: .3rem; }
181
+ .bex-sep { width: 1px; align-self: stretch; margin: .2rem .3rem; background: var(--dm-border); }
182
+ .bex-seg { display: inline-flex; border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); overflow: hidden; margin-left: .5rem; }
183
+ .bex-seg button { border: 0; background: none; color: var(--dm-text); padding: .25rem .65rem; cursor: pointer; font: inherit; font-size: var(--dm-text-sm); }
184
+ .bex-seg button + button { border-left: 1px solid var(--dm-border); }
185
+ .bex-seg button:hover { background: var(--dm-table-hover-bg, color-mix(in srgb, var(--dm-text) 6%, transparent)); }
186
+ .bex-seg button.is-on { background: var(--dm-primary); color: var(--dm-primary-text); }
187
+ .bex-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0; height: min(70vh, 820px); min-height: 460px;
188
+ border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); overflow: hidden; }
189
+ .bex-body.is-write { grid-template-columns: minmax(0, 1fr); }
190
+ .bex-body.is-write .bex-preview { display: none; }
191
+ .bex-body.is-preview { grid-template-columns: minmax(0, 1fr); }
192
+ .bex-body.is-preview .bex-write { display: none; }
193
+ .bex-pane { min-width: 0; min-height: 0; overflow: hidden; }
194
+ .bex-write { border-right: 1px solid var(--dm-border); }
195
+ .bex-body.is-write .bex-write { border-right: 0; }
196
+ .bex-preview { display: flex; flex-direction: column; }
197
+ .bex-samples-wrap { flex-shrink: 0; max-height: 40%; overflow-y: auto; padding: .6rem .75rem; border-bottom: 1px solid var(--dm-border);
198
+ background: var(--dm-surface-raised, color-mix(in srgb, var(--dm-text) 4%, transparent)); display: flex; flex-direction: column; gap: .4rem; }
199
+ .bex-samples-title { display: flex; align-items: center; gap: .3rem; font-size: var(--dm-text-sm); font-weight: 600; }
200
+ .bex-samples { display: flex; flex-direction: column; gap: .3rem; }
201
+ .bex-sample { display: flex; align-items: center; gap: .5rem; border-radius: var(--dm-radius-sm); }
202
+ .bex-sample:focus-visible { outline: 2px solid var(--dm-primary); outline-offset: 1px; }
203
+ .bex-sample-label { font-family: var(--dm-font-mono, monospace); font-size: var(--dm-text-xs); color: var(--dm-text-muted); flex: 0 0 9.5rem; text-align: right;
204
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
205
+ .bex-sample input { flex: 1; min-width: 0; }
206
+ .bex-kebab { border: 0; background: none; cursor: pointer; font-size: var(--dm-text-lg); color: var(--dm-text-muted); border-radius: var(--dm-radius-sm); padding: 0 .3rem; }
207
+ .bex-kebab:hover { color: var(--dm-primary); }
208
+ .bex-output { flex: 1; overflow-y: auto; padding: 1rem; background: var(--dm-background, var(--dm-surface)); }
209
+ .bex-css { height: 260px; border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); overflow: hidden; }
210
+ .bex-ref summary { cursor: pointer; list-style: revert; }
211
+ .bex-ref-body { font-size: var(--dm-text-sm); line-height: 1.7; }
212
+ .bex-ref-table { max-width: 40rem; margin: .5rem 0; }
213
+ @media (max-width: 900px) {
214
+ .bex-grid { grid-template-columns: 1fr; }
215
+ .bex-body, .bex-body.is-split { grid-template-columns: minmax(0, 1fr); height: auto; }
216
+ .bex-body .bex-write { height: 55vh; border-right: 0; border-bottom: 1px solid var(--dm-border); }
217
+ .bex-body .bex-preview { min-height: 40vh; }
218
+ }
219
+ </style>