domma-cms 0.48.0 → 0.49.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/CLAUDE.md +13 -10
- package/admin/css/admin.css +1 -1
- package/admin/js/lib/menu-tree.js +1 -1
- package/admin/js/lib/sidebar-renderer.js +3 -3
- package/admin/js/templates/menu-editor.html +26 -0
- package/admin/js/views/menu-editor.js +27 -12
- package/package.json +1 -1
- package/public/css/site.css +1 -1
- package/public/js/menu-decor.mjs +1 -1
- package/public/js/menu-float.js +1 -0
- package/public/js/site.js +1 -1
- package/server/services/markdown.js +4 -1
- package/server/services/menuRender.js +63 -3
- package/server/services/menus.js +77 -1
- package/server/templates/page.html +5 -0
package/CLAUDE.md
CHANGED
|
@@ -156,6 +156,8 @@ Menus live in `config/menus/<slug>.json`. The slot → menu map lives in `config
|
|
|
156
156
|
|
|
157
157
|
**Floating panel surface.** `float` also takes `radius`, `shadow` (`none|sm|md|lg|xl` → `--dm-shadow-*`), `accent` (preset key or hex → border tint + leading-edge stripe via `--dm-menu-accent`) and `opacity` (20-100 → `--dm-menu-panel-opacity`, blurred translucent surface). `floatToCss` exists twice - `server/services/menus.js` and `public/js/menu-decor.mjs` - and `tests/services/menus-float.test.js` asserts the copies agree.
|
|
158
158
|
|
|
159
|
+
**Floating panel behaviour.** `float` also takes `collapsible`, `collapsed`, `movable`, `resizable` and `title`. Any of the first two or `movable` makes `buildMenuNav` draw a chrome bar (grip + title + hamburger) via `buildFloatChrome`; `public/js/menu-float.js` (loaded from `page.html`, and called by `site.js` for the floating navbar) wires collapse/drag/resize. **Visitor state beats config**: position, size and collapsed state live in the visitor's own `S` storage under `dm_menu_float_<slug>` and are restored over `float` on load; double-clicking the bar clears them. A panel only stops tracking its `anchor` once actually moved or resized (`unanchor()` converts right/bottom/transform pinning to left/top on first interaction), so untouched panels stay responsive. `floatBehaviour` is the third function duplicated across `menus.js` / `menu-decor.mjs`, parity-tested alongside `floatToCss`. Below 768px site.css returns panels to the flow, so only collapse applies there.
|
|
160
|
+
|
|
159
161
|
## Admin sidebar (menu-data-driven)
|
|
160
162
|
|
|
161
163
|
The admin sidebar is now menu-data-driven - no more hardcoded `sidebar-config.js`. The menu lives at `config/menus/admin-sidebar.json` and is auto-seeded on first boot (idempotent, file-existence guarded) with the standard tree: Overview, Projects, Content, Data, System, Documentation. Renderer is at `admin/js/lib/sidebar-renderer.js`; it reads the menu, merges in plugin-registered items (via `hooks.registerSidebarItem({parent, item})`), filters by each item's optional `permission` field, then mounts. The **Menus** admin can edit the sidebar visually like any other menu - the item editor exposes the new `permission` field on each row (resource key from `permissionRegistry`). Plugin-injected items are render-time only and not shown in the Menus editor; admins hide them by adding a same-`text` item with `hidden: true` to the persisted menu.
|
|
@@ -195,21 +197,22 @@ holds `compareValues`/`sortEntries` - the same comparison ladder backs both the
|
|
|
195
197
|
|
|
196
198
|
## Key Gotchas
|
|
197
199
|
|
|
198
|
-
1. **
|
|
199
|
-
2. **
|
|
200
|
-
3.
|
|
200
|
+
1. **Structural menu items**: `{type:'separator'}` and `{type:'spacer', size}` carry no text/url - every renderer (menuRender, site.js navbar+footer, sidebar-renderer, menu-editor flatten/nest) needs a branch, and anything zipping items against rendered DOM must skip them or the indices shift. Domma's navbar has no spacer concept, so site.js strips them pre-render and re-inserts `<li>`s afterwards **against a snapshot** of the children, not the live HTMLCollection. `size: 'flex'` needs a flex parent, so the renderers stamp `dm-menu--has-flex-spacer` / `dm-admin-sidebar--has-flex-spacer` rather than making every list flex.
|
|
201
|
+
2. **Menu item sub-items**: use `items` key, NOT `children` - Domma navbar and the `[menu]` shortcode both read `items`.
|
|
202
|
+
3. **Domma collections**: use `.get(0)` NOT `[0]` to get native DOM elements.
|
|
203
|
+
4. **`.html()` strips interactive elements**: `E.modal().setContent(x)` also strips HTML - use light DOM slot
|
|
201
204
|
projection (`modal.element.appendChild(myDomElement)`) for buttons/inputs.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
5. **Landing layout**: `layout: landing` removes the standard page wrapper - add your own container CSS.
|
|
206
|
+
6. **Grid shortcode**: uses Domma Grid (`grid`, `grid-cols-N`) - NOT the `.col` compatibility class.
|
|
207
|
+
7. **Event delegation namespaces**: `$(doc).on('click.ns', '.sel', cb)` silently fails - use direct `addEventListener`
|
|
205
208
|
or unnamespaced `$(el).on('click', cb)`.
|
|
206
|
-
|
|
209
|
+
8. **Tabs component classes**: wrapper `.tabs`, list `.tab-list`, trigger `button.tab-item`, panel `.tab-panel` (not
|
|
207
210
|
`.tabs-nav`, `.tabs-content`, `.tabs-pane`).
|
|
208
|
-
|
|
211
|
+
9. **JWT_SECRET required**: `server/server.js` hard-fails on startup if `JWT_SECRET` is missing, insecure, or shorter
|
|
209
212
|
than 32 characters. Set it in `.env`.
|
|
210
|
-
|
|
213
|
+
10. **Page visibility enforcement**: `server/routes/public.js` checks `page.visibility` against the visitor's role
|
|
211
214
|
level - unauthenticated visitors cannot access private or role-restricted pages.
|
|
212
|
-
|
|
215
|
+
11. **404 page**: create `content/pages/404.md` to customise the not-found response; it is auto-served on missing routes.
|
|
213
216
|
|
|
214
217
|
## Docs
|
|
215
218
|
|
package/admin/css/admin.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.dashboard-layout{display:flex;flex-direction:column;height:100vh;min-height:100vh;overflow:hidden}.dashboard-wrapper{display:flex;flex:1;min-height:0;overflow:hidden}.dashboard-main{flex:1;min-width:0;overflow-y:auto}#admin-sidebar.dashboard-sidebar{display:flex;flex-direction:column;flex-shrink:0;overflow:hidden}#admin-sidebar .dm-admin-sidebar{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--dm-border) transparent}#admin-sidebar .dm-admin-sidebar::-webkit-scrollbar{width:8px}#admin-sidebar .dm-admin-sidebar::-webkit-scrollbar-track{background:transparent}#admin-sidebar .dm-admin-sidebar::-webkit-scrollbar-thumb{background:var(--dm-border);border-radius:4px}#admin-sidebar .dm-admin-sidebar::-webkit-scrollbar-thumb:hover{background:var(--dm-text-muted)}.view-container{padding:1.5rem 2rem;max-width:1200px}.view-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.5rem;gap:1rem}.view-header h1{font-size:1.5rem;font-weight:700;margin:0;display:flex;align-items:center;gap:.5rem}.view-header-actions{display:flex;gap:.75rem;flex-wrap:wrap}.view-header-actions .btn-primary,.view-header-actions .btn-success,.view-header-actions .btn-danger{margin-left:.5rem}.stat-card .card-body{display:flex;align-items:center;gap:1rem}.stat-icon{width:44px;height:44px;border-radius:8px;background:color-mix(in srgb,var(--dm-primary) 14%,transparent);color:var(--dm-primary, #5b8cff);display:flex;align-items:center;justify-content:center;font-size:1.25rem;flex-shrink:0}.stat-icon--success{background:color-mix(in srgb,var(--dm-success) 15%,transparent);color:var(--dm-success)}.stat-icon--warning{background:color-mix(in srgb,var(--dm-warning) 15%,transparent);color:var(--dm-warning)}.stat-value{font-size:1.75rem;font-weight:700;line-height:1}.stat-label{font-size:.8rem;color:var(--dm-text-muted, #888);margin-top:.25rem}.toolbar{display:flex;align-items:center;gap:.75rem}.form-check-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.9rem}.form-hint{display:block;font-size:.8rem;color:var(--dm-text-muted, #888);margin-top:.25rem}.dashboard-main:has(#editor-meta-tabs){overflow:hidden}.view-container:has(#editor-meta-tabs){display:flex;flex-direction:column;height:calc(100dvh - 60px);max-width:100%;padding-bottom:0}.view-container:has(#block-editor-body){max-width:100%}.view-container:has(#ep-editor-tabs){max-width:100%}.ep-editor-grid{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr) minmax(0,1.3fr);gap:1rem;align-items:start}.ep-editor-grid>.ep-tryit-col,.ep-editor-grid>.ep-results-col{position:sticky;top:1rem;display:flex;flex-direction:column;gap:1rem;min-width:0}@media(max-width:1500px){.ep-editor-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.ep-editor-grid>:first-child{grid-column:1 / -1}.ep-editor-grid>.ep-tryit-col,.ep-editor-grid>.ep-results-col{position:static}}@media(max-width:1024px){.ep-editor-grid{grid-template-columns:minmax(0,1fr)}}#editor-meta-tabs{flex:1;min-height:0;display:flex;flex-direction:column}#editor-meta-tabs .tab-content{flex:1;min-height:0;overflow:hidden}#editor-meta-tabs .tab-panel{height:100%;overflow-y:auto}#editor-meta-tabs .tab-panel:has(.editor-card),#live-preview-panel{overflow:hidden}.page-live-preview-frame{display:block;width:100%;height:calc(100vh - 180px);border:none;border-radius:6px}.editor-card{display:flex;flex-direction:column;height:100%}.editor-card .card-body{display:flex;flex-direction:column;flex:1;min-height:0;padding:0!important}.editor-toolbar{display:flex;align-items:center;gap:2px;padding:6px 10px;border-bottom:1px solid var(--dm-border, rgba(255, 255, 255, .08));flex-wrap:wrap;flex-shrink:0}.editor-toolbar-btn{background:none;border:none;color:var(--dm-text-muted, #888);padding:6px 8px;border-radius:4px;cursor:pointer;display:flex;align-items:center;transition:color .15s,background .15s}.editor-toolbar-btn:hover{color:var(--dm-text, #eee);background:var(--dm-hover-bg)}.editor-toolbar-sep{width:1px;height:20px;background:var(--dm-border, rgba(255, 255, 255, .08));margin:0 4px;flex-shrink:0}.editor-toolbar-right{margin-left:auto;display:flex;align-items:center;gap:2px}.editor-view-btn{background:none;border:none;color:var(--dm-text-muted, #888);padding:6px 8px;border-radius:4px;cursor:pointer;display:flex;align-items:center;transition:color .15s,background .15s}.editor-view-btn:hover{color:var(--dm-text, #eee);background:var(--dm-hover-bg)}.editor-view-btn.active{color:var(--dm-primary, #5b8cff);background:color-mix(in srgb,var(--dm-primary) 14%,transparent)}.editor-body{display:flex;flex:1;min-height:0}.editor-pane{flex:1;display:flex;flex-direction:column;min-width:0}.editor-pane--write,.editor-pane--preview{overflow:hidden}.editor-pane--write{flex-direction:row}.editor-line-numbers{padding:1rem .6rem 1rem .75rem;background:var(--dm-background-alt, rgba(0, 0, 0, .15));border-right:1px solid var(--dm-border, rgba(255, 255, 255, .08));color:var(--dm-text-muted, #666);font-family:Fira Code,Courier New,monospace;font-size:.9rem;line-height:1.6;text-align:right;user-select:none;white-space:pre;overflow:hidden;flex-shrink:0;min-width:2.5rem}.editor-line-numbers--foldable{padding:1rem 0;min-width:3rem;white-space:normal;text-align:left;display:flex;flex-direction:column}.editor-line-number-row{display:flex;align-items:center;justify-content:flex-end;padding-right:.6rem;gap:.15rem}.fold-toggle{display:inline-flex;align-items:center;justify-content:center;width:1em;font-size:.6rem;cursor:default;color:transparent}.fold-toggle--open,.fold-toggle--folded{cursor:pointer;color:var(--dm-text-muted, #666);transition:color .1s}.fold-toggle--open:hover,.fold-toggle--folded:hover{color:var(--dm-primary, #4a9eff)}.editor-line-num{min-width:1.5rem;text-align:right}.editor-mode-write .editor-pane--preview,.editor-mode-write .editor-divider,.editor-mode-preview .editor-pane--write,.editor-mode-preview .editor-divider{display:none}.editor-divider{width:1px;background:var(--dm-border, rgba(255,255,255,.08));flex-shrink:0}.editor-textarea{flex:1;resize:none;border:none;border-radius:0;background:transparent;font-family:Fira Code,Courier New,monospace;font-size:.9rem;padding:1rem;outline:none;color:inherit;line-height:1.6}.editor-preview{flex:1;padding:1rem;overflow-y:auto;line-height:1.7}.editor-preview h1,.editor-preview h2,.editor-preview h3{margin-top:1.5rem}.editor-preview p{margin-bottom:.75rem}.editor-preview code{background:var(--dm-code-bg);padding:.1em .3em;border-radius:3px;font-size:.9em}.editor-fullscreen{position:fixed;inset:0;z-index:1000;border-radius:0;margin:0}.editor-fullscreen .card-body{height:100vh}.media-picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:.75rem;max-height:400px;overflow-y:auto;padding:.25rem}.media-picker-item{cursor:pointer;border:2px solid transparent;border-radius:6px;overflow:hidden;transition:border-color .15s}.media-picker-item:hover{border-color:var(--dm-primary, #5b8cff)}.media-picker-item img{width:100%;height:80px;object-fit:cover;display:block}.media-picker-item span{display:block;font-size:.75rem;padding:4px 6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.docs-body h3{margin-top:1.25rem;margin-bottom:.5rem}.docs-body p{margin-bottom:.5rem;line-height:1.6}.docs-body ol,.docs-body ul{margin-bottom:.75rem;padding-left:1.25rem}.docs-body li{margin-bottom:.25rem}.docs-body hr{margin:1.25rem 0;border-color:var(--dm-border, rgba(255, 255, 255, .08))}.docs-body .table{margin-bottom:.75rem}.nav-items-header,.nav-item-row{display:flex;gap:.5rem;align-items:center;padding:.35rem .75rem}.nav-items-header{border-bottom:1px solid var(--dm-border, rgba(255,255,255,.08));padding-bottom:.4rem;margin-bottom:.25rem}.nav-item-row{border-bottom:1px solid var(--dm-border, rgba(255,255,255,.04))}.nav-item-row:last-child{border-bottom:none}.nav-col-indent{width:18px;flex-shrink:0;color:var(--dm-text-muted, #888);text-align:center;font-size:.85rem}.nav-col-main{flex:2;min-width:0}.nav-col-icon{flex:0 0 90px}.nav-col-parent{flex:2;min-width:0}.nav-col-action{flex-shrink:0}.nav-item-row--child{background:color-mix(in srgb,var(--dm-text) 3%,transparent)}.nav-col-action{display:flex;align-items:center;gap:.25rem}.nav-item-row--hidden{opacity:.45}.nav-item-row--hidden .item-text,.nav-item-row--hidden .footer-link-text{text-decoration:line-through}.btn-toggle-hidden{color:var(--dm-text-muted, #888)}.btn-toggle-hidden.active{color:var(--dm-warning, #f59e0b)}.fb-field-card.fb-drag-over{outline:2px dashed var(--dm-primary, #6366f1);outline-offset:-2px}.btn-ghost span[data-icon=trash]{color:var(--dm-danger, #dc2626)}.view-container:has(.fb-fields-layout){max-width:none}.fb-fields-layout{display:grid;grid-template-columns:minmax(0,6fr) minmax(320px,4fr);gap:1.25rem;align-items:start}.fb-preview-pane{position:sticky;top:1rem;min-width:0}@media(max-width:1100px){.fb-fields-layout{grid-template-columns:1fr}.fb-preview-pane{position:static}}.toggle-label{display:flex;align-items:center;gap:.4rem;font-size:.9rem;cursor:pointer;margin-right:1rem}.preset-toggles{display:flex;flex-wrap:wrap;margin-top:.75rem}.presets-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem}@media(max-width:900px){.presets-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:600px){.presets-grid{grid-template-columns:1fr}}.card-header{padding:.5rem 1rem}.card-header h2{margin:0;font-size:.9rem;font-weight:600;letter-spacing:.02em}.preset-card .card-header{display:flex;align-items:center;justify-content:space-between}.preset-card .card-header h3{margin:0;font-size:1rem}.media-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:1rem}.media-card{background:var(--dm-card-bg, rgba(255,255,255,.04));border:1px solid var(--dm-border, rgba(255,255,255,.08));border-radius:8px;overflow:hidden}.media-preview{height:130px;display:flex;align-items:center;justify-content:center;background:#0003;overflow:hidden}.media-thumb{width:100%;height:100%;object-fit:cover}.media-thumb--file{font-size:2rem;color:var(--dm-text-muted, #888)}.media-info{padding:.5rem .75rem;display:flex;flex-direction:column;gap:.15rem}.media-name{font-size:.8rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default}.media-rename-input{width:100%;font-size:.8rem;font-weight:500;padding:.1rem .25rem;border:1px solid var(--dm-primary, #7c6af7);border-radius:3px;background:var(--dm-input-bg, transparent);color:inherit;outline:none}.media-size{font-size:.75rem;color:var(--dm-text-muted, #888)}.media-actions{padding:.5rem .75rem;display:flex;gap:.4rem;border-top:1px solid var(--dm-border, rgba(255,255,255,.08))}#admin-topbar{height:60px;display:flex;align-items:center;gap:1rem;padding:0 1.25rem;background:var(--dm-navbar-bg, rgba(0,0,0,.3));border-bottom:1px solid var(--dm-border, rgba(255,255,255,.08));position:sticky;top:0;z-index:200;flex-shrink:0}.topbar-brand{display:flex;align-items:center;gap:.5rem;font-weight:700;font-size:.95rem;color:inherit;flex-shrink:0;margin-right:.5rem}.topbar-brand span[data-icon],.topbar-brand svg{color:var(--dm-primary, #5b8cff);width:22px;height:22px}.topbar-brand-text{opacity:.85}.topbar-brand-logo{height:28px;width:auto;display:inline-block;vertical-align:middle;margin-right:.4em;object-fit:contain}.topbar-user{display:flex;align-items:center;gap:.6rem;flex:1}.topbar-user-name{font-size:.875rem;font-weight:500}.topbar-role-badge{font-size:.7rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;padding:.15rem .45rem;border-radius:4px}.topbar-role-badge--admin{background:color-mix(in srgb,var(--dm-primary) 15%,transparent);color:var(--dm-primary)}.topbar-role-badge--manager{background:color-mix(in srgb,var(--dm-success) 15%,transparent);color:var(--dm-success)}.topbar-role-badge--editor{background:color-mix(in srgb,var(--dm-warning) 15%,transparent);color:var(--dm-warning)}.topbar-role-badge--subscriber{background:color-mix(in srgb,var(--dm-text-muted) 15%,transparent);color:var(--dm-text-muted)}.topbar-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.topbar-action-link{display:flex;align-items:center;gap:.35rem;font-size:.875rem;color:var(--dm-text-muted, #888);text-decoration:none;padding:.4rem .65rem;border-radius:6px;transition:color .15s,background .15s}.topbar-action-link:hover{color:var(--dm-text, #eee);background:var(--dm-hover-bg)}.topbar-action-link span[data-icon],.topbar-action-link svg{width:16px;height:16px}.topbar-signout{display:flex;align-items:center;gap:.35rem;font-size:.875rem}.topbar-signout span[data-icon],.topbar-signout svg{width:16px;height:16px}.login-wrap{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:1rem;background:var(--dm-background, #0f1117);z-index:100;overflow-y:auto}.login-card{width:100%;max-width:460px}.ob-done-icon{font-size:3rem;color:var(--dm-success);margin-bottom:1rem;text-align:center}.btn-skip{display:block;text-align:center;margin-top:.75rem;font-size:.85rem;color:var(--dm-text-muted, #888);text-decoration:none}.btn-skip:hover{text-decoration:underline}.theme-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.6rem}.theme-swatch{border:2px solid var(--dm-border, rgba(255,255,255,.1));border-radius:8px;overflow:hidden;cursor:pointer;transition:border-color .15s,transform .1s}.theme-swatch:hover{border-color:var(--dm-primary, #5b8cff);transform:translateY(-1px)}.theme-swatch.selected{border-color:var(--dm-primary, #5b8cff);box-shadow:0 0 0 2px var(--dm-primary, #5b8cff)}.theme-swatch-preview{height:52px;position:relative;display:flex;align-items:flex-end;padding:.35rem}.theme-swatch-accent{width:28px;height:6px;border-radius:3px}.theme-swatch-label{display:flex;justify-content:space-between;align-items:center;padding:.3rem .45rem;font-size:.7rem;background:var(--dm-card-bg, rgba(255,255,255,.04))}.theme-swatch-mode{color:var(--dm-text-muted, #888);font-size:.65rem}.login-logo{display:flex;align-items:center;gap:.75rem;margin-bottom:1.75rem;font-size:1.5rem}.login-logo span[data-icon]{font-size:2rem;color:var(--dm-primary, #5b8cff)}.login-logo h1{margin:0;font-size:1.5rem;font-weight:700}.login-heading{font-size:1.1rem;font-weight:600;margin-bottom:1.25rem}.btn-block{width:100%}.plugins-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:1rem}.plugin-card{display:flex;flex-direction:column}.plugin-card .card-body{flex:1}.plugin-header{display:flex;align-items:flex-start;gap:.75rem;margin-bottom:.75rem}.plugin-icon{width:40px;height:40px;border-radius:8px;background:color-mix(in srgb,var(--dm-primary) 14%,transparent);color:var(--dm-primary, #5b8cff);display:flex;align-items:center;justify-content:center;flex-shrink:0}.plugin-meta{flex:1;min-width:0}.plugin-name{font-weight:600;margin-bottom:.15rem}.plugin-version{font-size:.75rem;color:var(--dm-text-muted, #888)}.plugin-desc{font-size:.875rem;color:var(--dm-text-muted, #888);margin-bottom:1rem}.plugin-footer{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-top:1px solid var(--dm-border, rgba(255,255,255,.08))}.plugin-footer-actions{display:flex;align-items:center;gap:.5rem}#admin-sidebar .sidebar-link{position:relative!important}#admin-sidebar .sidebar-badge{position:absolute!important;right:.75rem!important;top:50%!important;transform:translateY(-50%)!important;margin-left:0!important;padding-left:.45rem!important;padding-right:.45rem!important}#admin-sidebar .sidebar-text{padding-right:2rem!important}#admin-sidebar.sidebar-dark{background:var(--dm-surface);border-color:var(--dm-border)}#admin-sidebar.sidebar-dark .sidebar-header{background:var(--dm-surface-raised);border-color:var(--dm-border)}#admin-sidebar.sidebar-dark .sidebar-header-title{color:var(--dm-text)}#admin-sidebar.sidebar-dark .sidebar-link{color:var(--dm-text-muted)}#admin-sidebar.sidebar-dark .sidebar-link:hover{color:var(--dm-text);background:var(--dm-surface-overlay)}#admin-sidebar.sidebar-dark .sidebar-link.active{color:var(--dm-primary);background:color-mix(in srgb,var(--dm-primary) 14%,transparent);border-left-color:var(--dm-primary)}#admin-sidebar.sidebar-dark .sidebar-heading{color:var(--dm-text-muted)}#admin-sidebar.sidebar-dark .sidebar-divider{background:var(--dm-border)}#admin-sidebar.sidebar-dark .sidebar-footer{background:var(--dm-surface-raised);border-color:var(--dm-border)}.sidebar-heading--collapsible{display:flex!important;align-items:center;justify-content:space-between;cursor:pointer;user-select:none}.sidebar-heading-toggle{display:flex;align-items:center;opacity:.5;flex-shrink:0;transition:transform .2s ease}.sidebar-heading--collapsible.is-collapsed .sidebar-heading-toggle{transform:rotate(-90deg)}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.p-0{padding:0!important}.text-muted{color:var(--dm-text-muted, #888)}@media(max-width:768px){.view-container{padding:1rem}.editor-body{flex-direction:column}.editor-divider{width:auto;height:1px}}.image-editor{display:flex;flex-direction:column;gap:0}.image-editor-toolbar{display:flex;flex-direction:row;align-items:center;gap:.25rem;padding:.5rem .75rem;border-bottom:1px solid var(--dm-border, rgba(255, 255, 255, .1))}.image-editor-sep{display:inline-block;width:1px;height:1.5rem;background:var(--dm-border, rgba(255, 255, 255, .15));margin:0 .25rem;flex-shrink:0}.editor-toolbar-btn.active{color:var(--dm-primary, #7c6af7);background:color-mix(in srgb,var(--dm-primary, #7c6af7) 15%,transparent)}.editor-toolbar-caret{font-size:.6rem;line-height:1;margin-left:2px;opacity:.6}.editor-toolbar-dropdown{position:absolute;z-index:1000;background:var(--dm-surface, #1e1e2e);border:1px solid var(--dm-border, rgba(255, 255, 255, .1));border-radius:8px;box-shadow:0 8px 24px #0006;padding:4px;min-width:160px;max-height:70vh;overflow-y:auto}.editor-toolbar-dropdown-heading{padding:6px 10px 3px;color:var(--dm-text-muted, #8a8a9a);font-size:.66rem;text-transform:uppercase;letter-spacing:.06em}.editor-toolbar-dropdown-item{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:none;border:none;color:var(--dm-text, #ddd);padding:7px 10px;border-radius:5px;cursor:pointer;font-size:.85rem;transition:background .12s,color .12s}.editor-toolbar-dropdown-item:hover{background:var(--dm-hover-bg);color:var(--dm-text, #fff)}.editor-toolbar-dropdown-item span[data-icon],.editor-toolbar-dropdown-item svg{width:15px;height:15px;flex-shrink:0;opacity:.75}.editor-toolbar{position:relative}.editor-effects-dropdown-menu{position:absolute;top:100%;z-index:1000;background:var(--dm-surface, #1e1e2e);border:1px solid var(--dm-border, rgba(255, 255, 255, .1));border-radius:8px;box-shadow:0 8px 24px #0006;padding:6px;min-width:180px;max-height:340px;overflow-y:auto}.editor-effects-category{padding:6px 8px 3px;font-size:.7rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--dm-text-muted, #888)}.editor-effects-item{display:block;width:100%;text-align:left;background:none;border:none;color:var(--dm-text, #ddd);padding:6px 10px;border-radius:5px;cursor:pointer;font-size:.85rem;transition:background .12s,color .12s}.editor-effects-item:hover{background:var(--dm-hover-bg);color:var(--dm-text, #fff)}.editor-icon-picker{position:absolute;z-index:1000;background:var(--dm-surface, #1e1e2e);border:1px solid var(--dm-border, rgba(255, 255, 255, .1));border-radius:8px;box-shadow:0 8px 24px #0006;padding:8px;width:320px}.editor-icon-picker-search{width:100%;margin-bottom:8px;font-size:.8rem}.editor-spacer-picker{position:absolute;z-index:9999;background:var(--dm-surface, #1e1e2e);border:1px solid var(--dm-border, #333);border-radius:8px;padding:12px;width:240px;box-shadow:0 8px 24px #00000073}.editor-spacer-picker-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--dm-text-muted, #888);margin-bottom:10px}.editor-spacer-picker-row{display:flex;align-items:center;gap:10px;margin-bottom:12px}.editor-spacer-slider{flex:1;accent-color:var(--dm-primary, #6366f1);cursor:pointer}.editor-spacer-slider-value{font-size:13px;font-weight:600;min-width:38px;text-align:right;color:var(--dm-text, #cdd6f4)}.editor-spacer-insert-btn{width:100%}.editor-icon-picker-size-row{display:flex;align-items:center;gap:8px;padding:0 8px 8px}.editor-icon-picker-size-row label{font-size:11px;color:var(--dm-text-muted, #888);white-space:nowrap}.editor-icon-picker-size{width:70px;font-size:12px;padding:3px 6px}.editor-icon-picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(52px,1fr));gap:2px;max-height:280px;overflow-y:auto}.editor-icon-picker-item{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:6px 2px;border-radius:5px;cursor:pointer;background:none;border:none;color:var(--dm-text, #ddd);font-size:.58rem;text-align:center;gap:4px;overflow:hidden;transition:background .12s,color .12s}.editor-icon-picker-item:hover{background:var(--dm-hover-bg);color:var(--dm-text, #fff)}.editor-icon-picker-item span[data-icon],.editor-icon-picker-item svg{width:16px;height:16px;flex-shrink:0}.editor-icon-picker-item>span:last-child{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.2}.editor-icon-picker-empty{grid-column:1 / -1;text-align:center;color:var(--dm-text-muted, #888);font-size:.8rem;padding:1.5rem}.image-editor-canvas{min-height:320px;max-height:520px;background:#111;overflow:hidden;display:flex;align-items:center;justify-content:center}.image-editor-canvas img{display:block;max-width:100%;max-height:520px}.image-editor-resize{display:flex;flex-direction:row;align-items:center;gap:.5rem;padding:.6rem .75rem;border-top:1px solid var(--dm-border, rgba(255, 255, 255, .1));font-size:.85rem}.image-editor-resize label{font-weight:600;color:var(--dm-text-muted, #888);font-size:.8rem}.image-editor-resize .form-input{width:5rem;padding:.25rem .5rem;font-size:.85rem}.image-editor-footer{display:flex;flex-direction:row;align-items:center;justify-content:space-between;padding:.75rem;border-top:1px solid var(--dm-border, rgba(255, 255, 255, .1));gap:.5rem}.image-editor-effects{border-top:1px solid var(--dm-border, rgba(255, 255, 255, .1))}.image-editor-tab-bar{display:flex;flex-direction:row;overflow-x:auto;border-bottom:1px solid var(--dm-border, rgba(255, 255, 255, .1));scrollbar-width:none}.image-editor-tab-bar::-webkit-scrollbar{display:none}.image-editor-tab-btn{display:inline-flex;align-items:center;gap:.35rem;padding:.5rem .85rem;font-size:.8rem;font-weight:500;border:none;border-bottom:2px solid transparent;background:transparent;cursor:pointer;color:var(--dm-text-muted, #888);white-space:nowrap;transition:color .15s,border-color .15s;flex-shrink:0}.image-editor-tab-btn:hover{color:var(--dm-text, #eee)}.image-editor-tab-btn.active{color:var(--dm-primary, #7c6af7);border-bottom-color:var(--dm-primary, #7c6af7)}.image-editor-tab-panel{padding:.75rem;max-height:200px;overflow-y:auto}.ie-presets-grid{display:flex;flex-wrap:wrap;gap:.35rem;margin-bottom:.5rem}.ie-preset-btn.active{color:var(--dm-primary, #7c6af7);background:color-mix(in srgb,var(--dm-primary, #7c6af7) 15%,transparent);border-color:var(--dm-primary, #7c6af7)}.ie-server-note{font-size:.73rem;color:var(--dm-text-muted, #888);font-style:italic;margin:.25rem 0 0;line-height:1.4}.ie-slider-row{display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem}.ie-slider-label{width:6rem;font-size:.8rem;color:var(--dm-text-muted, #888);font-weight:600;flex-shrink:0}.ie-slider{flex:1;accent-color:var(--dm-primary, #7c6af7);cursor:pointer}.ie-slider-val{width:3rem;font-size:.8rem;text-align:right;color:var(--dm-text, #eee);flex-shrink:0}.ie-select{flex:1;font-size:.85rem;padding:.25rem .5rem}.ie-colour-input{width:40px;height:28px;padding:0;border:1px solid var(--dm-border, rgba(255, 255, 255, .15));border-radius:4px;cursor:pointer;background:transparent;flex-shrink:0}.ie-wm-preview-row{display:flex;align-items:center;gap:.5rem;margin:.4rem 0}.ie-wm-preview-img{width:48px;height:48px;object-fit:contain;border-radius:4px;background:var(--dm-surface-raised, rgba(255, 255, 255, .06));flex-shrink:0}.ie-wm-preview-name{font-size:.8rem;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dm-text, #eee)}.ie-media-picker{padding:.25rem 0}.ie-media-picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(80px,1fr));gap:.5rem;max-height:300px;overflow-y:auto}.ie-media-thumb{display:flex;flex-direction:column;align-items:center;gap:.25rem;padding:.4rem;border-radius:6px;cursor:pointer;border:2px solid transparent;transition:border-color .15s,background .15s}.ie-media-thumb:hover{background:var(--dm-surface-raised, rgba(255, 255, 255, .08));border-color:var(--dm-primary, #7c6af7)}.ie-media-thumb img{width:72px;height:72px;object-fit:cover;border-radius:4px}.ie-media-thumb-name{font-size:.7rem;color:var(--dm-text-muted, #888);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:72px}.dm-slideover-left,.dm-slideover-right{width:min(40rem,95vw)!important;min-width:min(36rem,100vw)!important;max-width:100vw!important}.dm-slideover-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1.25rem;border-bottom:1px solid var(--dm-border, #333);flex-shrink:0}.dm-slideover-title{margin:0;font-size:1rem;font-weight:600}.dm-slideover-close{padding:.2rem .35rem!important;line-height:1;font-size:.75rem}.dm-slideover-body{flex:1;overflow:auto;overscroll-behavior:contain;min-height:0;padding:1.5rem!important;box-sizing:border-box}.dm-slideover-body:empty{flex:0 0 auto;padding:0!important}.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-body):not(.dm-slideover-close){padding:1.5rem!important;box-sizing:border-box;flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain}.dm-slideover-body,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close){scrollbar-width:thin;scrollbar-color:var(--dm-border) transparent}.dm-slideover-body::-webkit-scrollbar,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close)::-webkit-scrollbar{width:8px;height:8px}.dm-slideover-body::-webkit-scrollbar-track,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close)::-webkit-scrollbar-track{background:transparent}.dm-slideover-body::-webkit-scrollbar-thumb,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close)::-webkit-scrollbar-thumb{background:var(--dm-border);border-radius:4px}.dm-slideover-body::-webkit-scrollbar-thumb:hover,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close)::-webkit-scrollbar-thumb:hover{background:var(--dm-text-muted)}.dm-slideover-body>div{padding-left:0!important;padding-right:0!important}.dm-slideover-body>button.btn:last-child{margin-top:.5rem}.dconfig-textarea{font-family:monospace;min-height:100px;resize:vertical}.dm-editor-line-numbers{white-space:pre}.dm-code-inline{font-family:Fira Code,Courier New,monospace;font-size:.82em;padding:.15em .35em;border-radius:3px;background:var(--dm-background-alt, rgba(0, 0, 0, .18));color:var(--dm-text, inherit);border:1px solid var(--dm-border, rgba(255, 255, 255, .08))}.dm-code-block{font-family:Fira Code,Courier New,monospace;font-size:.82rem;line-height:1.6;padding:.65rem .9rem;border-radius:6px;background:var(--dm-background-alt, rgba(0, 0, 0, .18));color:var(--dm-text, inherit);border:1px solid var(--dm-border, rgba(255, 255, 255, .08));white-space:pre;overflow-x:auto;display:block;margin:.4rem 0}.tabs-centered{text-align:center}.tabs-centered .tab-list{display:inline-flex}.tabs-centered .tab-content{text-align:left}@media(max-width:768px){.view-header{flex-direction:column;align-items:flex-start}.theme-grid{grid-template-columns:repeat(2,1fr)}.nav-item-row{flex-wrap:wrap}.nav-col-icon,.nav-col-parent{display:none}#admin-topbar{padding:0 .75rem}.topbar-brand-text{display:none}.image-editor-toolbar{flex-wrap:wrap}}#topbar-bell{position:relative}.topbar-bell-badge{position:absolute;top:2px;right:2px;min-width:16px;height:16px;padding:0 4px;background:var(--dm-danger, #dc2626);color:var(--dm-danger-text, #fff);font-size:10px;font-weight:700;line-height:16px;border-radius:9999px;text-align:center;pointer-events:none}.cb-scaffolder{padding:1rem 1.1rem;background:var(--dm-background-alt);border:1px solid var(--dm-primary);border-radius:8px;color:var(--dm-text)}.cb-scaffolder-head{display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem}.cb-scaffolder-head .cb-icon{flex-shrink:0;color:var(--dm-primary)}.cb-scaffolder-head h2{margin:0;font-size:1.05rem;font-weight:700;color:var(--dm-text)}.cb-scaffolder-blurb{margin:0 0 .75rem;font-size:.88rem;line-height:1.5;color:var(--dm-text-muted)}.cb-recipe-list{display:flex;flex-direction:column;gap:.5rem}.cb-recipe-card{display:flex;align-items:flex-start;gap:.75rem;text-align:left;padding:.75rem 1rem;background:var(--dm-surface);border:1px solid var(--dm-border);border-radius:6px;cursor:pointer;transition:border-color .12s;width:100%;color:var(--dm-text)}.cb-recipe-card:hover{border-color:var(--dm-primary)}.cb-recipe-card .cb-icon{flex-shrink:0;font-size:1.5rem;color:var(--dm-primary);margin-top:.1rem}.cb-recipe-meta{flex:1}.cb-recipe-name{font-weight:600;margin-bottom:.15rem;font-size:.95rem;color:var(--dm-text)}.cb-recipe-desc{font-size:.83rem;line-height:1.45;color:var(--dm-text-muted)}.cb-apply-panel{padding:1rem 1.1rem;background:var(--dm-surface);border:1px solid var(--dm-primary);border-radius:8px;color:var(--dm-text)}.cb-apply-head{display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem}.cb-apply-head .cb-icon{color:var(--dm-primary);font-size:1.4rem}.cb-apply-head h3{margin:0;font-size:1rem;font-weight:600;color:var(--dm-text)}.cb-apply-desc,.cb-apply-hint{margin:.25rem 0 .75rem;font-size:.85rem;line-height:1.5;color:var(--dm-text-muted)}.cb-apply-hint{font-size:.75rem;margin-top:.2rem}.cb-apply-field{margin-bottom:.6rem}.cb-apply-field label{display:block;font-size:.82rem;font-weight:600;margin-bottom:.2rem;color:var(--dm-text)}.cb-apply-field input{width:100%;padding:.4rem .55rem;border:1px solid var(--dm-border);border-radius:4px;font-size:.88rem;background:var(--dm-surface);color:var(--dm-text)}.cb-apply-status{margin:.5rem 0;font-size:.85rem;min-height:1.2rem;color:var(--dm-text)}.cb-apply-status.cb-error{color:var(--dm-danger, #dc2626)}.cb-apply-buttons{display:flex;gap:.5rem;justify-content:flex-end;margin-top:.5rem}.cb-success-panel{padding:1rem 1.1rem;background:var(--dm-surface);border:1px solid var(--dm-success, #16a34a);border-radius:8px;color:var(--dm-text)}.cb-success-panel .cb-icon{color:var(--dm-success, #16a34a);font-size:1.4rem}.cb-success-summary{margin:.25rem 0 .5rem;padding-left:1.2rem;font-size:.86rem;line-height:1.6;color:var(--dm-text)}.cb-success-warning{margin:.5rem 0;padding:.5rem .75rem;background:var(--dm-background-alt);border-left:3px solid var(--dm-warning, #eab308);border-radius:0 4px 4px 0;font-size:.82rem;color:var(--dm-text-muted)}.cb-success-snippet-label{margin:0 0 .25rem;font-size:.82rem;font-weight:600;color:var(--dm-text)}.cb-success-actions{display:flex;gap:.5rem;margin-top:.5rem;flex-wrap:wrap}.cb-code{position:relative}.cb-code pre{background:var(--dm-background-alt);color:var(--dm-text);display:block;padding:.6rem 2.4rem .6rem .75rem;white-space:pre;overflow-x:auto;margin:0;border-radius:6px;font-size:.82em;font-family:monospace;line-height:1.45}.cb-code .cb-copy{position:absolute;top:.35rem;right:.35rem;background:none;border:none;cursor:pointer;opacity:.45;padding:.2rem;line-height:1;color:var(--dm-text)}.cb-code .cb-copy:hover{opacity:1}.cb-intro{padding:.75rem 1rem;border-left:3px solid var(--dm-primary);background:var(--dm-background-alt);border-radius:0 6px 6px 0}.cb-intro p{margin:0;font-size:.92rem;line-height:1.5;color:var(--dm-text)}.cb-tutorial-link{display:flex;align-items:center;gap:.6rem;padding:.65rem .85rem;border:1px solid var(--dm-border);border-radius:6px;text-decoration:none;color:var(--dm-text);font-size:.88rem;transition:border-color .12s}.cb-tutorial-link:hover{border-color:var(--dm-primary)}.cb-tutorial-link .cb-icon{flex-shrink:0;color:var(--dm-primary)}.cb-tutorial-link .cb-arrow{color:var(--dm-text-muted);font-size:1.1rem;margin-left:auto}.menu-tree-row{display:grid;grid-template-columns:16px 1.4fr 1.4fr .9fr .7fr .9fr repeat(6,auto);gap:6px;align-items:center;padding:4px 0;transition:opacity .15s ease}.menu-tree-row--hidden{opacity:.5}.menu-tree-grip{cursor:grab;color:var(--dm-text-muted)}.menu-tree{padding:8px 0}.menu-tree-row--separator{display:grid;grid-template-columns:16px minmax(80px,1fr) repeat(7,auto);gap:6px;align-items:center;padding:2px 0;opacity:.85}.menu-tree-row--separator:hover{opacity:1}.menu-tree-row--separator .me-sep-line{display:flex;align-items:center;gap:10px;width:100%}.menu-tree-row--separator .me-sep-line:before,.menu-tree-row--separator .me-sep-line:after{content:"";flex:1;border-top:1px dashed var(--dm-border, rgba(128, 128, 128, .55))}.menu-tree-row--separator .me-sep-label{font-size:.66rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--dm-text-muted, #8a8a8a);white-space:nowrap}.dm-admin-sidebar .sidebar-divider{border:0;border-top:1px solid var(--dm-border, rgba(128, 128, 128, .5));margin:.5rem .75rem;opacity:.8}.menu-tree-row .me-icon-wrap{display:flex;gap:2px}.menu-tree-row .me-icon-wrap .me-icon{flex:1;min-width:0}.menu-tree-row .me-icon-wrap .me-icon-pick{flex-shrink:0;padding:.2rem .45rem;font-size:.85em}.dm-admin-sidebar{display:flex;flex-direction:column;gap:2px;padding:8px 0;font-size:.875rem}.dm-admin-sidebar details{margin:0}.dm-admin-sidebar details>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:.5rem;padding:.4rem .75rem;border-radius:4px;user-select:none;font-size:.78rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--dm-text-muted, #888)}.dm-admin-sidebar details details>summary{font-size:.875rem;font-weight:500;text-transform:none;letter-spacing:0;color:inherit}.dm-admin-sidebar .dm-menu-badge{font-size:.7rem;font-weight:600}.dm-admin-sidebar details>summary::-webkit-details-marker{display:none}.dm-admin-sidebar details>summary:after{content:"\25b8";margin-left:auto;transition:transform .15s ease;color:var(--dm-text-muted, #999);font-size:.85em}.dm-admin-sidebar details[open]>summary:after{transform:rotate(90deg)}.dm-admin-sidebar details>div{padding-left:1rem;border-left:1px solid var(--dm-border-light, rgba(0, 0, 0, .08));margin-left:.95rem}.dm-admin-sidebar .sidebar-link{display:flex;align-items:center;gap:.5rem;padding:.35rem .75rem;text-decoration:none;color:var(--dm-text, inherit);border-radius:4px}.dm-admin-sidebar .sidebar-link:hover,.dm-admin-sidebar details>summary:hover{background:var(--dm-hover-bg, rgba(0, 0, 0, .05))}.dm-admin-sidebar .sidebar-link.active{background:var(--dm-active-bg, rgba(0, 0, 0, .08));font-weight:600}.dm-admin-sidebar .sidebar-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dm-admin-sidebar [data-icon]{flex-shrink:0}.dm-admin-sidebar-header{position:sticky;top:0;z-index:2;background:var(--dm-surface);display:flex;align-items:center;gap:.5rem;padding:.65rem .85rem;margin:0 0 .5rem;font-weight:600;font-size:.95rem;letter-spacing:.01em;color:var(--dm-text, inherit);border-bottom:1px solid var(--dm-border-light, rgba(0, 0, 0, .08))}.dm-admin-sidebar--dark .dm-admin-sidebar-header{background:#1a1d24}.dm-admin-sidebar--light .dm-admin-sidebar-header{background:#fff}.dm-admin-sidebar-header [data-icon]{color:var(--dm-text-muted, #999)}.dm-admin-sidebar .sidebar-badge{margin-left:auto;padding:0 .4rem;min-width:1.25rem;height:1.1rem;line-height:1.1rem;text-align:center;font-size:.7rem;font-weight:600;border-radius:999px;background:var(--dm-active-bg, rgba(0, 0, 0, .12));color:var(--dm-text-muted, #888)}.dm-admin-sidebar .sidebar-link.active .sidebar-badge{background:var(--dm-primary, #4a86e8);color:var(--dm-primary-text, #fff)}.dm-admin-sidebar .sidebar-children{overflow:hidden;transition:max-height .22s ease,opacity .18s ease}.dm-admin-sidebar details:not([open])>.sidebar-children{opacity:0}.dm-admin-sidebar{position:relative}.dm-admin-sidebar-handle{position:absolute;top:0;right:0;width:6px;height:100%;cursor:col-resize;background:transparent;z-index:10;transition:background .15s}.dm-admin-sidebar-handle:hover,.dm-admin-sidebar-handle:active{background:var(--dm-primary, #4a86e8);opacity:.35}.dm-admin-sidebar--dark{background:#1a1d24;color:#e3e6eb}.dm-admin-sidebar--dark .sidebar-link,.dm-admin-sidebar--dark summary{color:#e3e6eb}.dm-admin-sidebar--dark .sidebar-link:hover,.dm-admin-sidebar--dark details>summary:hover{background:#ffffff0f}.dm-admin-sidebar--dark .sidebar-link.active{background:#4a86e840}.dm-admin-sidebar--dark .sidebar-badge{background:#ffffff1f;color:#cbd0d8}.dm-admin-sidebar--light{background:#fff;color:#1a1d24;border-right:1px solid rgba(0,0,0,.08)}.dm-admin-sidebar--light .sidebar-link,.dm-admin-sidebar--light summary{color:#1a1d24}.dm-admin-sidebar--light .sidebar-link:hover,.dm-admin-sidebar--light details>summary:hover{background:#0000000a}.dm-admin-sidebar--light .sidebar-link.active{background:#4a86e826}.dm-admin-sidebar--transparent{background:transparent}.page-header{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;margin:0 0 1.25rem;padding-bottom:.75rem;border-bottom:1px solid var(--dm-border-light, rgba(0, 0, 0, .08))}.page-header h2{margin:0;display:flex;align-items:center;gap:.5rem;font-size:1.4rem;line-height:1.2}.page-header h2 [data-icon]{color:var(--dm-text-muted, #888)}.header-actions{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.card+.card,.card+p,p+.card,.card+.toolbar,.toolbar+.card,.card+.grid{margin-top:1rem}.toolbar{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin:0 0 1rem}.btn+.btn,.btn+a.btn,a.btn+.btn,a.btn+a.btn{margin-left:.35rem}[style*=inline-flex] .btn+.btn,[style*=inline-flex] .btn+a.btn,[style*=inline-flex] a.btn+.btn{margin-left:0}.card-body>[id$=-form],.card-body>form{margin:.25rem 0}.card-header{display:flex;align-items:center;gap:.5rem}.card-header h3{margin:0;font-size:1.05rem}.card-header [data-icon]{color:var(--dm-text-muted, #888)}.page-header+p.text-muted{margin:-.5rem 0 1rem}.dm-pe-grid{gap:.75rem 1rem}.dm-pe-grid .form-group{display:flex;flex-direction:column;gap:.25rem;margin:0}.dm-pe-grid .form-group.col-span-2{grid-column:1 / -1}.dm-pe-grid label{font-size:.85rem;font-weight:600;color:var(--dm-text, inherit)}.dm-pe-grid label .required{color:var(--dm-danger, #c33);margin-left:2px}.dm-pe-grid .form-hint{font-size:.75rem;color:var(--dm-text-muted, #888);margin-top:.1rem}.dm-pe-grid .form-input{width:100%;padding:.4rem .6rem}.dm-pe-grid textarea.form-input{resize:vertical;min-height:2.5rem}.dm-menu-badge{display:inline-block;margin-left:6px;padding:1px 7px;font-size:.7em;font-weight:700;line-height:1.4;border-radius:999px;background:var(--dm-text-muted);color:var(--dm-text-inverse, #fff);vertical-align:middle}.menu-tree-row--slim{display:grid;grid-template-columns:16px 1fr auto repeat(9,auto);gap:6px;align-items:center}.menu-tree-row--slim .me-row-label{font-weight:600}.menu-tree-row--slim .me-row-url{color:var(--dm-text-muted);font-size:.85em}.menu-tree-row.me-drop-above{box-shadow:inset 0 2px 0 0 var(--dm-primary)}.menu-tree-row.me-drop-below{box-shadow:inset 0 -2px 0 0 var(--dm-primary)}.menu-tree-row.me-drop-into{background:color-mix(in srgb,var(--dm-primary) 12%,transparent);box-shadow:inset 0 0 0 1px var(--dm-primary);border-radius:var(--dm-radius-sm, 4px)}.menu-tree-row[draggable=true] .menu-tree-grip{cursor:grabbing}.me-pages-picker{display:flex;gap:6px;align-items:center;margin-bottom:6px}.me-pages-picker .form-input{flex:1;min-width:0}.me-colour-control{display:flex;gap:6px;align-items:center}.me-colour-control .me-colour-hex{width:48px;height:32px;padding:2px;flex:none}.me-bind-summary{padding:.6rem .8rem;border:1px solid var(--dm-border, rgba(128, 128, 128, .35));border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-text) 4%,transparent);font-size:.9em}.me-bind-summary code{font-size:.9em}.me-item-form{display:flex;flex-direction:column;gap:10px;padding:4px 2px}.me-item-form .form-field{display:flex;flex-direction:column;gap:4px}.me-item-form .form-label{font-size:.85em;color:var(--dm-text-muted)}.me-form-section{margin:12px 0 2px;padding-top:10px;border-top:1px solid var(--dm-border-light, rgba(0,0,0,.15));font-size:.78em;text-transform:uppercase;letter-spacing:.06em;color:var(--dm-text-muted)}.me-item-form .me-form-section:first-child{margin-top:0;padding-top:0;border-top:0}.me-item-form .me-colour-hex{width:48px;height:32px;padding:2px}.me-item-form .me-field-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}.me-item-form .me-field-grid .form-field{margin:0}.me-item-form .me-icon-field{display:flex;align-items:center;gap:8px}.me-item-form .me-icon-field .form-input{flex:1}.me-item-form .me-icon-preview{width:24px;height:24px;flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;color:var(--dm-text-muted)}#admin-sidebar.dm-menu-floating{border:1px solid var(--dm-border);border-radius:var(--dm-radius-md, 8px);box-shadow:var(--dm-shadow-lg);overflow:hidden}@media(max-width:768px){#admin-sidebar.dm-menu-floating{position:static!important;inset:auto!important;max-height:none!important;transform:none!important}.dashboard-wrapper{padding-left:0!important;padding-right:0!important}}[data-logic-section]{border:1px solid var(--dm-border);border-radius:var(--dm-radius-md, 6px);padding:.9rem 1rem 1rem;margin-bottom:1rem;background:color-mix(in srgb,var(--dm-text) 3%,transparent)}[data-logic-section] .fb-logic-cond-row,[data-logic-section] .fb-logic-val-rule{padding:.55rem .65rem;border-radius:var(--dm-radius-sm, 4px);border:1px solid var(--dm-border);background:var(--dm-surface, transparent)}[data-logic-section] .fb-logic-cond-row:last-of-type,[data-logic-section] .fb-logic-val-rule:last-of-type{margin-bottom:.75rem}[data-logic-section]>.btn{margin-top:.15rem}[data-logic-section] textarea{min-height:5rem}.project-filter-notice{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;margin:0 0 1rem;padding:.6rem .85rem;border:1px solid var(--dm-border);border-left:3px solid var(--dm-info, var(--dm-primary));border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-info, var(--dm-primary)) 6%,transparent);font-size:.85rem;color:var(--dm-text-muted)}.project-filter-notice a{color:var(--dm-primary);font-weight:600}
|
|
1
|
+
.dashboard-layout{display:flex;flex-direction:column;height:100vh;min-height:100vh;overflow:hidden}.dashboard-wrapper{display:flex;flex:1;min-height:0;overflow:hidden}.dashboard-main{flex:1;min-width:0;overflow-y:auto}#admin-sidebar.dashboard-sidebar{display:flex;flex-direction:column;flex-shrink:0;overflow:hidden}#admin-sidebar .dm-admin-sidebar{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--dm-border) transparent}#admin-sidebar .dm-admin-sidebar::-webkit-scrollbar{width:8px}#admin-sidebar .dm-admin-sidebar::-webkit-scrollbar-track{background:transparent}#admin-sidebar .dm-admin-sidebar::-webkit-scrollbar-thumb{background:var(--dm-border);border-radius:4px}#admin-sidebar .dm-admin-sidebar::-webkit-scrollbar-thumb:hover{background:var(--dm-text-muted)}.view-container{padding:1.5rem 2rem;max-width:1200px}.view-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.5rem;gap:1rem}.view-header h1{font-size:1.5rem;font-weight:700;margin:0;display:flex;align-items:center;gap:.5rem}.view-header-actions{display:flex;gap:.75rem;flex-wrap:wrap}.view-header-actions .btn-primary,.view-header-actions .btn-success,.view-header-actions .btn-danger{margin-left:.5rem}.stat-card .card-body{display:flex;align-items:center;gap:1rem}.stat-icon{width:44px;height:44px;border-radius:8px;background:color-mix(in srgb,var(--dm-primary) 14%,transparent);color:var(--dm-primary, #5b8cff);display:flex;align-items:center;justify-content:center;font-size:1.25rem;flex-shrink:0}.stat-icon--success{background:color-mix(in srgb,var(--dm-success) 15%,transparent);color:var(--dm-success)}.stat-icon--warning{background:color-mix(in srgb,var(--dm-warning) 15%,transparent);color:var(--dm-warning)}.stat-value{font-size:1.75rem;font-weight:700;line-height:1}.stat-label{font-size:.8rem;color:var(--dm-text-muted, #888);margin-top:.25rem}.toolbar{display:flex;align-items:center;gap:.75rem}.form-check-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.9rem}.form-hint{display:block;font-size:.8rem;color:var(--dm-text-muted, #888);margin-top:.25rem}.dashboard-main:has(#editor-meta-tabs){overflow:hidden}.view-container:has(#editor-meta-tabs){display:flex;flex-direction:column;height:calc(100dvh - 60px);max-width:100%;padding-bottom:0}.view-container:has(#block-editor-body){max-width:100%}.view-container:has(#ep-editor-tabs){max-width:100%}.ep-editor-grid{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr) minmax(0,1.3fr);gap:1rem;align-items:start}.ep-editor-grid>.ep-tryit-col,.ep-editor-grid>.ep-results-col{position:sticky;top:1rem;display:flex;flex-direction:column;gap:1rem;min-width:0}@media(max-width:1500px){.ep-editor-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.ep-editor-grid>:first-child{grid-column:1 / -1}.ep-editor-grid>.ep-tryit-col,.ep-editor-grid>.ep-results-col{position:static}}@media(max-width:1024px){.ep-editor-grid{grid-template-columns:minmax(0,1fr)}}#editor-meta-tabs{flex:1;min-height:0;display:flex;flex-direction:column}#editor-meta-tabs .tab-content{flex:1;min-height:0;overflow:hidden}#editor-meta-tabs .tab-panel{height:100%;overflow-y:auto}#editor-meta-tabs .tab-panel:has(.editor-card),#live-preview-panel{overflow:hidden}.page-live-preview-frame{display:block;width:100%;height:calc(100vh - 180px);border:none;border-radius:6px}.editor-card{display:flex;flex-direction:column;height:100%}.editor-card .card-body{display:flex;flex-direction:column;flex:1;min-height:0;padding:0!important}.editor-toolbar{display:flex;align-items:center;gap:2px;padding:6px 10px;border-bottom:1px solid var(--dm-border, rgba(255, 255, 255, .08));flex-wrap:wrap;flex-shrink:0}.editor-toolbar-btn{background:none;border:none;color:var(--dm-text-muted, #888);padding:6px 8px;border-radius:4px;cursor:pointer;display:flex;align-items:center;transition:color .15s,background .15s}.editor-toolbar-btn:hover{color:var(--dm-text, #eee);background:var(--dm-hover-bg)}.editor-toolbar-sep{width:1px;height:20px;background:var(--dm-border, rgba(255, 255, 255, .08));margin:0 4px;flex-shrink:0}.editor-toolbar-right{margin-left:auto;display:flex;align-items:center;gap:2px}.editor-view-btn{background:none;border:none;color:var(--dm-text-muted, #888);padding:6px 8px;border-radius:4px;cursor:pointer;display:flex;align-items:center;transition:color .15s,background .15s}.editor-view-btn:hover{color:var(--dm-text, #eee);background:var(--dm-hover-bg)}.editor-view-btn.active{color:var(--dm-primary, #5b8cff);background:color-mix(in srgb,var(--dm-primary) 14%,transparent)}.editor-body{display:flex;flex:1;min-height:0}.editor-pane{flex:1;display:flex;flex-direction:column;min-width:0}.editor-pane--write,.editor-pane--preview{overflow:hidden}.editor-pane--write{flex-direction:row}.editor-line-numbers{padding:1rem .6rem 1rem .75rem;background:var(--dm-background-alt, rgba(0, 0, 0, .15));border-right:1px solid var(--dm-border, rgba(255, 255, 255, .08));color:var(--dm-text-muted, #666);font-family:Fira Code,Courier New,monospace;font-size:.9rem;line-height:1.6;text-align:right;user-select:none;white-space:pre;overflow:hidden;flex-shrink:0;min-width:2.5rem}.editor-line-numbers--foldable{padding:1rem 0;min-width:3rem;white-space:normal;text-align:left;display:flex;flex-direction:column}.editor-line-number-row{display:flex;align-items:center;justify-content:flex-end;padding-right:.6rem;gap:.15rem}.fold-toggle{display:inline-flex;align-items:center;justify-content:center;width:1em;font-size:.6rem;cursor:default;color:transparent}.fold-toggle--open,.fold-toggle--folded{cursor:pointer;color:var(--dm-text-muted, #666);transition:color .1s}.fold-toggle--open:hover,.fold-toggle--folded:hover{color:var(--dm-primary, #4a9eff)}.editor-line-num{min-width:1.5rem;text-align:right}.editor-mode-write .editor-pane--preview,.editor-mode-write .editor-divider,.editor-mode-preview .editor-pane--write,.editor-mode-preview .editor-divider{display:none}.editor-divider{width:1px;background:var(--dm-border, rgba(255,255,255,.08));flex-shrink:0}.editor-textarea{flex:1;resize:none;border:none;border-radius:0;background:transparent;font-family:Fira Code,Courier New,monospace;font-size:.9rem;padding:1rem;outline:none;color:inherit;line-height:1.6}.editor-preview{flex:1;padding:1rem;overflow-y:auto;line-height:1.7}.editor-preview h1,.editor-preview h2,.editor-preview h3{margin-top:1.5rem}.editor-preview p{margin-bottom:.75rem}.editor-preview code{background:var(--dm-code-bg);padding:.1em .3em;border-radius:3px;font-size:.9em}.editor-fullscreen{position:fixed;inset:0;z-index:1000;border-radius:0;margin:0}.editor-fullscreen .card-body{height:100vh}.media-picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:.75rem;max-height:400px;overflow-y:auto;padding:.25rem}.media-picker-item{cursor:pointer;border:2px solid transparent;border-radius:6px;overflow:hidden;transition:border-color .15s}.media-picker-item:hover{border-color:var(--dm-primary, #5b8cff)}.media-picker-item img{width:100%;height:80px;object-fit:cover;display:block}.media-picker-item span{display:block;font-size:.75rem;padding:4px 6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.docs-body h3{margin-top:1.25rem;margin-bottom:.5rem}.docs-body p{margin-bottom:.5rem;line-height:1.6}.docs-body ol,.docs-body ul{margin-bottom:.75rem;padding-left:1.25rem}.docs-body li{margin-bottom:.25rem}.docs-body hr{margin:1.25rem 0;border-color:var(--dm-border, rgba(255, 255, 255, .08))}.docs-body .table{margin-bottom:.75rem}.nav-items-header,.nav-item-row{display:flex;gap:.5rem;align-items:center;padding:.35rem .75rem}.nav-items-header{border-bottom:1px solid var(--dm-border, rgba(255,255,255,.08));padding-bottom:.4rem;margin-bottom:.25rem}.nav-item-row{border-bottom:1px solid var(--dm-border, rgba(255,255,255,.04))}.nav-item-row:last-child{border-bottom:none}.nav-col-indent{width:18px;flex-shrink:0;color:var(--dm-text-muted, #888);text-align:center;font-size:.85rem}.nav-col-main{flex:2;min-width:0}.nav-col-icon{flex:0 0 90px}.nav-col-parent{flex:2;min-width:0}.nav-col-action{flex-shrink:0}.nav-item-row--child{background:color-mix(in srgb,var(--dm-text) 3%,transparent)}.nav-col-action{display:flex;align-items:center;gap:.25rem}.nav-item-row--hidden{opacity:.45}.nav-item-row--hidden .item-text,.nav-item-row--hidden .footer-link-text{text-decoration:line-through}.btn-toggle-hidden{color:var(--dm-text-muted, #888)}.btn-toggle-hidden.active{color:var(--dm-warning, #f59e0b)}.fb-field-card.fb-drag-over{outline:2px dashed var(--dm-primary, #6366f1);outline-offset:-2px}.btn-ghost span[data-icon=trash]{color:var(--dm-danger, #dc2626)}.view-container:has(.fb-fields-layout){max-width:none}.fb-fields-layout{display:grid;grid-template-columns:minmax(0,6fr) minmax(320px,4fr);gap:1.25rem;align-items:start}.fb-preview-pane{position:sticky;top:1rem;min-width:0}@media(max-width:1100px){.fb-fields-layout{grid-template-columns:1fr}.fb-preview-pane{position:static}}.toggle-label{display:flex;align-items:center;gap:.4rem;font-size:.9rem;cursor:pointer;margin-right:1rem}.preset-toggles{display:flex;flex-wrap:wrap;margin-top:.75rem}.presets-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem}@media(max-width:900px){.presets-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:600px){.presets-grid{grid-template-columns:1fr}}.card-header{padding:.5rem 1rem}.card-header h2{margin:0;font-size:.9rem;font-weight:600;letter-spacing:.02em}.preset-card .card-header{display:flex;align-items:center;justify-content:space-between}.preset-card .card-header h3{margin:0;font-size:1rem}.media-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:1rem}.media-card{background:var(--dm-card-bg, rgba(255,255,255,.04));border:1px solid var(--dm-border, rgba(255,255,255,.08));border-radius:8px;overflow:hidden}.media-preview{height:130px;display:flex;align-items:center;justify-content:center;background:#0003;overflow:hidden}.media-thumb{width:100%;height:100%;object-fit:cover}.media-thumb--file{font-size:2rem;color:var(--dm-text-muted, #888)}.media-info{padding:.5rem .75rem;display:flex;flex-direction:column;gap:.15rem}.media-name{font-size:.8rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default}.media-rename-input{width:100%;font-size:.8rem;font-weight:500;padding:.1rem .25rem;border:1px solid var(--dm-primary, #7c6af7);border-radius:3px;background:var(--dm-input-bg, transparent);color:inherit;outline:none}.media-size{font-size:.75rem;color:var(--dm-text-muted, #888)}.media-actions{padding:.5rem .75rem;display:flex;gap:.4rem;border-top:1px solid var(--dm-border, rgba(255,255,255,.08))}#admin-topbar{height:60px;display:flex;align-items:center;gap:1rem;padding:0 1.25rem;background:var(--dm-navbar-bg, rgba(0,0,0,.3));border-bottom:1px solid var(--dm-border, rgba(255,255,255,.08));position:sticky;top:0;z-index:200;flex-shrink:0}.topbar-brand{display:flex;align-items:center;gap:.5rem;font-weight:700;font-size:.95rem;color:inherit;flex-shrink:0;margin-right:.5rem}.topbar-brand span[data-icon],.topbar-brand svg{color:var(--dm-primary, #5b8cff);width:22px;height:22px}.topbar-brand-text{opacity:.85}.topbar-brand-logo{height:28px;width:auto;display:inline-block;vertical-align:middle;margin-right:.4em;object-fit:contain}.topbar-user{display:flex;align-items:center;gap:.6rem;flex:1}.topbar-user-name{font-size:.875rem;font-weight:500}.topbar-role-badge{font-size:.7rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;padding:.15rem .45rem;border-radius:4px}.topbar-role-badge--admin{background:color-mix(in srgb,var(--dm-primary) 15%,transparent);color:var(--dm-primary)}.topbar-role-badge--manager{background:color-mix(in srgb,var(--dm-success) 15%,transparent);color:var(--dm-success)}.topbar-role-badge--editor{background:color-mix(in srgb,var(--dm-warning) 15%,transparent);color:var(--dm-warning)}.topbar-role-badge--subscriber{background:color-mix(in srgb,var(--dm-text-muted) 15%,transparent);color:var(--dm-text-muted)}.topbar-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.topbar-action-link{display:flex;align-items:center;gap:.35rem;font-size:.875rem;color:var(--dm-text-muted, #888);text-decoration:none;padding:.4rem .65rem;border-radius:6px;transition:color .15s,background .15s}.topbar-action-link:hover{color:var(--dm-text, #eee);background:var(--dm-hover-bg)}.topbar-action-link span[data-icon],.topbar-action-link svg{width:16px;height:16px}.topbar-signout{display:flex;align-items:center;gap:.35rem;font-size:.875rem}.topbar-signout span[data-icon],.topbar-signout svg{width:16px;height:16px}.login-wrap{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:1rem;background:var(--dm-background, #0f1117);z-index:100;overflow-y:auto}.login-card{width:100%;max-width:460px}.ob-done-icon{font-size:3rem;color:var(--dm-success);margin-bottom:1rem;text-align:center}.btn-skip{display:block;text-align:center;margin-top:.75rem;font-size:.85rem;color:var(--dm-text-muted, #888);text-decoration:none}.btn-skip:hover{text-decoration:underline}.theme-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.6rem}.theme-swatch{border:2px solid var(--dm-border, rgba(255,255,255,.1));border-radius:8px;overflow:hidden;cursor:pointer;transition:border-color .15s,transform .1s}.theme-swatch:hover{border-color:var(--dm-primary, #5b8cff);transform:translateY(-1px)}.theme-swatch.selected{border-color:var(--dm-primary, #5b8cff);box-shadow:0 0 0 2px var(--dm-primary, #5b8cff)}.theme-swatch-preview{height:52px;position:relative;display:flex;align-items:flex-end;padding:.35rem}.theme-swatch-accent{width:28px;height:6px;border-radius:3px}.theme-swatch-label{display:flex;justify-content:space-between;align-items:center;padding:.3rem .45rem;font-size:.7rem;background:var(--dm-card-bg, rgba(255,255,255,.04))}.theme-swatch-mode{color:var(--dm-text-muted, #888);font-size:.65rem}.login-logo{display:flex;align-items:center;gap:.75rem;margin-bottom:1.75rem;font-size:1.5rem}.login-logo span[data-icon]{font-size:2rem;color:var(--dm-primary, #5b8cff)}.login-logo h1{margin:0;font-size:1.5rem;font-weight:700}.login-heading{font-size:1.1rem;font-weight:600;margin-bottom:1.25rem}.btn-block{width:100%}.plugins-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:1rem}.plugin-card{display:flex;flex-direction:column}.plugin-card .card-body{flex:1}.plugin-header{display:flex;align-items:flex-start;gap:.75rem;margin-bottom:.75rem}.plugin-icon{width:40px;height:40px;border-radius:8px;background:color-mix(in srgb,var(--dm-primary) 14%,transparent);color:var(--dm-primary, #5b8cff);display:flex;align-items:center;justify-content:center;flex-shrink:0}.plugin-meta{flex:1;min-width:0}.plugin-name{font-weight:600;margin-bottom:.15rem}.plugin-version{font-size:.75rem;color:var(--dm-text-muted, #888)}.plugin-desc{font-size:.875rem;color:var(--dm-text-muted, #888);margin-bottom:1rem}.plugin-footer{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-top:1px solid var(--dm-border, rgba(255,255,255,.08))}.plugin-footer-actions{display:flex;align-items:center;gap:.5rem}#admin-sidebar .sidebar-link{position:relative!important}#admin-sidebar .sidebar-badge{position:absolute!important;right:.75rem!important;top:50%!important;transform:translateY(-50%)!important;margin-left:0!important;padding-left:.45rem!important;padding-right:.45rem!important}#admin-sidebar .sidebar-text{padding-right:2rem!important}#admin-sidebar.sidebar-dark{background:var(--dm-surface);border-color:var(--dm-border)}#admin-sidebar.sidebar-dark .sidebar-header{background:var(--dm-surface-raised);border-color:var(--dm-border)}#admin-sidebar.sidebar-dark .sidebar-header-title{color:var(--dm-text)}#admin-sidebar.sidebar-dark .sidebar-link{color:var(--dm-text-muted)}#admin-sidebar.sidebar-dark .sidebar-link:hover{color:var(--dm-text);background:var(--dm-surface-overlay)}#admin-sidebar.sidebar-dark .sidebar-link.active{color:var(--dm-primary);background:color-mix(in srgb,var(--dm-primary) 14%,transparent);border-left-color:var(--dm-primary)}#admin-sidebar.sidebar-dark .sidebar-heading{color:var(--dm-text-muted)}#admin-sidebar.sidebar-dark .sidebar-divider{background:var(--dm-border)}#admin-sidebar.sidebar-dark .sidebar-footer{background:var(--dm-surface-raised);border-color:var(--dm-border)}.sidebar-heading--collapsible{display:flex!important;align-items:center;justify-content:space-between;cursor:pointer;user-select:none}.sidebar-heading-toggle{display:flex;align-items:center;opacity:.5;flex-shrink:0;transition:transform .2s ease}.sidebar-heading--collapsible.is-collapsed .sidebar-heading-toggle{transform:rotate(-90deg)}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.p-0{padding:0!important}.text-muted{color:var(--dm-text-muted, #888)}@media(max-width:768px){.view-container{padding:1rem}.editor-body{flex-direction:column}.editor-divider{width:auto;height:1px}}.image-editor{display:flex;flex-direction:column;gap:0}.image-editor-toolbar{display:flex;flex-direction:row;align-items:center;gap:.25rem;padding:.5rem .75rem;border-bottom:1px solid var(--dm-border, rgba(255, 255, 255, .1))}.image-editor-sep{display:inline-block;width:1px;height:1.5rem;background:var(--dm-border, rgba(255, 255, 255, .15));margin:0 .25rem;flex-shrink:0}.editor-toolbar-btn.active{color:var(--dm-primary, #7c6af7);background:color-mix(in srgb,var(--dm-primary, #7c6af7) 15%,transparent)}.editor-toolbar-caret{font-size:.6rem;line-height:1;margin-left:2px;opacity:.6}.editor-toolbar-dropdown{position:absolute;z-index:1000;background:var(--dm-surface, #1e1e2e);border:1px solid var(--dm-border, rgba(255, 255, 255, .1));border-radius:8px;box-shadow:0 8px 24px #0006;padding:4px;min-width:160px;max-height:70vh;overflow-y:auto}.editor-toolbar-dropdown-heading{padding:6px 10px 3px;color:var(--dm-text-muted, #8a8a9a);font-size:.66rem;text-transform:uppercase;letter-spacing:.06em}.editor-toolbar-dropdown-item{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:none;border:none;color:var(--dm-text, #ddd);padding:7px 10px;border-radius:5px;cursor:pointer;font-size:.85rem;transition:background .12s,color .12s}.editor-toolbar-dropdown-item:hover{background:var(--dm-hover-bg);color:var(--dm-text, #fff)}.editor-toolbar-dropdown-item span[data-icon],.editor-toolbar-dropdown-item svg{width:15px;height:15px;flex-shrink:0;opacity:.75}.editor-toolbar{position:relative}.editor-effects-dropdown-menu{position:absolute;top:100%;z-index:1000;background:var(--dm-surface, #1e1e2e);border:1px solid var(--dm-border, rgba(255, 255, 255, .1));border-radius:8px;box-shadow:0 8px 24px #0006;padding:6px;min-width:180px;max-height:340px;overflow-y:auto}.editor-effects-category{padding:6px 8px 3px;font-size:.7rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--dm-text-muted, #888)}.editor-effects-item{display:block;width:100%;text-align:left;background:none;border:none;color:var(--dm-text, #ddd);padding:6px 10px;border-radius:5px;cursor:pointer;font-size:.85rem;transition:background .12s,color .12s}.editor-effects-item:hover{background:var(--dm-hover-bg);color:var(--dm-text, #fff)}.editor-icon-picker{position:absolute;z-index:1000;background:var(--dm-surface, #1e1e2e);border:1px solid var(--dm-border, rgba(255, 255, 255, .1));border-radius:8px;box-shadow:0 8px 24px #0006;padding:8px;width:320px}.editor-icon-picker-search{width:100%;margin-bottom:8px;font-size:.8rem}.editor-spacer-picker{position:absolute;z-index:9999;background:var(--dm-surface, #1e1e2e);border:1px solid var(--dm-border, #333);border-radius:8px;padding:12px;width:240px;box-shadow:0 8px 24px #00000073}.editor-spacer-picker-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--dm-text-muted, #888);margin-bottom:10px}.editor-spacer-picker-row{display:flex;align-items:center;gap:10px;margin-bottom:12px}.editor-spacer-slider{flex:1;accent-color:var(--dm-primary, #6366f1);cursor:pointer}.editor-spacer-slider-value{font-size:13px;font-weight:600;min-width:38px;text-align:right;color:var(--dm-text, #cdd6f4)}.editor-spacer-insert-btn{width:100%}.editor-icon-picker-size-row{display:flex;align-items:center;gap:8px;padding:0 8px 8px}.editor-icon-picker-size-row label{font-size:11px;color:var(--dm-text-muted, #888);white-space:nowrap}.editor-icon-picker-size{width:70px;font-size:12px;padding:3px 6px}.editor-icon-picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(52px,1fr));gap:2px;max-height:280px;overflow-y:auto}.editor-icon-picker-item{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:6px 2px;border-radius:5px;cursor:pointer;background:none;border:none;color:var(--dm-text, #ddd);font-size:.58rem;text-align:center;gap:4px;overflow:hidden;transition:background .12s,color .12s}.editor-icon-picker-item:hover{background:var(--dm-hover-bg);color:var(--dm-text, #fff)}.editor-icon-picker-item span[data-icon],.editor-icon-picker-item svg{width:16px;height:16px;flex-shrink:0}.editor-icon-picker-item>span:last-child{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.2}.editor-icon-picker-empty{grid-column:1 / -1;text-align:center;color:var(--dm-text-muted, #888);font-size:.8rem;padding:1.5rem}.image-editor-canvas{min-height:320px;max-height:520px;background:#111;overflow:hidden;display:flex;align-items:center;justify-content:center}.image-editor-canvas img{display:block;max-width:100%;max-height:520px}.image-editor-resize{display:flex;flex-direction:row;align-items:center;gap:.5rem;padding:.6rem .75rem;border-top:1px solid var(--dm-border, rgba(255, 255, 255, .1));font-size:.85rem}.image-editor-resize label{font-weight:600;color:var(--dm-text-muted, #888);font-size:.8rem}.image-editor-resize .form-input{width:5rem;padding:.25rem .5rem;font-size:.85rem}.image-editor-footer{display:flex;flex-direction:row;align-items:center;justify-content:space-between;padding:.75rem;border-top:1px solid var(--dm-border, rgba(255, 255, 255, .1));gap:.5rem}.image-editor-effects{border-top:1px solid var(--dm-border, rgba(255, 255, 255, .1))}.image-editor-tab-bar{display:flex;flex-direction:row;overflow-x:auto;border-bottom:1px solid var(--dm-border, rgba(255, 255, 255, .1));scrollbar-width:none}.image-editor-tab-bar::-webkit-scrollbar{display:none}.image-editor-tab-btn{display:inline-flex;align-items:center;gap:.35rem;padding:.5rem .85rem;font-size:.8rem;font-weight:500;border:none;border-bottom:2px solid transparent;background:transparent;cursor:pointer;color:var(--dm-text-muted, #888);white-space:nowrap;transition:color .15s,border-color .15s;flex-shrink:0}.image-editor-tab-btn:hover{color:var(--dm-text, #eee)}.image-editor-tab-btn.active{color:var(--dm-primary, #7c6af7);border-bottom-color:var(--dm-primary, #7c6af7)}.image-editor-tab-panel{padding:.75rem;max-height:200px;overflow-y:auto}.ie-presets-grid{display:flex;flex-wrap:wrap;gap:.35rem;margin-bottom:.5rem}.ie-preset-btn.active{color:var(--dm-primary, #7c6af7);background:color-mix(in srgb,var(--dm-primary, #7c6af7) 15%,transparent);border-color:var(--dm-primary, #7c6af7)}.ie-server-note{font-size:.73rem;color:var(--dm-text-muted, #888);font-style:italic;margin:.25rem 0 0;line-height:1.4}.ie-slider-row{display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem}.ie-slider-label{width:6rem;font-size:.8rem;color:var(--dm-text-muted, #888);font-weight:600;flex-shrink:0}.ie-slider{flex:1;accent-color:var(--dm-primary, #7c6af7);cursor:pointer}.ie-slider-val{width:3rem;font-size:.8rem;text-align:right;color:var(--dm-text, #eee);flex-shrink:0}.ie-select{flex:1;font-size:.85rem;padding:.25rem .5rem}.ie-colour-input{width:40px;height:28px;padding:0;border:1px solid var(--dm-border, rgba(255, 255, 255, .15));border-radius:4px;cursor:pointer;background:transparent;flex-shrink:0}.ie-wm-preview-row{display:flex;align-items:center;gap:.5rem;margin:.4rem 0}.ie-wm-preview-img{width:48px;height:48px;object-fit:contain;border-radius:4px;background:var(--dm-surface-raised, rgba(255, 255, 255, .06));flex-shrink:0}.ie-wm-preview-name{font-size:.8rem;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dm-text, #eee)}.ie-media-picker{padding:.25rem 0}.ie-media-picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(80px,1fr));gap:.5rem;max-height:300px;overflow-y:auto}.ie-media-thumb{display:flex;flex-direction:column;align-items:center;gap:.25rem;padding:.4rem;border-radius:6px;cursor:pointer;border:2px solid transparent;transition:border-color .15s,background .15s}.ie-media-thumb:hover{background:var(--dm-surface-raised, rgba(255, 255, 255, .08));border-color:var(--dm-primary, #7c6af7)}.ie-media-thumb img{width:72px;height:72px;object-fit:cover;border-radius:4px}.ie-media-thumb-name{font-size:.7rem;color:var(--dm-text-muted, #888);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:72px}.dm-slideover-left,.dm-slideover-right{width:min(40rem,95vw)!important;min-width:min(36rem,100vw)!important;max-width:100vw!important}.dm-slideover-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1.25rem;border-bottom:1px solid var(--dm-border, #333);flex-shrink:0}.dm-slideover-title{margin:0;font-size:1rem;font-weight:600}.dm-slideover-close{padding:.2rem .35rem!important;line-height:1;font-size:.75rem}.dm-slideover-body{flex:1;overflow:auto;overscroll-behavior:contain;min-height:0;padding:1.5rem!important;box-sizing:border-box}.dm-slideover-body:empty{flex:0 0 auto;padding:0!important}.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-body):not(.dm-slideover-close){padding:1.5rem!important;box-sizing:border-box;flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain}.dm-slideover-body,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close){scrollbar-width:thin;scrollbar-color:var(--dm-border) transparent}.dm-slideover-body::-webkit-scrollbar,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close)::-webkit-scrollbar{width:8px;height:8px}.dm-slideover-body::-webkit-scrollbar-track,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close)::-webkit-scrollbar-track{background:transparent}.dm-slideover-body::-webkit-scrollbar-thumb,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close)::-webkit-scrollbar-thumb{background:var(--dm-border);border-radius:4px}.dm-slideover-body::-webkit-scrollbar-thumb:hover,.dm-slideover>div:not(.dm-slideover-header):not(.dm-slideover-close)::-webkit-scrollbar-thumb:hover{background:var(--dm-text-muted)}.dm-slideover-body>div{padding-left:0!important;padding-right:0!important}.dm-slideover-body>button.btn:last-child{margin-top:.5rem}.dconfig-textarea{font-family:monospace;min-height:100px;resize:vertical}.dm-editor-line-numbers{white-space:pre}.dm-code-inline{font-family:Fira Code,Courier New,monospace;font-size:.82em;padding:.15em .35em;border-radius:3px;background:var(--dm-background-alt, rgba(0, 0, 0, .18));color:var(--dm-text, inherit);border:1px solid var(--dm-border, rgba(255, 255, 255, .08))}.dm-code-block{font-family:Fira Code,Courier New,monospace;font-size:.82rem;line-height:1.6;padding:.65rem .9rem;border-radius:6px;background:var(--dm-background-alt, rgba(0, 0, 0, .18));color:var(--dm-text, inherit);border:1px solid var(--dm-border, rgba(255, 255, 255, .08));white-space:pre;overflow-x:auto;display:block;margin:.4rem 0}.tabs-centered{text-align:center}.tabs-centered .tab-list{display:inline-flex}.tabs-centered .tab-content{text-align:left}@media(max-width:768px){.view-header{flex-direction:column;align-items:flex-start}.theme-grid{grid-template-columns:repeat(2,1fr)}.nav-item-row{flex-wrap:wrap}.nav-col-icon,.nav-col-parent{display:none}#admin-topbar{padding:0 .75rem}.topbar-brand-text{display:none}.image-editor-toolbar{flex-wrap:wrap}}#topbar-bell{position:relative}.topbar-bell-badge{position:absolute;top:2px;right:2px;min-width:16px;height:16px;padding:0 4px;background:var(--dm-danger, #dc2626);color:var(--dm-danger-text, #fff);font-size:10px;font-weight:700;line-height:16px;border-radius:9999px;text-align:center;pointer-events:none}.cb-scaffolder{padding:1rem 1.1rem;background:var(--dm-background-alt);border:1px solid var(--dm-primary);border-radius:8px;color:var(--dm-text)}.cb-scaffolder-head{display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem}.cb-scaffolder-head .cb-icon{flex-shrink:0;color:var(--dm-primary)}.cb-scaffolder-head h2{margin:0;font-size:1.05rem;font-weight:700;color:var(--dm-text)}.cb-scaffolder-blurb{margin:0 0 .75rem;font-size:.88rem;line-height:1.5;color:var(--dm-text-muted)}.cb-recipe-list{display:flex;flex-direction:column;gap:.5rem}.cb-recipe-card{display:flex;align-items:flex-start;gap:.75rem;text-align:left;padding:.75rem 1rem;background:var(--dm-surface);border:1px solid var(--dm-border);border-radius:6px;cursor:pointer;transition:border-color .12s;width:100%;color:var(--dm-text)}.cb-recipe-card:hover{border-color:var(--dm-primary)}.cb-recipe-card .cb-icon{flex-shrink:0;font-size:1.5rem;color:var(--dm-primary);margin-top:.1rem}.cb-recipe-meta{flex:1}.cb-recipe-name{font-weight:600;margin-bottom:.15rem;font-size:.95rem;color:var(--dm-text)}.cb-recipe-desc{font-size:.83rem;line-height:1.45;color:var(--dm-text-muted)}.cb-apply-panel{padding:1rem 1.1rem;background:var(--dm-surface);border:1px solid var(--dm-primary);border-radius:8px;color:var(--dm-text)}.cb-apply-head{display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem}.cb-apply-head .cb-icon{color:var(--dm-primary);font-size:1.4rem}.cb-apply-head h3{margin:0;font-size:1rem;font-weight:600;color:var(--dm-text)}.cb-apply-desc,.cb-apply-hint{margin:.25rem 0 .75rem;font-size:.85rem;line-height:1.5;color:var(--dm-text-muted)}.cb-apply-hint{font-size:.75rem;margin-top:.2rem}.cb-apply-field{margin-bottom:.6rem}.cb-apply-field label{display:block;font-size:.82rem;font-weight:600;margin-bottom:.2rem;color:var(--dm-text)}.cb-apply-field input{width:100%;padding:.4rem .55rem;border:1px solid var(--dm-border);border-radius:4px;font-size:.88rem;background:var(--dm-surface);color:var(--dm-text)}.cb-apply-status{margin:.5rem 0;font-size:.85rem;min-height:1.2rem;color:var(--dm-text)}.cb-apply-status.cb-error{color:var(--dm-danger, #dc2626)}.cb-apply-buttons{display:flex;gap:.5rem;justify-content:flex-end;margin-top:.5rem}.cb-success-panel{padding:1rem 1.1rem;background:var(--dm-surface);border:1px solid var(--dm-success, #16a34a);border-radius:8px;color:var(--dm-text)}.cb-success-panel .cb-icon{color:var(--dm-success, #16a34a);font-size:1.4rem}.cb-success-summary{margin:.25rem 0 .5rem;padding-left:1.2rem;font-size:.86rem;line-height:1.6;color:var(--dm-text)}.cb-success-warning{margin:.5rem 0;padding:.5rem .75rem;background:var(--dm-background-alt);border-left:3px solid var(--dm-warning, #eab308);border-radius:0 4px 4px 0;font-size:.82rem;color:var(--dm-text-muted)}.cb-success-snippet-label{margin:0 0 .25rem;font-size:.82rem;font-weight:600;color:var(--dm-text)}.cb-success-actions{display:flex;gap:.5rem;margin-top:.5rem;flex-wrap:wrap}.cb-code{position:relative}.cb-code pre{background:var(--dm-background-alt);color:var(--dm-text);display:block;padding:.6rem 2.4rem .6rem .75rem;white-space:pre;overflow-x:auto;margin:0;border-radius:6px;font-size:.82em;font-family:monospace;line-height:1.45}.cb-code .cb-copy{position:absolute;top:.35rem;right:.35rem;background:none;border:none;cursor:pointer;opacity:.45;padding:.2rem;line-height:1;color:var(--dm-text)}.cb-code .cb-copy:hover{opacity:1}.cb-intro{padding:.75rem 1rem;border-left:3px solid var(--dm-primary);background:var(--dm-background-alt);border-radius:0 6px 6px 0}.cb-intro p{margin:0;font-size:.92rem;line-height:1.5;color:var(--dm-text)}.cb-tutorial-link{display:flex;align-items:center;gap:.6rem;padding:.65rem .85rem;border:1px solid var(--dm-border);border-radius:6px;text-decoration:none;color:var(--dm-text);font-size:.88rem;transition:border-color .12s}.cb-tutorial-link:hover{border-color:var(--dm-primary)}.cb-tutorial-link .cb-icon{flex-shrink:0;color:var(--dm-primary)}.cb-tutorial-link .cb-arrow{color:var(--dm-text-muted);font-size:1.1rem;margin-left:auto}.menu-tree-row{display:grid;grid-template-columns:16px 1.4fr 1.4fr .9fr .7fr .9fr repeat(6,auto);gap:6px;align-items:center;padding:4px 0;transition:opacity .15s ease}.menu-tree-row--hidden{opacity:.5}.menu-tree-grip{cursor:grab;color:var(--dm-text-muted)}.menu-tree{padding:8px 0}.menu-tree-row--separator{display:grid;grid-template-columns:16px minmax(80px,1fr) repeat(7,auto);gap:6px;align-items:center;padding:2px 0;opacity:.85}.menu-tree-row--separator:hover{opacity:1}.menu-tree-row--separator .me-sep-line{display:flex;align-items:center;gap:10px;width:100%}.menu-tree-row--separator .me-sep-line:before,.menu-tree-row--separator .me-sep-line:after{content:"";flex:1;border-top:1px dashed var(--dm-border, rgba(128, 128, 128, .55))}.menu-tree-row--separator .me-sep-label{font-size:.66rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--dm-text-muted, #8a8a8a);white-space:nowrap}.dm-admin-sidebar .sidebar-divider{border:0;border-top:1px solid var(--dm-border, rgba(128, 128, 128, .5));margin:.5rem .75rem;opacity:.8}.dm-admin-sidebar .sidebar-spacer--xs{height:var(--dm-space-1, .25rem)}.dm-admin-sidebar .sidebar-spacer--sm{height:var(--dm-space-2, .5rem)}.dm-admin-sidebar .sidebar-spacer--md{height:var(--dm-space-4, 1rem)}.dm-admin-sidebar .sidebar-spacer--lg{height:var(--dm-space-6, 1.5rem)}.dm-admin-sidebar .sidebar-spacer--xl{height:var(--dm-space-10, 2.5rem)}.dm-admin-sidebar .sidebar-spacer--flex{flex:1 1 auto;min-height:var(--dm-space-4, 1rem)}.dm-admin-sidebar--has-flex-spacer{display:flex;flex-direction:column;min-height:100%}.menu-tree-row--spacer{display:grid;grid-template-columns:16px minmax(80px,1fr) repeat(7,auto);gap:6px;align-items:center;padding:2px 0;opacity:.85}.menu-tree-row--spacer:hover{opacity:1}.menu-tree-row--spacer .me-spacer-line{display:flex;align-items:center;gap:8px;width:100%}.menu-tree-row--spacer .me-spacer-line:before,.menu-tree-row--spacer .me-spacer-line:after{content:"";flex:1;border-top:1px dotted var(--dm-border, rgba(128, 128, 128, .55))}.menu-tree-row--spacer .me-spacer-label{font-size:.66rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--dm-text-muted, #8a8a8a);white-space:nowrap}.menu-tree-row--spacer .me-spacer-size{width:auto;min-width:5.5rem;height:26px;padding:0 .35rem;font-size:.75rem}.menu-tree-row .me-icon-wrap{display:flex;gap:2px}.menu-tree-row .me-icon-wrap .me-icon{flex:1;min-width:0}.menu-tree-row .me-icon-wrap .me-icon-pick{flex-shrink:0;padding:.2rem .45rem;font-size:.85em}.dm-admin-sidebar{display:flex;flex-direction:column;gap:2px;padding:8px 0;font-size:.875rem}.dm-admin-sidebar details{margin:0}.dm-admin-sidebar details>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:.5rem;padding:.4rem .75rem;border-radius:4px;user-select:none;font-size:.78rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--dm-text-muted, #888)}.dm-admin-sidebar details details>summary{font-size:.875rem;font-weight:500;text-transform:none;letter-spacing:0;color:inherit}.dm-admin-sidebar .dm-menu-badge{font-size:.7rem;font-weight:600}.dm-admin-sidebar details>summary::-webkit-details-marker{display:none}.dm-admin-sidebar details>summary:after{content:"\25b8";margin-left:auto;transition:transform .15s ease;color:var(--dm-text-muted, #999);font-size:.85em}.dm-admin-sidebar details[open]>summary:after{transform:rotate(90deg)}.dm-admin-sidebar details>div{padding-left:1rem;border-left:1px solid var(--dm-border-light, rgba(0, 0, 0, .08));margin-left:.95rem}.dm-admin-sidebar .sidebar-link{display:flex;align-items:center;gap:.5rem;padding:.35rem .75rem;text-decoration:none;color:var(--dm-text, inherit);border-radius:4px}.dm-admin-sidebar .sidebar-link:hover,.dm-admin-sidebar details>summary:hover{background:var(--dm-hover-bg, rgba(0, 0, 0, .05))}.dm-admin-sidebar .sidebar-link.active{background:var(--dm-active-bg, rgba(0, 0, 0, .08));font-weight:600}.dm-admin-sidebar .sidebar-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dm-admin-sidebar [data-icon]{flex-shrink:0}.dm-admin-sidebar-header{position:sticky;top:0;z-index:2;background:var(--dm-surface);display:flex;align-items:center;gap:.5rem;padding:.65rem .85rem;margin:0 0 .5rem;font-weight:600;font-size:.95rem;letter-spacing:.01em;color:var(--dm-text, inherit);border-bottom:1px solid var(--dm-border-light, rgba(0, 0, 0, .08))}.dm-admin-sidebar--dark .dm-admin-sidebar-header{background:#1a1d24}.dm-admin-sidebar--light .dm-admin-sidebar-header{background:#fff}.dm-admin-sidebar-header [data-icon]{color:var(--dm-text-muted, #999)}.dm-admin-sidebar .sidebar-badge{margin-left:auto;padding:0 .4rem;min-width:1.25rem;height:1.1rem;line-height:1.1rem;text-align:center;font-size:.7rem;font-weight:600;border-radius:999px;background:var(--dm-active-bg, rgba(0, 0, 0, .12));color:var(--dm-text-muted, #888)}.dm-admin-sidebar .sidebar-link.active .sidebar-badge{background:var(--dm-primary, #4a86e8);color:var(--dm-primary-text, #fff)}.dm-admin-sidebar .sidebar-children{overflow:hidden;transition:max-height .22s ease,opacity .18s ease}.dm-admin-sidebar details:not([open])>.sidebar-children{opacity:0}.dm-admin-sidebar{position:relative}.dm-admin-sidebar-handle{position:absolute;top:0;right:0;width:6px;height:100%;cursor:col-resize;background:transparent;z-index:10;transition:background .15s}.dm-admin-sidebar-handle:hover,.dm-admin-sidebar-handle:active{background:var(--dm-primary, #4a86e8);opacity:.35}.dm-admin-sidebar--dark{background:#1a1d24;color:#e3e6eb}.dm-admin-sidebar--dark .sidebar-link,.dm-admin-sidebar--dark summary{color:#e3e6eb}.dm-admin-sidebar--dark .sidebar-link:hover,.dm-admin-sidebar--dark details>summary:hover{background:#ffffff0f}.dm-admin-sidebar--dark .sidebar-link.active{background:#4a86e840}.dm-admin-sidebar--dark .sidebar-badge{background:#ffffff1f;color:#cbd0d8}.dm-admin-sidebar--light{background:#fff;color:#1a1d24;border-right:1px solid rgba(0,0,0,.08)}.dm-admin-sidebar--light .sidebar-link,.dm-admin-sidebar--light summary{color:#1a1d24}.dm-admin-sidebar--light .sidebar-link:hover,.dm-admin-sidebar--light details>summary:hover{background:#0000000a}.dm-admin-sidebar--light .sidebar-link.active{background:#4a86e826}.dm-admin-sidebar--transparent{background:transparent}.page-header{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;margin:0 0 1.25rem;padding-bottom:.75rem;border-bottom:1px solid var(--dm-border-light, rgba(0, 0, 0, .08))}.page-header h2{margin:0;display:flex;align-items:center;gap:.5rem;font-size:1.4rem;line-height:1.2}.page-header h2 [data-icon]{color:var(--dm-text-muted, #888)}.header-actions{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.card+.card,.card+p,p+.card,.card+.toolbar,.toolbar+.card,.card+.grid{margin-top:1rem}.toolbar{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin:0 0 1rem}.btn+.btn,.btn+a.btn,a.btn+.btn,a.btn+a.btn{margin-left:.35rem}[style*=inline-flex] .btn+.btn,[style*=inline-flex] .btn+a.btn,[style*=inline-flex] a.btn+.btn{margin-left:0}.card-body>[id$=-form],.card-body>form{margin:.25rem 0}.card-header{display:flex;align-items:center;gap:.5rem}.card-header h3{margin:0;font-size:1.05rem}.card-header [data-icon]{color:var(--dm-text-muted, #888)}.page-header+p.text-muted{margin:-.5rem 0 1rem}.dm-pe-grid{gap:.75rem 1rem}.dm-pe-grid .form-group{display:flex;flex-direction:column;gap:.25rem;margin:0}.dm-pe-grid .form-group.col-span-2{grid-column:1 / -1}.dm-pe-grid label{font-size:.85rem;font-weight:600;color:var(--dm-text, inherit)}.dm-pe-grid label .required{color:var(--dm-danger, #c33);margin-left:2px}.dm-pe-grid .form-hint{font-size:.75rem;color:var(--dm-text-muted, #888);margin-top:.1rem}.dm-pe-grid .form-input{width:100%;padding:.4rem .6rem}.dm-pe-grid textarea.form-input{resize:vertical;min-height:2.5rem}.dm-menu-badge{display:inline-block;margin-left:6px;padding:1px 7px;font-size:.7em;font-weight:700;line-height:1.4;border-radius:999px;background:var(--dm-text-muted);color:var(--dm-text-inverse, #fff);vertical-align:middle}.menu-tree-row--slim{display:grid;grid-template-columns:16px 1fr auto repeat(9,auto);gap:6px;align-items:center}.menu-tree-row--slim .me-row-label{font-weight:600}.menu-tree-row--slim .me-row-url{color:var(--dm-text-muted);font-size:.85em}.menu-tree-row.me-drop-above{box-shadow:inset 0 2px 0 0 var(--dm-primary)}.menu-tree-row.me-drop-below{box-shadow:inset 0 -2px 0 0 var(--dm-primary)}.menu-tree-row.me-drop-into{background:color-mix(in srgb,var(--dm-primary) 12%,transparent);box-shadow:inset 0 0 0 1px var(--dm-primary);border-radius:var(--dm-radius-sm, 4px)}.menu-tree-row[draggable=true] .menu-tree-grip{cursor:grabbing}.me-pages-picker{display:flex;gap:6px;align-items:center;margin-bottom:6px}.me-pages-picker .form-input{flex:1;min-width:0}.me-colour-control{display:flex;gap:6px;align-items:center}.me-colour-control .me-colour-hex{width:48px;height:32px;padding:2px;flex:none}.me-bind-summary{padding:.6rem .8rem;border:1px solid var(--dm-border, rgba(128, 128, 128, .35));border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-text) 4%,transparent);font-size:.9em}.me-bind-summary code{font-size:.9em}.me-item-form{display:flex;flex-direction:column;gap:10px;padding:4px 2px}.me-item-form .form-field{display:flex;flex-direction:column;gap:4px}.me-item-form .form-label{font-size:.85em;color:var(--dm-text-muted)}.me-form-section{margin:12px 0 2px;padding-top:10px;border-top:1px solid var(--dm-border-light, rgba(0,0,0,.15));font-size:.78em;text-transform:uppercase;letter-spacing:.06em;color:var(--dm-text-muted)}.me-item-form .me-form-section:first-child{margin-top:0;padding-top:0;border-top:0}.me-item-form .me-colour-hex{width:48px;height:32px;padding:2px}.me-item-form .me-field-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}.me-item-form .me-field-grid .form-field{margin:0}.me-item-form .me-icon-field{display:flex;align-items:center;gap:8px}.me-item-form .me-icon-field .form-input{flex:1}.me-item-form .me-icon-preview{width:24px;height:24px;flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;color:var(--dm-text-muted)}#admin-sidebar.dm-menu-floating{border:1px solid var(--dm-border);border-radius:var(--dm-radius-md, 8px);box-shadow:var(--dm-shadow-lg);overflow:hidden}@media(max-width:768px){#admin-sidebar.dm-menu-floating{position:static!important;inset:auto!important;max-height:none!important;transform:none!important}.dashboard-wrapper{padding-left:0!important;padding-right:0!important}}[data-logic-section]{border:1px solid var(--dm-border);border-radius:var(--dm-radius-md, 6px);padding:.9rem 1rem 1rem;margin-bottom:1rem;background:color-mix(in srgb,var(--dm-text) 3%,transparent)}[data-logic-section] .fb-logic-cond-row,[data-logic-section] .fb-logic-val-rule{padding:.55rem .65rem;border-radius:var(--dm-radius-sm, 4px);border:1px solid var(--dm-border);background:var(--dm-surface, transparent)}[data-logic-section] .fb-logic-cond-row:last-of-type,[data-logic-section] .fb-logic-val-rule:last-of-type{margin-bottom:.75rem}[data-logic-section]>.btn{margin-top:.15rem}[data-logic-section] textarea{min-height:5rem}.project-filter-notice{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;margin:0 0 1rem;padding:.6rem .85rem;border:1px solid var(--dm-border);border-left:3px solid var(--dm-info, var(--dm-primary));border-radius:var(--dm-radius-md, 6px);background:color-mix(in srgb,var(--dm-info, var(--dm-primary)) 6%,transparent);font-size:.85rem;color:var(--dm-text-muted)}.project-filter-notice a{color:var(--dm-primary);font-weight:600}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function isWithin(e,s,
|
|
1
|
+
export function isWithin(e,s,p){let t=e.find(i=>i.id===s);for(;t;){if(t.id===p)return!0;t=t.parentId?e.find(i=>i.id===t.parentId):null}return!1}export function applyDrop(e,s,p,t){if(s===p)return!1;const i=e.find(n=>n.id===s),r=e.find(n=>n.id===p);if(!i||!r||isWithin(e,p,s))return!1;const o=r.type==="separator"||r.type==="spacer",c=t==="into"&&o?"below":t;if(e.splice(e.indexOf(i),1),c==="into"){i.parentId=r.id;let n=e.indexOf(r)+1;for(;n<e.length&&isWithin(e,e[n].id,r.id);)n++;e.splice(n,0,i)}else{i.parentId=r.parentId;const n=e.indexOf(r);if(c==="above")e.splice(n,0,i);else{let d=n+1;for(;d<e.length&&isWithin(e,e[d].id,r.id);)d++;e.splice(d,0,i)}}return!0}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as c}from"../api.js";import{colourToCss as
|
|
2
|
-
<summary${u}>${t} <span class="sidebar-text">${n}</span>${
|
|
1
|
+
import{api as c}from"../api.js";import{colourToCss as R,applyFloat as _}from"/public/js/menu-decor.mjs?v=20260829-float-panel";import{groupPluginItems as K,stripItemByUrl as G,insertFoldersBeforeSystem as O,pruneEmptySynthesisedFolders as V,MANAGE_PLUGINS_URL as Y}from"./sidebar-grouping.js";function g(e){return String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}const J=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu"}];function B(e,a){return a?!e||!e.length?!1:e.includes(a)?!0:["read","create","update","delete"].some(t=>e.includes(`${a}.${t}`)):!0}function I(e,a){const t=[];for(const n of e){if(n.hidden||n.permission&&!B(a,n.permission))continue;const i=Array.isArray(n.items)&&n.items.length?I(n.items,a):[];t.push({...n,items:i})}return t}function Q(e,a){if(!a||!a.length)return e;const t=(n,i)=>{for(const d of n){if((d.text||"").toLowerCase()===i.toLowerCase())return d;if(Array.isArray(d.items)){const r=t(d.items,i);if(r)return r}}return null};for(const n of a)if(n.parent){const i=t(e,n.parent);i?(i.items=Array.isArray(i.items)?i.items:[],i.items.push(n.item)):e.push(n.item)}else e.push(n.item);return e}async function Z(e){if(!B(e,"projects"))return[];let a=[];try{a=await c.projects.list()}catch{return[]}const t={};await Promise.all(a.map(async i=>{try{const d=await c.projects.artefacts(i.slug);t[i.slug]=d}catch{t[i.slug]={}}}));const n=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return a.map(i=>{const r="#/projects/"+encodeURIComponent(i.slug),u=t[i.slug]||{},f=[{text:"Overview",url:r,icon:i.icon||"folder"}];for(const p of n){const y=u[p.key],h=Array.isArray(y)?y.length:0;h<=0||f.push({text:`${p.text} (${h})`,url:r+p.path,icon:p.icon})}f.push({text:"Settings",url:r+"/settings",icon:"settings"});const m=i.enabled===!1;return{text:i.name||i.slug,icon:i.icon||"folder",items:f,...m&&{colour:"neutral",badge:{text:"off"}}}})}function ee(e,a){for(const t of e)if((t.text||"").toLowerCase()==="projects"){t.items=Array.isArray(t.items)?t.items.slice():[],t.items.push(...a);return}}function te(e,a){if(!e.url||!a)return"";const t=a[e.url];return t==null||t<=0?"":`<span class="sidebar-badge">${g(String(t))}</span>`}function ne(e){const a=(e.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),t=(e.url||"").match(/^#\/projects\/([^/]+)/);return`sidebar.expanded.${t?`project-${decodeURIComponent(t[1])}.`:""}${a}`}function N(e,a){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="spacer"){const m=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${g(m)}" aria-hidden="true"></div>`}const t=e.icon?`<span data-icon="${g(e.icon)}"></span>`:"",n=g(e.text||""),i=e.url?g(e.url):"",d=Array.isArray(e.items)&&e.items.length>0,r=e.colour?R(e.colour):"",u=r?` style="color:${g(r)}"`:"";let f="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const m=e.badge.variant?R(e.badge.variant):"";f=`<span class="dm-menu-badge"${m?` style="background:${g(m)};color:var(--dm-text-inverse,#fff)"`:""}>${g(String(e.badge.text))}</span>`}if(d){const m=ne(e),p=S.get(m)!==!1,y=e.items.map(h=>N(h,a)).join("");return`<details data-state-key="${g(m)}"${p?" open":""}>
|
|
2
|
+
<summary${u}>${t} <span class="sidebar-text">${n}</span>${f}</summary>
|
|
3
3
|
<div class="sidebar-children">${y}</div>
|
|
4
|
-
</details>`}return`<a href="${
|
|
4
|
+
</details>`}return`<a href="${i}" class="sidebar-link" data-url="${i}"${u}>${t} <span class="sidebar-text">${n}</span>${te(e,a)}${f}</a>`}async function se(){const e=h=>h.then(b=>Array.isArray(b)?b.length:Array.isArray(b?.entries)?b.entries.length:0).catch(()=>0),[a,t,n,i,d,r,u,f,m,p,y]=await Promise.all([e(c.pages.list()),e(c.media.list()),e(c.collections.list()),e(c.forms.list()),e(c.views.list()),e(c.actions.list()),e(c.blocks.list()),e(c.components.list()),e(c.users.list()),e(c.plugins.list()),c.system?.notifications?.unreadCount?.().then(h=>h?.count??0).catch(()=>0)??Promise.resolve(0)]);return{"#/pages":a,"#/media":t,"#/collections":n,"#/forms":i,"#/views":d,"#/actions":r,"#/blocks":u,"#/components":f,"#/users":m,"#/plugins":p,"#/system/notifications":y}}async function ie(){try{const e=await c.settings.get();return e?.adminBrand?.title||e?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:e,permissions:a}){const t=$(e).get(0);if(!t)return;let n,i=null,d=null,r=null,u=null;try{const s=await c.menus.get("admin-sidebar");n=Array.isArray(s?.items)?s.items:null,i=s?.variant||null,d=s?.position||null,r=s?.style||null,u=s?.float||null}catch{n=null}(!n||!n.length)&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),n=J.slice());let f=[];try{f=await H.get("/api/sidebar/registered-items")||[]}catch{}n=G(n,Y);const{toolsFolder:m,pluginsFolder:p,parented:y}=K(f);n=Q(n,y),n=O(n,[m,p]);const h=await Z(a);h.length&&ee(n,h),n=I(n,a),n=V(n);const[b,T]=await Promise.all([se().catch(()=>({})),ie()]),U=i?` dm-admin-sidebar--${g(i)}`:"";function j(s,l="px"){if(s==null)return s;const o=String(s).trim();return/^\d+(\.\d+)?$/.test(o)?`${o}${l}`:o}let E="";if(r){const s=[];r.fontFamily&&s.push(`font-family: ${r.fontFamily}, sans-serif`),r.fontSize&&s.push(`font-size: ${j(r.fontSize)}`),r.fontWeight&&s.push(`font-weight: ${r.fontWeight}`),r.letterSpacing&&s.push(`letter-spacing: ${j(r.letterSpacing,"em")}`);const l=[];if(s.length&&l.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${s.join("; ")} }`),r.iconSize){const o=j(r.iconSize);l.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${o} !important; height: ${o} !important; }`)}l.length&&(E=`<style data-admin-sidebar-style>${l.join(" ")}</style>`)}const q=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span> ${g(T)}</div>`,X=n.some(s=>s&&s.type==="spacer"&&s.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",W=`${E}<nav class="dm-admin-sidebar${U}${X}">${q}${n.map(s=>N(s,b)).join("")}</nav>`,L=document.createRange();L.selectNodeContents(t);const D=L.createContextualFragment(W);t.replaceChildren(D),Domma.icons.scan(t);const v=Number.parseInt(S.get("sidebar.width"),10);Number.isFinite(v)&&v>=180&&v<=480&&(t.style.width=v+"px");const x=document.querySelector(".dashboard-wrapper");if(["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(s=>{t.style[s]=""}),t.classList.remove("dm-menu-floating"),x&&(x.style.paddingLeft="",x.style.paddingRight=""),d==="floating"){_(t,{position:"floating",float:u||{}});const l=u&&u.anchor||"TL",o=Number.isFinite(Number(u?.offsetX))?Number(u.offsetX):16,w=t.getBoundingClientRect().width||v||260;if(x){const C=Math.round(w+o*2)+"px";l==="R"||l==="TR"||l==="BR"?x.style.paddingRight=C:x.style.paddingLeft=C}}const A=document.createElement("div");A.className="dm-admin-sidebar-handle",A.setAttribute("aria-label","Resize sidebar"),t.appendChild(A),t.querySelectorAll("details").forEach(s=>{s.addEventListener("toggle",function(){const l=this.getAttribute("data-state-key");l&&S.set(l,this.open);const o=this.querySelector(":scope > .sidebar-children");if(o)if(this.open){const w=o.scrollHeight;o.style.maxHeight="0",requestAnimationFrame(()=>{o.style.maxHeight=w+"px",o.addEventListener("transitionend",function C(){o.style.maxHeight="none",o.removeEventListener("transitionend",C)})})}else{const w=o.scrollHeight;o.style.maxHeight=w+"px",requestAnimationFrame(()=>{o.style.maxHeight="0"})}})});let k=!1,P=0,F=0;A.addEventListener("mousedown",s=>{k=!0,P=s.clientX,F=t.getBoundingClientRect().width,document.body.style.cursor="col-resize",document.body.style.userSelect="none",s.preventDefault()}),document.addEventListener("mousemove",s=>{if(!k)return;const l=Math.max(180,Math.min(480,F+(s.clientX-P)));t.style.width=l+"px"}),document.addEventListener("mouseup",()=>{k&&(k=!1,document.body.style.cursor="",document.body.style.userSelect="",S.set("sidebar.width",parseInt(t.style.width,10)))});function z(){const s=location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${s}"]`).addClass("active")}z(),M.subscribe("router:afterChange",z)}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
<div class="toolbar">
|
|
21
21
|
<button class="btn btn-sm btn-secondary" id="me-add-item"><span data-icon="plus"></span> Add item</button>
|
|
22
22
|
<button class="btn btn-sm btn-ghost" id="me-add-separator"><span data-icon="minus"></span> Add separator</button>
|
|
23
|
+
<button class="btn btn-sm btn-ghost" id="me-add-spacer"><span data-icon="move"></span> Add spacer</button>
|
|
23
24
|
</div>
|
|
24
25
|
<div id="me-items-list" class="menu-tree"></div>
|
|
25
26
|
</div>
|
|
@@ -126,6 +127,31 @@
|
|
|
126
127
|
<input type="number" id="me-float-opacity" name="floatOpacity" class="form-input" placeholder="100" min="20" max="100">
|
|
127
128
|
<small class="form-hint">Below 100 the panel surface goes translucent with a blur behind it. Text stays fully opaque.</small>
|
|
128
129
|
</div>
|
|
130
|
+
<div class="form-group dm-float-field" style="display:none">
|
|
131
|
+
<label for="me-float-title">Panel title</label>
|
|
132
|
+
<input type="text" id="me-float-title" name="floatTitle" class="form-input" placeholder="menu name" maxlength="60">
|
|
133
|
+
<small class="form-hint">Shown in the panel's bar, next to the drag handle and hamburger. Blank uses the menu's own name.</small>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="form-group dm-float-field" style="display:none">
|
|
136
|
+
<label>Panel behaviour</label>
|
|
137
|
+
<label style="display:flex;align-items:center;gap:.5rem;font-weight:400;cursor:pointer">
|
|
138
|
+
<input type="checkbox" id="me-float-collapsible" name="floatCollapsible" style="width:auto;margin:0">
|
|
139
|
+
Collapsible - a hamburger button folds the panel away and back
|
|
140
|
+
</label>
|
|
141
|
+
<label style="display:flex;align-items:center;gap:.5rem;font-weight:400;cursor:pointer;margin-left:1.5rem" class="dm-float-collapsed-field">
|
|
142
|
+
<input type="checkbox" id="me-float-collapsed" name="floatCollapsed" style="width:auto;margin:0">
|
|
143
|
+
Start collapsed
|
|
144
|
+
</label>
|
|
145
|
+
<label style="display:flex;align-items:center;gap:.5rem;font-weight:400;cursor:pointer">
|
|
146
|
+
<input type="checkbox" id="me-float-movable" name="floatMovable" style="width:auto;margin:0">
|
|
147
|
+
Movable - visitors drag the panel by its bar
|
|
148
|
+
</label>
|
|
149
|
+
<label style="display:flex;align-items:center;gap:.5rem;font-weight:400;cursor:pointer">
|
|
150
|
+
<input type="checkbox" id="me-float-resizable" name="floatResizable" style="width:auto;margin:0">
|
|
151
|
+
Resizable - visitors drag the panel's bottom-right corner
|
|
152
|
+
</label>
|
|
153
|
+
<small class="form-hint">Each visitor's own position, size and collapsed state are remembered in their browser; the settings above stay the starting point. Double-clicking the bar resets a panel. Below 768px panels return to the page flow, so only collapsing applies there.</small>
|
|
154
|
+
</div>
|
|
129
155
|
<div class="form-group">
|
|
130
156
|
<label>Open on hover</label>
|
|
131
157
|
<label style="display:flex;align-items:center;gap:.5rem;font-weight:400;cursor:pointer">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{api as S}from"../api.js";import{colourToCss as
|
|
2
|
-
<div class="menu-tree-row menu-tree-row--separator" data-id="${
|
|
1
|
+
import{api as S}from"../api.js";import{colourToCss as ct}from"/public/js/menu-decor.mjs?v=20260829-float-panel";import{openIconPicker as rt}from"../lib/card-builder.js";import{applyDrop as pt}from"../lib/menu-tree.js";let mt=1;const D=()=>"i_"+mt++,ft=760;function et(t,d=null,s=[]){for(const o of t||[]){const n=D();if(o&&o.type==="separator"){s.push({id:n,parentId:d,type:"separator"});continue}if(o&&o.type==="spacer"){s.push({id:n,parentId:d,type:"spacer",size:o.size||"md"});continue}s.push({id:n,parentId:d,text:o.text||"",url:o.url||"",icon:o.icon||"",visibility:o.visibility||"",permission:o.permission||"",hidden:!!o.hidden,bundled:!!o.bundled,badge:o.badge?{...o.badge}:null,pill:o.pill?{...o.pill}:null,colour:o.colour||""}),Array.isArray(o.items)&&o.items.length&&et(o.items,n,s)}return s}function ut(t){const d=new Map;for(const o of t)d.has(o.parentId)||d.set(o.parentId,[]),d.get(o.parentId).push(o);function s(o){return(d.get(o)||[]).map(n=>{if(n.type==="separator")return{type:"separator"};if(n.type==="spacer")return{type:"spacer",...n.size&&n.size!=="md"&&{size:n.size}};const i=s(n.id);return{text:(n.text||"").trim(),url:(n.url||"").trim(),...n.icon&&{icon:n.icon.trim()},...n.visibility&&{visibility:n.visibility},...n.permission&&{permission:n.permission},...n.hidden&&{hidden:!0},...n.bundled&&{bundled:!0},...n.badge&&(n.badge.text||n.badge.countFrom)?{badge:{...n.badge.text&&{text:n.badge.text},...n.badge.countFrom&&{countFrom:n.badge.countFrom},...n.badge.variant&&{variant:n.badge.variant}}}:{},...n.pill&&n.pill.variant?{pill:{style:n.pill.style||"filled",variant:n.pill.variant}}:{},...n.colour&&{colour:n.colour},...i.length&&{items:i}}})}return s(null)}function b(t){return String(t||"").replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<")}function bt(t,d){let s=0,o=d.find(n=>n.id===t);for(;o&&o.parentId;)s++,o=d.find(n=>n.id===o.parentId);return s}function nt(t,d){const{top:s,height:o}=t.getBoundingClientRect(),n=o?(d-s)/o:.5;return n<.25?"above":n>.75?"below":"into"}const ot=["me-drop-above","me-drop-below","me-drop-into"];function G(t){t.get(0).querySelectorAll(".menu-tree-row").forEach(d=>d.classList.remove(...ot))}function gt(t,d,s){let o=null;t.get(0).querySelectorAll(".menu-tree-row").forEach(n=>{const i=n.querySelector(".menu-tree-grip");n.draggable=!1,i&&(i.addEventListener("mousedown",()=>{n.draggable=!0}),i.addEventListener("mouseup",()=>{n.draggable=!1})),n.addEventListener("dragstart",r=>{o=n.dataset.id,n.style.opacity="0.45",r.dataTransfer.effectAllowed="move";try{r.dataTransfer.setData("text/plain",o)}catch{}}),n.addEventListener("dragend",()=>{n.style.opacity="",n.draggable=!1,o=null,G(t)}),n.addEventListener("dragover",r=>{if(!o||o===n.dataset.id)return;r.preventDefault(),r.dataTransfer.dropEffect="move";const w=nt(n,r.clientY);G(t),n.classList.add(`me-drop-${w}`)}),n.addEventListener("dragleave",()=>n.classList.remove(...ot)),n.addEventListener("drop",r=>{r.preventDefault();const w=n.dataset.id,k=nt(n,r.clientY);if(G(t),!o)return;const y=pt(d(),o,w,k);o=null,y&&s()})})}function vt(t,d,s=()=>""){t.html("");function o(n){for(const i of d.filter(r=>r.parentId===n)){const r=bt(i.id,d);if(i.type==="separator"){t.append(`
|
|
2
|
+
<div class="menu-tree-row menu-tree-row--separator" data-id="${i.id}" style="margin-left:${r*28}px">
|
|
3
3
|
<span class="menu-tree-grip" data-icon="move"></span>
|
|
4
4
|
<span class="me-sep-line"><span class="me-sep-label">Separator</span></span>
|
|
5
5
|
<button class="btn btn-sm btn-ghost me-ins-item" data-tooltip="Add item below"><span data-icon="plus"></span></button>
|
|
@@ -10,23 +10,38 @@ import{api as S}from"../api.js";import{colourToCss as rt}from"/public/js/menu-de
|
|
|
10
10
|
<button class="btn btn-sm btn-ghost me-down" data-tooltip="Move down"><span data-icon="arrow-down"></span></button>
|
|
11
11
|
<button class="btn btn-sm btn-ghost me-remove" data-tooltip="Remove"><span data-icon="trash"></span></button>
|
|
12
12
|
</div>
|
|
13
|
-
`);continue}const
|
|
14
|
-
|
|
13
|
+
`);continue}if(i.type==="spacer"){const P=["xs","sm","md","lg","xl","flex"].map(O=>`<option value="${O}"${(i.size||"md")===O?" selected":""}>${O==="flex"?"flexible":O}</option>`).join("");t.append(`
|
|
14
|
+
<div class="menu-tree-row menu-tree-row--spacer" data-id="${i.id}" style="margin-left:${r*28}px">
|
|
15
|
+
<span class="menu-tree-grip" data-icon="move"></span>
|
|
16
|
+
<span class="me-spacer-line">
|
|
17
|
+
<span class="me-spacer-label">Spacer</span>
|
|
18
|
+
<select class="form-input me-spacer-size" data-tooltip="Spacer size - flexible fills the leftover room">${P}</select>
|
|
19
|
+
</span>
|
|
20
|
+
<button class="btn btn-sm btn-ghost me-ins-item" data-tooltip="Add item below"><span data-icon="plus"></span></button>
|
|
21
|
+
<button class="btn btn-sm btn-ghost me-ins-sep" data-tooltip="Add separator below"><span data-icon="minus"></span></button>
|
|
22
|
+
<button class="btn btn-sm btn-ghost me-outdent" data-tooltip="Outdent"><span data-icon="chevron-left"></span></button>
|
|
23
|
+
<button class="btn btn-sm btn-ghost me-indent" data-tooltip="Indent"><span data-icon="chevron-right"></span></button>
|
|
24
|
+
<button class="btn btn-sm btn-ghost me-up" data-tooltip="Move up"><span data-icon="arrow-up"></span></button>
|
|
25
|
+
<button class="btn btn-sm btn-ghost me-down" data-tooltip="Move down"><span data-icon="arrow-down"></span></button>
|
|
26
|
+
<button class="btn btn-sm btn-ghost me-remove" data-tooltip="Remove"><span data-icon="trash"></span></button>
|
|
27
|
+
</div>
|
|
28
|
+
`);continue}const w=i.badge&&(i.badge.text||i.badge.countFrom)?`<span class="dm-menu-badge">${b(String(i.badge.text||"#"))}</span>`:"",k=ct(i.colour),y=k?` style="color:${b(k)}"`:"";t.append(`
|
|
29
|
+
<div class="menu-tree-row menu-tree-row--slim${i.hidden?" menu-tree-row--hidden":""}" data-id="${i.id}" style="margin-left:${r*28}px">
|
|
15
30
|
<span class="menu-tree-grip" data-icon="move"></span>
|
|
16
|
-
<span class="me-row-label"${
|
|
17
|
-
<span class="me-row-url">${b(
|
|
31
|
+
<span class="me-row-label"${y}>${b(i.text||"(untitled)")}${w}</span>
|
|
32
|
+
<span class="me-row-url">${b(i.url||"")}</span>
|
|
18
33
|
<button class="btn btn-sm btn-ghost me-ins-item" data-tooltip="Add item below"><span data-icon="plus"></span></button>
|
|
19
34
|
<button class="btn btn-sm btn-ghost me-ins-sep" data-tooltip="Add separator below"><span data-icon="minus"></span></button>
|
|
20
35
|
<button class="btn btn-sm btn-ghost me-outdent" data-tooltip="Outdent"><span data-icon="chevron-left"></span></button>
|
|
21
36
|
<button class="btn btn-sm btn-ghost me-indent" data-tooltip="Indent"><span data-icon="chevron-right"></span></button>
|
|
22
37
|
<button class="btn btn-sm btn-ghost me-up" data-tooltip="Move up"><span data-icon="arrow-up"></span></button>
|
|
23
38
|
<button class="btn btn-sm btn-ghost me-down" data-tooltip="Move down"><span data-icon="arrow-down"></span></button>
|
|
24
|
-
<button class="btn btn-sm btn-ghost me-hidden ${
|
|
39
|
+
<button class="btn btn-sm btn-ghost me-hidden ${i.hidden?"active":""}" data-tooltip="${i.hidden?"Show":"Hide"}"><span data-icon="eye-off"></span></button>
|
|
25
40
|
<button class="btn btn-sm btn-secondary me-edit" data-tooltip="Edit"><span data-icon="edit"></span></button>
|
|
26
41
|
<button class="btn btn-sm btn-ghost me-remove" data-tooltip="Remove"><span data-icon="trash"></span></button>
|
|
27
42
|
</div>
|
|
28
|
-
`),i(o.id)}}i(null),Domma.icons.scan(t.get(0)),t.get(0).querySelectorAll("[data-tooltip]").forEach(n=>{E.tooltip(n,{content:n.getAttribute("data-tooltip"),position:"top"})})}function F(t,l){t.find(".menu-tree-row").each(function(){const a=$(this).data("id"),i=l.find(k=>k.id===a);if(!i||i.type==="separator")return;const n=$(this).find(".me-text");n.length&&(i.text=n.val());const o=$(this).find(".me-url");o.length&&(i.url=o.val());const c=$(this).find(".me-icon");c.length&&(i.icon=c.val());const h=$(this).find(".me-vis");h.length&&(i.visibility=h.val());const x=$(this).find(".me-perm");x.length&&(i.permission=x.val())})}function _t(t,l){const a={};for(const i of l){const n=t.querySelector(`[name="${i}"]`);n&&(a[i]=n.type==="checkbox"?n.checked:n.value)}return a}function g(t,l={},...a){const i=document.createElement(t);Object.assign(i,l);for(const n of a)n!=null&&i.append(n);return i}function j(t,l,a){const i=g("label",{className:"form-field"},g("span",{className:"form-label",textContent:t}),l);return a&&i.append(g("small",{className:"form-hint",textContent:a,style:"display:block;font-size:.78em;opacity:.7;margin-top:.25rem;line-height:1.35"})),i}function ht(t,l){const a=[["","- Everyone -"],["public","Public - anyone, incl. logged-out"],...(l||[]).map(n=>[n.name,`${n.label||n.name} and above`]),["private","Private - super-admin only"]];t&&!a.some(([n])=>n===t)&&a.push([t,`${t} (custom)`]);const i=B(a,t||"");return i.name="visibility",i}function B(t,l){const a=g("select",{className:"form-select"});return t.forEach(([i,n])=>a.add(new Option(n,i))),a.value=l??"",a}function K(t,l){const a=typeof l=="string"&&l.startsWith("#"),i=B([["","- none -"],["primary","primary"],["success","success"],["danger","danger"],["warning","warning"],["info","info"],["neutral","neutral"],["__custom__","Custom\u2026"]],a?"__custom__":l||""),n=g("input",{type:"color",className:"form-input me-colour-hex",value:a?l:"#000000"});n.style.display=a?"":"none",i.addEventListener("change",()=>{n.style.display=i.value==="__custom__"?"":"none"});const o=j(t,i);return o.append(n),o._read=()=>i.value==="__custom__"?n.value:i.value,o}function yt(t,l){const a=g("input",{className:"form-input me-icon",name:"icon",value:l||""}),i=g("span",{className:"me-icon-preview"});l&&i.setAttribute("data-icon",l);const n=g("button",{type:"button",className:"btn btn-sm btn-ghost",textContent:"Browse\u2026"}),o=c=>{c?i.setAttribute("data-icon",c):i.removeAttribute("data-icon"),Domma.icons.scan(i.parentNode||i)};return n.addEventListener("click",()=>ct(c=>{a.value=c,o(c)})),a.addEventListener("input",()=>o(a.value.trim())),j(t,g("div",{className:"me-icon-field"},a,n,i))}function Z(...t){return g("div",{className:"me-field-grid"},...t)}async function wt(t,l,a,i,n){const o=f=>g("h4",{className:"me-form-section",textContent:f}),c=j("Label",g("input",{className:"form-input",name:"text",value:t.text||""}),"The text shown for this item."),h=j("URL",g("input",{className:"form-input",name:"url",value:t.url||""}),"Where it links. Must start with /, #, https://, or mailto: . Leave blank for a parent that only groups sub-items."),x=yt("Icon",t.icon||""),k=j("Visibility",ht(t.visibility||"",i),"Who can see this item, by role seniority. Picking a role shows it to that role and anyone more senior. Blank = everyone."),D=B([["","- no permission gate -"],...(l||[]).map(f=>[f.key,f.label||f.key])],t.permission||"");D.name="permission";const Y=j("Permission",D,"Hide unless the viewer holds this specific permission. Independent of Visibility - both must pass."),J=j("Badge text",g("input",{className:"form-input",name:"badgeText",value:t.badge?.text||""})),X=B([["","- none -"],...(a||[]).map(f=>[f.slug,f.title||f.slug])],t.badge?.countFrom||"");X.name="badgeCount";const V=j("Badge count from",X,"Show a live count of this collection\u2019s entries as the badge."),U=K("Badge colour",t.badge?.variant||""),d=B([["link","Link"],["pill","Pill"]],t.pill?"pill":"link");d.name="renderAs";const w=j("Render as",d,"Pill draws the item as a rounded chip. Pill styling applies to top-level navbar items."),v=j("Pill style",B([["filled","Filled"],["outline","Outline"]],t.pill?.style||"filled"));v.querySelector("select").name="pillStyle";const z=K("Pill colour",t.pill?.variant||""),C=g("div",{className:"me-pill-wrap"},Z(v,z));C.style.display=t.pill?"":"none",d.addEventListener("change",()=>{C.style.display=d.value==="pill"?"":"none"});const R=K("Text colour",t.colour||""),W=g("button",{className:"btn btn-primary",textContent:"Apply"}),A=g("div",{className:"me-item-form"},o("Link"),c,h,x,o("Access"),Z(k,Y),o("Badge"),Z(J,U),V,o("Appearance"),w,C,R,W),_=E.slideover({title:"Edit menu item",size:"lg",position:"right"});_.element.appendChild(A),_.open();const T=_.element.closest(".dm-slideover")||_.element.querySelector(".dm-slideover")||_.element;T.style.setProperty("width",ut+"px","important"),T.style.setProperty("max-width","95vw","important"),Domma.icons.scan(A);const I=f=>A.querySelector(`[name="${f}"]`)?.value||"";W.addEventListener("click",()=>{t.text=I("text"),t.url=I("url"),t.icon=I("icon"),t.visibility=I("visibility"),t.permission=I("permission");const f=I("badgeText"),L=I("badgeCount"),N=U._read();if(t.badge=f||L?{...f&&{text:f},...L&&{countFrom:L},...N&&{variant:N}}:null,I("renderAs")==="pill"){const P=z._read();t.pill={style:I("pillStyle")||"filled",...P&&{variant:P}}}else t.pill=null;t.colour=R._read(),_.close(),n()})}export const menuEditorView={templateUrl:"/admin/js/templates/menu-editor.html",async onMount(t){const l=location.hash.match(/^#\/menus\/edit\/(.+)$/),a=l?decodeURIComponent(l[1]):null,i=!a,n=E.loader(t.get(0),{type:"dots"});let o,c,h,x,k,D,Y;try{if(i)o={slug:"",name:"",description:"",items:[]};else try{o=await S.menus.get(a)}catch(e){E.toast(`Failed to load menu: ${e.message||e}`,{type:"error"}),location.hash="#/menus";return}c=await S.projects.list().catch(()=>[]),h=await H.get("/api/auth/permissions-registry").catch(()=>({resources:[]})),x=await S.collections.list().catch(()=>[]),k=await S.get("/collections/roles/entries?limit=100").catch(()=>null),D=await S.menuLocations.registry().catch(()=>[]),Y=await S.pages.list().catch(()=>[])}finally{n.destroy()}const X=(Array.isArray(k)?k:k?.entries||[]).map(e=>({name:e.data?.name,label:e.data?.label,level:e.data?.level??99})).filter(e=>e.name).sort((e,s)=>s.level-e.level),V='<option value="">- no permission gate -</option>',U=e=>V+(h.resources||[]).map(s=>{const r=(e||"")===s.key?" selected":"";return`<option value="${s.key}"${r}>${s.label||s.key}</option>`}).join("");t.find("#me-title").text(i?"New menu":`Edit "${o.slug}"`);let d=et(o.items);const w=t.find("#me-items-list");function v(){vt(w,d,U),gt(w,()=>d,v)}v(),t.find("#me-add-item").on("click",()=>{F(w,d),d.push({id:q(),parentId:null,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1}),v()}),t.find("#me-add-separator").on("click",()=>{F(w,d),d.push({id:q(),parentId:null,type:"separator"}),v()});function z(e){return function(){F(w,d);const s=$(this).closest(".menu-tree-row").data("id"),r=d.findIndex(p=>p.id===s);if(r===-1)return;const m=d[r],u=e==="separator"?{id:q(),parentId:m.parentId,type:"separator"}:{id:q(),parentId:m.parentId,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1};d.splice(r+1,0,u),v()}}t.off("click",".me-ins-item").on("click",".me-ins-item",z("item")),t.off("click",".me-ins-sep").on("click",".me-ins-sep",z("separator")),t.off("click",".me-remove").on("click",".me-remove",function(){const e=$(this).closest(".menu-tree-row").data("id");d=d.filter(s=>s.id!==e&&s.parentId!==e),v()}),t.off("click",".me-indent").on("click",".me-indent",function(){F(w,d);const e=$(this).closest(".menu-tree-row").data("id"),s=d.find(u=>u.id===e);if(!s)return;const r=d.filter(u=>u.parentId===s.parentId),m=r.findIndex(u=>u.id===e);m<=0||r[m-1].type!=="separator"&&(s.parentId=r[m-1].id,v())}),t.off("click",".me-outdent").on("click",".me-outdent",function(){F(w,d);const e=$(this).closest(".menu-tree-row").data("id"),s=d.find(m=>m.id===e);if(!s||s.parentId==null)return;const r=d.find(m=>m.id===s.parentId);s.parentId=r?r.parentId:null,v()}),t.off("click",".me-up").on("click",".me-up",function(){F(w,d);const e=$(this).closest(".menu-tree-row").data("id"),s=d.filter(p=>p.parentId===d.find(y=>y.id===e)?.parentId),r=s.findIndex(p=>p.id===e);if(r<=0)return;const m=d.indexOf(s[r]),u=d.indexOf(s[r-1]);[d[m],d[u]]=[d[u],d[m]],v()}),t.off("click",".me-down").on("click",".me-down",function(){F(w,d);const e=$(this).closest(".menu-tree-row").data("id"),s=d.filter(p=>p.parentId===d.find(y=>y.id===e)?.parentId),r=s.findIndex(p=>p.id===e);if(r===-1||r>=s.length-1)return;const m=d.indexOf(s[r]),u=d.indexOf(s[r+1]);[d[m],d[u]]=[d[u],d[m]],v()}),t.off("click",".me-hidden").on("click",".me-hidden",function(){F(w,d);const e=$(this).closest(".menu-tree-row").data("id"),s=d.find(r=>r.id===e);s&&(s.hidden=!s.hidden),v()}),t.off("click",".me-edit").on("click",".me-edit",function(){const e=$(this).closest(".menu-tree-row").data("id"),s=d.find(r=>r.id===e);!s||s.type==="separator"||wt(s,h.resources||[],x,X,()=>v())}),E.tabs(t.find("#me-tabs").get(0)),t.find("#me-variant").val(o.variant||""),t.find("#me-position").val(o.position||"");const C=t.find("#me-appearOnHover").get(0);C&&(C.checked=o.appearOnHover===!0),t.find("#me-float-anchor").val(o.float?.anchor||"TL"),t.find("#me-float-offsetX").val(o.float?.offsetX??""),t.find("#me-float-offsetY").val(o.float?.offsetY??""),t.find("#me-float-width").val(o.float?.width??""),t.find("#me-float-radius").val(o.float?.radius??""),t.find("#me-float-shadow").val(o.float?.shadow||""),t.find("#me-float-opacity").val(o.float?.opacity??"");const R=o.float?.accent||"",W=R.startsWith("#"),A=t.find("#me-float-accent"),_=t.find("#me-float-accent-hex");A.val(W?"__custom__":R),W&&_.val(R);const T=()=>{_.get(0).style.display=A.val()==="__custom__"?"":"none"};T(),A.on("change",T);const I=()=>A.val()==="__custom__"?_.val():A.val(),f=()=>{const e=t.find("#me-position").val()==="floating";t.find(".dm-float-field").each(function(){this.style.display=e?"":"none"}),e&&T();const s=t.find("#me-orientation").val()==="vertical";t.find(".dm-vertical-field").each(function(){this.style.display=s?"":"none"})};t.find("#me-orientation").val(o.orientation||""),t.find("#me-side").val(o.side||"left"),f(),t.find("#me-position").on("change",f),t.find("#me-orientation").on("change",f),t.find("#me-fontFamily").val(o.style?.fontFamily||""),t.find("#me-fontSize").val(o.style?.fontSize||""),t.find("#me-fontWeight").val(o.style?.fontWeight||""),t.find("#me-letterSpacing").val(o.style?.letterSpacing||""),t.find("#me-iconSize").val(o.style?.iconSize||"");const L=t.find("#me-bind-slot"),N=t.find("#me-bind-projects"),P=t.find("#me-bind-pages");L.html('<option value="">- not bound -</option>'+(D||[]).map(e=>`<option value="${b(e.slot)}">${b(e.label||e.slot)} (${b(e.slot)})</option>`).join("")),N.html(c.map(e=>`<option value="${b(e.slug)}">${b(e.name||e.slug)}</option>`).join("")),L.val(o.binding?.slot||"");const ot=new Set(o.binding?.projects||[]);Array.from(N.get(0).options).forEach(e=>{e.selected=ot.has(e.value)}),P.val((o.binding?.pages||[]).join(`
|
|
29
|
-
`)),t.find("#me-bind-page-pick").html('<option value="">- pick a page -</option>'+
|
|
30
|
-
`).map(
|
|
31
|
-
`)),
|
|
32
|
-
`).map(
|
|
43
|
+
`),o(i.id)}}o(null),Domma.icons.scan(t.get(0)),t.get(0).querySelectorAll("[data-tooltip]").forEach(n=>{E.tooltip(n,{content:n.getAttribute("data-tooltip"),position:"top"})})}function j(t,d){t.find(".menu-tree-row").each(function(){const s=$(this).data("id"),o=d.find(y=>y.id===s);if(!o||o.type==="separator")return;if(o.type==="spacer"){const y=$(this).find(".me-spacer-size");y.length&&(o.size=y.val());return}const n=$(this).find(".me-text");n.length&&(o.text=n.val());const i=$(this).find(".me-url");i.length&&(o.url=i.val());const r=$(this).find(".me-icon");r.length&&(o.icon=r.val());const w=$(this).find(".me-vis");w.length&&(o.visibility=w.val());const k=$(this).find(".me-perm");k.length&&(o.permission=k.val())})}function _t(t,d){const s={};for(const o of d){const n=t.querySelector(`[name="${o}"]`);n&&(s[o]=n.type==="checkbox"?n.checked:n.value)}return s}function g(t,d={},...s){const o=document.createElement(t);Object.assign(o,d);for(const n of s)n!=null&&o.append(n);return o}function A(t,d,s){const o=g("label",{className:"form-field"},g("span",{className:"form-label",textContent:t}),d);return s&&o.append(g("small",{className:"form-hint",textContent:s,style:"display:block;font-size:.78em;opacity:.7;margin-top:.25rem;line-height:1.35"})),o}function ht(t,d){const s=[["","- Everyone -"],["public","Public - anyone, incl. logged-out"],...(d||[]).map(n=>[n.name,`${n.label||n.name} and above`]),["private","Private - super-admin only"]];t&&!s.some(([n])=>n===t)&&s.push([t,`${t} (custom)`]);const o=M(s,t||"");return o.name="visibility",o}function M(t,d){const s=g("select",{className:"form-select"});return t.forEach(([o,n])=>s.add(new Option(n,o))),s.value=d??"",s}function K(t,d){const s=typeof d=="string"&&d.startsWith("#"),o=M([["","- none -"],["primary","primary"],["success","success"],["danger","danger"],["warning","warning"],["info","info"],["neutral","neutral"],["__custom__","Custom\u2026"]],s?"__custom__":d||""),n=g("input",{type:"color",className:"form-input me-colour-hex",value:s?d:"#000000"});n.style.display=s?"":"none",o.addEventListener("change",()=>{n.style.display=o.value==="__custom__"?"":"none"});const i=A(t,o);return i.append(n),i._read=()=>o.value==="__custom__"?n.value:o.value,i}function yt(t,d){const s=g("input",{className:"form-input me-icon",name:"icon",value:d||""}),o=g("span",{className:"me-icon-preview"});d&&o.setAttribute("data-icon",d);const n=g("button",{type:"button",className:"btn btn-sm btn-ghost",textContent:"Browse\u2026"}),i=r=>{r?o.setAttribute("data-icon",r):o.removeAttribute("data-icon"),Domma.icons.scan(o.parentNode||o)};return n.addEventListener("click",()=>rt(r=>{s.value=r,i(r)})),s.addEventListener("input",()=>i(s.value.trim())),A(t,g("div",{className:"me-icon-field"},s,n,o))}function Z(...t){return g("div",{className:"me-field-grid"},...t)}async function wt(t,d,s,o,n){const i=u=>g("h4",{className:"me-form-section",textContent:u}),r=A("Label",g("input",{className:"form-input",name:"text",value:t.text||""}),"The text shown for this item."),w=A("URL",g("input",{className:"form-input",name:"url",value:t.url||""}),"Where it links. Must start with /, #, https://, or mailto: . Leave blank for a parent that only groups sub-items."),k=yt("Icon",t.icon||""),y=A("Visibility",ht(t.visibility||"",o),"Who can see this item, by role seniority. Picking a role shows it to that role and anyone more senior. Blank = everyone."),P=M([["","- no permission gate -"],...(d||[]).map(u=>[u.key,u.label||u.key])],t.permission||"");P.name="permission";const O=A("Permission",P,"Hide unless the viewer holds this specific permission. Independent of Visibility - both must pass."),J=A("Badge text",g("input",{className:"form-input",name:"badgeText",value:t.badge?.text||""})),X=M([["","- none -"],...(s||[]).map(u=>[u.slug,u.title||u.slug])],t.badge?.countFrom||"");X.name="badgeCount";const V=A("Badge count from",X,"Show a live count of this collection\u2019s entries as the badge."),U=K("Badge colour",t.badge?.variant||""),l=M([["link","Link"],["pill","Pill"]],t.pill?"pill":"link");l.name="renderAs";const x=A("Render as",l,"Pill draws the item as a rounded chip. Pill styling applies to top-level navbar items."),v=A("Pill style",M([["filled","Filled"],["outline","Outline"]],t.pill?.style||"filled"));v.querySelector("select").name="pillStyle";const W=K("Pill colour",t.pill?.variant||""),R=g("div",{className:"me-pill-wrap"},Z(v,W));R.style.display=t.pill?"":"none",l.addEventListener("change",()=>{R.style.display=l.value==="pill"?"":"none"});const T=K("Text colour",t.colour||""),q=g("button",{className:"btn btn-primary",textContent:"Apply"}),z=g("div",{className:"me-item-form"},i("Link"),r,w,k,i("Access"),Z(y,O),i("Badge"),Z(J,U),V,i("Appearance"),x,R,T,q),_=E.slideover({title:"Edit menu item",size:"lg",position:"right"});_.element.appendChild(z),_.open();const B=_.element.closest(".dm-slideover")||_.element.querySelector(".dm-slideover")||_.element;B.style.setProperty("width",ft+"px","important"),B.style.setProperty("max-width","95vw","important"),Domma.icons.scan(z);const I=u=>z.querySelector(`[name="${u}"]`)?.value||"";q.addEventListener("click",()=>{t.text=I("text"),t.url=I("url"),t.icon=I("icon"),t.visibility=I("visibility"),t.permission=I("permission");const u=I("badgeText"),F=I("badgeCount"),N=U._read();if(t.badge=u||F?{...u&&{text:u},...F&&{countFrom:F},...N&&{variant:N}}:null,I("renderAs")==="pill"){const L=W._read();t.pill={style:I("pillStyle")||"filled",...L&&{variant:L}}}else t.pill=null;t.colour=T._read(),_.close(),n()})}export const menuEditorView={templateUrl:"/admin/js/templates/menu-editor.html",async onMount(t){const d=location.hash.match(/^#\/menus\/edit\/(.+)$/),s=d?decodeURIComponent(d[1]):null,o=!s,n=E.loader(t.get(0),{type:"dots"});let i,r,w,k,y,P,O;try{if(o)i={slug:"",name:"",description:"",items:[]};else try{i=await S.menus.get(s)}catch(e){E.toast(`Failed to load menu: ${e.message||e}`,{type:"error"}),location.hash="#/menus";return}r=await S.projects.list().catch(()=>[]),w=await H.get("/api/auth/permissions-registry").catch(()=>({resources:[]})),k=await S.collections.list().catch(()=>[]),y=await S.get("/collections/roles/entries?limit=100").catch(()=>null),P=await S.menuLocations.registry().catch(()=>[]),O=await S.pages.list().catch(()=>[])}finally{n.destroy()}const X=(Array.isArray(y)?y:y?.entries||[]).map(e=>({name:e.data?.name,label:e.data?.label,level:e.data?.level??99})).filter(e=>e.name).sort((e,a)=>a.level-e.level),V='<option value="">- no permission gate -</option>',U=e=>V+(w.resources||[]).map(a=>{const c=(e||"")===a.key?" selected":"";return`<option value="${a.key}"${c}>${a.label||a.key}</option>`}).join("");t.find("#me-title").text(o?"New menu":`Edit "${i.slug}"`);let l=et(i.items);const x=t.find("#me-items-list");function v(){vt(x,l,U),gt(x,()=>l,v)}v(),t.find("#me-add-item").on("click",()=>{j(x,l),l.push({id:D(),parentId:null,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1}),v()}),t.find("#me-add-separator").on("click",()=>{j(x,l),l.push({id:D(),parentId:null,type:"separator"}),v()}),t.find("#me-add-spacer").on("click",()=>{j(x,l),l.push({id:D(),parentId:null,type:"spacer",size:"md"}),v()});function W(e){return function(){j(x,l);const a=$(this).closest(".menu-tree-row").data("id"),c=l.findIndex(h=>h.id===a);if(c===-1)return;const f=l[c],p={separator:{type:"separator"},spacer:{type:"spacer",size:"md"}}[e],m=p?{id:D(),parentId:f.parentId,...p}:{id:D(),parentId:f.parentId,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1};l.splice(c+1,0,m),v()}}t.off("click",".me-ins-item").on("click",".me-ins-item",W("item")),t.off("click",".me-ins-sep").on("click",".me-ins-sep",W("separator")),t.off("click",".me-remove").on("click",".me-remove",function(){const e=$(this).closest(".menu-tree-row").data("id");l=l.filter(a=>a.id!==e&&a.parentId!==e),v()}),t.off("click",".me-indent").on("click",".me-indent",function(){j(x,l);const e=$(this).closest(".menu-tree-row").data("id"),a=l.find(p=>p.id===e);if(!a)return;const c=l.filter(p=>p.parentId===a.parentId),f=c.findIndex(p=>p.id===e);f<=0||["separator","spacer"].includes(c[f-1].type)||(a.parentId=c[f-1].id,v())}),t.off("click",".me-outdent").on("click",".me-outdent",function(){j(x,l);const e=$(this).closest(".menu-tree-row").data("id"),a=l.find(f=>f.id===e);if(!a||a.parentId==null)return;const c=l.find(f=>f.id===a.parentId);a.parentId=c?c.parentId:null,v()}),t.off("click",".me-up").on("click",".me-up",function(){j(x,l);const e=$(this).closest(".menu-tree-row").data("id"),a=l.filter(m=>m.parentId===l.find(h=>h.id===e)?.parentId),c=a.findIndex(m=>m.id===e);if(c<=0)return;const f=l.indexOf(a[c]),p=l.indexOf(a[c-1]);[l[f],l[p]]=[l[p],l[f]],v()}),t.off("click",".me-down").on("click",".me-down",function(){j(x,l);const e=$(this).closest(".menu-tree-row").data("id"),a=l.filter(m=>m.parentId===l.find(h=>h.id===e)?.parentId),c=a.findIndex(m=>m.id===e);if(c===-1||c>=a.length-1)return;const f=l.indexOf(a[c]),p=l.indexOf(a[c+1]);[l[f],l[p]]=[l[p],l[f]],v()}),t.off("click",".me-hidden").on("click",".me-hidden",function(){j(x,l);const e=$(this).closest(".menu-tree-row").data("id"),a=l.find(c=>c.id===e);a&&(a.hidden=!a.hidden),v()}),t.off("click",".me-edit").on("click",".me-edit",function(){const e=$(this).closest(".menu-tree-row").data("id"),a=l.find(c=>c.id===e);!a||["separator","spacer"].includes(a.type)||wt(a,w.resources||[],k,X,()=>v())}),E.tabs(t.find("#me-tabs").get(0)),t.find("#me-variant").val(i.variant||""),t.find("#me-position").val(i.position||"");const R=t.find("#me-appearOnHover").get(0);R&&(R.checked=i.appearOnHover===!0),t.find("#me-float-anchor").val(i.float?.anchor||"TL"),t.find("#me-float-offsetX").val(i.float?.offsetX??""),t.find("#me-float-offsetY").val(i.float?.offsetY??""),t.find("#me-float-width").val(i.float?.width??""),t.find("#me-float-radius").val(i.float?.radius??""),t.find("#me-float-shadow").val(i.float?.shadow||""),t.find("#me-float-opacity").val(i.float?.opacity??""),t.find("#me-float-title").val(i.float?.title||"");for(const[e,a]of[["#me-float-collapsible",i.float?.collapsible],["#me-float-collapsed",i.float?.collapsed],["#me-float-movable",i.float?.movable],["#me-float-resizable",i.float?.resizable]]){const c=t.find(e).get(0);c&&(c.checked=a===!0)}const T=i.float?.accent||"",q=T.startsWith("#"),z=t.find("#me-float-accent"),_=t.find("#me-float-accent-hex");z.val(q?"__custom__":T),q&&_.val(T);const B=()=>{_.get(0).style.display=z.val()==="__custom__"?"":"none"};B(),z.on("change",B);const I=()=>z.val()==="__custom__"?_.val():z.val(),u=()=>{const e=t.find("#me-position").val()==="floating";t.find(".dm-float-field").each(function(){this.style.display=e?"":"none"}),e&&B();const a=t.find("#me-orientation").val()==="vertical";t.find(".dm-vertical-field").each(function(){this.style.display=a?"":"none"});const c=!!(t.find("#me-float-collapsible").get(0)||{}).checked;t.find(".dm-float-collapsed-field").each(function(){this.style.display=e&&c?"":"none"})};t.find("#me-orientation").val(i.orientation||""),t.find("#me-side").val(i.side||"left"),u(),t.find("#me-position").on("change",u),t.find("#me-orientation").on("change",u),t.find("#me-float-collapsible").on("change",u),t.find("#me-fontFamily").val(i.style?.fontFamily||""),t.find("#me-fontSize").val(i.style?.fontSize||""),t.find("#me-fontWeight").val(i.style?.fontWeight||""),t.find("#me-letterSpacing").val(i.style?.letterSpacing||""),t.find("#me-iconSize").val(i.style?.iconSize||"");const F=t.find("#me-bind-slot"),N=t.find("#me-bind-projects"),L=t.find("#me-bind-pages");F.html('<option value="">- not bound -</option>'+(P||[]).map(e=>`<option value="${b(e.slot)}">${b(e.label||e.slot)} (${b(e.slot)})</option>`).join("")),N.html(r.map(e=>`<option value="${b(e.slug)}">${b(e.name||e.slug)}</option>`).join("")),F.val(i.binding?.slot||"");const it=new Set(i.binding?.projects||[]);Array.from(N.get(0).options).forEach(e=>{e.selected=it.has(e.value)}),L.val((i.binding?.pages||[]).join(`
|
|
44
|
+
`)),t.find("#me-bind-page-pick").html('<option value="">- pick a page -</option>'+O.map(e=>{const a=e.urlPath||e.url||"";return a?`<option value="${b(a)}">${b(e.title||a)} - ${b(a)}</option>`:""}).join("")),t.find("#me-bind-page-add").on("click",()=>{const e=t.find("#me-bind-page-pick").val();if(!e)return;const a=L.val().split(`
|
|
45
|
+
`).map(c=>c.trim()).filter(Boolean);a.includes(e)||a.push(e),L.val(a.join(`
|
|
46
|
+
`)),Y()});function Q(){const e=F.val();return e?{slot:e,projects:Array.from(N.get(0).selectedOptions).map(a=>a.value),pages:L.val().split(`
|
|
47
|
+
`).map(a=>a.trim()).filter(Boolean)}:null}function Y(){const e=Q(),a=t.find("#me-bind-summary");if(!e){a.html('<span class="text-muted">Not bound - this menu renders only where it is mapped or placed by a shortcode.</span>');return}const c=[];e.projects.length&&c.push(`pages in ${e.projects.map(m=>`<code>${b(m)}</code>`).join(", ")}`),e.pages.length&&c.push(`pages matching ${e.pages.map(m=>`<code>${b(m)}</code>`).join(", ")}`);const f=e.pages.filter(m=>!/^\/[^\s?#*]*(\/\*)?$|^\/\*$/.test(m)),p=f.length?`<div class="text-danger" style="margin-top:.4rem">Not valid page patterns: ${f.map(m=>`<code>${b(m)}</code>`).join(", ")}</div>`:"";a.html(c.length?`Takes over the <code>${b(e.slot)}</code> slot on ${c.join(" and ")}.${p}`:`<span class="text-warning">Slot <code>${b(e.slot)}</code> selected but no project or page chosen - the binding will never match.</span>`)}F.on("change",Y),N.on("change",Y),L.on("input",Y),Y();const{getProjectFromHash:at}=await import("../lib/project-context.js"),st=o?at():null,tt=t.find("#me-project");tt.html('<option value="">- none -</option>'+r.map(e=>`<option value="${b(e.slug)}">${b(e.name||e.slug)}</option>`).join("")),t.find("#me-slug").val(i.slug||""),t.find("#me-name").val(i.name||""),t.find("#me-description").val(i.description||""),tt.val(i.meta?.project||st||""),Domma.icons.scan(t.get(0)),t.find("#me-save").on("click",async()=>{j(x,l);const e={variant:t.find("#me-variant").val(),position:t.find("#me-position").val(),appearOnHover:!!(t.find("#me-appearOnHover").get(0)||{}).checked,floatAnchor:t.find("#me-float-anchor").val(),floatOffsetX:t.find("#me-float-offsetX").val(),floatOffsetY:t.find("#me-float-offsetY").val(),floatWidth:t.find("#me-float-width").val(),floatRadius:t.find("#me-float-radius").val(),floatShadow:t.find("#me-float-shadow").val(),floatAccent:I(),floatOpacity:t.find("#me-float-opacity").val(),floatTitle:t.find("#me-float-title").val().trim(),floatCollapsible:!!(t.find("#me-float-collapsible").get(0)||{}).checked,floatCollapsed:!!(t.find("#me-float-collapsed").get(0)||{}).checked,floatMovable:!!(t.find("#me-float-movable").get(0)||{}).checked,floatResizable:!!(t.find("#me-float-resizable").get(0)||{}).checked,orientation:t.find("#me-orientation").val(),side:t.find("#me-side").val(),fontFamily:t.find("#me-fontFamily").val(),fontSize:t.find("#me-fontSize").val(),fontWeight:t.find("#me-fontWeight").val(),letterSpacing:t.find("#me-letterSpacing").val(),iconSize:t.find("#me-iconSize").val()},a={slug:t.find("#me-slug").val().trim(),name:t.find("#me-name").val().trim(),description:t.find("#me-description").val().trim(),project:t.find("#me-project").val()},c=["fontFamily","fontSize","fontWeight","letterSpacing","iconSize"],f=Object.fromEntries(Object.entries(e).filter(([h,C])=>c.includes(h)&&C)),p={};if(e.position==="floating"){e.floatAnchor&&(p.anchor=e.floatAnchor);for(const[h,C]of[["offsetX",e.floatOffsetX],["offsetY",e.floatOffsetY],["width",e.floatWidth],["radius",e.floatRadius],["opacity",e.floatOpacity]])C!==""&&C!=null&&Number.isFinite(Number(C))&&(p[h]=Number(C));e.floatShadow&&(p.shadow=e.floatShadow),e.floatAccent&&(p.accent=e.floatAccent),e.floatTitle&&(p.title=e.floatTitle),e.floatCollapsible&&(p.collapsible=!0),e.floatCollapsible&&e.floatCollapsed&&(p.collapsed=!0),e.floatMovable&&(p.movable=!0),e.floatResizable&&(p.resizable=!0)}const m={slug:a.slug,name:a.name,description:a.description,...e.variant&&{variant:e.variant},...e.position&&{position:e.position},...e.orientation&&{orientation:e.orientation},...e.orientation==="vertical"&&e.side&&{side:e.side},...Object.keys(p).length&&{float:p},...e.appearOnHover&&{appearOnHover:!0},...Object.keys(f).length&&{style:f},binding:Q(),items:ut(l),meta:{...i.meta||{},project:a.project||null}};try{if(o)await S.menus.create(m);else if(m.slug!==i.slug){await S.menus.create(m);const h=await S.menuLocations.get();let C=!1;for(const[lt,dt]of Object.entries(h))dt===i.slug&&(h[lt]=m.slug,C=!0);C&&await S.menuLocations.save(h),await S.menus.remove(i.slug)}else await S.menus.update(i.slug,m);E.toast("Saved.",{type:"success"}),location.hash="#/menus/edit/"+encodeURIComponent(m.slug)}catch(h){E.toast(`Save failed: ${h.message||h}`,{type:"error"})}})}};
|