MorphGuard / static /js /context_theme.js
juanquy's picture
Defer context theme updates on link click to prevent browser from cancelling navigation inside iframe
2356537
Raw
History Blame Contribute Delete
35 kB
/**
* MorphGuard Context-Aware Theme Integration
*
* This module integrates the dynamic theming system with the application context,
* adapting themes based on:
* 1. User activity and behavior patterns
* 2. Application state and content
* 3. Environmental signals
* 4. Task-specific optimizations
*/
class ContextTheme {
constructor(options = {}) {
// Default options
this.options = {
detectionResultTheming: true, // Change theme based on detection results
dataVisualizationTheming: true, // Optimize for data visualization
attentionModeEnabled: true, // Focus mode for important tasks
securityAlertTheming: true, // Special theme for security alerts
adaptToContentDensity: true, // Adjust based on content density
accessibilityAdaptation: true, // Adjust based on accessibility needs
behavioralAdaptation: true, // Learn from user behavior
...options
};
// Get theme manager instance
this.themeManager = window.morphGuardTheme;
if (!this.themeManager) {
console.error('Theme manager not found. Ensure dynamic_theme.js is loaded first.');
return;
}
// Application context state
this.appContext = {
currentView: null,
currentTask: null,
detectionResult: null,
securityAlerts: [],
contentDensity: 'normal',
userBehavior: {
prefersDarkMode: null,
prefersHighContrast: null,
clickPatterns: [],
sessionDuration: 0,
frequentActions: {}
},
accessibilityNeeds: {
reducedMotion: false,
highContrast: false,
largerText: false
},
environment: {
cpuUsage: null,
memoryUsage: null,
batteryStatus: null,
networkStatus: 'online'
}
};
// Task-specific theme overrides
this.taskThemes = {
detection: {
variables: {
'--accent-primary': '#3b82f6', // Blue focus for detection
'--card-bg': 'rgba(59, 130, 246, 0.05)'
}
},
demorph: {
variables: {
'--accent-primary': '#8b5cf6', // Purple focus for demorphing
'--card-bg': 'rgba(139, 92, 246, 0.05)'
}
},
training: {
variables: {
'--accent-primary': '#10b981', // Green focus for training
'--card-bg': 'rgba(16, 185, 129, 0.05)'
}
},
security: {
variables: {
'--accent-primary': '#ef4444', // Red focus for security
'--card-bg': 'rgba(239, 68, 68, 0.05)',
'--border-color': 'rgba(239, 68, 68, 0.3)'
}
},
analytics: {
variables: {
'--accent-primary': '#f59e0b', // Amber focus for analytics
'--card-bg': 'rgba(245, 158, 11, 0.05)',
'--chart-grid': 'rgba(245, 158, 11, 0.2)'
}
}
};
// Detection result theme modifiers
this.detectionThemes = {
morphed: {
variables: {
'--card-bg': 'var(--detection-morph)',
'--border-color': 'var(--error)'
}
},
real: {
variables: {
'--card-bg': 'var(--detection-safe)',
'--border-color': 'var(--success)'
}
}
};
// Initialize
this.init();
}
/**
* Initialize the context-aware theme integration
*/
init() {
// Detect initial context
this.detectInitialContext();
// Add observers for context changes
this.setupContextObservers();
// Add theme manager observer
this.themeManager.addObserver((event) => {
// When theme changes from outside, update our state but don't override
if (event.type === 'themeChange' && !this.isContextOverride) {
this.baseTheme = event.theme;
}
});
// Store base theme
this.baseTheme = this.themeManager.activeTheme;
// Context override flag
this.isContextOverride = false;
// Expose on window for debugging
window.morphGuardContextTheme = this;
console.log('MorphGuard Context Theme initialized');
}
/**
* Detect initial application context
*/
detectInitialContext() {
// Check URL for current view
const path = window.location.pathname;
if (path.includes('/demo')) {
this.appContext.currentView = 'demo';
} else if (path.includes('/setup')) {
this.appContext.currentView = 'setup';
} else if (path.includes('/lead')) {
this.appContext.currentView = 'lead';
} else if (path === '/') {
this.appContext.currentView = 'landing';
}
// Check for reduced motion preference
this.appContext.accessibilityNeeds.reducedMotion =
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// Update theme based on initial context
this.updateThemeForContext();
// Detect environment
this.detectEnvironment();
}
/**
* Setup observers for context changes
*/
setupContextObservers() {
// Watch for view changes via navigation
this.observeNavigation();
// Watch for detection results
this.observeDetectionResults();
// Watch for security alerts
this.observeSecurityAlerts();
// Watch for task changes
this.observeTaskChanges();
// Track user behavior
this.trackUserBehavior();
// Monitor environment
this.monitorEnvironment();
}
/**
* Observe navigation for view changes
*/
observeNavigation() {
// Listen for clicks on navigation links
document.addEventListener('click', (e) => {
try {
// Check if a navigation link was clicked
const link = e.target.closest('a');
if (link && link.href && link.href.startsWith(window.location.origin)) {
const path = new URL(link.href).pathname;
// Determine view from path
let view = 'unknown';
if (path.includes('/demo')) {
view = 'demo';
} else if (path.includes('/setup')) {
view = 'setup';
} else if (path.includes('/lead')) {
view = 'lead';
} else if (path === '/') {
view = 'landing';
}
// Update context if view changed
if (view !== this.appContext.currentView) {
this.appContext.currentView = view;
// Defer theme update to prevent browser from cancelling navigation inside iframes
setTimeout(() => {
this.updateThemeForContext();
}, 0);
}
}
} catch (err) {
console.error('Error in observeNavigation click listener:', err);
}
});
// Add history state change listener
window.addEventListener('popstate', () => {
const path = window.location.pathname;
// Determine view from path
let view = 'unknown';
if (path.includes('/demo')) {
view = 'demo';
} else if (path.includes('/setup')) {
view = 'setup';
} else if (path.includes('/lead')) {
view = 'lead';
} else if (path === '/') {
view = 'landing';
}
// Update context if view changed
if (view !== this.appContext.currentView) {
this.appContext.currentView = view;
this.updateThemeForContext();
}
});
}
/**
* Observe detection results
*/
observeDetectionResults() {
// Watch for detection result elements being added to the DOM
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
// Check for detection result elements
const detectionResult = document.querySelector('.detection-result');
if (detectionResult) {
// Extract result from data attributes
const isMorphed = detectionResult.getAttribute('data-is-morphed') === 'true';
const confidence = parseFloat(detectionResult.getAttribute('data-confidence') || '0');
// Update context
this.appContext.detectionResult = { isMorphed, confidence };
// Apply theme if enabled
if (this.options.detectionResultTheming) {
this.applyDetectionResultTheme(isMorphed);
}
}
}
}
});
// Start observing
observer.observe(document.body, { childList: true, subtree: true });
// Listen for custom detection events
document.addEventListener('morphguard:detection', (e) => {
if (e.detail && typeof e.detail.isMorphed !== 'undefined') {
// Update context
this.appContext.detectionResult = {
isMorphed: e.detail.isMorphed,
confidence: e.detail.confidence || 0
};
// Apply theme if enabled
if (this.options.detectionResultTheming) {
this.applyDetectionResultTheme(e.detail.isMorphed);
}
}
});
}
/**
* Observe security alerts
*/
observeSecurityAlerts() {
// Watch for alert elements being added to the DOM
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
// Check for alert elements
const alerts = document.querySelectorAll('.alert-security, .security-alert');
if (alerts.length > 0) {
// Update context
this.appContext.securityAlerts = Array.from(alerts).map(alert => ({
message: alert.textContent,
level: alert.getAttribute('data-level') || 'warning',
timestamp: new Date().toISOString()
}));
// Apply theme if enabled
if (this.options.securityAlertTheming && this.appContext.securityAlerts.length > 0) {
this.applySecurityAlertTheme();
}
}
}
}
});
// Start observing
observer.observe(document.body, { childList: true, subtree: true });
// Listen for custom alert events
document.addEventListener('morphguard:alert', (e) => {
if (e.detail) {
// Add alert to context
this.appContext.securityAlerts.push({
message: e.detail.message || 'Security alert',
level: e.detail.level || 'warning',
timestamp: new Date().toISOString()
});
// Apply theme if enabled
if (this.options.securityAlertTheming && this.appContext.securityAlerts.length > 0) {
this.applySecurityAlertTheme();
}
}
});
}
/**
* Observe task changes
*/
observeTaskChanges() {
// Watch for task indicator elements
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
// Check for task indicator elements
const taskIndicator = document.querySelector('[data-current-task]');
if (taskIndicator) {
const task = taskIndicator.getAttribute('data-current-task');
if (task && task !== this.appContext.currentTask) {
this.appContext.currentTask = task;
this.applyTaskTheme(task);
}
}
}
}
});
// Start observing
observer.observe(document.body, { childList: true, subtree: true });
// Check for task buttons
document.addEventListener('click', (e) => {
try {
// Check for task buttons
const taskButton = e.target.closest('[data-task]');
if (taskButton) {
const task = taskButton.getAttribute('data-task');
if (task && task !== this.appContext.currentTask) {
this.appContext.currentTask = task;
this.applyTaskTheme(task);
}
}
} catch (err) {
console.error('Error in task button click listener:', err);
}
});
// Listen for custom task events
document.addEventListener('morphguard:task', (e) => {
if (e.detail && e.detail.task) {
this.appContext.currentTask = e.detail.task;
this.applyTaskTheme(e.detail.task);
}
});
}
/**
* Track user behavior for behavioral adaptation
*/
trackUserBehavior() {
if (!this.options.behavioralAdaptation) return;
// Session start time
const sessionStart = Date.now();
// Periodically update session duration
setInterval(() => {
this.appContext.userBehavior.sessionDuration = (Date.now() - sessionStart) / 1000;
}, 60000); // Every minute
document.addEventListener('click', (e) => {
try {
// Record the clicked element
const target = e.target;
if (!target) return;
const elementType = target.tagName ? target.tagName.toLowerCase() : 'unknown';
const classNames = target.classList ? Array.from(target.classList).join(' ') : '';
// Add to click patterns
this.appContext.userBehavior.clickPatterns.push({
elementType,
classNames,
timestamp: new Date().toISOString()
});
// Limit array size
if (this.appContext.userBehavior.clickPatterns.length > 100) {
this.appContext.userBehavior.clickPatterns.shift();
}
// Track frequent actions
const actionKey = `${elementType}:${classNames}`;
this.appContext.userBehavior.frequentActions[actionKey] =
(this.appContext.userBehavior.frequentActions[actionKey] || 0) + 1;
// Analyze patterns periodically
this.analyzeUserBehavior();
} catch (err) {
console.error('Error in click pattern tracking listener:', err);
}
});
// Check initial theme preference
this.appContext.userBehavior.prefersDarkMode =
this.themeManager.isDarkTheme(this.themeManager.currentTheme);
// Check for high contrast preference
this.appContext.userBehavior.prefersHighContrast =
this.themeManager.currentTheme === 'highContrast';
// Check for accessibility preferences
const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reducedMotion) {
this.appContext.accessibilityNeeds.reducedMotion = true;
}
}
/**
* Monitor environment
*/
monitorEnvironment() {
// Check network status
window.addEventListener('online', () => {
this.appContext.environment.networkStatus = 'online';
});
window.addEventListener('offline', () => {
this.appContext.environment.networkStatus = 'offline';
// Apply offline theme if needed
this.applyOfflineTheme();
});
// Monitor battery status if available
if ('getBattery' in navigator) {
navigator.getBattery().then(battery => {
// Update battery status
const updateBattery = () => {
this.appContext.environment.batteryStatus = {
level: battery.level,
charging: battery.charging
};
// Apply low battery theme if needed
if (battery.level < 0.2 && !battery.charging) {
this.applyLowBatteryTheme();
}
};
// Listen for battery changes
battery.addEventListener('levelchange', updateBattery);
battery.addEventListener('chargingchange', updateBattery);
// Initial update
updateBattery();
});
}
// Monitor content density
this.monitorContentDensity();
}
/**
* Monitor content density
*/
monitorContentDensity() {
if (!this.options.adaptToContentDensity) return;
// Calculate content density periodically
const calculateDensity = () => {
const contentArea = document.querySelector('main') || document.body;
const contentHeight = contentArea.scrollHeight;
const viewportHeight = window.innerHeight;
const density = contentHeight / viewportHeight;
// Categorize density
let densityCategory = 'normal';
if (density > 3) {
densityCategory = 'very-high';
} else if (density > 2) {
densityCategory = 'high';
} else if (density < 0.5) {
densityCategory = 'low';
}
// Update context if changed
if (densityCategory !== this.appContext.contentDensity) {
this.appContext.contentDensity = densityCategory;
// Apply theme adjustments if needed
this.applyContentDensityTheme(densityCategory);
}
};
// Calculate initially and on resize
calculateDensity();
window.addEventListener('resize', calculateDensity);
// Also recalculate periodically in case content changes
setInterval(calculateDensity, 5000);
}
/**
* Detect environment factors
*/
detectEnvironment() {
// Check network status
this.appContext.environment.networkStatus = navigator.onLine ? 'online' : 'offline';
// Check if running in a resource-constrained environment
const isLowPower = window.navigator.hardwareConcurrency <= 2;
if (isLowPower) {
// Apply low-power optimizations
this.applyLowPowerTheme();
}
}
/**
* Update theme based on current context
*/
updateThemeForContext() {
// View-specific theming
switch (this.appContext.currentView) {
case 'demo':
// For demo view, use accent colors that highlight detection functions
this.applyViewTheme('demo');
break;
case 'setup':
// For setup view, use accent colors that indicate configuration
this.applyViewTheme('setup');
break;
case 'lead':
// For lead view, use accent colors that are inviting
this.applyViewTheme('lead');
break;
case 'landing':
// For landing view, use bold, attention-grabbing colors
this.applyViewTheme('landing');
break;
default:
// Reset to base theme
this.resetToBaseTheme();
break;
}
}
/**
* Apply theme for specific view
*/
applyViewTheme(view) {
let themeVariables = {};
switch (view) {
case 'demo':
themeVariables = {
'--accent-primary': '#3b82f6', // Blue
'--accent-secondary': '#60a5fa'
};
break;
case 'setup':
themeVariables = {
'--accent-primary': '#8b5cf6', // Purple
'--accent-secondary': '#a78bfa'
};
break;
case 'lead':
themeVariables = {
'--accent-primary': '#10b981', // Green
'--accent-secondary': '#34d399'
};
break;
case 'landing':
themeVariables = {
'--accent-primary': '#f59e0b', // Amber
'--accent-secondary': '#fbbf24'
};
break;
}
// Apply temporary theme variables
this.applyThemeVariables(themeVariables);
}
/**
* Apply theme for detection result
*/
applyDetectionResultTheme(isMorphed) {
// Apply theme based on detection result
const themeVariables = isMorphed ?
this.detectionThemes.morphed.variables :
this.detectionThemes.real.variables;
// Apply temporary theme variables
this.applyThemeVariables(themeVariables);
// Set timeout to reset theme after a few seconds
setTimeout(() => {
this.resetToBaseTheme();
}, 5000);
}
/**
* Apply theme for security alerts
*/
applySecurityAlertTheme() {
// Get the highest severity alert
const alerts = this.appContext.securityAlerts;
let highestSeverity = 'info';
for (const alert of alerts) {
if (alert.level === 'critical') {
highestSeverity = 'critical';
break;
} else if (alert.level === 'high' && highestSeverity !== 'critical') {
highestSeverity = 'high';
} else if (alert.level === 'medium' && highestSeverity !== 'critical' && highestSeverity !== 'high') {
highestSeverity = 'medium';
}
}
// Apply theme based on severity
let themeVariables = {};
switch (highestSeverity) {
case 'critical':
themeVariables = {
'--accent-primary': '#dc2626', // Bright red
'--accent-secondary': '#ef4444',
'--border-color': 'rgba(220, 38, 38, 0.5)',
'--card-bg': 'rgba(220, 38, 38, 0.05)'
};
break;
case 'high':
themeVariables = {
'--accent-primary': '#ea580c', // Orange
'--accent-secondary': '#f97316',
'--border-color': 'rgba(234, 88, 12, 0.5)',
'--card-bg': 'rgba(234, 88, 12, 0.05)'
};
break;
case 'medium':
themeVariables = {
'--accent-primary': '#f59e0b', // Amber
'--accent-secondary': '#fbbf24',
'--border-color': 'rgba(245, 158, 11, 0.5)',
'--card-bg': 'rgba(245, 158, 11, 0.05)'
};
break;
default:
themeVariables = {
'--accent-primary': '#3b82f6', // Blue
'--accent-secondary': '#60a5fa',
'--border-color': 'rgba(59, 130, 246, 0.5)',
'--card-bg': 'rgba(59, 130, 246, 0.05)'
};
}
// Apply temporary theme variables
this.applyThemeVariables(themeVariables);
}
/**
* Apply theme for specific task
*/
applyTaskTheme(task) {
if (!this.taskThemes[task]) {
this.resetToBaseTheme();
return;
}
// Apply task-specific theme
this.applyThemeVariables(this.taskThemes[task].variables);
}
/**
* Apply theme for offline mode
*/
applyOfflineTheme() {
// Use grayscale theme for offline mode
const themeVariables = {
'--accent-primary': '#6b7280', // Gray
'--accent-secondary': '#9ca3af',
'--border-color': '#d1d5db'
};
// Apply temporary theme variables
this.applyThemeVariables(themeVariables);
}
/**
* Apply theme for low battery
*/
applyLowBatteryTheme() {
// Force dark theme to save power
if (!this.themeManager.isDarkTheme(this.themeManager.activeTheme)) {
this.isContextOverride = true;
this.themeManager.setTheme('dark');
this.isContextOverride = false;
}
}
/**
* Apply theme for low-power devices
*/
applyLowPowerTheme() {
// Disable animations
document.documentElement.style.setProperty('--transition-normal', '0s');
document.documentElement.style.setProperty('--transition-fast', '0s');
document.documentElement.style.setProperty('--transition-slow', '0s');
// Use simpler theme
const themeVariables = {
'--shadow-sm': 'none',
'--shadow-md': 'none',
'--shadow-lg': 'none',
'--shadow-xl': 'none'
};
// Apply theme variables
this.applyThemeVariables(themeVariables);
}
/**
* Apply theme based on content density
*/
applyContentDensityTheme(density) {
let themeVariables = {};
switch (density) {
case 'very-high':
// For very high density, reduce spacing and increase contrast
themeVariables = {
'--spacing-md': '0.5rem',
'--spacing-lg': '1rem',
'--spacing-xl': '1.5rem',
'--text-primary': this.themeManager.isDarkTheme(this.themeManager.activeTheme) ?
'#ffffff' : '#000000'
};
break;
case 'high':
// For high density, slightly reduce spacing
themeVariables = {
'--spacing-md': '0.75rem',
'--spacing-lg': '1.25rem',
'--spacing-xl': '1.75rem'
};
break;
case 'low':
// For low density, increase spacing
themeVariables = {
'--spacing-md': '1.25rem',
'--spacing-lg': '2rem',
'--spacing-xl': '3rem'
};
break;
default:
// Reset to defaults
themeVariables = {
'--spacing-md': '1rem',
'--spacing-lg': '1.5rem',
'--spacing-xl': '2rem'
};
}
// Apply theme variables
this.applyThemeVariables(themeVariables);
}
/**
* Apply theme variables without changing base theme
*/
applyThemeVariables(variables) {
// Mark as context override
this.isContextOverride = true;
// Apply variables
for (const [property, value] of Object.entries(variables)) {
document.documentElement.style.setProperty(property, value);
}
// Clear context override flag
this.isContextOverride = false;
}
/**
* Reset to base theme
*/
resetToBaseTheme() {
// Only reset if this is a context override
if (this.isContextOverride) {
// Get current theme configuration
const theme = this.themeManager.themes[this.baseTheme] || this.themeManager.themes.light;
// Reset all variables
for (const [property, value] of Object.entries(theme.variables)) {
document.documentElement.style.setProperty(property, value);
}
// Clear context override flag
this.isContextOverride = false;
}
}
/**
* Analyze user behavior for behavioral adaptation
*/
analyzeUserBehavior() {
// Skip if behavioral adaptation is disabled
if (!this.options.behavioralAdaptation) return;
// Only analyze periodically
if (!this._lastAnalysisTime || (Date.now() - this._lastAnalysisTime > 60000)) {
this._lastAnalysisTime = Date.now();
// Analyze click patterns
this.analyzeClickPatterns();
// Check for frequent actions
this.checkFrequentActions();
// Check for accessibility needs
this.checkAccessibilityNeeds();
}
}
/**
* Analyze click patterns
*/
analyzeClickPatterns() {
const patterns = this.appContext.userBehavior.clickPatterns;
if (patterns.length < 10) return;
// Example analysis: check if user frequently clicks on the same elements
const elementCounts = {};
patterns.forEach(pattern => {
const key = `${pattern.elementType}:${pattern.classNames}`;
elementCounts[key] = (elementCounts[key] || 0) + 1;
});
// Find most frequently clicked elements
const sortedElements = Object.entries(elementCounts)
.sort((a, b) => b[1] - a[1])
.slice(0, 3);
// If there's a clear favorite, highlight it in the theme
if (sortedElements.length > 0 && sortedElements[0][1] > patterns.length * 0.3) {
const favElement = sortedElements[0][0].split(':')[1];
// If the favorite element is a navigation item, highlight that section
if (favElement.includes('nav') || favElement.includes('menu')) {
const section = this.getNavigationSection(favElement);
if (section) {
this.highlightSection(section);
}
}
}
}
/**
* Check for frequent actions
*/
checkFrequentActions() {
const actions = this.appContext.userBehavior.frequentActions;
const totalActions = Object.values(actions).reduce((sum, count) => sum + count, 0);
// Skip if not enough actions
if (totalActions < 20) return;
// Check if user frequently performs certain actions
for (const [action, count] of Object.entries(actions)) {
// If action is performed frequently
if (count > totalActions * 0.2) {
// Check for specific actions
if (action.includes('detection') || action.includes('detect')) {
// User frequently uses detection - optimize UI for that
this.optimizeUIForDetection();
} else if (action.includes('demorph')) {
// User frequently uses demorphing - optimize UI for that
this.optimizeUIForDemorphing();
} else if (action.includes('training') || action.includes('train')) {
// User frequently uses training - optimize UI for that
this.optimizeUIForTraining();
}
}
}
}
/**
* Check for accessibility needs
*/
checkAccessibilityNeeds() {
if (!this.options.accessibilityAdaptation) return;
// Check if user might need high contrast
const highContrastClicks = this.appContext.userBehavior.clickPatterns.filter(
pattern => pattern.classNames.includes('high-contrast') ||
pattern.classNames.includes('zoom') ||
pattern.classNames.includes('accessibility')
).length;
// If user has clicked on accessibility-related elements
if (highContrastClicks > 0 && !this.appContext.accessibilityNeeds.highContrast) {
this.appContext.accessibilityNeeds.highContrast = true;
// Suggest high contrast theme
this.suggestHighContrastTheme();
}
// Check if user might need larger text
const textSizeClicks = this.appContext.userBehavior.clickPatterns.filter(
pattern => pattern.classNames.includes('text-size') ||
pattern.classNames.includes('font-size') ||
pattern.classNames.includes('zoom')
).length;
// If user has clicked on text size elements
if (textSizeClicks > 0 && !this.appContext.accessibilityNeeds.largerText) {
this.appContext.accessibilityNeeds.largerText = true;
// Apply larger text
this.applyLargerText();
}
}
/**
* Get navigation section from element class
*/
getNavigationSection(className) {
// Parse class names to find section indicators
const sections = ['detection', 'demorph', 'training', 'setup', 'analytics'];
for (const section of sections) {
if (className.includes(section)) {
return section;
}
}
return null;
}
/**
* Highlight a specific section in the theme
*/
highlightSection(section) {
// Apply task-specific theme if available
if (this.taskThemes[section]) {
this.applyThemeVariables(this.taskThemes[section].variables);
} else {
// Generic highlight
this.applyThemeVariables({
'--accent-primary': '#3b82f6',
'--card-bg': 'rgba(59, 130, 246, 0.05)'
});
}
}
/**
* Optimize UI for detection tasks
*/
optimizeUIForDetection() {
// Apply detection-optimized theme
this.applyThemeVariables({
'--accent-primary': '#3b82f6', // Blue focus for detection
'--card-bg': 'rgba(59, 130, 246, 0.05)'
});
// Find and enhance detection UI elements
const detectionButtons = document.querySelectorAll('[data-task="detection"], .detection-button');
detectionButtons.forEach(button => {
button.style.transform = 'scale(1.05)';
button.style.boxShadow = '0 4px 6px rgba(59, 130, 246, 0.25)';
});
}
/**
* Optimize UI for demorphing tasks
*/
optimizeUIForDemorphing() {
// Apply demorphing-optimized theme
this.applyThemeVariables({
'--accent-primary': '#8b5cf6', // Purple focus for demorphing
'--card-bg': 'rgba(139, 92, 246, 0.05)'
});
// Find and enhance demorphing UI elements
const demorphButtons = document.querySelectorAll('[data-task="demorph"], .demorph-button');
demorphButtons.forEach(button => {
button.style.transform = 'scale(1.05)';
button.style.boxShadow = '0 4px 6px rgba(139, 92, 246, 0.25)';
});
}
/**
* Optimize UI for training tasks
*/
optimizeUIForTraining() {
// Apply training-optimized theme
this.applyThemeVariables({
'--accent-primary': '#10b981', // Green focus for training
'--card-bg': 'rgba(16, 185, 129, 0.05)'
});
// Find and enhance training UI elements
const trainingButtons = document.querySelectorAll('[data-task="training"], .training-button');
trainingButtons.forEach(button => {
button.style.transform = 'scale(1.05)';
button.style.boxShadow = '0 4px 6px rgba(16, 185, 129, 0.25)';
});
}
/**
* Suggest high contrast theme
*/
suggestHighContrastTheme() {
// Create suggestion element if not exists
if (!document.querySelector('.theme-suggestion')) {
const suggestion = document.createElement('div');
suggestion.className = 'theme-suggestion';
suggestion.innerHTML = `
<div class="theme-suggestion-content">
<p>Would you prefer a high contrast theme for better visibility?</p>
<div class="theme-suggestion-actions">
<button class="btn" id="apply-high-contrast">Yes, enable high contrast</button>
<button class="btn btn-secondary" id="dismiss-suggestion">No thanks</button>
</div>
</div>
`;
// Add styles
suggestion.style.position = 'fixed';
suggestion.style.bottom = '20px';
suggestion.style.right = '20px';
suggestion.style.maxWidth = '300px';
suggestion.style.backgroundColor = 'var(--card-bg)';
suggestion.style.borderRadius = 'var(--radius-lg)';
suggestion.style.boxShadow = 'var(--shadow-lg)';
suggestion.style.padding = 'var(--spacing-md)';
suggestion.style.zIndex = '1000';
// Add to document
document.body.appendChild(suggestion);
// Add event listeners
document.getElementById('apply-high-contrast').addEventListener('click', () => {
this.themeManager.setTheme('highContrast');
suggestion.remove();
});
document.getElementById('dismiss-suggestion').addEventListener('click', () => {
suggestion.remove();
});
}
}
/**
* Apply larger text
*/
applyLargerText() {
// Increase font size variables
this.applyThemeVariables({
'--font-size-xs': '0.875rem', // Increase from 0.75rem
'--font-size-sm': '1rem', // Increase from 0.875rem
'--font-size-md': '1.125rem', // Increase from 1rem
'--font-size-lg': '1.25rem', // Increase from 1.125rem
'--font-size-xl': '1.5rem', // Increase from 1.25rem
'--font-size-2xl': '1.875rem' // Increase from 1.5rem
});
}
/**
* Get current context state
*/
getContext() {
return {
appContext: { ...this.appContext },
isContextOverride: this.isContextOverride,
baseTheme: this.baseTheme
};
}
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
// Check if theme manager is loaded
if (window.morphGuardTheme) {
// Initialize context theme
window.morphGuardContextTheme = new ContextTheme();
} else {
// Wait for theme manager to load
const checkInterval = setInterval(() => {
if (window.morphGuardTheme) {
window.morphGuardContextTheme = new ContextTheme();
clearInterval(checkInterval);
}
}, 100);
// Stop checking after 5 seconds
setTimeout(() => {
clearInterval(checkInterval);
}, 5000);
}
});
// Export for module use
if (typeof module !== 'undefined' && module.exports) {
module.exports = ContextTheme;
}