domma-cms 0.6.6 → 0.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +1 -1
  2. package/admin/css/admin.css +1 -1
  3. package/admin/js/templates/navigation.html +1 -1
  4. package/admin/js/views/navigation.js +18 -16
  5. package/bin/cli.js +2 -1
  6. package/config/navigation.json +4 -3
  7. package/config/plugins.json +19 -19
  8. package/package.json +1 -1
  9. package/plugins/{example-analytics → analytics}/plugin.js +5 -5
  10. package/plugins/{example-analytics → analytics}/plugin.json +2 -2
  11. package/plugins/{example-analytics → analytics}/public/inject-body.html +2 -2
  12. package/plugins/analytics/public/inject-head.html +1 -0
  13. package/plugins/{example-analytics → analytics}/stats.json +2 -2
  14. package/server/services/renderer.js +31 -2
  15. package/plugins/example-analytics/public/inject-head.html +0 -1
  16. package/plugins/form-builder/data/forms/contacts.json +0 -66
  17. package/plugins/form-builder/data/forms/enquiries.json +0 -103
  18. package/plugins/form-builder/data/forms/feedback.json +0 -131
  19. package/plugins/form-builder/data/forms/notes.json +0 -79
  20. package/plugins/form-builder/data/forms/to-do.json +0 -100
  21. package/plugins/form-builder/data/submissions/contacts.json +0 -1
  22. package/plugins/form-builder/data/submissions/enquiries.json +0 -1
  23. package/plugins/form-builder/data/submissions/feedback.json +0 -1
  24. package/plugins/form-builder/data/submissions/notes.json +0 -1
  25. package/plugins/form-builder/data/submissions/to-do.json +0 -1
  26. /package/plugins/{example-analytics → analytics}/admin/templates/analytics.html +0 -0
  27. /package/plugins/{example-analytics → analytics}/admin/views/analytics.js +0 -0
  28. /package/plugins/{example-analytics → analytics}/config.js +0 -0
package/README.md CHANGED
@@ -77,7 +77,7 @@ my-site/
77
77
 
78
78
  ├── plugins/ # CMS plugins
79
79
  │ ├── domma-effects/
80
- │ ├── example-analytics/
80
+ │ ├── analytics/
81
81
  │ └── form-builder/
82
82
 
83
83
  ├── public/ # Public frontend assets (CSS, JS)
@@ -1 +1 @@
1
- .dashboard-layout{display:flex;flex-direction:column;min-height:100vh}.dashboard-wrapper{display:flex;flex:1;min-height:0}.dashboard-main{flex:1;min-width:0;overflow-y:auto}.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:.5rem}.stat-card .card-body{display:flex;align-items:center;gap:1rem}.stat-icon{width:44px;height:44px;border-radius:8px;background:var(--primary-alpha, rgba(91,140,255,.15));color:var(--primary, #5b8cff);display:flex;align-items:center;justify-content:center;font-size:1.25rem;flex-shrink:0}.stat-icon--success{background:#34d39926;color:#34d399}.stat-icon--warning{background:#fbbf2426;color:#fbbf24}.stat-value{font-size:1.75rem;font-weight:700;line-height:1}.stat-label{font-size:.8rem;color:var(--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(--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}#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(--border-color, rgba(255, 255, 255, .08));flex-wrap:wrap;flex-shrink:0}.editor-toolbar-btn{background:none;border:none;color:var(--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(--text, #eee);background:#ffffff14}.editor-toolbar-sep{width:1px;height:20px;background:var(--border-color, 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(--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(--text, #eee);background:#ffffff14}.editor-view-btn.active{color:var(--primary, #5b8cff);background:var(--primary-alpha, rgba(91, 140, 255, .12))}.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(--border-color, 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-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(--border-color, 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:#ffffff0f;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(--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(--border-color, 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(--border-color, rgba(255,255,255,.08));padding-bottom:.4rem;margin-bottom:.25rem}.nav-item-row{border-bottom:1px solid var(--border-color, rgba(255,255,255,.04))}.nav-item-row:last-child{border-bottom:none}.nav-col-indent{width:18px;flex-shrink:0;color:var(--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:#ffffff05}.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(auto-fill,minmax(280px,1fr));gap:1rem}.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(--card-bg, rgba(255,255,255,.04));border:1px solid var(--border-color, 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(--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(--primary, #7c6af7);border-radius:3px;background:var(--input-bg, transparent);color:inherit;outline:none}.media-size{font-size:.75rem;color:var(--text-muted, #888)}.media-actions{padding:.5rem .75rem;display:flex;gap:.4rem;border-top:1px solid var(--border-color, rgba(255,255,255,.08))}#admin-topbar{height:60px;display:flex;align-items:center;gap:1rem;padding:0 1.25rem;background:var(--navbar-bg, rgba(0,0,0,.3));border-bottom:1px solid var(--border-color, 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(--primary, #5b8cff);width:22px;height:22px}.topbar-brand-text{opacity:.85}.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:#5b8cff26;color:#5b8cff}.topbar-role-badge--manager{background:#34d39926;color:#34d399}.topbar-role-badge--editor{background:#fbbf2426;color:#fbbf24}.topbar-role-badge--subscriber{background:#94a3b826;color:#94a3b8}.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(--text-muted, #888);text-decoration:none;padding:.4rem .65rem;border-radius:6px;transition:color .15s,background .15s}.topbar-action-link:hover{color:var(--text, #eee);background:#ffffff0f}.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(--body-bg, #0f1117);z-index:100;overflow-y:auto}.login-card{width:100%;max-width:460px}.ob-done-icon{font-size:3rem;color:#34d399;margin-bottom:1rem;text-align:center}.btn-skip{display:block;text-align:center;margin-top:.75rem;font-size:.85rem;color:var(--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(--border-color, rgba(255,255,255,.1));border-radius:8px;overflow:hidden;cursor:pointer;transition:border-color .15s,transform .1s}.theme-swatch:hover{border-color:var(--primary, #5b8cff);transform:translateY(-1px)}.theme-swatch.selected{border-color:var(--primary, #5b8cff);box-shadow:0 0 0 2px var(--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(--card-bg, rgba(255,255,255,.04))}.theme-swatch-mode{color:var(--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(--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:var(--primary-alpha, rgba(91,140,255,.15));color:var(--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(--text-muted, #888)}.plugin-desc{font-size:.875rem;color:var(--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(--border-color, rgba(255,255,255,.08))}#admin-sidebar .sidebar-link{position:relative!important}#admin-sidebar .sidebar-badge{position:absolute!important;right:1rem!important;top:50%!important;transform:translateY(-50%)!important;margin-left:0!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:var(--dm-primary-alpha, rgba(91,140,255,.12));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(--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(--border, rgba(255, 255, 255, .1))}.image-editor-sep{display:inline-block;width:1px;height:1.5rem;background:var(--border, rgba(255, 255, 255, .15));margin:0 .25rem;flex-shrink:0}.editor-toolbar-btn.active{color:var(--primary, #7c6af7);background:color-mix(in srgb,var(--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(--border-color, rgba(255, 255, 255, .1));border-radius:8px;box-shadow:0 8px 24px #0006;padding:4px;min-width:160px}.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:#ffffff14;color:var(--dm-text-bright, #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(--border-color, 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:#ffffff14;color:var(--dm-text-bright, #fff)}.editor-icon-picker{position:absolute;z-index:1000;background:var(--dm-surface, #1e1e2e);border:1px solid var(--border-color, 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(--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:#ffffff14;color:var(--dm-text-bright, #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(--border, rgba(255, 255, 255, .1));font-size:.85rem}.image-editor-resize label{font-weight:600;color:var(--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(--border, rgba(255, 255, 255, .1));gap:.5rem}.image-editor-effects{border-top:1px solid var(--border, rgba(255, 255, 255, .1))}.image-editor-tab-bar{display:flex;flex-direction:row;overflow-x:auto;border-bottom:1px solid var(--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(--text-muted, #888);white-space:nowrap;transition:color .15s,border-color .15s;flex-shrink:0}.image-editor-tab-btn:hover{color:var(--text, #eee)}.image-editor-tab-btn.active{color:var(--primary, #7c6af7);border-bottom-color:var(--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(--primary, #7c6af7);background:color-mix(in srgb,var(--primary, #7c6af7) 15%,transparent);border-color:var(--primary, #7c6af7)}.ie-server-note{font-size:.73rem;color:var(--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(--text-muted, #888);font-weight:600;flex-shrink:0}.ie-slider{flex:1;accent-color:var(--primary, #7c6af7);cursor:pointer}.ie-slider-val{width:3rem;font-size:.8rem;text-align:right;color:var(--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(--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(--surface-2, 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(--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(--surface-2, rgba(255, 255, 255, .08));border-color:var(--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(--text-muted, #888);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:72px}.dm-slideover-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;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-y:auto;min-height:0}.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-surface-subtle, 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-surface-subtle, 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}
1
+ .dashboard-layout{display:flex;flex-direction:column;min-height:100vh}.dashboard-wrapper{display:flex;flex:1;min-height:0}.dashboard-main{flex:1;min-width:0;overflow-y:auto}.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:.5rem}.stat-card .card-body{display:flex;align-items:center;gap:1rem}.stat-icon{width:44px;height:44px;border-radius:8px;background:var(--primary-alpha, rgba(91,140,255,.15));color:var(--primary, #5b8cff);display:flex;align-items:center;justify-content:center;font-size:1.25rem;flex-shrink:0}.stat-icon--success{background:#34d39926;color:#34d399}.stat-icon--warning{background:#fbbf2426;color:#fbbf24}.stat-value{font-size:1.75rem;font-weight:700;line-height:1}.stat-label{font-size:.8rem;color:var(--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(--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}#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(--border-color, rgba(255, 255, 255, .08));flex-wrap:wrap;flex-shrink:0}.editor-toolbar-btn{background:none;border:none;color:var(--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(--text, #eee);background:#ffffff14}.editor-toolbar-sep{width:1px;height:20px;background:var(--border-color, 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(--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(--text, #eee);background:#ffffff14}.editor-view-btn.active{color:var(--primary, #5b8cff);background:var(--primary-alpha, rgba(91, 140, 255, .12))}.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(--border-color, 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-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(--border-color, 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:#ffffff0f;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(--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(--border-color, 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(--border-color, rgba(255,255,255,.08));padding-bottom:.4rem;margin-bottom:.25rem}.nav-item-row{border-bottom:1px solid var(--border-color, rgba(255,255,255,.04))}.nav-item-row:last-child{border-bottom:none}.nav-col-indent{width:18px;flex-shrink:0;color:var(--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:#ffffff05}.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(--text-muted, #888)}.btn-toggle-hidden.active{color:var(--color-warning, #f59e0b)}.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(auto-fill,minmax(280px,1fr));gap:1rem}.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(--card-bg, rgba(255,255,255,.04));border:1px solid var(--border-color, 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(--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(--primary, #7c6af7);border-radius:3px;background:var(--input-bg, transparent);color:inherit;outline:none}.media-size{font-size:.75rem;color:var(--text-muted, #888)}.media-actions{padding:.5rem .75rem;display:flex;gap:.4rem;border-top:1px solid var(--border-color, rgba(255,255,255,.08))}#admin-topbar{height:60px;display:flex;align-items:center;gap:1rem;padding:0 1.25rem;background:var(--navbar-bg, rgba(0,0,0,.3));border-bottom:1px solid var(--border-color, 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(--primary, #5b8cff);width:22px;height:22px}.topbar-brand-text{opacity:.85}.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:#5b8cff26;color:#5b8cff}.topbar-role-badge--manager{background:#34d39926;color:#34d399}.topbar-role-badge--editor{background:#fbbf2426;color:#fbbf24}.topbar-role-badge--subscriber{background:#94a3b826;color:#94a3b8}.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(--text-muted, #888);text-decoration:none;padding:.4rem .65rem;border-radius:6px;transition:color .15s,background .15s}.topbar-action-link:hover{color:var(--text, #eee);background:#ffffff0f}.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(--body-bg, #0f1117);z-index:100;overflow-y:auto}.login-card{width:100%;max-width:460px}.ob-done-icon{font-size:3rem;color:#34d399;margin-bottom:1rem;text-align:center}.btn-skip{display:block;text-align:center;margin-top:.75rem;font-size:.85rem;color:var(--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(--border-color, rgba(255,255,255,.1));border-radius:8px;overflow:hidden;cursor:pointer;transition:border-color .15s,transform .1s}.theme-swatch:hover{border-color:var(--primary, #5b8cff);transform:translateY(-1px)}.theme-swatch.selected{border-color:var(--primary, #5b8cff);box-shadow:0 0 0 2px var(--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(--card-bg, rgba(255,255,255,.04))}.theme-swatch-mode{color:var(--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(--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:var(--primary-alpha, rgba(91,140,255,.15));color:var(--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(--text-muted, #888)}.plugin-desc{font-size:.875rem;color:var(--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(--border-color, rgba(255,255,255,.08))}#admin-sidebar .sidebar-link{position:relative!important}#admin-sidebar .sidebar-badge{position:absolute!important;right:1rem!important;top:50%!important;transform:translateY(-50%)!important;margin-left:0!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:var(--dm-primary-alpha, rgba(91,140,255,.12));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(--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(--border, rgba(255, 255, 255, .1))}.image-editor-sep{display:inline-block;width:1px;height:1.5rem;background:var(--border, rgba(255, 255, 255, .15));margin:0 .25rem;flex-shrink:0}.editor-toolbar-btn.active{color:var(--primary, #7c6af7);background:color-mix(in srgb,var(--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(--border-color, rgba(255, 255, 255, .1));border-radius:8px;box-shadow:0 8px 24px #0006;padding:4px;min-width:160px}.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:#ffffff14;color:var(--dm-text-bright, #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(--border-color, 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:#ffffff14;color:var(--dm-text-bright, #fff)}.editor-icon-picker{position:absolute;z-index:1000;background:var(--dm-surface, #1e1e2e);border:1px solid var(--border-color, 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(--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:#ffffff14;color:var(--dm-text-bright, #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(--border, rgba(255, 255, 255, .1));font-size:.85rem}.image-editor-resize label{font-weight:600;color:var(--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(--border, rgba(255, 255, 255, .1));gap:.5rem}.image-editor-effects{border-top:1px solid var(--border, rgba(255, 255, 255, .1))}.image-editor-tab-bar{display:flex;flex-direction:row;overflow-x:auto;border-bottom:1px solid var(--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(--text-muted, #888);white-space:nowrap;transition:color .15s,border-color .15s;flex-shrink:0}.image-editor-tab-btn:hover{color:var(--text, #eee)}.image-editor-tab-btn.active{color:var(--primary, #7c6af7);border-bottom-color:var(--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(--primary, #7c6af7);background:color-mix(in srgb,var(--primary, #7c6af7) 15%,transparent);border-color:var(--primary, #7c6af7)}.ie-server-note{font-size:.73rem;color:var(--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(--text-muted, #888);font-weight:600;flex-shrink:0}.ie-slider{flex:1;accent-color:var(--primary, #7c6af7);cursor:pointer}.ie-slider-val{width:3rem;font-size:.8rem;text-align:right;color:var(--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(--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(--surface-2, 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(--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(--surface-2, rgba(255, 255, 255, .08));border-color:var(--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(--text-muted, #888);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:72px}.dm-slideover-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;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-y:auto;min-height:0}.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-surface-subtle, 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-surface-subtle, 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}
@@ -49,7 +49,7 @@
49
49
  <span class="nav-col-main text-muted"><small>URL</small></span>
50
50
  <span class="nav-col-icon text-muted"><small>Icon</small></span>
51
51
  <span class="nav-col-parent text-muted"><small>Parent</small></span>
52
- <span class="nav-col-action"></span>
52
+ <span class="nav-col-action text-muted"><small>Actions</small></span>
53
53
  </div>
54
54
  <div id="nav-items-list"></div>
55
55
  </div>
@@ -1,20 +1,22 @@
1
- import{api as f}from"../api.js";let x=1;const d=t=>String(t||"").replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;");function b(t){const e=[];return(t||[]).forEach(r=>{const o=x++;e.push({_id:o,text:r.text||"",url:r.url||"",icon:r.icon||"",parentId:null}),(r.items||r.children||[]).forEach(l=>{e.push({_id:x++,text:l.text||"",url:l.url||"",icon:l.icon||"",parentId:o})})}),e}function I(t){return t.filter(e=>e.parentId===null).map(e=>{const r=t.filter(l=>l.parentId===e._id).map(l=>({text:l.text,url:l.url,...l.icon&&{icon:l.icon}})),o={text:e.text,url:e.url,...e.icon&&{icon:e.icon}};return r.length&&(o.items=r),o})}function g(t){const e=[];return t.filter(r=>r.parentId===null).forEach(r=>{e.push(r),t.filter(o=>o.parentId===r._id).forEach(o=>e.push(o))}),e}export const navigationView={templateUrl:"/admin/js/templates/navigation.html",async onMount(t){let[e,r]=await Promise.all([f.navigation.get().catch(()=>({brand:{},items:[]})),f.settings.get().catch(()=>({}))]),o=b(e.items),l=(r.footer?.links||[]).map(s=>({text:s.text||"",url:s.url||""}));const v=()=>{t.find(".nav-item-row").each(function(){const i=parseInt($(this).data("id"),10),n=o.find(a=>a._id===i);if(!n)return;n.text=$(this).find(".item-text").val(),n.url=$(this).find(".item-url").val(),n.icon=$(this).find(".item-icon").val();const c=$(this).find(".item-parent").val();n.parentId=c?parseInt(c,10):null});const s=new Set(o.filter(i=>i.parentId!==null).map(i=>i._id));o.forEach(i=>{i.parentId!==null&&s.has(i.parentId)&&(i.parentId=null)})},p=()=>{const s=t.find("#nav-items-list").empty(),i=o.filter(n=>n.parentId===null);g(o).forEach(n=>{const c=n.parentId!==null,a='<option value="">\u2014 top-level \u2014</option>'+i.filter(u=>u._id!==n._id).map(u=>`<option value="${u._id}"${u._id===n.parentId?" selected":""}>${d(u.text)||"(untitled)"}</option>`).join("");s.append(`
2
- <div class="nav-item-row${c?" nav-item-row--child":""}" data-id="${n._id}">
3
- <span class="nav-col-indent">${c?"\u21B3":""}</span>
4
- <input type="text" class="form-input item-text nav-col-main" value="${d(n.text)}" placeholder="Label">
5
- <input type="text" class="form-input item-url nav-col-main" value="${d(n.url)}" placeholder="/url">
6
- <input type="text" class="form-input item-icon nav-col-icon" value="${d(n.icon)}" placeholder="icon">
7
- <select class="form-select item-parent nav-col-parent">${a}</select>
1
+ import{api as v}from"../api.js";let b=1;const f=e=>String(e||"").replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;");function g(e){const i=[];return(e||[]).forEach(s=>{const l=b++;i.push({_id:l,text:s.text||"",url:s.url||"",icon:s.icon||"",hidden:s.hidden||!1,parentId:null}),(s.items||s.children||[]).forEach(o=>{i.push({_id:b++,text:o.text||"",url:o.url||"",icon:o.icon||"",hidden:o.hidden||!1,parentId:l})})}),i}function k(e){return e.filter(i=>i.parentId===null).map(i=>{const s=e.filter(o=>o.parentId===i._id).map(o=>({text:o.text,url:o.url,...o.icon&&{icon:o.icon},...o.hidden&&{hidden:!0}})),l={text:i.text,url:i.url,...i.icon&&{icon:i.icon},...i.hidden&&{hidden:!0}};return s.length&&(l.items=s),l})}function I(e){const i=[];return e.filter(s=>s.parentId===null).forEach(s=>{i.push(s),e.filter(l=>l.parentId===s._id).forEach(l=>i.push(l))}),i}export const navigationView={templateUrl:"/admin/js/templates/navigation.html",async onMount(e){let[i,s]=await Promise.all([v.navigation.get().catch(()=>({brand:{},items:[]})),v.settings.get().catch(()=>({}))]),l=g(i.items),o=(s.footer?.links||[]).map(d=>({text:d.text||"",url:d.url||"",...d.hidden&&{hidden:!0}}));const p=()=>{e.find("#nav-items-list .nav-item-row").each(function(){const t=parseInt($(this).data("id"),10),n=l.find(a=>a._id===t);if(!n)return;n.text=$(this).find(".item-text").val(),n.url=$(this).find(".item-url").val(),n.icon=$(this).find(".item-icon").val(),n.hidden=$(this).find(".btn-toggle-hidden").attr("data-hidden")==="true";const r=$(this).find(".item-parent").val();n.parentId=r?parseInt(r,10):null});const d=new Set(l.filter(t=>t.parentId!==null).map(t=>t._id));l.forEach(t=>{t.parentId!==null&&d.has(t.parentId)&&(t.parentId=null)})},c=()=>{const d=e.find("#nav-items-list").empty(),t=l.filter(n=>n.parentId===null);I(l).forEach(n=>{const r=n.parentId!==null,a=n.hidden===!0,x='<option value="">\u2014 top-level \u2014</option>'+t.filter(h=>h._id!==n._id).map(h=>`<option value="${h._id}"${h._id===n.parentId?" selected":""}>${f(h.text)||"(untitled)"}</option>`).join("");d.append(`
2
+ <div class="nav-item-row${r?" nav-item-row--child":""}${a?" nav-item-row--hidden":""}" data-id="${n._id}">
3
+ <span class="nav-col-indent">${r?"\u21B3":""}</span>
4
+ <input type="text" class="form-input item-text nav-col-main" value="${f(n.text)}" placeholder="Label">
5
+ <input type="text" class="form-input item-url nav-col-main" value="${f(n.url)}" placeholder="/url">
6
+ <input type="text" class="form-input item-icon nav-col-icon" value="${f(n.icon)}" placeholder="icon">
7
+ <select class="form-select item-parent nav-col-parent">${x}</select>
8
8
  <span class="nav-col-action">
9
+ <button class="btn btn-sm btn-ghost btn-toggle-hidden${a?" active":""}" data-id="${n._id}" data-hidden="${a}" data-tooltip="${a?"Show":"Hide"}"><span data-icon="eye-off"></span></button>
9
10
  <button class="btn btn-sm btn-danger btn-remove-item" data-id="${n._id}" data-tooltip="Remove"><span data-icon="trash"></span></button>
10
11
  </span>
11
12
  </div>
12
- `)}),Domma.icons.scan("#nav-items-list"),document.querySelectorAll("#nav-items-list [data-tooltip]").forEach(n=>{E.tooltip(n,{content:n.getAttribute("data-tooltip"),position:"top"})})},m=()=>{const s=t.find("#footer-links-list").empty();l.forEach((i,n)=>{s.append(`
13
- <div class="nav-item-row" data-footer-idx="${n}">
14
- <input type="text" class="form-input footer-link-text nav-col-main" value="${d(i.text)}" placeholder="Label">
15
- <input type="text" class="form-input footer-link-url nav-col-main" value="${d(i.url)}" placeholder="/url">
16
- <span class="nav-col-action">
17
- <button class="btn btn-sm btn-danger btn-remove-footer" data-idx="${n}" data-tooltip="Remove"><span data-icon="trash"></span></button>
18
- </span>
19
- </div>
20
- `)}),Domma.icons.scan("#footer-links-list"),document.querySelectorAll("#footer-links-list [data-tooltip]").forEach(i=>{E.tooltip(i,{content:i.getAttribute("data-tooltip"),position:"top"})})},h=()=>{l=[],t.find("#footer-links-list .nav-item-row").each(function(){l.push({text:$(this).find(".footer-link-text").val().trim(),url:$(this).find(".footer-link-url").val().trim()})})};t.find("#add-footer-link").on("click",()=>{h(),l.push({text:"",url:""}),m()}),t.off("click",".btn-remove-footer").on("click",".btn-remove-footer",function(){h();const s=parseInt($(this).data("idx"),10);l.splice(s,1),m()}),t.find("#field-brand-text").val(e.brand?.text||""),t.find("#field-brand-url").val(e.brand?.url||"/"),t.find("#field-brand-icon").val(e.brand?.icon||""),t.find("#field-nav-variant").val(e.variant||"dark"),p(),m(),t.find("#add-nav-item").on("click",()=>{v(),o.push({_id:x++,text:"",url:"",icon:"",parentId:null}),p()}),t.off("click",".btn-remove-item").on("click",".btn-remove-item",function(){v();const s=parseInt($(this).data("id"),10);o=o.filter(i=>i._id!==s&&i.parentId!==s),p()}),t.off("change",".item-parent").on("change",".item-parent",function(){v(),p()}),t.find("#save-nav-btn").on("click",async()=>{v(),h();const s=I(o.map(a=>({...a,text:a.text.trim(),url:a.url.trim(),icon:a.icon.trim()}))).filter(a=>a.text||a.url),i=t.find("#field-brand-icon").val().trim(),n={brand:{text:t.find("#field-brand-text").val().trim(),url:t.find("#field-brand-url").val().trim()||"/",...i&&{icon:i}},items:s,variant:t.find("#field-nav-variant").val(),position:e.position||"sticky"},c=l.filter(a=>a.text||a.url);try{await f.navigation.save(n),e=n,o=b(e.items),p(),E.toast("Navigation saved.",{type:"success"})}catch(a){console.error("[navigation] save failed:",a),E.toast("Failed to save navigation.",{type:"error"});return}try{const a=await f.settings.get().catch(()=>({}));await f.settings.save({...a,footer:{...a.footer||{},links:c}}),l=c,m()}catch(a){console.error("[navigation] footer links save failed:",a),E.toast("Footer links could not be saved.",{type:"warning"})}})}};
13
+ `)}),Domma.icons.scan("#nav-items-list"),document.querySelectorAll("#nav-items-list [data-tooltip]").forEach(n=>{E.tooltip(n,{content:n.getAttribute("data-tooltip"),position:"top"})})},u=()=>{const d=e.find("#footer-links-list").empty();o.forEach((t,n)=>{const r=t.hidden===!0;d.append(`
14
+ <div class="nav-item-row${r?" nav-item-row--hidden":""}" data-footer-idx="${n}">
15
+ <input type="text" class="form-input footer-link-text nav-col-main" value="${f(t.text)}" placeholder="Label">
16
+ <input type="text" class="form-input footer-link-url nav-col-main" value="${f(t.url)}" placeholder="/url">
17
+ <span class="nav-col-action">
18
+ <button class="btn btn-sm btn-ghost btn-toggle-hidden${r?" active":""}" data-idx="${n}" data-hidden="${r}" data-tooltip="${r?"Show":"Hide"}"><span data-icon="eye-off"></span></button>
19
+ <button class="btn btn-sm btn-danger btn-remove-footer" data-idx="${n}" data-tooltip="Remove"><span data-icon="trash"></span></button>
20
+ </span>
21
+ </div>
22
+ `)}),Domma.icons.scan("#footer-links-list"),document.querySelectorAll("#footer-links-list [data-tooltip]").forEach(t=>{E.tooltip(t,{content:t.getAttribute("data-tooltip"),position:"top"})})},m=()=>{o=[],e.find("#footer-links-list .nav-item-row").each(function(){const d=$(this).find(".btn-toggle-hidden").attr("data-hidden")==="true",t={text:$(this).find(".footer-link-text").val().trim(),url:$(this).find(".footer-link-url").val().trim()};d&&(t.hidden=!0),o.push(t)})};e.find("#add-footer-link").on("click",()=>{m(),o.push({text:"",url:"",hidden:!1}),u()}),e.off("click",".btn-remove-footer").on("click",".btn-remove-footer",function(){m();const d=parseInt($(this).data("idx"),10);o.splice(d,1),u()}),e.find("#field-brand-text").val(i.brand?.text||""),e.find("#field-brand-url").val(i.brand?.url||"/"),e.find("#field-brand-icon").val(i.brand?.icon||""),e.find("#field-nav-variant").val(i.variant||"dark"),c(),u(),e.find("#add-nav-item").on("click",()=>{p(),l.push({_id:b++,text:"",url:"",icon:"",hidden:!1,parentId:null}),c()}),e.off("click",".btn-remove-item").on("click",".btn-remove-item",function(){p();const d=parseInt($(this).data("id"),10);l=l.filter(t=>t._id!==d&&t.parentId!==d),c()}),e.off("click","#nav-items-list .btn-toggle-hidden").on("click","#nav-items-list .btn-toggle-hidden",function(d){d.stopPropagation(),p();const t=parseInt($(this).data("id"),10),n=l.find(r=>r._id===t);n&&(n.hidden=!n.hidden),c()}),e.off("click","#footer-links-list .btn-toggle-hidden").on("click","#footer-links-list .btn-toggle-hidden",function(d){d.stopPropagation(),m();const t=parseInt($(this).data("idx"),10);o[t]&&(o[t].hidden=!o[t].hidden),u()}),e.off("change",".item-parent").on("change",".item-parent",function(){p(),c()}),e.find("#save-nav-btn").on("click",async()=>{p(),m();const d=k(l.map(a=>({...a,text:a.text.trim(),url:a.url.trim(),icon:a.icon.trim()}))).filter(a=>a.text||a.url),t=e.find("#field-brand-icon").val().trim(),n={brand:{text:e.find("#field-brand-text").val().trim(),url:e.find("#field-brand-url").val().trim()||"/",...t&&{icon:t}},items:d,variant:e.find("#field-nav-variant").val(),position:i.position||"sticky"},r=o.filter(a=>a.text||a.url);try{await v.navigation.save(n),i=n,l=g(i.items),c(),E.toast("Navigation saved.",{type:"success"})}catch(a){console.error("[navigation] save failed:",a),E.toast("Failed to save navigation.",{type:"error"});return}try{const a=await v.settings.get().catch(()=>({}));await v.settings.save({...a,footer:{...a.footer||{},links:r}}),o=r,u()}catch(a){console.error("[navigation] footer links save failed:",a),E.toast("Footer links could not be saved.",{type:"warning"})}})}};
package/bin/cli.js CHANGED
@@ -237,7 +237,8 @@ const DEFAULT_NAV = {
237
237
  // Plugins enabled by default in a fresh project
238
238
  const ENABLED_BY_DEFAULT = new Set([
239
239
  'domma-effects',
240
- 'form-builder',
240
+ 'analytics',
241
+ 'theme-roller',
241
242
  ]);
242
243
 
243
244
  // Discover all plugins and set enabled state accordingly
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "brand": {
3
- "text": "My Site",
4
- "logo": null,
5
- "url": "/"
3
+ "text": "Domma CMS Base",
4
+ "url": "/",
5
+ "icon": "home"
6
6
  },
7
7
  "items": [
8
8
  {
@@ -34,6 +34,7 @@
34
34
  "text": "Resources",
35
35
  "url": "/resources",
36
36
  "icon": "book-open",
37
+ "hidden": true,
37
38
  "items": [
38
39
  {
39
40
  "text": "Overview",
@@ -1,19 +1,19 @@
1
- {
2
- "example-analytics": {
3
- "enabled": true,
4
- "settings": {}
5
- },
6
- "theme-roller": {
7
- "enabled": true,
8
- "settings": {}
9
- },
10
- "domma-effects": {
11
- "enabled": true,
12
- "settings": {
13
- "respectMotion": false,
14
- "defaultDuration": 600,
15
- "defaultAnimation": "fade",
16
- "defaultThreshold": 0.1
17
- }
18
- }
19
- }
1
+ {
2
+ "analytics": {
3
+ "enabled": true,
4
+ "settings": {}
5
+ },
6
+ "theme-roller": {
7
+ "enabled": true,
8
+ "settings": {}
9
+ },
10
+ "domma-effects": {
11
+ "enabled": true,
12
+ "settings": {
13
+ "respectMotion": false,
14
+ "defaultDuration": 600,
15
+ "defaultAnimation": "fade",
16
+ "defaultThreshold": 0.1
17
+ }
18
+ }
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Example Analytics Plugin — Server
2
+ * Analytics Plugin — Server
3
3
  * Tracks page hits in a JSON file alongside the plugin.
4
4
  * Endpoints:
5
- * POST /api/plugins/example-analytics/hit - public: record a hit { url }
6
- * GET /api/plugins/example-analytics/stats - admin: return all hit counts
7
- * DELETE /api/plugins/example-analytics/stats - admin: reset all stats
5
+ * POST /api/plugins/analytics/hit - public: record a hit { url }
6
+ * GET /api/plugins/analytics/stats - admin: return all hit counts
7
+ * DELETE /api/plugins/analytics/stats - admin: reset all stats
8
8
  */
9
9
  import fs from 'fs/promises';
10
10
  import path from 'path';
11
- import { fileURLToPath } from 'url';
11
+ import {fileURLToPath} from 'url';
12
12
 
13
13
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
14
  const STATS_FILE = path.join(__dirname, 'stats.json');
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "example-analytics",
2
+ "name": "analytics",
3
3
  "displayName": "Analytics",
4
4
  "version": "1.0.0",
5
5
  "description": "Basic page view analytics. Tracks hits per page using a simple JSON store.",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "views": {
17
17
  "plugin-analytics": {
18
- "entry": "example-analytics/admin/views/analytics.js",
18
+ "entry": "analytics/admin/views/analytics.js",
19
19
  "exportName": "analyticsView"
20
20
  }
21
21
  }
@@ -1,9 +1,9 @@
1
- <!-- example-analytics: page view tracker -->
1
+ <!-- analytics: page view tracker -->
2
2
  <script>
3
3
  (function () {
4
4
  var url = window.location.pathname;
5
5
  if (typeof fetch === 'function') {
6
- fetch('/api/plugins/example-analytics/hit', {
6
+ fetch('/api/plugins/analytics/hit', {
7
7
  method: 'POST',
8
8
  headers: { 'Content-Type': 'application/json' },
9
9
  body: JSON.stringify({ url: url })
@@ -0,0 +1 @@
1
+ <!-- analytics: head injection (empty — tracking is done via body script) -->
@@ -1,12 +1,12 @@
1
1
  {
2
- "/": 138,
2
+ "/": 139,
3
3
  "/about": 71,
4
4
  "/blog": 30,
5
5
  "/contact": 30,
6
6
  "/resources/typography": 4,
7
7
  "/resources": 13,
8
8
  "/resources/shortcodes": 14,
9
- "/resources/cards": 15,
9
+ "/resources/cards": 19,
10
10
  "/resources/interactive": 13,
11
11
  "/resources/grid": 6,
12
12
  "/forms": 14,
@@ -30,6 +30,26 @@ async function getTemplate() {
30
30
  }
31
31
  let _templateCache = null;
32
32
 
33
+ /**
34
+ * Strip hidden items from the navigation config before public injection.
35
+ * Returns a shallow clone — never mutates the cached config object.
36
+ *
37
+ * @param {object} nav - Raw navigation config from getConfig('navigation')
38
+ * @returns {object} Filtered navigation object safe for public injection
39
+ */
40
+ function filterHiddenNavItems(nav) {
41
+ const items = (nav.items || [])
42
+ .filter(item => !item.hidden)
43
+ .map(item => {
44
+ if (!item.items?.length) return item;
45
+ const visibleChildren = item.items.filter(c => !c.hidden);
46
+ return visibleChildren.length === item.items.length
47
+ ? item
48
+ : {...item, items: visibleChildren};
49
+ });
50
+ return {...nav, items};
51
+ }
52
+
33
53
  /**
34
54
  * Render a page to a full HTML string.
35
55
  *
@@ -98,9 +118,18 @@ export async function renderPage(page) {
98
118
  showNavbar: preset.navbar !== false,
99
119
  showFooter: preset.footer !== false,
100
120
  showSidebar: preset.sidebar === true || page.sidebar === true,
101
- navJson: JSON.stringify(navigation).replace(/<\/script>/gi, '<\\/script>'),
121
+ navJson: JSON.stringify(filterHiddenNavItems(navigation)).replace(/<\/script>/gi, '<\\/script>'),
102
122
  siteJson: JSON.stringify(Object.assign(
103
- { title: site.title, footer: site.footer, social: site.social || null },
123
+ {
124
+ title: site.title,
125
+ footer: site.footer
126
+ ? {
127
+ ...site.footer,
128
+ links: (site.footer.links || []).filter(l => !l.hidden)
129
+ }
130
+ : site.footer,
131
+ social: site.social || null
132
+ },
104
133
  site.backToTop?.enabled ? { backToTop: site.backToTop } : {},
105
134
  site.cookieConsent?.enabled ? {cookieConsent: site.cookieConsent} : {},
106
135
  autoTheme ? {autoTheme} : {}
@@ -1 +0,0 @@
1
- <!-- example-analytics: head injection (empty — tracking is done via body script) -->
@@ -1,66 +0,0 @@
1
- {
2
- "slug": "contacts",
3
- "title": "Contacts",
4
- "description": "Contact Information",
5
- "fields": [
6
- {
7
- "name": "full_name",
8
- "type": "string",
9
- "label": "Full Name",
10
- "required": false,
11
- "placeholder": "Full Name",
12
- "helper": "Full Name",
13
- "minLength": 8,
14
- "maxLength": 255
15
- },
16
- {
17
- "type": "spacer"
18
- },
19
- {
20
- "name": "phone_number",
21
- "type": "tel",
22
- "label": "Phone Number",
23
- "required": false,
24
- "placeholder": "Phone Number",
25
- "helper": "Primary Phone Number"
26
- },
27
- {
28
- "type": "spacer"
29
- },
30
- {
31
- "name": "email_address",
32
- "type": "string",
33
- "label": "Email Address",
34
- "required": false,
35
- "placeholder": "Email Address",
36
- "helper": "Email Address",
37
- "minLength": 8,
38
- "maxLength": 255
39
- }
40
- ],
41
- "settings": {
42
- "submitText": "Submit",
43
- "successMessage": "Thank you for your submission.",
44
- "layout": "stacked",
45
- "honeypot": true,
46
- "rateLimitPerMinute": 3
47
- },
48
- "actions": {
49
- "email": {
50
- "enabled": false,
51
- "recipients": "",
52
- "subjectPrefix": "[Contacts]"
53
- },
54
- "webhook": {
55
- "enabled": false,
56
- "url": "",
57
- "method": "POST"
58
- },
59
- "collection": {
60
- "enabled": true,
61
- "slug": "contacts"
62
- }
63
- },
64
- "createdAt": "2026-03-17T12:35:44.569Z",
65
- "updatedAt": "2026-03-17T12:35:44.569Z"
66
- }
@@ -1,103 +0,0 @@
1
- {
2
- "slug": "enquiries",
3
- "title": "Enquiries",
4
- "description": "Get in touch with us",
5
- "fields": [
6
- {
7
- "name": "full_name",
8
- "type": "string",
9
- "label": "Full Name",
10
- "required": true,
11
- "placeholder": "Your full name",
12
- "helper": "",
13
- "validation": {
14
- "min": 2,
15
- "max": 100
16
- }
17
- },
18
- {
19
- "name": "email",
20
- "type": "email",
21
- "label": "Email Address",
22
- "required": true,
23
- "placeholder": "your@email.com",
24
- "helper": ""
25
- },
26
- {
27
- "name": "phone",
28
- "type": "tel",
29
- "label": "Phone Number",
30
- "required": false,
31
- "placeholder": "+44 7700 000000",
32
- "helper": "Optional"
33
- },
34
- {
35
- "name": "subject",
36
- "type": "select",
37
- "label": "Subject",
38
- "required": true,
39
- "placeholder": "Please select a subject",
40
- "helper": "",
41
- "options": [
42
- {
43
- "value": "general",
44
- "label": "General Enquiry"
45
- },
46
- {
47
- "value": "support",
48
- "label": "Support"
49
- },
50
- {
51
- "value": "sales",
52
- "label": "Sales"
53
- },
54
- {
55
- "value": "partnership",
56
- "label": "Partnership"
57
- },
58
- {
59
- "value": "other",
60
- "label": "Other"
61
- }
62
- ]
63
- },
64
- {
65
- "name": "message",
66
- "type": "textarea",
67
- "label": "Message",
68
- "required": true,
69
- "placeholder": "How can we help you?",
70
- "helper": "",
71
- "rows": 4,
72
- "validation": {
73
- "min": 10,
74
- "max": 2000
75
- }
76
- }
77
- ],
78
- "settings": {
79
- "submitText": "Send Message",
80
- "successMessage": "Thanks for reaching out! We'll get back to you shortly.",
81
- "layout": "stacked",
82
- "honeypot": true,
83
- "rateLimitPerMinute": 3
84
- },
85
- "actions": {
86
- "email": {
87
- "enabled": false,
88
- "recipients": "",
89
- "subjectPrefix": "[enquiries]"
90
- },
91
- "webhook": {
92
- "enabled": false,
93
- "url": "",
94
- "method": "POST"
95
- },
96
- "collection": {
97
- "enabled": true,
98
- "slug": "enquiries"
99
- }
100
- },
101
- "createdAt": "2026-03-17T12:35:44.569Z",
102
- "updatedAt": "2026-03-17T12:35:44.569Z"
103
- }
@@ -1,131 +0,0 @@
1
- {
2
- "slug": "feedback",
3
- "title": "Feedback",
4
- "description": "Share your feedback with us",
5
- "fields": [
6
- {
7
- "name": "name",
8
- "type": "string",
9
- "label": "Your Name",
10
- "required": true,
11
- "placeholder": "Please enter your full name",
12
- "helper": "Please enter your full name",
13
- "validation": {
14
- "min": 2,
15
- "max": 100
16
- }
17
- },
18
- {
19
- "name": "email",
20
- "type": "email",
21
- "label": "Email Address",
22
- "required": true,
23
- "placeholder": "your@email.com",
24
- "helper": "Please enter your email address"
25
- },
26
- {
27
- "name": "rating",
28
- "type": "select",
29
- "label": "Overall Rating",
30
- "required": true,
31
- "helper": "Tell us how we are doing!",
32
- "options": [
33
- {
34
- "value": "none",
35
- "label": "Please Choose"
36
- },
37
- {
38
- "value": "excellent",
39
- "label": "Excellent"
40
- },
41
- {
42
- "value": "good",
43
- "label": "Good"
44
- },
45
- {
46
- "value": "average",
47
- "label": "Average"
48
- },
49
- {
50
- "value": "poor",
51
- "label": "Poor"
52
- }
53
- ]
54
- },
55
- {
56
- "name": "category",
57
- "type": "select",
58
- "label": "Category",
59
- "required": true,
60
- "placeholder": "Please select a category",
61
- "helper": "",
62
- "options": [
63
- {
64
- "value": "general",
65
- "label": "General"
66
- },
67
- {
68
- "value": "bug-report",
69
- "label": "Bug Report"
70
- },
71
- {
72
- "value": "feature-request",
73
- "label": "Feature Request"
74
- },
75
- {
76
- "value": "praise",
77
- "label": "Praise"
78
- }
79
- ]
80
- },
81
- {
82
- "name": "subject",
83
- "type": "string",
84
- "label": "Subject",
85
- "required": true,
86
- "placeholder": "Brief summary of your feedback",
87
- "helper": "",
88
- "validation": {
89
- "max": 200
90
- }
91
- },
92
- {
93
- "name": "message",
94
- "type": "textarea",
95
- "label": "Your Feedback",
96
- "required": true,
97
- "placeholder": "Please share your thoughts in detail…",
98
- "helper": "Please share your thoughts in detail…",
99
- "rows": 4,
100
- "validation": {
101
- "min": 10,
102
- "max": 2000
103
- }
104
- }
105
- ],
106
- "settings": {
107
- "submitText": "Submit Feedback",
108
- "successMessage": "Thank you for your feedback! We appreciate you taking the time.",
109
- "layout": "stacked",
110
- "honeypot": true,
111
- "rateLimitPerMinute": 3
112
- },
113
- "actions": {
114
- "email": {
115
- "enabled": true,
116
- "recipients": "",
117
- "subjectPrefix": "[feedback]"
118
- },
119
- "webhook": {
120
- "enabled": false,
121
- "url": "",
122
- "method": "POST"
123
- },
124
- "collection": {
125
- "enabled": true,
126
- "slug": "feedback"
127
- }
128
- },
129
- "createdAt": "2026-03-17T12:35:44.569Z",
130
- "updatedAt": "2026-03-17T12:35:44.569Z"
131
- }
@@ -1,79 +0,0 @@
1
- {
2
- "slug": "notes",
3
- "title": "Notes",
4
- "description": "Free-form notes with categories and tags.",
5
- "fields": [
6
- {
7
- "name": "title",
8
- "type": "string",
9
- "label": "Title",
10
- "required": true,
11
- "placeholder": "Note title"
12
- },
13
- {
14
- "name": "content",
15
- "type": "textarea",
16
- "label": "Content",
17
- "required": true,
18
- "placeholder": "Write your note here…",
19
- "rows": 5
20
- },
21
- {
22
- "name": "category",
23
- "type": "select",
24
- "label": "Category",
25
- "required": false,
26
- "options": [
27
- {
28
- "value": "general",
29
- "label": "General"
30
- },
31
- {
32
- "value": "idea",
33
- "label": "Idea"
34
- },
35
- {
36
- "value": "reminder",
37
- "label": "Reminder"
38
- },
39
- {
40
- "value": "reference",
41
- "label": "Reference"
42
- }
43
- ]
44
- },
45
- {
46
- "name": "tags",
47
- "type": "string",
48
- "label": "Tags",
49
- "required": false,
50
- "placeholder": "Comma-separated tags",
51
- "helper": "Separate tags with commas"
52
- }
53
- ],
54
- "settings": {
55
- "submitText": "Save Note",
56
- "successMessage": "Note saved successfully.",
57
- "layout": "stacked",
58
- "honeypot": true,
59
- "rateLimitPerMinute": 3
60
- },
61
- "actions": {
62
- "email": {
63
- "enabled": false,
64
- "recipients": "",
65
- "subjectPrefix": "[notes]"
66
- },
67
- "webhook": {
68
- "enabled": false,
69
- "url": "",
70
- "method": "POST"
71
- },
72
- "collection": {
73
- "enabled": true,
74
- "slug": "notes"
75
- }
76
- },
77
- "createdAt": "2026-03-17T12:35:44.569Z",
78
- "updatedAt": "2026-03-17T12:35:44.569Z"
79
- }
@@ -1,100 +0,0 @@
1
- {
2
- "slug": "to-do",
3
- "title": "To-Do",
4
- "description": "Task tracking with status, priority, and due dates.",
5
- "fields": [
6
- {
7
- "name": "title",
8
- "type": "string",
9
- "label": "Title",
10
- "required": true,
11
- "placeholder": "Task title"
12
- },
13
- {
14
- "name": "description",
15
- "type": "textarea",
16
- "label": "Description",
17
- "required": false,
18
- "placeholder": "Task details…",
19
- "rows": 3
20
- },
21
- {
22
- "name": "status",
23
- "type": "select",
24
- "label": "Status",
25
- "required": true,
26
- "options": [
27
- {
28
- "value": "pending",
29
- "label": "Pending"
30
- },
31
- {
32
- "value": "in-progress",
33
- "label": "In Progress"
34
- },
35
- {
36
- "value": "done",
37
- "label": "Done"
38
- }
39
- ]
40
- },
41
- {
42
- "name": "priority",
43
- "type": "select",
44
- "label": "Priority",
45
- "required": false,
46
- "options": [
47
- {
48
- "value": "low",
49
- "label": "Low"
50
- },
51
- {
52
- "value": "medium",
53
- "label": "Medium"
54
- },
55
- {
56
- "value": "high",
57
- "label": "High"
58
- }
59
- ]
60
- },
61
- {
62
- "name": "due_date",
63
- "type": "date",
64
- "label": "Due Date",
65
- "required": false
66
- },
67
- {
68
- "name": "assigned_to",
69
- "type": "string",
70
- "label": "Assigned To",
71
- "required": false,
72
- "placeholder": "Name or email"
73
- }
74
- ],
75
- "settings": {
76
- "submitText": "Add Task",
77
- "successMessage": "Task added successfully.",
78
- "layout": "stacked",
79
- "honeypot": true,
80
- "rateLimitPerMinute": 3
81
- },
82
- "actions": {
83
- "email": {
84
- "enabled": false,
85
- "recipients": "",
86
- "subjectPrefix": "[to-do]"
87
- },
88
- "webhook": {
89
- "enabled": false,
90
- "url": "",
91
- "method": "POST"
92
- },
93
- "collection": {
94
- "enabled": true,
95
- "slug": "to-do"
96
- }
97
- },
98
- "createdAt": "2026-03-17T12:35:44.569Z",
99
- "updatedAt": "2026-03-17T12:35:44.569Z"
100
- }