diff --git a/HeadySystems_v13/apps/heady_admin_ui/css/settings.css b/HeadySystems_v13/apps/heady_admin_ui/css/settings.css
new file mode 100644
index 00000000..2db88636
--- /dev/null
+++ b/HeadySystems_v13/apps/heady_admin_ui/css/settings.css
@@ -0,0 +1,686 @@
+/* ========================================
+ Heady Admin UI - Settings Page Styles
+ ======================================== */
+
+/* Settings Container Layout */
+.settings-container {
+ display: flex;
+ min-height: calc(100vh - 80px);
+ gap: var(--spacing-lg);
+ max-width: 1400px;
+ margin: 0 auto;
+ padding: var(--spacing-xl);
+}
+
+/* Settings Sidebar Navigation */
+.settings-sidebar {
+ width: 250px;
+ flex-shrink: 0;
+}
+
+.settings-nav {
+ position: sticky;
+ top: var(--spacing-xl);
+ background: var(--bg-card);
+ backdrop-filter: blur(10px);
+ border-radius: var(--radius-lg);
+ padding: var(--spacing-md);
+ border: 1px solid var(--border-color);
+}
+
+.settings-nav-title {
+ font-size: 1.25rem;
+ font-weight: 600;
+ margin-bottom: var(--spacing-md);
+ color: var(--text-primary);
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.settings-nav-item {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+ padding: var(--spacing-sm) var(--spacing-md);
+ margin-bottom: var(--spacing-xs);
+ border-radius: var(--radius-md);
+ background: transparent;
+ border: none;
+ color: var(--text-secondary);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ width: 100%;
+ text-align: left;
+ font-size: 0.95rem;
+}
+
+.settings-nav-item:hover {
+ background: rgba(255, 255, 255, 0.05);
+ color: var(--text-primary);
+}
+
+.settings-nav-item.active {
+ background: var(--gradient-purple);
+ color: white;
+ font-weight: 500;
+}
+
+.settings-nav-item .icon {
+ font-size: 1.2rem;
+}
+
+/* Settings Content Area */
+.settings-content {
+ flex: 1;
+ overflow-y: auto;
+}
+
+/* Settings Section */
+.settings-section {
+ background: var(--bg-card);
+ backdrop-filter: blur(10px);
+ border-radius: var(--radius-lg);
+ padding: var(--spacing-xl);
+ margin-bottom: var(--spacing-lg);
+ border: 1px solid var(--border-color);
+}
+
+.settings-section:last-child {
+ margin-bottom: 0;
+}
+
+.settings-section-title {
+ font-size: 1.5rem;
+ font-weight: 600;
+ margin-bottom: var(--spacing-md);
+ color: var(--text-primary);
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.settings-section-description {
+ color: var(--text-secondary);
+ margin-bottom: var(--spacing-xl);
+ line-height: 1.6;
+}
+
+/* Settings Form Group */
+.settings-group {
+ margin-bottom: var(--spacing-xl);
+}
+
+.settings-group:last-child {
+ margin-bottom: 0;
+}
+
+.settings-label {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: var(--spacing-md);
+}
+
+.settings-label-text {
+ font-weight: 500;
+ color: var(--text-primary);
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.settings-label-description {
+ font-size: 0.875rem;
+ color: var(--text-secondary);
+ margin-top: var(--spacing-xs);
+}
+
+/* Toggle Switch */
+.toggle-switch {
+ position: relative;
+ width: 52px;
+ height: 28px;
+ cursor: pointer;
+}
+
+.toggle-switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.toggle-slider {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: var(--radius-full);
+ transition: all 0.3s ease;
+}
+
+.toggle-slider::before {
+ content: '';
+ position: absolute;
+ height: 20px;
+ width: 20px;
+ left: 4px;
+ bottom: 4px;
+ background: white;
+ border-radius: 50%;
+ transition: all 0.3s ease;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+}
+
+.toggle-switch input:checked + .toggle-slider {
+ background: var(--gradient-purple);
+}
+
+.toggle-switch input:checked + .toggle-slider::before {
+ transform: translateX(24px);
+}
+
+.toggle-switch:hover .toggle-slider {
+ background: rgba(255, 255, 255, 0.15);
+}
+
+.toggle-switch input:checked:hover + .toggle-slider {
+ background: linear-gradient(135deg, #7786f1 0%, #8559b0 100%);
+}
+
+/* Range Slider */
+.range-slider {
+ width: 100%;
+}
+
+.range-slider-container {
+ position: relative;
+ margin-bottom: var(--spacing-sm);
+}
+
+.range-slider input[type="range"] {
+ width: 100%;
+ height: 6px;
+ border-radius: var(--radius-full);
+ background: rgba(255, 255, 255, 0.1);
+ outline: none;
+ -webkit-appearance: none;
+ cursor: pointer;
+}
+
+.range-slider input[type="range"]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: var(--gradient-purple);
+ cursor: pointer;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ transition: all 0.2s ease;
+}
+
+.range-slider input[type="range"]::-webkit-slider-thumb:hover {
+ transform: scale(1.2);
+ box-shadow: 0 0 0 8px rgba(99, 102, 241, 0.2);
+}
+
+.range-slider input[type="range"]::-moz-range-thumb {
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ background: var(--gradient-purple);
+ cursor: pointer;
+ border: none;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ transition: all 0.2s ease;
+}
+
+.range-slider input[type="range"]::-moz-range-thumb:hover {
+ transform: scale(1.2);
+ box-shadow: 0 0 0 8px rgba(99, 102, 241, 0.2);
+}
+
+.range-slider-labels {
+ display: flex;
+ justify-content: space-between;
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+}
+
+.range-slider-value {
+ text-align: center;
+ font-weight: 600;
+ color: var(--color-primary);
+ margin-top: var(--spacing-xs);
+}
+
+/* Color Picker */
+.color-picker-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
+ gap: var(--spacing-sm);
+ margin-bottom: var(--spacing-md);
+}
+
+.color-option {
+ width: 50px;
+ height: 50px;
+ border-radius: var(--radius-md);
+ border: 2px solid transparent;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ position: relative;
+}
+
+.color-option:hover {
+ transform: scale(1.1);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+}
+
+.color-option.active {
+ border-color: white;
+ box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.2);
+}
+
+.color-option.active::after {
+ content: '✓';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: white;
+ font-size: 1.5rem;
+ font-weight: bold;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
+}
+
+.color-custom {
+ display: flex;
+ gap: var(--spacing-sm);
+ align-items: center;
+}
+
+.color-custom input[type="color"] {
+ width: 50px;
+ height: 50px;
+ border: 2px solid var(--border-color);
+ border-radius: var(--radius-md);
+ cursor: pointer;
+ background: transparent;
+}
+
+.color-custom input[type="text"] {
+ flex: 1;
+ padding: var(--spacing-sm) var(--spacing-md);
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-md);
+ color: var(--text-primary);
+ font-family: var(--font-mono);
+}
+
+/* Select Dropdown */
+.select-dropdown {
+ position: relative;
+ width: 100%;
+}
+
+.select-dropdown select {
+ width: 100%;
+ padding: var(--spacing-sm) var(--spacing-md);
+ padding-right: 2.5rem;
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-md);
+ color: var(--text-primary);
+ font-size: 0.95rem;
+ cursor: pointer;
+ appearance: none;
+ transition: all 0.2s ease;
+}
+
+.select-dropdown select:hover {
+ background: rgba(255, 255, 255, 0.08);
+ border-color: var(--color-primary);
+}
+
+.select-dropdown select:focus {
+ outline: none;
+ border-color: var(--color-primary);
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
+}
+
+.select-dropdown::after {
+ content: '▼';
+ position: absolute;
+ right: var(--spacing-md);
+ top: 50%;
+ transform: translateY(-50%);
+ pointer-events: none;
+ color: var(--text-secondary);
+ font-size: 0.75rem;
+}
+
+/* Checkbox Group */
+.checkbox-group {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-sm);
+}
+
+.checkbox-item {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+ padding: var(--spacing-sm);
+ border-radius: var(--radius-md);
+ transition: background 0.2s ease;
+}
+
+.checkbox-item:hover {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.checkbox-item input[type="checkbox"] {
+ width: 20px;
+ height: 20px;
+ cursor: pointer;
+ accent-color: var(--color-primary);
+}
+
+.checkbox-item label {
+ cursor: pointer;
+ color: var(--text-primary);
+ user-select: none;
+}
+
+/* Button Styles */
+.settings-button {
+ padding: var(--spacing-sm) var(--spacing-lg);
+ background: var(--gradient-purple);
+ color: white;
+ border: none;
+ border-radius: var(--radius-md);
+ font-size: 0.95rem;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ display: inline-flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.settings-button:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
+}
+
+.settings-button.secondary {
+ background: rgba(255, 255, 255, 0.1);
+}
+
+.settings-button.secondary:hover {
+ background: rgba(255, 255, 255, 0.15);
+}
+
+.settings-button.danger {
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
+}
+
+.settings-button.danger:hover {
+ box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
+}
+
+.settings-button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.settings-button:disabled:hover {
+ transform: none;
+ box-shadow: none;
+}
+
+.settings-buttons {
+ display: flex;
+ gap: var(--spacing-md);
+ margin-top: var(--spacing-xl);
+}
+
+/* Keyboard Shortcut Item */
+.shortcut-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--spacing-md);
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: var(--radius-md);
+ margin-bottom: var(--spacing-sm);
+}
+
+.shortcut-label {
+ color: var(--text-primary);
+ font-weight: 500;
+}
+
+.shortcut-key {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--spacing-xs);
+ padding: var(--spacing-xs) var(--spacing-sm);
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-sm);
+ font-family: var(--font-mono);
+ font-size: 0.875rem;
+ color: var(--text-primary);
+}
+
+.shortcut-key kbd {
+ background: rgba(255, 255, 255, 0.1);
+ padding: 2px 6px;
+ border-radius: var(--radius-sm);
+}
+
+/* System Info */
+.system-info-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: var(--spacing-md);
+}
+
+.system-info-card {
+ padding: var(--spacing-md);
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: var(--radius-md);
+ border: 1px solid var(--border-color);
+}
+
+.system-info-label {
+ font-size: 0.875rem;
+ color: var(--text-secondary);
+ margin-bottom: var(--spacing-xs);
+}
+
+.system-info-value {
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: var(--text-primary);
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.system-info-value .icon {
+ font-size: 1.5rem;
+}
+
+/* Storage Bar */
+.storage-bar {
+ width: 100%;
+ height: 8px;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: var(--radius-full);
+ overflow: hidden;
+ margin-top: var(--spacing-sm);
+}
+
+.storage-fill {
+ height: 100%;
+ background: var(--gradient-purple);
+ border-radius: var(--radius-full);
+ transition: width 0.3s ease;
+}
+
+.storage-text {
+ font-size: 0.875rem;
+ color: var(--text-secondary);
+ margin-top: var(--spacing-xs);
+}
+
+/* Save Status Indicator */
+.save-status {
+ position: fixed;
+ bottom: var(--spacing-xl);
+ right: var(--spacing-xl);
+ padding: var(--spacing-sm) var(--spacing-lg);
+ background: var(--bg-card);
+ backdrop-filter: blur(10px);
+ border: 1px solid var(--border-color);
+ border-radius: var(--radius-full);
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+ font-size: 0.875rem;
+ opacity: 0;
+ transform: translateY(20px);
+ transition: all 0.3s ease;
+ pointer-events: none;
+ z-index: 1000;
+}
+
+.save-status.visible {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.save-status .icon {
+ font-size: 1rem;
+}
+
+.save-status.saving {
+ color: var(--color-warning);
+}
+
+.save-status.saved {
+ color: var(--color-success);
+}
+
+.save-status.error {
+ color: var(--color-error);
+}
+
+/* Responsive Design */
+@media (max-width: 968px) {
+ .settings-container {
+ flex-direction: column;
+ padding: var(--spacing-md);
+ }
+
+ .settings-sidebar {
+ width: 100%;
+ }
+
+ .settings-nav {
+ position: static;
+ }
+
+ .settings-nav-item {
+ display: inline-flex;
+ width: auto;
+ }
+
+ .color-picker-grid {
+ grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
+ }
+
+ .color-option {
+ width: 40px;
+ height: 40px;
+ }
+
+ .system-info-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* Animation Speed Classes */
+body[data-animation-speed="off"] * {
+ animation: none !important;
+ transition: none !important;
+}
+
+body[data-animation-speed="slow"] * {
+ animation-duration: 1.5s !important;
+ transition-duration: 0.5s !important;
+}
+
+body[data-animation-speed="normal"] * {
+ /* Default speeds */
+}
+
+body[data-animation-speed="fast"] * {
+ animation-duration: 0.5s !important;
+ transition-duration: 0.1s !important;
+}
+
+/* Reduce Motion */
+@media (prefers-reduced-motion: reduce) {
+ body:not([data-reduce-motion="false"]) * {
+ animation: none !important;
+ transition: none !important;
+ }
+}
+
+body[data-reduce-motion="true"] * {
+ animation: none !important;
+ transition: none !important;
+}
+
+/* Font Size Classes */
+body[data-font-size="small"] {
+ font-size: 14px;
+}
+
+body[data-font-size="medium"] {
+ font-size: 16px;
+}
+
+body[data-font-size="large"] {
+ font-size: 18px;
+}
+
+body[data-font-size="xlarge"] {
+ font-size: 20px;
+}
+
+/* Compact Mode */
+body[data-compact-mode="true"] {
+ --spacing-xs: 0.125rem;
+ --spacing-sm: 0.25rem;
+ --spacing-md: 0.5rem;
+ --spacing-lg: 0.75rem;
+ --spacing-xl: 1rem;
+ --spacing-2xl: 1.5rem;
+}
+
+body[data-compact-mode="true"] .settings-section {
+ padding: var(--spacing-lg);
+}
+
+body[data-compact-mode="true"] .settings-container {
+ padding: var(--spacing-lg);
+}
diff --git a/HeadySystems_v13/apps/heady_admin_ui/index.html b/HeadySystems_v13/apps/heady_admin_ui/index.html
index 0087274d..b8464ae8 100644
--- a/HeadySystems_v13/apps/heady_admin_ui/index.html
+++ b/HeadySystems_v13/apps/heady_admin_ui/index.html
@@ -42,6 +42,9 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 🎨
+ Appearance Settings
+
+
+ Customize the look and feel of your dashboard
+
+
+
+
+
+
+
+ 🌓
+ Theme
+
+
+ Choose your preferred color theme
+
+
+
+
+
+
+
+
+
+
+
+ 🎨
+ Accent Color
+
+
+ Choose a primary accent color for the interface
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ⚡
+ Animation Speed
+
+
+ Control the speed of UI animations
+
+
+
+
+
+
+ Off
+ Slow
+ Normal
+ Fast
+
+
Normal
+
+
+
+
+
+
+
+
+ ♿
+ Reduce Motion
+
+
+ Minimize animations for accessibility
+
+
+
+
+
+
+
+
+
+
+
+ 🔤
+ Font Size
+
+
+ Adjust text size for better readability
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📐
+ Compact Mode
+
+
+ Use denser spacing for more content
+
+
+
+
+
+
+
+
+
+
+ 📊
+ Dashboard Preferences
+
+
+ Configure your dashboard experience
+
+
+
+
+
+
+
+ 🏠
+ Default View
+
+
+ Section to show when dashboard loads
+
+
+
+
+
+
+
+
+
+
+
+ 👁️
+ Visible Verticals
+
+
+ Choose which verticals to display on the main dashboard
+
+
+
+
+
+
+
+
+
+ 🔄
+ Auto-refresh
+
+
+ Automatically refresh dashboard data
+
+
+
+
+
+
+
+
+
+
+
+ ⏱️
+ Refresh Interval
+
+
+ How often to refresh data
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 🔔
+ Notification Sound
+
+
+ Play sound for notifications
+
+
+
+
+
+
+
+
+
+ 🔊
+ Notification Volume
+
+
+
+
+
+
+
+
+ ⌨️
+ Keyboard Shortcuts
+
+
+ View and customize keyboard shortcuts
+
+
+
+
+
+ Dashboard View
+ Alt + D
+
+
+ Verticals View
+ Alt + V
+
+
+ Governance View
+ Alt + G
+
+
+ Activity View
+ Alt + A
+
+
+ Settings
+ Alt + S
+
+
+ Toggle Theme
+ Alt + T
+
+
+ Search
+ Alt + F
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 🔒
+ Data & Privacy
+
+
+ Manage your data and privacy preferences
+
+
+
+
+
+
+
+ 📅
+ Data Retention
+
+
+ How long to keep activity logs
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📊
+ Anonymous Usage Analytics
+
+
+ Help improve Heady by sharing anonymous usage data
+
+
+
+
+
+
+
+
+
+ 💾
+ Export Your Data
+
+
+ Download all your settings and data as JSON
+
+
+
+
+
+
+
+ 🗑️
+ Clear Cache
+
+
+ Clear all locally stored data and reset to defaults
+
+
+
+
+
+
+
+
+ 💻
+ System Information
+
+
+ View system details and service status
+
+
+
+
+
Version
+
+ 🏷️
+ v13.0.0
+
+
+
+
+
Build Date
+
+ 📅
+ 2026-01-26
+
+
+
+
+
MCP Gateway
+
+ 🟢
+ Connected
+
+
+
+
+
Governance Lock
+
+ 🔒
+ v1.2.0 Active
+
+
+
+
+
Trust Domain
+
+ 🌐
+ headysystems.com
+
+
+
+
+
Active Sessions
+
+ ⚡
+ 8
+
+
+
+
+
+
+
+ 💾
+ Storage Usage
+
+
+ Loading storage information...
+
+
+
0 KB / 5 MB (0%)
+
+
+
+
+
+
+
+
+
+
+
+ 💾
+ Saved
+
+
+
+
+
+
+
+
diff --git a/HeadySystems_v13/apps/heady_admin_ui/js/main.js b/HeadySystems_v13/apps/heady_admin_ui/js/main.js
index b3e55276..5f862bfc 100644
--- a/HeadySystems_v13/apps/heady_admin_ui/js/main.js
+++ b/HeadySystems_v13/apps/heady_admin_ui/js/main.js
@@ -3,27 +3,132 @@
* Handles interactivity and dynamic updates
*/
+// Apply Settings Globally
+function applySettings() {
+ // Only apply if settingsManager exists (it won't on settings.html during initial load)
+ if (typeof window.settingsManager === 'undefined') {
+ return;
+ }
+
+ const settings = window.settingsManager;
+
+ // Apply theme
+ const theme = settings.get('theme');
+ applyTheme(theme);
+
+ // Apply accent color
+ const accentColor = settings.get('accentColor');
+ applyAccentColor(accentColor);
+
+ // Apply animation speed
+ const animationSpeed = settings.get('animationSpeed');
+ applyAnimationSpeed(animationSpeed);
+
+ // Apply reduce motion
+ const reduceMotion = settings.get('reduceMotion');
+ applyReduceMotion(reduceMotion);
+
+ // Apply font size
+ const fontSize = settings.get('fontSize');
+ applyFontSize(fontSize);
+
+ // Apply compact mode
+ const compactMode = settings.get('compactMode');
+ applyCompactMode(compactMode);
+
+ // Listen for settings changes
+ settings.onChange((key, newValue) => {
+ if (key === 'theme' || key === '*') applyTheme(settings.get('theme'));
+ if (key === 'accentColor' || key === '*') applyAccentColor(settings.get('accentColor'));
+ if (key === 'animationSpeed' || key === '*') applyAnimationSpeed(settings.get('animationSpeed'));
+ if (key === 'reduceMotion' || key === '*') applyReduceMotion(settings.get('reduceMotion'));
+ if (key === 'fontSize' || key === '*') applyFontSize(settings.get('fontSize'));
+ if (key === 'compactMode' || key === '*') applyCompactMode(settings.get('compactMode'));
+ });
+}
+
+function applyTheme(theme) {
+ const body = document.body;
+
+ // Handle auto theme
+ if (theme === 'auto') {
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ theme = prefersDark ? 'dark' : 'light';
+ }
+
+ if (theme === 'light') {
+ body.classList.add('light-theme');
+ // Update theme toggle icon if it exists
+ const icon = document.querySelector('.theme-toggle .icon');
+ if (icon && icon.textContent.includes('🌙')) {
+ icon.textContent = '☀️';
+ }
+ } else {
+ body.classList.remove('light-theme');
+ // Update theme toggle icon if it exists
+ const icon = document.querySelector('.theme-toggle .icon');
+ if (icon && icon.textContent.includes('☀️')) {
+ icon.textContent = '🌙';
+ }
+ }
+}
+
+function applyAccentColor(color) {
+ document.documentElement.style.setProperty('--color-primary', color);
+}
+
+function applyAnimationSpeed(speed) {
+ document.body.setAttribute('data-animation-speed', speed);
+}
+
+function applyReduceMotion(enabled) {
+ document.body.setAttribute('data-reduce-motion', enabled);
+}
+
+function applyFontSize(size) {
+ document.body.setAttribute('data-font-size', size);
+}
+
+function applyCompactMode(enabled) {
+ document.body.setAttribute('data-compact-mode', enabled);
+}
+
// Theme Toggle
function initThemeToggle() {
- const themeToggle = document.querySelector('.theme-toggle');
+ const themeToggleButtons = document.querySelectorAll('.theme-toggle');
const body = document.body;
- if (themeToggle) {
+ themeToggleButtons.forEach(themeToggle => {
+ // Skip if this is the settings link
+ if (themeToggle.tagName === 'A') return;
+
themeToggle.addEventListener('click', () => {
- body.classList.toggle('light-theme');
- const icon = themeToggle.querySelector('.icon');
- icon.textContent = body.classList.contains('light-theme') ? '☀️' : '🌙';
+ const newTheme = body.classList.contains('light-theme') ? 'dark' : 'light';
- // Save preference
- localStorage.setItem('theme', body.classList.contains('light-theme') ? 'light' : 'dark');
+ // Update via settings manager if available
+ if (typeof window.settingsManager !== 'undefined') {
+ window.settingsManager.set('theme', newTheme);
+ } else {
+ // Fallback for backwards compatibility
+ body.classList.toggle('light-theme');
+ const icon = themeToggle.querySelector('.icon');
+ if (icon) {
+ icon.textContent = body.classList.contains('light-theme') ? '☀️' : '🌙';
+ }
+ localStorage.setItem('theme', newTheme);
+ }
});
+ });
- // Load saved theme
+ // Load saved theme (fallback if settings manager not available)
+ if (typeof window.settingsManager === 'undefined') {
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'light') {
body.classList.add('light-theme');
- const icon = themeToggle.querySelector('.icon');
- icon.textContent = '☀️';
+ themeToggleButtons.forEach(toggle => {
+ const icon = toggle.querySelector('.icon');
+ if (icon) icon.textContent = '☀️';
+ });
}
}
}
@@ -267,10 +372,24 @@ function initKeyboardNavigation() {
document.querySelector('[data-view="governance"]')?.click();
}
+ // Alt + A for Activity
+ if (e.altKey && e.key === 'a') {
+ e.preventDefault();
+ document.querySelector('[data-view="activity"]')?.click();
+ }
+
+ // Alt + S for Settings
+ if (e.altKey && e.key === 's') {
+ e.preventDefault();
+ window.location.href = 'settings.html';
+ }
+
// Alt + T for Theme Toggle
if (e.altKey && e.key === 't') {
e.preventDefault();
- document.querySelector('.theme-toggle')?.click();
+ const themeToggle = Array.from(document.querySelectorAll('.theme-toggle'))
+ .find(el => el.tagName !== 'A');
+ themeToggle?.click();
}
});
}
@@ -297,6 +416,9 @@ function playWelcomeAnimation() {
document.addEventListener('DOMContentLoaded', () => {
console.log('🚀 Heady Admin UI Initialized');
+ // Apply settings first
+ applySettings();
+
// Initialize all features
initThemeToggle();
initNavigation();
diff --git a/HeadySystems_v13/apps/heady_admin_ui/js/settings-manager.js b/HeadySystems_v13/apps/heady_admin_ui/js/settings-manager.js
new file mode 100644
index 00000000..628a8e4b
--- /dev/null
+++ b/HeadySystems_v13/apps/heady_admin_ui/js/settings-manager.js
@@ -0,0 +1,309 @@
+/**
+ * Heady Admin UI - Settings Manager
+ * Manages user preferences and settings with localStorage persistence
+ */
+
+class SettingsManager {
+ constructor() {
+ // Default settings configuration
+ this.defaults = {
+ // Appearance Settings
+ theme: 'dark',
+ accentColor: '#6366f1',
+ animationSpeed: 'normal',
+ reduceMotion: false,
+ fontSize: 'medium',
+ compactMode: false,
+
+ // Dashboard Preferences
+ defaultView: 'dashboard',
+ visibleVerticals: ['make', 'field', 'legacy', 'kinetic', 'bio', 'ed', 'guard'],
+ autoRefresh: true,
+ autoRefreshInterval: 30000,
+ notificationSound: true,
+ notificationVolume: 0.5,
+
+ // Data & Privacy
+ dataRetention: 30,
+ analytics: true,
+
+ // Keyboard Shortcuts
+ shortcuts: {
+ 'dashboard': 'Alt+D',
+ 'verticals': 'Alt+V',
+ 'governance': 'Alt+G',
+ 'activity': 'Alt+A',
+ 'settings': 'Alt+S',
+ 'theme': 'Alt+T',
+ 'search': 'Alt+F'
+ }
+ };
+
+ // Load settings from localStorage or use defaults
+ this.settings = this.load();
+
+ // Callbacks for settings changes
+ this.changeCallbacks = [];
+
+ // Auto-save debounce timer
+ this.saveTimer = null;
+ this.saveDelay = 500; // 500ms debounce
+ }
+
+ /**
+ * Load settings from localStorage
+ * @returns {Object} Settings object
+ */
+ load() {
+ try {
+ const stored = localStorage.getItem('heady_settings');
+ if (stored) {
+ const parsed = JSON.parse(stored);
+ // Merge with defaults to ensure all keys exist
+ return { ...this.defaults, ...parsed };
+ }
+ } catch (error) {
+ console.error('Failed to load settings:', error);
+ }
+ return { ...this.defaults };
+ }
+
+ /**
+ * Save settings to localStorage
+ * @param {boolean} immediate - Skip debounce and save immediately
+ */
+ save(immediate = false) {
+ if (immediate) {
+ this._performSave();
+ } else {
+ // Debounce save
+ clearTimeout(this.saveTimer);
+ this.saveTimer = setTimeout(() => this._performSave(), this.saveDelay);
+ }
+ }
+
+ /**
+ * Internal method to perform the actual save
+ * @private
+ */
+ _performSave() {
+ try {
+ localStorage.setItem('heady_settings', JSON.stringify(this.settings));
+ this._notifySaveStatus('saved');
+ } catch (error) {
+ console.error('Failed to save settings:', error);
+ this._notifySaveStatus('error');
+ }
+ }
+
+ /**
+ * Get a setting value
+ * @param {string} key - Setting key (supports dot notation for nested keys)
+ * @returns {*} Setting value
+ */
+ get(key) {
+ // Support dot notation for nested keys
+ const keys = key.split('.');
+ let value = this.settings;
+
+ for (const k of keys) {
+ if (value && typeof value === 'object' && k in value) {
+ value = value[k];
+ } else {
+ return undefined;
+ }
+ }
+
+ return value;
+ }
+
+ /**
+ * Set a setting value
+ * @param {string} key - Setting key (supports dot notation for nested keys)
+ * @param {*} value - Setting value
+ * @param {boolean} immediate - Skip debounce and save immediately
+ */
+ set(key, value, immediate = false) {
+ // Support dot notation for nested keys
+ const keys = key.split('.');
+ const lastKey = keys.pop();
+ let obj = this.settings;
+
+ // Navigate to the nested object
+ for (const k of keys) {
+ if (!(k in obj)) {
+ obj[k] = {};
+ }
+ obj = obj[k];
+ }
+
+ // Set the value
+ const oldValue = obj[lastKey];
+ obj[lastKey] = value;
+
+ // Notify callbacks
+ this._notifyChange(key, value, oldValue);
+
+ // Save to localStorage
+ this.save(immediate);
+
+ this._notifySaveStatus('saving');
+ }
+
+ /**
+ * Reset all settings to defaults
+ */
+ reset() {
+ this.settings = { ...this.defaults };
+ this.save(true);
+
+ // Notify all callbacks about the reset
+ this._notifyChange('*', this.settings, null);
+ }
+
+ /**
+ * Export settings as JSON
+ * @returns {string} JSON string of settings
+ */
+ export() {
+ return JSON.stringify(this.settings, null, 2);
+ }
+
+ /**
+ * Import settings from JSON
+ * @param {string|Object} data - JSON string or object
+ * @returns {boolean} Success status
+ */
+ import(data) {
+ try {
+ const imported = typeof data === 'string' ? JSON.parse(data) : data;
+
+ // Validate imported data
+ if (!imported || typeof imported !== 'object') {
+ throw new Error('Invalid settings data');
+ }
+
+ // Merge with defaults to ensure all required keys exist
+ this.settings = { ...this.defaults, ...imported };
+ this.save(true);
+
+ // Notify all callbacks about the import
+ this._notifyChange('*', this.settings, null);
+
+ return true;
+ } catch (error) {
+ console.error('Failed to import settings:', error);
+ return false;
+ }
+ }
+
+ /**
+ * Register a callback for settings changes
+ * @param {Function} callback - Callback function(key, newValue, oldValue)
+ */
+ onChange(callback) {
+ if (typeof callback === 'function') {
+ this.changeCallbacks.push(callback);
+ }
+ }
+
+ /**
+ * Unregister a callback
+ * @param {Function} callback - Callback function to remove
+ */
+ offChange(callback) {
+ const index = this.changeCallbacks.indexOf(callback);
+ if (index > -1) {
+ this.changeCallbacks.splice(index, 1);
+ }
+ }
+
+ /**
+ * Notify all callbacks of a settings change
+ * @private
+ * @param {string} key - Changed setting key
+ * @param {*} newValue - New value
+ * @param {*} oldValue - Old value
+ */
+ _notifyChange(key, newValue, oldValue) {
+ this.changeCallbacks.forEach(callback => {
+ try {
+ callback(key, newValue, oldValue);
+ } catch (error) {
+ console.error('Settings callback error:', error);
+ }
+ });
+ }
+
+ /**
+ * Notify about save status
+ * @private
+ * @param {string} status - 'saving', 'saved', or 'error'
+ */
+ _notifySaveStatus(status) {
+ // Dispatch custom event for save status
+ const event = new CustomEvent('settings-save-status', {
+ detail: { status }
+ });
+ window.dispatchEvent(event);
+ }
+
+ /**
+ * Get storage usage information
+ * @returns {Object} Storage info with used, total, and percentage
+ */
+ getStorageInfo() {
+ try {
+ const settingsSize = new Blob([JSON.stringify(this.settings)]).size;
+ const totalUsed = Object.keys(localStorage).reduce((total, key) => {
+ return total + new Blob([localStorage.getItem(key)]).size;
+ }, 0);
+
+ // localStorage limit is typically 5-10MB, we'll use 5MB as conservative estimate
+ const limit = 5 * 1024 * 1024;
+
+ return {
+ settingsSize: this._formatBytes(settingsSize),
+ totalUsed: this._formatBytes(totalUsed),
+ limit: this._formatBytes(limit),
+ percentage: Math.round((totalUsed / limit) * 100)
+ };
+ } catch (error) {
+ console.error('Failed to get storage info:', error);
+ return {
+ settingsSize: 'Unknown',
+ totalUsed: 'Unknown',
+ limit: 'Unknown',
+ percentage: 0
+ };
+ }
+ }
+
+ /**
+ * Format bytes to human-readable string
+ * @private
+ * @param {number} bytes
+ * @returns {string}
+ */
+ _formatBytes(bytes) {
+ if (bytes === 0) return '0 Bytes';
+
+ const k = 1024;
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+
+ return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
+ }
+
+ /**
+ * Clear all settings data
+ */
+ clearAll() {
+ localStorage.removeItem('heady_settings');
+ this.settings = { ...this.defaults };
+ this._notifyChange('*', this.settings, null);
+ }
+}
+
+// Create global instance
+window.settingsManager = new SettingsManager();
diff --git a/HeadySystems_v13/apps/heady_admin_ui/settings.html b/HeadySystems_v13/apps/heady_admin_ui/settings.html
new file mode 100644
index 00000000..153f1206
--- /dev/null
+++ b/HeadySystems_v13/apps/heady_admin_ui/settings.html
@@ -0,0 +1,1015 @@
+
+
+
+
+
+
+
Settings - Heady Admin Dashboard
+
+
+
+
+
+