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,148 +1,150 @@
1
- <div class="view-header">
2
- <h1><span data-icon="component"></span> <span id="component-editor-title">New Component</span></h1>
3
- <div style="display:flex;gap:.5rem;align-items:center;">
4
- <select id="component-quick-switch" class="form-input form-input--sm"
5
- title="Switch to another component"
6
- style="display:none;max-width:260px;">
7
- <option value="">Switch to…</option>
1
+ <!-- Component editor. Bound with M.applyBindings - see admin/js/views/component-editor.js. No page
2
+ heading or Back button: the Tool banner ("Components › <name>") carries the title, Save (Ctrl+S),
3
+ Export and All components. Keep double-brace placeholders out of this file: the router renders it through _.render. -->
4
+ <div class="cex">
5
+ <div class="cex-bar">
6
+ <span class="cex-status" data-bind-class="statusCls.value">
7
+ <span class="cex-dot" aria-hidden="true"></span><span data-bind-text="statusText.value">Compiling…</span>
8
+ </span>
9
+ <code class="cex-tag" data-bind-text="tagText.value"></code>
10
+ <span class="cex-chip is-warn" data-bind-hidden="!dirty.value">Unsaved</span>
11
+ <span class="cex-chip" data-bind-hidden="!readOnly.value">Read-only - from a plugin</span>
12
+ <span class="cex-spacer"></span>
13
+ <select id="component-quick-switch" class="form-input form-input--sm cex-switch" hidden
14
+ aria-label="Open another component" title="Open another component">
15
+ <option value="">Open another…</option>
8
16
  </select>
9
- <a href="#/components" class="btn btn-ghost btn-sm">
10
- <span data-icon="arrow-left"></span> All Components
11
- </a>
12
- <button id="export-component-btn" class="btn btn-ghost btn-sm" style="display:none;" title="Export as .dmcomponent.json">
13
- <span data-icon="download"></span> Export
14
- </button>
15
- <button id="save-component-btn" class="btn btn-primary">
16
- <span data-icon="save"></span> Save Component
17
- </button>
18
17
  </div>
19
- </div>
20
18
 
21
- <!-- Name + meta -->
22
- <div class="card card-collapsible mb-3">
23
- <div class="card-header">
24
- <h2>Component Name</h2>
25
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
26
- </div>
27
- <div class="card-body" style="max-width:520px;">
28
- <label class="form-label">Name <span style="color:var(--dm-danger,#f87171);">*</span></label>
29
- <input id="component-name" type="text" class="form-input" placeholder="e.g. pricing-table">
30
- <small class="text-muted">Registered as <code id="component-tag-hint">&lt;dm-…&gt;</code>. Lowercase letters, digits, and hyphens. Cannot be changed after creation.</small>
31
- <div class="mt-3">
32
- <label class="form-label">Project</label>
33
- <select id="component-project" class="form-input">
34
- <option value="">- none -</option>
35
- </select>
36
- <small class="text-muted">Tag this component to a project for grouping.</small>
37
- </div>
38
- <div class="mt-3">
39
- <label class="form-check-label" title="Included in fresh installs via the seed script">
40
- <input id="component-bundled" type="checkbox" class="form-check"> Bundled
41
- </label>
42
- </div>
43
- </div>
44
- </div>
19
+ <p class="cex-alert" data-bind-hidden="!readOnly.value">
20
+ <span data-icon="lock" data-icon-size="14"></span>
21
+ This component comes from a plugin, so it cannot be changed here. <button type="button" class="btn btn-sm btn-ghost"
22
+ data-on-click="copyToNew">Copy to a new component</button> to make your own version.
23
+ </p>
45
24
 
46
- <!-- Editor body - tabs on the left, preview on the right -->
47
- <div class="card card-collapsible editor-card mb-3">
48
- <div class="card-header">
49
- <h2>Source</h2>
50
- <span class="card-collapse-icon" data-icon="chevron-down"></span>
25
+ <div class="cex-unsaved" data-bind-hidden="!dirty.value">
26
+ <span>You have unsaved changes.</span>
27
+ <span class="cex-spacer"></span>
28
+ <button type="button" class="btn btn-sm btn-ghost" data-on-click="discard">Discard</button>
29
+ <button type="button" class="btn btn-sm btn-primary" data-on-click="save" data-bind-disabled="saveBlocked.value">
30
+ <span data-icon="save" data-icon-size="13"></span> Save</button>
51
31
  </div>
52
- <div class="card-body p-0">
53
32
 
54
- <!-- Status / compile bar -->
55
- <div id="component-status-bar" style="
56
- display:flex;align-items:center;justify-content:space-between;gap:.75rem;
57
- padding:.45rem .75rem;
58
- border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));
59
- background:var(--dm-background-alt,rgba(0,0,0,.05));
60
- font-size:.78rem;">
33
+ <div class="cex-card">
34
+ <div class="cex-card-title">Details</div>
35
+ <div class="cex-grid">
61
36
  <div>
62
- <span id="component-compile-indicator"
63
- style="display:inline-block;min-width:.7rem;height:.7rem;border-radius:50%;background:var(--dm-text-muted);margin-right:.4rem;"></span>
64
- <span id="component-compile-status">Compiling…</span>
37
+ <label class="form-label" for="component-name">Name <span class="cex-req">*</span>
38
+ <span data-help-title="Name" data-help="The component is used on a page as a tag made from its name - pricing-table becomes &lt;dm-pricing-table&gt;. Start with a letter; then lowercase letters, digits and hyphens. It cannot be changed once the component exists (every page that uses it names it)."></span></label>
39
+ <input id="component-name" type="text" class="form-input" placeholder="e.g. pricing-table" maxlength="64"
40
+ data-model="name.value" data-bind-disabled="nameLocked.value">
41
+ <small class="cex-err" data-bind-hidden="!nameErr.value" data-bind-text="nameErr.value"></small>
42
+ </div>
43
+ <div>
44
+ <label class="form-label" for="component-project">Project
45
+ <span data-help="File the component under a project - it then shows in that project's lists. Leave it on none for a site-wide one."></span></label>
46
+ <select id="component-project" class="form-input" data-model="project.value" data-bind-disabled="readOnly.value">
47
+ <option value="">- none -</option>
48
+ </select>
49
+ </div>
50
+ <div class="cex-setting">
51
+ <label class="form-label" for="component-bundled">Bundled
52
+ <span data-help="A bundled component is included in fresh installs by the seed script. Leave it off for one that belongs to this site only."></span></label>
53
+ <input id="component-bundled" type="checkbox" class="form-switch" data-model="bundled.value" data-bind-disabled="readOnly.value">
65
54
  </div>
66
- <span id="component-cursor-pos" style="display:none;"></span>
67
55
  </div>
68
- <pre id="component-compile-errors" hidden style="
69
- margin:0;
70
- padding:.75rem;
71
- background:color-mix(in srgb, var(--dm-danger) 8%, transparent);
72
- color:var(--dm-danger);
73
- border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));
74
- white-space:pre-wrap;
75
- font-size:.78rem;
76
- font-family:var(--dm-font-mono,monospace);"></pre>
77
-
78
- <div style="display:flex;min-height:460px;">
79
-
80
- <!-- Left: tabbed source editors -->
81
- <div style="flex:1.4;display:flex;flex-direction:column;border-right:1px solid var(--dm-border,rgba(255,255,255,.08));">
56
+ </div>
82
57
 
83
- <!-- Tab bar -->
84
- <div id="component-tab-bar" role="tablist"
85
- style="display:flex;gap:.15rem;padding:.25rem .5rem 0;border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));">
86
- <button type="button" class="component-tab active" data-tab="template" role="tab" aria-selected="true">&lt;template&gt;</button>
87
- <button type="button" class="component-tab" data-tab="props" role="tab">&lt;props&gt;</button>
88
- <button type="button" class="component-tab" data-tab="script" role="tab">&lt;script&gt;</button>
89
- <button type="button" class="component-tab" data-tab="style" role="tab">&lt;style&gt;</button>
58
+ <div class="cex-card cex-source">
59
+ <div class="cex-card-title">Source
60
+ <span data-help-title="The four blocks" data-help="template: the markup, with a prop's name in double curly braces where its value goes. props: a JSON object - one entry per prop, each with a type (string, number, boolean, array, object), an optional default and a label. script: an optional export default with data() and methods. style: CSS scoped to the component. It recompiles as you type; a component that does not compile cannot be saved."></span>
61
+ </div>
62
+ <pre class="cex-errors" data-bind-hidden="!hasErrors.value" data-bind-text="compileErrors.value"></pre>
63
+ <div class="cex-split">
64
+ <div class="cex-editors">
65
+ <div class="cex-tabs" role="tablist" aria-label="Source blocks">
66
+ <button type="button" class="cex-tab" role="tab" data-on-click="showTab('template')" data-bind-class="activeTab.value === 'template' && 'is-on'">&lt;template&gt;</button>
67
+ <button type="button" class="cex-tab" role="tab" data-on-click="showTab('props')" data-bind-class="activeTab.value === 'props' && 'is-on'">&lt;props&gt;</button>
68
+ <button type="button" class="cex-tab" role="tab" data-on-click="showTab('script')" data-bind-class="activeTab.value === 'script' && 'is-on'">&lt;script&gt;</button>
69
+ <button type="button" class="cex-tab" role="tab" data-on-click="showTab('style')" data-bind-class="activeTab.value === 'style' && 'is-on'">&lt;style&gt;</button>
90
70
  </div>
91
-
92
- <!-- One simple-editor mount per tab. CSS Grid stacks all four
93
- panes in the same 1×1 cell so switching tabs is just a
94
- `hidden` attribute flip. Vertical resize handle lets users
95
- drag the editor as tall as they want. -->
96
- <div id="component-editor-surface"
97
- style="height:min(72vh, 860px);min-height:360px;resize:vertical;overflow:hidden;display:grid;grid-template-columns:1fr;grid-template-rows:1fr;">
98
- <div id="component-src-template" class="component-tab-pane" data-pane="template"
99
- style="grid-column:1/2;grid-row:1/2;min-height:0;overflow:hidden;"></div>
100
- <div id="component-src-props" class="component-tab-pane" data-pane="props" hidden
101
- style="grid-column:1/2;grid-row:1/2;min-height:0;overflow:hidden;"></div>
102
- <div id="component-src-script" class="component-tab-pane" data-pane="script" hidden
103
- style="grid-column:1/2;grid-row:1/2;min-height:0;overflow:hidden;"></div>
104
- <div id="component-src-style" class="component-tab-pane" data-pane="style" hidden
105
- style="grid-column:1/2;grid-row:1/2;min-height:0;overflow:hidden;"></div>
71
+ <!-- Four simple-editors stacked in one grid cell; switching tabs flips `hidden`. -->
72
+ <div class="cex-surface">
73
+ <div id="component-src-template" class="cex-pane" data-bind-hidden="activeTab.value !== 'template'"></div>
74
+ <div id="component-src-props" class="cex-pane" data-bind-hidden="activeTab.value !== 'props'"></div>
75
+ <div id="component-src-script" class="cex-pane" data-bind-hidden="activeTab.value !== 'script'"></div>
76
+ <div id="component-src-style" class="cex-pane" data-bind-hidden="activeTab.value !== 'style'"></div>
106
77
  </div>
107
78
  </div>
108
-
109
- <!-- Right: iframe preview + props panel -->
110
- <div style="flex:1;display:flex;flex-direction:column;min-width:260px;">
111
-
112
- <div style="padding:.4rem .75rem;font-size:.72rem;letter-spacing:.05em;text-transform:uppercase;color:var(--dm-text-muted,#888);border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));">
113
- Live preview
114
- </div>
115
- <iframe id="component-preview-iframe"
116
- src="/admin/preview/component-preview.html"
117
- style="flex:1;min-height:200px;border:0;background:var(--dm-surface,#fff);"></iframe>
118
-
119
- <div style="padding:.4rem .75rem;font-size:.72rem;letter-spacing:.05em;text-transform:uppercase;color:var(--dm-text-muted,#888);border-top:1px solid var(--dm-border,rgba(255,255,255,.08));border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));">
120
- Preview props
121
- </div>
122
- <div id="component-preview-props-panel" style="padding:.75rem;overflow-y:auto;max-height:260px;">
123
- <p class="text-muted" style="font-size:.8rem;margin:0;">Declare props in the <code>&lt;props&gt;</code> tab to populate this panel.</p>
79
+ <div class="cex-preview">
80
+ <div class="cex-head">Live preview
81
+ <span data-help="The component as a page would show it, rebuilt after every change. Try props in the panel below without touching the source."></span></div>
82
+ <iframe id="component-preview-iframe" src="/admin/preview/component-preview.html" title="Live preview"></iframe>
83
+ <div class="cex-head">Preview props
84
+ <span data-help="One row per prop from the props block. Change a value to see it in the preview - it is not saved. Right-click a row (or ⋮, or Shift+F10) to reset it, copy it as an attribute or as a whole tag, or jump to it in the props block."></span></div>
85
+ <div id="component-preview-props-panel" class="cex-props">
86
+ <p class="cex-note">Declare props in the &lt;props&gt; tab to see them here.</p>
124
87
  </div>
125
88
  </div>
126
-
127
89
  </div>
128
90
  </div>
129
91
  </div>
130
92
 
131
93
  <style>
132
- .component-tab {
133
- background:transparent;border:none;padding:.4rem .75rem;
134
- font-size:.8rem;color:var(--dm-text-muted,#aaa);cursor:pointer;
135
- border-bottom:2px solid transparent;margin-bottom:-1px;
136
- font-family:var(--dm-font-mono,monospace);
94
+ .cex { display: flex; flex-direction: column; gap: .75rem; }
95
+ .cex [hidden] { display: none !important; }
96
+ .cex-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
97
+ .cex-spacer { flex: 1; }
98
+ .cex-status { display: inline-flex; align-items: center; gap: .4rem; font-size: var(--dm-text-sm); color: var(--dm-text-muted); }
99
+ .cex-dot { width: .6rem; height: .6rem; border-radius: 50%; background: var(--dm-text-muted); }
100
+ .cex-status.is-ok .cex-dot { background: var(--dm-success); }
101
+ .cex-status.is-error { color: var(--dm-danger); }
102
+ .cex-status.is-error .cex-dot { background: var(--dm-danger); }
103
+ .cex-tag { font-size: var(--dm-text-sm); }
104
+ .cex-chip { display: inline-flex; align-items: center; font-size: var(--dm-text-xs); font-weight: 600; padding: .1rem .5rem; border-radius: 999px;
105
+ background: color-mix(in srgb, var(--dm-text) 10%, transparent); color: var(--dm-text-muted); white-space: nowrap; }
106
+ .cex-chip.is-warn { background: color-mix(in srgb, var(--dm-warning) 18%, transparent); color: var(--dm-warning); }
107
+ .cex-switch { width: auto; max-width: 16rem; }
108
+ .cex-alert { margin: 0; display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; padding: .55rem .8rem; border-radius: var(--dm-radius-md);
109
+ background: color-mix(in srgb, var(--dm-info, var(--dm-primary)) 12%, transparent); color: var(--dm-text); font-size: var(--dm-text-sm); }
110
+ .cex-unsaved { display: flex; align-items: center; gap: .5rem; padding: .5rem .8rem; border-radius: var(--dm-radius-md);
111
+ background: color-mix(in srgb, var(--dm-warning) 14%, transparent); color: var(--dm-text); font-size: var(--dm-text-sm); }
112
+ .cex-unsaved .btn { display: inline-flex; align-items: center; gap: .3rem; }
113
+ .cex-card { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); padding: .75rem .9rem; display: flex; flex-direction: column; gap: .6rem; }
114
+ .cex-card-title { display: flex; align-items: center; gap: .3rem; font-weight: 600; }
115
+ .cex-grid { display: grid; grid-template-columns: minmax(12rem, 1fr) minmax(10rem, 1fr) auto; gap: .85rem; align-items: start; }
116
+ .cex-grid .form-label { display: flex; align-items: center; gap: .3rem; }
117
+ .cex-req { color: var(--dm-danger); }
118
+ .cex-err { display: block; margin-top: .25rem; color: var(--dm-danger); font-size: var(--dm-text-xs); }
119
+ .cex-setting { display: flex; flex-direction: column; gap: .35rem; }
120
+ .cex-errors { margin: 0; padding: .6rem .75rem; border-radius: var(--dm-radius-sm); white-space: pre-wrap; font-size: var(--dm-text-xs);
121
+ font-family: var(--dm-font-mono, monospace); background: color-mix(in srgb, var(--dm-danger) 10%, transparent); color: var(--dm-danger); }
122
+ .cex-split { display: flex; min-height: 460px; border: 1px solid var(--dm-border); border-radius: var(--dm-radius-sm); overflow: hidden; }
123
+ .cex-editors { flex: 1.4; display: flex; flex-direction: column; min-width: 0; border-right: 1px solid var(--dm-border); }
124
+ .cex-tabs { display: flex; gap: .15rem; padding: .25rem .5rem 0; border-bottom: 1px solid var(--dm-border); }
125
+ .cex-tab { background: transparent; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; padding: .4rem .75rem; cursor: pointer;
126
+ font: inherit; font-size: var(--dm-text-sm); font-family: var(--dm-font-mono, monospace); color: var(--dm-text-muted); }
127
+ .cex-tab:hover { color: var(--dm-text); }
128
+ .cex-tab.is-on { color: var(--dm-text); border-bottom-color: var(--dm-primary); font-weight: 600; }
129
+ .cex-surface { height: min(72vh, 860px); min-height: 360px; resize: vertical; overflow: hidden; display: grid; grid-template-columns: 1fr; grid-template-rows: 1fr; }
130
+ .cex-pane { grid-column: 1 / 2; grid-row: 1 / 2; min-height: 0; overflow: hidden; }
131
+ .cex-preview { flex: 1; display: flex; flex-direction: column; min-width: 260px; }
132
+ .cex-head { display: flex; align-items: center; gap: .3rem; padding: .4rem .75rem; font-size: var(--dm-text-xs); letter-spacing: .05em; text-transform: uppercase;
133
+ color: var(--dm-text-muted); border-bottom: 1px solid var(--dm-border); }
134
+ .cex-head + iframe + .cex-head { border-top: 1px solid var(--dm-border); }
135
+ .cex-preview iframe { flex: 1; min-height: 200px; border: 0; background: var(--dm-surface); }
136
+ .cex-props { padding: .5rem .5rem .75rem; overflow-y: auto; max-height: 260px; }
137
+ .cex-note { margin: 0; padding: .25rem; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
138
+ .cex-prop { display: flex; align-items: center; gap: .5rem; padding: .3rem .25rem; border-radius: var(--dm-radius-sm); }
139
+ .cex-prop:hover, .cex-prop:focus-within, .cex-prop:focus-visible { background: var(--dm-table-hover-bg, color-mix(in srgb, var(--dm-text) 6%, transparent)); outline: none; }
140
+ .cex-prop-label { min-width: 7.5rem; font-size: var(--dm-text-xs); color: var(--dm-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
141
+ .cex-prop-input { flex: 1; font-family: inherit; }
142
+ textarea.cex-prop-input { font-family: var(--dm-font-mono, monospace); }
143
+ .cex-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; }
144
+ .cex-kebab:hover { color: var(--dm-primary); }
145
+ @media (max-width: 900px) {
146
+ .cex-split { flex-direction: column; }
147
+ .cex-editors { border-right: 0; border-bottom: 1px solid var(--dm-border); }
148
+ .cex-grid { grid-template-columns: 1fr; }
137
149
  }
138
- .component-tab.active {
139
- color:var(--dm-text,inherit);
140
- border-bottom-color:var(--dm-primary,#5aa8ff);
141
- font-weight:600;
142
- }
143
- .component-tab:hover:not(.active) { color:var(--dm-text,inherit); }
144
-
145
- /* Layout-critical rules are inline on the elements above so they survive any
146
- templater/sanitiser that may swallow <style> blocks. This one is cosmetic. */
147
- .component-tab-pane[hidden] { display:none; }
148
150
  </style>