aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
985 lines (982 loc) • 34.9 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
import { useRef, useState, useEffect, useCallback, useContext, createContext } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { cn } from '../../lib/utilsComprehensive.js';
// Visual anomaly detection using computer vision techniques
class VisualAnomalyDetector {
constructor() {
this.canvas = document.createElement("canvas");
this.context = this.canvas.getContext("2d");
this.referenceSnapshots = new Map();
this.anomalyThreshold = 0.15; // 15% difference threshold
}
captureElementSnapshot(element, id) {
try {
const rect = element.getBoundingClientRect();
this.canvas.width = rect.width;
this.canvas.height = rect.height;
// Create a more detailed visual representation
const computedStyles = window.getComputedStyle(element);
const elementClone = element.cloneNode(true);
// Render element to canvas using html2canvas-like technique
const serializer = new XMLSerializer();
const html = serializer.serializeToString(elementClone);
const blob = new Blob([html], {
type: "text/html"
});
// For now, use a simplified approach capturing computed style data
const snapshot = this.context.createImageData(rect.width || 100, rect.height || 50);
const styleHash = this.computeStyleHash(computedStyles);
// Create a visual fingerprint based on element properties
for (let i = 0; i < snapshot.data.length; i += 4) {
snapshot.data[i] = (styleHash & 0xff0000) >> 16; // Red
snapshot.data[i + 1] = (styleHash & 0x00ff00) >> 8; // Green
snapshot.data[i + 2] = styleHash & 0x0000ff; // Blue
snapshot.data[i + 3] = 255; // Alpha
}
return snapshot;
} catch (error) {
console.warn("Failed to capture element snapshot:", error);
return null;
}
}
computeStyleHash(styles) {
const importantProps = ["width", "height", "background", "border", "opacity", "transform", "filter", "position", "top", "left", "display", "visibility", "zIndex"];
let hash = 0;
importantProps.forEach(prop => {
const value = styles.getPropertyValue(prop);
for (let i = 0; i < value.length; i++) {
hash = (hash << 5) - hash + value.charCodeAt(i);
hash = hash & hash; // Convert to 32-bit integer
}
});
return Math.abs(hash);
}
detectAnomalies(element, id) {
const issues = [];
const currentSnapshot = this.captureElementSnapshot(element, id);
if (!currentSnapshot) return issues;
const referenceSnapshot = this.referenceSnapshots.get(id);
if (referenceSnapshot) {
const difference = this.calculateImageDifference(referenceSnapshot, currentSnapshot);
if (difference > this.anomalyThreshold) {
issues.push({
id: `visual-anomaly-${Date.now()}`,
type: "visual-glitch",
severity: difference > 0.5 ? "critical" : difference > 0.3 ? "high" : "medium",
description: `Visual anomaly detected: ${(difference * 100).toFixed(1)}% difference from reference`,
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
difference,
hasReference: true
}
});
}
} else {
// Store as reference if this is the first healthy snapshot
this.referenceSnapshots.set(id, currentSnapshot);
}
// Check for common visual issues
issues.push(...this.checkCommonVisualIssues(element));
return issues;
}
calculateImageDifference(img1, img2) {
if (img1.data.length !== img2.data.length) return 1.0;
let totalDifference = 0;
const length = img1.data.length;
for (let i = 0; i < length; i += 4) {
const rDiff = Math.abs(img1.data[i] - img2.data[i]);
const gDiff = Math.abs(img1.data[i + 1] - img2.data[i + 1]);
const bDiff = Math.abs(img1.data[i + 2] - img2.data[i + 2]);
const aDiff = Math.abs(img1.data[i + 3] - img2.data[i + 3]);
totalDifference += (rDiff + gDiff + bDiff + aDiff) / (255 * 4);
}
return totalDifference / (length / 4);
}
checkCommonVisualIssues(element) {
const issues = [];
const styles = window.getComputedStyle(element);
const rect = element.getBoundingClientRect();
// Check for invisible elements
if (styles.opacity === "0" || styles.visibility === "hidden" || styles.display === "none") {
issues.push({
id: `invisible-element-${Date.now()}`,
type: "visual-glitch",
severity: "high",
description: "Element is not visible to users",
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
opacity: styles.opacity,
visibility: styles.visibility,
display: styles.display
}
});
}
// Check for elements outside viewport
if (rect.right < 0 || rect.left > window.innerWidth || rect.bottom < 0 || rect.top > window.innerHeight) {
issues.push({
id: `outside-viewport-${Date.now()}`,
type: "visual-glitch",
severity: "medium",
description: "Element is positioned outside the viewport",
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
rect: {
...rect
}
}
});
}
// Check for extremely small elements
if (rect.width < 1 || rect.height < 1) {
issues.push({
id: `tiny-element-${Date.now()}`,
type: "visual-glitch",
severity: "medium",
description: "Element has collapsed dimensions",
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
width: rect.width,
height: rect.height
}
});
}
// Check for broken transforms
const transform = styles.transform;
if (transform && transform !== "none" && transform.includes("NaN")) {
issues.push({
id: `broken-transform-${Date.now()}`,
type: "visual-glitch",
severity: "critical",
description: "CSS transform contains invalid values",
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
transform
}
});
}
return issues;
}
updateReference(element, id) {
const snapshot = this.captureElementSnapshot(element, id);
if (snapshot) {
this.referenceSnapshots.set(id, snapshot);
}
}
}
// Accessibility anomaly detector
class AccessibilityAnomalyDetector {
detectIssues(element) {
const issues = [];
// Check for missing ARIA attributes
const ariaIssues = this.checkAriaCompliance(element);
issues.push(...ariaIssues);
// Check for color contrast issues
const contrastIssues = this.checkColorContrast(element);
issues.push(...contrastIssues);
// Check for keyboard accessibility
const keyboardIssues = this.checkKeyboardAccessibility(element);
issues.push(...keyboardIssues);
// Check for focus management
const focusIssues = this.checkFocusManagement(element);
issues.push(...focusIssues);
return issues;
}
checkAriaCompliance(element) {
const issues = [];
// Check for interactive elements without proper ARIA
const interactiveElements = element.querySelectorAll("button, a, input, select, textarea");
interactiveElements.forEach((el, index) => {
const htmlEl = el;
if (!htmlEl.getAttribute("aria-label") && !htmlEl.getAttribute("aria-labelledby") && !htmlEl.textContent?.trim()) {
issues.push({
id: `missing-aria-label-${Date.now()}-${index}`,
type: "accessibility",
severity: "high",
description: `Interactive element ${el.tagName.toLowerCase()} lacks accessible name`,
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
element: el.tagName.toLowerCase(),
hasTextContent: !!htmlEl.textContent?.trim()
}
});
}
});
// Check for images without alt text
const images = element.querySelectorAll("img");
images.forEach((img, index) => {
if (!img.getAttribute("alt") && !img.getAttribute("aria-label")) {
issues.push({
id: `missing-alt-text-${Date.now()}-${index}`,
type: "accessibility",
severity: "high",
description: "Image missing alternative text",
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
src: img.src
}
});
}
});
return issues;
}
checkColorContrast(element) {
const issues = [];
const styles = window.getComputedStyle(element);
const backgroundColor = styles.backgroundColor;
const color = styles.color;
if (backgroundColor && color && backgroundColor !== "rgba(0, 0, 0, 0)" && backgroundColor !== "transparent") {
const contrastRatio = this.calculateContrastRatio(color, backgroundColor);
if (contrastRatio < 4.5) {
issues.push({
id: `low-contrast-${Date.now()}`,
type: "accessibility",
severity: contrastRatio < 3 ? "critical" : "high",
description: `Insufficient color contrast: ${contrastRatio.toFixed(2)}:1`,
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
contrastRatio,
color,
backgroundColor
}
});
}
}
return issues;
}
calculateContrastRatio(color1, color2) {
// Simplified contrast calculation
const rgb1 = this.parseColor(color1);
const rgb2 = this.parseColor(color2);
const l1 = this.relativeLuminance(rgb1);
const l2 = this.relativeLuminance(rgb2);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
parseColor(color) {
// Simple RGB extraction (works for rgb() and rgba() formats)
const match = color.match(/rgba?\(([^)]+)\)/);
if (match) {
const values = match[1].split(",").map(v => parseInt(v.trim()));
return [values[0] || 0, values[1] || 0, values[2] || 0];
}
return [0, 0, 0]; // Fallback
}
relativeLuminance([r, g, b]) {
const rsRGB = r / 255;
const gsRGB = g / 255;
const bsRGB = b / 255;
const rLin = rsRGB <= 0.03928 ? rsRGB / 12.92 : Math.pow((rsRGB + 0.055) / 1.055, 2.4);
const gLin = gsRGB <= 0.03928 ? gsRGB / 12.92 : Math.pow((gsRGB + 0.055) / 1.055, 2.4);
const bLin = bsRGB <= 0.03928 ? bsRGB / 12.92 : Math.pow((bsRGB + 0.055) / 1.055, 2.4);
return 0.2126 * rLin + 0.7152 * gLin + 0.0722 * bLin;
}
checkKeyboardAccessibility(element) {
const issues = [];
// Check for interactive elements that aren't keyboard accessible
const interactiveElements = element.querySelectorAll("div[onclick], span[onclick]");
interactiveElements.forEach((el, index) => {
const htmlEl = el;
if (htmlEl.tabIndex < 0 && !htmlEl.getAttribute("role")) {
issues.push({
id: `keyboard-inaccessible-${Date.now()}-${index}`,
type: "accessibility",
severity: "high",
description: "Interactive element not keyboard accessible",
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
tagName: el.tagName.toLowerCase(),
hasOnClick: !!htmlEl.onclick
}
});
}
});
return issues;
}
checkFocusManagement(element) {
const issues = [];
// Check for focus traps without proper management
const focusableElements = element.querySelectorAll('button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (focusableElements.length > 10) {
// Large number of focusable elements might need focus management
let hasFocusManagement = false;
focusableElements.forEach(el => {
if (el.getAttribute("aria-describedby") || el.getAttribute("aria-controls")) {
hasFocusManagement = true;
}
});
if (!hasFocusManagement) {
issues.push({
id: `focus-management-missing-${Date.now()}`,
type: "accessibility",
severity: "medium",
description: "Complex interface may need better focus management",
detectedAt: Date.now(),
autoFixable: false,
fixAttempts: 0,
resolved: false,
metadata: {
focusableCount: focusableElements.length
}
});
}
}
return issues;
}
}
// Performance anomaly detector
class PerformanceAnomalyDetector {
constructor() {
this.renderTimes = new Map();
this.memoryUsage = new Map();
}
recordRenderTime(componentId, time) {
if (!this.renderTimes.has(componentId)) {
this.renderTimes.set(componentId, []);
}
const times = this.renderTimes.get(componentId);
times.push(time);
// Keep only recent measurements
if (times.length > 50) {
times.splice(0, times.length - 50);
}
}
detectPerformanceIssues(componentId) {
const issues = [];
const renderTimes = this.renderTimes.get(componentId);
if (!renderTimes || renderTimes.length < 5) return issues;
const avgRenderTime = renderTimes.reduce((sum, time) => sum + time, 0) / renderTimes.length;
const maxRenderTime = Math.max(...renderTimes);
// Check for slow rendering
if (avgRenderTime > 16.67) {
// 60fps threshold
issues.push({
id: `slow-render-${Date.now()}`,
type: "performance",
severity: avgRenderTime > 33.33 ? "critical" : avgRenderTime > 25 ? "high" : "medium",
description: `Slow rendering detected: ${avgRenderTime.toFixed(2)}ms average`,
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
avgRenderTime,
maxRenderTime,
sampleCount: renderTimes.length
}
});
}
// Check for rendering spikes
const recentSpikes = renderTimes.filter(time => time > avgRenderTime * 2);
if (recentSpikes.length > renderTimes.length * 0.1) {
issues.push({
id: `render-spikes-${Date.now()}`,
type: "performance",
severity: "high",
description: `Frequent rendering spikes detected: ${recentSpikes.length} spikes`,
detectedAt: Date.now(),
autoFixable: true,
fixAttempts: 0,
resolved: false,
metadata: {
spikes: recentSpikes.length,
threshold: avgRenderTime * 2
}
});
}
// Check memory usage if available
if (performance.memory) {
const memUsage = performance.memory.usedJSHeapSize / 1024 / 1024;
if (memUsage > 50) {
// 50MB threshold
issues.push({
id: `high-memory-${Date.now()}`,
type: "memory-leak",
severity: memUsage > 100 ? "critical" : "high",
description: `High memory usage: ${memUsage.toFixed(1)}MB`,
detectedAt: Date.now(),
autoFixable: false,
fixAttempts: 0,
resolved: false,
metadata: {
memoryUsage: memUsage
}
});
}
}
return issues;
}
}
// Main self-healing system
class SelfHealingSystem {
constructor() {
this.visualDetector = new VisualAnomalyDetector();
this.a11yDetector = new AccessibilityAnomalyDetector();
this.perfDetector = new PerformanceAnomalyDetector();
this.componentHealth = new Map();
this.healingStrategies = this.initializeHealingStrategies();
this.activeHealing = new Map();
}
initializeHealingStrategies() {
return [{
name: "Visual Glitch Recovery",
priority: 1,
conditions: issue => issue.type === "visual-glitch",
actions: [{
id: "style-reset",
targetComponent: "",
actionType: "style-reset",
description: "Reset component styles to default state",
implementation: async () => this.resetComponentStyles(),
rollback: async () => this.restoreComponentStyles(),
successRate: 0.85
}, {
id: "dom-rebuild",
targetComponent: "",
actionType: "dom-rebuild",
description: "Rebuild component DOM structure",
implementation: async () => this.rebuildComponentDOM(),
rollback: async () => this.restoreComponentDOM(),
successRate: 0.75
}],
successRate: 0.8,
executionTime: 500
}, {
name: "Accessibility Fix",
priority: 2,
conditions: issue => issue.type === "accessibility",
actions: [{
id: "aria-fix",
targetComponent: "",
actionType: "accessibility-fix",
description: "Apply automatic ARIA attributes",
implementation: async () => this.fixAriaAttributes(),
rollback: async () => this.removeAriaAttributes(),
successRate: 0.95
}],
successRate: 0.9,
executionTime: 200
}, {
name: "Performance Optimization",
priority: 3,
conditions: issue => issue.type === "performance",
actions: [{
id: "perf-optimize",
targetComponent: "",
actionType: "performance-optimization",
description: "Apply performance optimizations",
implementation: async () => this.optimizePerformance(),
rollback: async () => this.revertPerformanceChanges(),
successRate: 0.7
}],
successRate: 0.65,
executionTime: 1000
}];
}
async diagnoseComponent(element, componentId, componentType) {
const startTime = performance.now();
// Collect issues from all detectors
const visualIssues = this.visualDetector.detectAnomalies(element, componentId);
const a11yIssues = this.a11yDetector.detectIssues(element);
const perfIssues = this.perfDetector.detectPerformanceIssues(componentId);
const allIssues = [...visualIssues, ...a11yIssues, ...perfIssues];
// Calculate health score
const healthScore = this.calculateHealthScore(allIssues);
// Determine status
let status = "healthy";
if (allIssues.some(i => i.severity === "critical")) status = "critical";else if (allIssues.some(i => i.severity === "high")) status = "warning";
const diagnosis = {
componentId,
componentType,
healthScore,
issues: allIssues,
lastChecked: Date.now(),
recoveryAttempts: this.componentHealth.get(componentId)?.recoveryAttempts || 0,
status
};
this.componentHealth.set(componentId, diagnosis);
// Record performance
const diagnosisTime = performance.now() - startTime;
this.perfDetector.recordRenderTime(`diagnosis-${componentId}`, diagnosisTime);
// Auto-heal if necessary
if (status === "critical" || status === "warning" && healthScore < 0.7) {
await this.initiateHealing(componentId, element);
}
return diagnosis;
}
calculateHealthScore(issues) {
if (issues.length === 0) return 1.0;
const severityWeights = {
low: 0.1,
medium: 0.3,
high: 0.6,
critical: 1.0
};
const totalImpact = issues.reduce((sum, issue) => sum + severityWeights[issue.severity], 0);
const maxPossibleImpact = issues.length * 1.0;
return Math.max(0, 1 - totalImpact / maxPossibleImpact);
}
async initiateHealing(componentId, element) {
const health = this.componentHealth.get(componentId);
if (!health || health.status === "healing") return false;
// Update status
health.status = "healing";
health.recoveryAttempts++;
// Find applicable healing strategies
const applicableStrategies = this.healingStrategies.filter(strategy => health.issues.some(issue => strategy.conditions(issue)));
// Sort by priority
applicableStrategies.sort((a, b) => a.priority - b.priority);
let healingSuccess = false;
for (const strategy of applicableStrategies) {
try {
const actions = strategy.actions.map(action => ({
...action,
targetComponent: componentId
}));
this.activeHealing.set(componentId, actions);
// Execute healing actions
for (const action of actions) {
const success = await action.implementation();
action.appliedAt = Date.now();
if (success) {
healingSuccess = true;
console.log(`✅ Healing action ${action.actionType} succeeded for ${componentId}`);
} else {
console.warn(`❌ Healing action ${action.actionType} failed for ${componentId}`);
await action.rollback();
}
}
if (healingSuccess) break;
} catch (error) {
console.error(`Healing strategy ${strategy.name} failed:`, error);
}
}
// Re-diagnose after healing
const updatedHealth = await this.diagnoseComponent(element, componentId, health.componentType);
if (healingSuccess && updatedHealth.healthScore > health.healthScore) {
updatedHealth.status = updatedHealth.healthScore > 0.8 ? "healthy" : "warning";
console.log(`🎉 Component ${componentId} successfully healed! Health: ${(updatedHealth.healthScore * 100).toFixed(1)}%`);
} else {
updatedHealth.status = "failed";
console.error(`💀 Healing failed for component ${componentId}`);
}
return healingSuccess;
}
// Healing implementation methods
async resetComponentStyles() {
try {
// Implementation would reset component styles
await new Promise(resolve => setTimeout(resolve, 100));
return true;
} catch {
return false;
}
}
async restoreComponentStyles() {
// Rollback implementation
await new Promise(resolve => setTimeout(resolve, 50));
}
async rebuildComponentDOM() {
try {
// Implementation would trigger React re-render
await new Promise(resolve => setTimeout(resolve, 200));
return true;
} catch {
return false;
}
}
async restoreComponentDOM() {
await new Promise(resolve => setTimeout(resolve, 100));
}
async fixAriaAttributes() {
try {
// Implementation would add missing ARIA attributes
await new Promise(resolve => setTimeout(resolve, 50));
return true;
} catch {
return false;
}
}
async removeAriaAttributes() {
await new Promise(resolve => setTimeout(resolve, 25));
}
async optimizePerformance() {
try {
// Implementation would apply performance optimizations
await new Promise(resolve => setTimeout(resolve, 300));
return true;
} catch {
return false;
}
}
async revertPerformanceChanges() {
await new Promise(resolve => setTimeout(resolve, 150));
}
// Public API
getComponentHealth(componentId) {
return this.componentHealth.get(componentId);
}
getAllComponentHealth() {
return Array.from(this.componentHealth.values());
}
getActiveHealing() {
return Array.from(this.activeHealing.entries()).map(([componentId, actions]) => ({
componentId,
actions
}));
}
recordRenderTime(componentId, time) {
this.perfDetector.recordRenderTime(componentId, time);
}
}
// React Context for the self-healing system
const SelfHealingContext = /*#__PURE__*/createContext({
system: null,
diagnoseComponent: async () => ({}),
getComponentHealth: () => undefined,
getAllHealth: () => []
});
// Provider component
function GlassSelfHealingProvider({
children,
onHealingStarted,
onHealingCompleted,
diagnosticInterval = 5000
}) {
useReducedMotion();
const systemRef = useRef();
const [systemHealth, setSystemHealth] = useState([]);
// Initialize system
useEffect(() => {
systemRef.current = new SelfHealingSystem();
// Periodic health updates
const updateInterval = setInterval(() => {
if (systemRef.current) {
const allHealth = systemRef.current.getAllComponentHealth();
setSystemHealth(allHealth);
}
}, diagnosticInterval);
return () => clearInterval(updateInterval);
}, [diagnosticInterval]);
const diagnoseComponent = useCallback(async (element, componentId, componentType) => {
if (!systemRef.current) throw new Error("Self-healing system not initialized");
return systemRef.current.diagnoseComponent(element, componentId, componentType);
}, []);
const getComponentHealth = useCallback(componentId => {
return systemRef.current?.getComponentHealth(componentId);
}, []);
const getAllHealth = useCallback(() => {
return systemRef.current?.getAllComponentHealth() || [];
}, []);
const value = {
system: systemRef.current || null,
diagnoseComponent,
getComponentHealth,
getAllHealth
};
return jsx(SelfHealingContext.Provider, {
value: value,
children: children
});
}
// Hook to use the self-healing system
function useSelfHealing() {
const context = useContext(SelfHealingContext);
if (!context) {
throw new Error("useSelfHealing must be used within GlassSelfHealingProvider");
}
return context;
}
// Self-healing component wrapper
function GlassSelfHealingWrapper({
children,
componentId,
componentType = "unknown",
monitoringEnabled = true,
healingEnabled = true,
className
}) {
const prefersReducedMotion = useReducedMotion();
const {
diagnoseComponent,
getComponentHealth
} = useSelfHealing();
const elementRef = useRef(null);
const [health, setHealth] = useState(null);
const lastDiagnosisRef = useRef(0);
// Automatic monitoring
useEffect(() => {
if (!monitoringEnabled || !elementRef.current) return;
const runDiagnosis = async () => {
const now = Date.now();
if (now - lastDiagnosisRef.current < 3000) return; // Throttle diagnostics
lastDiagnosisRef.current = now;
try {
const diagnosis = await diagnoseComponent(elementRef.current, componentId, componentType);
setHealth(diagnosis);
} catch (error) {
console.warn("Self-healing diagnosis failed:", error);
}
};
// Initial diagnosis
runDiagnosis();
// Periodic monitoring
const monitorInterval = setInterval(runDiagnosis, 10000);
// Monitor for DOM mutations
const observer = new MutationObserver(() => {
runDiagnosis();
});
if (elementRef.current) {
observer.observe(elementRef.current, {
childList: true,
attributes: true,
subtree: true
});
}
return () => {
clearInterval(monitorInterval);
observer.disconnect();
};
}, [monitoringEnabled, componentId, componentType, diagnoseComponent]);
const statusColor = health ? {
healthy: "var(--glass-color-success)",
warning: "var(--glass-color-warning)",
critical: "var(--glass-color-danger)",
healing: "var(--glass-color-primary)",
failed: "#7f1d1d"
}[health.status] : "var(--glass-gray-500)";
return jsxs("div", {
ref: elementRef,
className: cn("relative self-healing-wrapper", health?.status === "healing" && "self-healing-active", className),
"data-component-id": componentId,
"data-component-type": componentType,
"data-health-score": health?.healthScore,
"data-status": health?.status,
children: [children, process.env.NODE_ENV === "development" && monitoringEnabled && health && jsx(motion.div, {
className: 'absolute glass-top-1 right-1 w-3 h-3 glass-radius-full glass-border-2 glass-border-white/20',
style: {
backgroundColor: statusColor
},
initial: {
scale: 0
},
animate: prefersReducedMotion ? {} : {
scale: 1
},
title: `Health: ${(health.healthScore * 100).toFixed(1)}% | Status: ${health.status} | Issues: ${health.issues.length}`
})]
});
}
// System health dashboard
function GlassSelfHealingDashboard({
className,
showOnlyUnhealthy = false
}) {
const prefersReducedMotion = useReducedMotion();
const {
getAllHealth
} = useSelfHealing();
const [allHealth, setAllHealth] = useState([]);
const [showDashboard, setShowDashboard] = useState(false);
useEffect(() => {
const updateHealth = () => {
const health = getAllHealth();
setAllHealth(showOnlyUnhealthy ? health.filter(h => h.status !== "healthy") : health);
};
updateHealth();
const interval = setInterval(updateHealth, 2000);
return () => clearInterval(interval);
}, [getAllHealth, showOnlyUnhealthy]);
const criticalCount = allHealth.filter(h => h.status === "critical").length;
const warningCount = allHealth.filter(h => h.status === "warning").length;
const healingCount = allHealth.filter(h => h.status === "healing").length;
return jsxs("div", {
className: cn("fixed top-4 left-4 z-50", className),
children: [jsxs(motion.button, {
className: cn("w-12 h-12 glass-radius-full glass-surface-primary glass-elev-3", "flex items-center justify-center glass-text-primary", "transition-all duration-300 hover:scale-105"),
onClick: () => setShowDashboard(!showDashboard),
whileHover: {
scale: 1.05
},
whileTap: {
scale: 0.95
},
children: ["\uD83C\uDFE5", criticalCount + warningCount + healingCount > 0 && jsx(motion.div, {
className: 'absolute glass-top-1 -right-1 w-3 h-3 glass-surface-red glass-radius-full glass-text-xs text-primary glass-flex glass-items-center glass-justify-center',
initial: {
scale: 0
},
animate: prefersReducedMotion ? {} : {
scale: 1
},
children: criticalCount + warningCount + healingCount
})]
}), jsx(AnimatePresence, {
children: showDashboard && jsxs(motion.div, {
className: cn("absolute top-14 left-0 w-80 max-h-96 overflow-y-auto", "glass-surface-primary glass-elev-4 glass-radius-lg glass-p-4 glass-gap-3"),
initial: {
opacity: 0,
y: -10,
scale: 0.95
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
y: 0,
scale: 1
},
exit: {
opacity: 0,
y: -10,
scale: 0.95
},
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsx("h3", {
className: 'glass-text-sm font-medium text-primary',
children: "Self-Healing Dashboard"
}), jsx("button", {
onClick: () => setShowDashboard(false),
className: 'glass-text-xs glass-text-secondary hover:text-primary glass-focus glass-touch-target glass-contrast-guard',
children: "\u2715"
})]
}), allHealth.map(health => jsxs(motion.div, {
className: "glass-p-3 glass-surface-secondary glass-radius-md",
initial: {
opacity: 0,
x: -10
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
x: 0
},
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsx("span", {
className: 'glass-text-sm text-primary font-medium',
children: health.componentType
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("div", {
className: 'w-3 h-3 glass-radius-full',
style: {
backgroundColor: {
healthy: "var(--glass-color-success)",
warning: "var(--glass-color-warning)",
critical: "var(--glass-color-danger)",
healing: "var(--glass-color-primary)",
failed: "#7f1d1d"
}[health.status]
}
}), jsxs("span", {
className: "glass-text-xs glass-text-secondary",
children: [(health.healthScore * 100).toFixed(0), "%"]
})]
})]
}), health.issues.length > 0 && jsxs("div", {
className: "glass-mt-1 glass-text-xs glass-text-tertiary",
children: [health.issues.length, " issue", health.issues.length !== 1 ? "s" : "", " detected"]
})]
}, health.componentId)), allHealth.length === 0 && jsx("div", {
className: 'text-center glass-text-sm glass-text-secondary glass-py-4',
children: "All components healthy! \uD83C\uDF89"
})]
})
})]
});
}
// Hook for component self-monitoring
function useComponentSelfHealing(componentId, componentType = "component") {
const {
diagnoseComponent
} = useSelfHealing();
const [health, setHealth] = useState(null);
const elementRef = useRef(null);
const runHealthCheck = useCallback(async () => {
if (elementRef.current) {
try {
const diagnosis = await diagnoseComponent(elementRef.current, componentId, componentType);
setHealth(diagnosis);
return diagnosis;
} catch (error) {
console.warn(`Health check failed for ${componentId}:`, error);
return null;
}
}
return null;
}, [diagnoseComponent, componentId, componentType]);
return {
health,
runHealthCheck,
elementRef,
isHealthy: health?.status === "healthy",
isHealing: health?.status === "healing",
isCritical: health?.status === "critical"
};
}
// Presets for different healing configurations
const selfHealingPresets = {
strict: {
monitoringInterval: 2000,
healingThreshold: 0.9,
autoHealingEnabled: true,
visualMonitoring: true,
accessibilityMonitoring: true,
performanceMonitoring: true
},
balanced: {
monitoringInterval: 5000,
healingThreshold: 0.7,
autoHealingEnabled: true,
visualMonitoring: true,
accessibilityMonitoring: true,
performanceMonitoring: true
},
relaxed: {
monitoringInterval: 10000,
healingThreshold: 0.5,
autoHealingEnabled: false,
visualMonitoring: false,
accessibilityMonitoring: true,
performanceMonitoring: false
},
development: {
monitoringInterval: 1000,
healingThreshold: 0.8,
autoHealingEnabled: true,
visualMonitoring: true,
accessibilityMonitoring: true,
performanceMonitoring: true
}
};
export { GlassSelfHealingDashboard, GlassSelfHealingProvider, GlassSelfHealingWrapper, selfHealingPresets, useComponentSelfHealing, useSelfHealing };
//# sourceMappingURL=GlassSelfHealingSystem.js.map