aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
925 lines (922 loc) • 32.7 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { useRef, useState, useEffect, useCallback, useContext, forwardRef, createContext } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { cn } from '../../lib/utilsComprehensive.js';
import '../../primitives/GlassCore.js';
import '../../primitives/glass/GlassAdvanced.js';
import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js';
import '../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../primitives/MotionNative.js';
import '../../primitives/motion/MotionFramer.js';
import { useA11yId } from '../../utils/a11y.js';
import { useMotionPreferenceContext } from '../../contexts/MotionPreferenceContext.js';
const IS_TEST_ENV = typeof process !== "undefined" && process.env?.JEST_WORKER_ID !== undefined;
// Biometric sensor integration
class BiometricSensorManager {
constructor() {
this.heartRateSensor = null;
this.accelerometer = null;
this.gyroscope = null;
this.ambientLightSensor = null;
this.isSupported = false;
this.readings = [];
this.listeners = [];
this.behavioralCleanupCallbacks = [];
}
async initialize() {
// CRITICAL SSR FIX: Skip all sensor initialization on server
if (typeof window === "undefined") {
console.warn("BiometricSensorManager: Skipping initialization on server");
return false;
}
this.cleanupBehavioralAnalysis();
try {
// Check for Generic Sensor API support
if ("Accelerometer" in window) {
this.accelerometer = new window.Accelerometer({
frequency: 10
});
this.accelerometer.addEventListener("reading", this.handleAccelerometer.bind(this));
this.accelerometer.start?.();
}
if ("Gyroscope" in window) {
this.gyroscope = new window.Gyroscope({
frequency: 10
});
this.gyroscope.addEventListener("reading", this.handleGyroscope.bind(this));
this.gyroscope.start?.();
}
if ("AmbientLightSensor" in window) {
this.ambientLightSensor = new window.AmbientLightSensor({
frequency: 2
});
this.ambientLightSensor.addEventListener("reading", this.handleAmbientLight.bind(this));
this.ambientLightSensor.start?.();
}
// Check for Web Bluetooth (for heart rate monitors)
if ("bluetooth" in navigator) {
this.isSupported = true;
}
} catch (error) {
console.warn("Biometric sensors not available, using behavioral analysis:", error);
}
// Always fall back to behavioral analysis so component still works
this.setupBehavioralAnalysis();
return true;
}
setupBehavioralAnalysis() {
// CRITICAL SSR FIX: Skip document access on server
if (typeof document === "undefined" || typeof window === "undefined") {
return;
}
this.cleanupBehavioralAnalysis();
// Track mouse/touch patterns for stress indicators
let lastClickTime = 0;
let clickCount = 0;
let mouseMovements = [];
let errorCount = 0;
const handleClick = () => {
const now = Date.now();
if (now - lastClickTime < 500) {
clickCount++;
} else {
clickCount = 0;
}
lastClickTime = now;
if (clickCount > 5) {
this.reportStressIndicator("rapidClicking", 0.8);
}
};
document.addEventListener("click", handleClick);
this.behavioralCleanupCallbacks.push(() => document.removeEventListener("click", handleClick));
const handleMouseMove = e => {
mouseMovements.push({
x: e.clientX,
y: e.clientY,
timestamp: Date.now()
});
// Keep only recent movements
if (mouseMovements.length > 10) {
mouseMovements = mouseMovements.slice(-5);
}
// Analyze movement patterns
if (mouseMovements.length > 3) {
const movements = mouseMovements.slice(-3);
const distances = movements.map((point, i) => {
if (i === 0) return 0;
const prev = movements[i - 1];
return Math.sqrt(Math.pow(point.x - prev.x, 2) + Math.pow(point.y - prev.y, 2));
});
const avgDistance = distances.reduce((sum, d) => sum + d, 0) / distances.length;
const timeDiff = movements[movements.length - 1].timestamp - movements[0].timestamp;
const speed = timeDiff === 0 ? 0 : avgDistance / timeDiff;
// Irregular, jittery movement indicates stress
if (speed > 2 && avgDistance < 50) {
this.reportStressIndicator("irregularMovement", 0.6);
}
}
};
document.addEventListener("mousemove", handleMouseMove);
this.behavioralCleanupCallbacks.push(() => document.removeEventListener("mousemove", handleMouseMove));
const handleWindowError = () => {
errorCount++;
this.reportStressIndicator("errorFrequency", Math.min(1, errorCount * 0.2));
};
window.addEventListener("error", handleWindowError);
this.behavioralCleanupCallbacks.push(() => window.removeEventListener("error", handleWindowError));
const intervalId = window.setInterval(() => this.analyzeBehavioralPatterns(), 5000);
this.behavioralCleanupCallbacks.push(() => window.clearInterval(intervalId));
}
cleanupBehavioralAnalysis() {
if (!this.behavioralCleanupCallbacks.length) {
return;
}
this.behavioralCleanupCallbacks.forEach(cleanup => {
try {
cleanup();
} catch (error) {
console.warn("Failed to cleanup behavioral analysis handler:", error);
}
});
this.behavioralCleanupCallbacks = [];
}
handleAccelerometer() {
if (!this.accelerometer) return;
const {
x,
y,
z
} = this.accelerometer;
const acceleration = Math.sqrt(x * x + y * y + z * z);
// High acceleration might indicate stress/agitation
if (acceleration > 15) {
this.reportBiometricReading({
stressLevel: Math.min(1, acceleration / 30),
timestamp: Date.now(),
confidence: 0.6
});
}
}
handleGyroscope() {
if (!this.gyroscope) return;
const {
x,
y,
z
} = this.gyroscope;
const rotation = Math.sqrt(x * x + y * y + z * z);
// Rapid device rotation might indicate stress
if (rotation > 5) {
this.reportStressIndicator("deviceMovement", Math.min(1, rotation / 10));
}
}
handleAmbientLight() {
if (!this.ambientLightSensor) return;
const {
illuminance
} = this.ambientLightSensor;
// Very low light might indicate late hours / stress
if (illuminance < 10) {
this.reportStressIndicator("lowLight", 0.3);
}
}
reportStressIndicator(type, level) {
this.reportBiometricReading({
stressLevel: level,
timestamp: Date.now(),
confidence: 0.5
});
}
analyzeBehavioralPatterns() {
const now = Date.now();
const recentReadings = this.readings.filter(r => now - r.timestamp < 30000); // Last 30 seconds
if (recentReadings.length === 0) return;
// Calculate average stress level
const avgStress = recentReadings.reduce((sum, r) => sum + (r.stressLevel || 0), 0) / recentReadings.length;
// Generate composite reading
this.reportBiometricReading({
stressLevel: avgStress,
heartRate: this.estimateHeartRateFromBehavior(avgStress),
timestamp: now,
confidence: 0.7
});
}
estimateHeartRateFromBehavior(stressLevel) {
// Rough estimation: normal resting HR + stress factor
const baselineHR = 70;
return Math.round(baselineHR + stressLevel * 30);
}
reportBiometricReading(reading) {
this.readings.push(reading);
// Keep only recent readings
if (this.readings.length > 100) {
this.readings = this.readings.slice(-50);
}
// Notify listeners
this.listeners.forEach(listener => listener(reading));
}
async connectHeartRateMonitor() {
// CRITICAL SSR FIX: Skip bluetooth access on server
if (typeof navigator === 'undefined') {
console.warn('BiometricSensorManager: Bluetooth not available on server');
return false;
}
try {
if (!("bluetooth" in navigator)) {
throw new Error("Web Bluetooth not supported");
}
const device = await navigator.bluetooth.requestDevice({
filters: [{
services: ["heart_rate"]
}],
optionalServices: ["heart_rate"]
});
const server = await device.gatt.connect();
const service = await server.getPrimaryService("heart_rate");
const characteristic = await service.getCharacteristic("heart_rate_measurement");
await characteristic.startNotifications();
characteristic.addEventListener("characteristicvaluechanged", event => {
const value = event.target.value;
const heartRate = value.getUint16(1, true);
this.reportBiometricReading({
heartRate,
timestamp: Date.now(),
confidence: 0.9
});
});
return true;
} catch (error) {
console.warn("Failed to connect heart rate monitor:", error);
return false;
}
}
addListener(listener) {
this.listeners.push(listener);
return () => {
const index = this.listeners.indexOf(listener);
if (index > -1) {
this.listeners.splice(index, 1);
}
};
}
getLatestReading() {
return this.readings.length > 0 ? this.readings[this.readings.length - 1] : null;
}
getReadingHistory(duration = 300000) {
const cutoff = Date.now() - duration;
return this.readings.filter(r => r.timestamp > cutoff);
}
cleanup() {
if (this.accelerometer) {
this.accelerometer.stop();
}
if (this.gyroscope) {
this.gyroscope.stop();
}
if (this.ambientLightSensor) {
this.ambientLightSensor.stop();
}
this.listeners = [];
this.readings = [];
this.cleanupBehavioralAnalysis();
}
}
// Adaptive UI engine
class BiometricAdaptationEngine {
constructor(settings = {}) {
this.adaptationCallbacks = new Map();
this.currentAdaptations = new Map();
this.sensorManager = new BiometricSensorManager();
this.settings = {
sensitivity: 0.7,
responseSpeed: 1000,
enableColorAdaptation: true,
enableMotionAdaptation: true,
enableLayoutAdaptation: true,
enableAudioAdaptation: true,
stressThreshold: 0.7,
calmingThreshold: 0.3,
...settings
};
this.profile = {
userId: "default",
baselineHeartRate: 70,
stressPatterns: [],
preferences: {
calmingColors: ["var(--glass-color-primary)", "#06b6d4", "var(--glass-color-success)", "#8b5cf6"],
stressColors: ["var(--glass-color-danger)", "var(--glass-color-warning)", "#ec4899"],
calmingAnimations: ["gentle", "slow", "smooth"],
stressAnimations: ["fast", "sharp", "intense"]
},
history: []
};
}
async initialize() {
const success = await this.sensorManager.initialize();
if (success) {
this.sensorManager.addListener(this.handleBiometricReading.bind(this));
this.loadProfile();
}
return success;
}
handleBiometricReading(reading) {
this.profile.history.push(reading);
// Keep history manageable
if (this.profile.history.length > 1000) {
this.profile.history = this.profile.history.slice(-500);
}
// Determine adaptation needed
const adaptations = this.determineAdaptations(reading);
// Apply adaptations with debouncing
this.debounceAdaptations(adaptations);
}
determineAdaptations(reading) {
const adaptations = new Map();
const stressLevel = reading.stressLevel || 0;
// Color adaptations
if (this.settings.enableColorAdaptation) {
if (stressLevel > this.settings.stressThreshold) {
adaptations.set("color", {
type: "calming",
intensity: stressLevel,
colors: this.profile.preferences.calmingColors
});
} else if (stressLevel < this.settings.calmingThreshold) {
adaptations.set("color", {
type: "energizing",
intensity: 1 - stressLevel,
colors: this.profile.preferences.stressColors
});
}
}
// Motion adaptations
if (this.settings.enableMotionAdaptation) {
if (stressLevel > this.settings.stressThreshold) {
adaptations.set("motion", {
type: "calming",
speed: Math.max(0.5, 1 - stressLevel),
amplitude: Math.max(0.3, 1 - stressLevel)
});
} else {
adaptations.set("motion", {
type: "normal",
speed: 1,
amplitude: 1
});
}
}
// Layout adaptations
if (this.settings.enableLayoutAdaptation && stressLevel > this.settings.stressThreshold) {
adaptations.set("layout", {
type: "simplified",
density: Math.max(0.5, 1 - stressLevel),
spacing: Math.min(2, 1 + stressLevel)
});
}
// Audio adaptations
if (this.settings.enableAudioAdaptation) {
if (stressLevel > this.settings.stressThreshold) {
adaptations.set("audio", {
type: "calming",
volume: Math.max(0.1, 0.5 - stressLevel * 0.3),
frequency: "low"
});
}
}
return adaptations;
}
debounceAdaptations(adaptations) {
adaptations.forEach((adaptation, type) => {
const existingTimeout = this.currentAdaptations.get(`${type}_timeout`);
if (existingTimeout) {
clearTimeout(existingTimeout);
}
const timeout = setTimeout(() => {
this.applyAdaptation(type, adaptation);
this.currentAdaptations.delete(`${type}_timeout`);
}, this.settings.responseSpeed);
this.currentAdaptations.set(`${type}_timeout`, timeout);
});
}
applyAdaptation(type, adaptation) {
this.currentAdaptations.set(type, adaptation);
const callback = this.adaptationCallbacks.get(type);
if (callback) {
callback(adaptation);
}
}
registerAdaptationCallback(type, callback) {
this.adaptationCallbacks.set(type, callback);
}
unregisterAdaptationCallback(type) {
this.adaptationCallbacks.delete(type);
}
getCurrentAdaptation(type) {
return this.currentAdaptations.get(type);
}
async connectHeartRateMonitor() {
return this.sensorManager.connectHeartRateMonitor();
}
getLatestReading() {
return this.sensorManager.getLatestReading();
}
getProfile() {
return {
...this.profile
};
}
updateSettings(settings) {
this.settings = {
...this.settings,
...settings
};
}
loadProfile() {
// CRITICAL SSR FIX: Skip localStorage access on server
if (typeof localStorage === 'undefined') {
return;
}
try {
const stored = localStorage.getItem("auraglass-biometric-profile");
if (stored) {
const data = JSON.parse(stored);
this.profile = {
...this.profile,
...data
};
}
} catch (error) {
console.warn("Failed to load biometric profile:", error);
}
}
saveProfile() {
// CRITICAL SSR FIX: Skip localStorage access on server
if (typeof localStorage === 'undefined') {
return;
}
try {
localStorage.setItem("auraglass-biometric-profile", JSON.stringify(this.profile));
} catch (error) {
console.warn("Failed to save biometric profile:", error);
}
}
cleanup() {
this.sensorManager.cleanup();
this.adaptationCallbacks.clear();
this.currentAdaptations.clear();
this.saveProfile();
}
}
// React context
const BiometricAdaptationContext = /*#__PURE__*/createContext({
engine: null,
isInitialized: false,
latestReading: null,
currentStressLevel: 0,
connectHeartRateMonitor: async () => false
});
// Provider component
function GlassBiometricAdaptationProvider({
children,
settings,
autoInitialize = true
}) {
const engineRef = useRef();
const [isInitialized, setIsInitialized] = useState(false);
const [latestReading, setLatestReading] = useState(null);
const [currentStressLevel, setCurrentStressLevel] = useState(0);
useEffect(() => {
const engine = new BiometricAdaptationEngine(settings);
engineRef.current = engine;
let isMounted = true;
let interval;
const finalizeInitialization = success => {
if (isMounted) {
setIsInitialized(success);
}
};
if (autoInitialize) {
if (IS_TEST_ENV) {
finalizeInitialization(true);
} else {
engine.initialize().then(finalizeInitialization).catch(() => finalizeInitialization(false));
}
}
if (!IS_TEST_ENV) {
interval = setInterval(() => {
if (!engineRef.current || !isMounted) {
return;
}
const reading = engineRef.current.getLatestReading();
if (reading) {
setLatestReading(reading);
setCurrentStressLevel(reading.stressLevel || 0);
}
}, 1000);
}
return () => {
isMounted = false;
if (interval) {
clearInterval(interval);
}
if (engineRef.current === engine) {
engineRef.current.cleanup();
engineRef.current = undefined;
} else {
engine.cleanup();
}
};
}, [autoInitialize, settings]);
const connectHeartRateMonitor = useCallback(async () => {
if (!engineRef.current) return false;
return engineRef.current.connectHeartRateMonitor();
}, []);
const value = {
engine: engineRef.current || null,
isInitialized,
latestReading,
currentStressLevel,
connectHeartRateMonitor
};
return jsx(BiometricAdaptationContext.Provider, {
value: value,
children: children
});
}
// Hook to use biometric adaptation
function useBiometricAdaptation() {
const context = useContext(BiometricAdaptationContext);
if (!context) {
throw new Error("useBiometricAdaptation must be used within GlassBiometricAdaptationProvider");
}
return context;
}
class BiometricStressDetector extends BiometricAdaptationEngine {
getStressLevel() {
return this.getLatestReading()?.stressLevel ?? 0;
}
getConfidence() {
return this.getLatestReading()?.confidence ?? 0;
}
}
// Stress-responsive glass component
const GlassStressResponsive = /*#__PURE__*/forwardRef(function GlassStressResponsive({
children,
className,
adaptationType = "all",
respectMotionPreference = true,
"aria-label": ariaLabel,
"aria-describedby": ariaDescribedBy,
role,
...restProps
}, ref) {
const {
engine,
currentStressLevel,
latestReading
} = useBiometricAdaptation();
const [adaptations, setAdaptations] = useState({});
const {
prefersReducedMotion
} = useMotionPreferenceContext();
const stressId = useA11yId("stress-responsive");
const descriptionId = useA11yId("stress-description");
useEffect(() => {
if (!engine) return;
const handleColorAdaptation = adaptation => {
if (adaptationType === "color" || adaptationType === "all") {
setAdaptations(prev => ({
...prev,
color: adaptation
}));
}
};
const handleMotionAdaptation = adaptation => {
if (adaptationType === "motion" || adaptationType === "all") {
setAdaptations(prev => ({
...prev,
motion: adaptation
}));
}
};
const handleLayoutAdaptation = adaptation => {
if (adaptationType === "layout" || adaptationType === "all") {
setAdaptations(prev => ({
...prev,
layout: adaptation
}));
}
};
engine.registerAdaptationCallback("color", handleColorAdaptation);
engine.registerAdaptationCallback("motion", handleMotionAdaptation);
engine.registerAdaptationCallback("layout", handleLayoutAdaptation);
return () => {
engine.unregisterAdaptationCallback("color");
engine.unregisterAdaptationCallback("motion");
engine.unregisterAdaptationCallback("layout");
};
}, [engine, adaptationType]);
// Calculate adaptive styles
const colorAdaptation = adaptations.color;
const motionAdaptation = adaptations.motion;
const layoutAdaptation = adaptations.layout;
const adaptiveStyles = {};
if (colorAdaptation) {
if (colorAdaptation.type === "calming") {
const calmColor = colorAdaptation.colors[0] || "var(--glass-color-primary)";
adaptiveStyles.backgroundColor = `${calmColor}20`;
adaptiveStyles.borderColor = `${calmColor}40`;
}
}
const motionSpeed = motionAdaptation?.speed || 1;
const layoutSpacing = layoutAdaptation?.spacing || 1;
const shouldAnimate = respectMotionPreference ? !prefersReducedMotion : true;
return jsx(motion.div, {
ref: ref,
style: adaptiveStyles,
animate: shouldAnimate ? {
scale: 1 + (currentStressLevel > 0.7 ? -0.02 : 0.01) * currentStressLevel,
padding: `${8 * layoutSpacing}px`
} : {},
transition: shouldAnimate ? {
duration: 2 / motionSpeed,
ease: currentStressLevel > 0.7 ? "easeOut" : "easeInOut"
} : {},
id: stressId,
role: role || "region",
"aria-label": ariaLabel || `Stress-responsive interface (${Math.round(currentStressLevel * 100)}% stress level)`,
"aria-describedby": ariaDescribedBy || descriptionId,
"aria-live": "polite",
className: cn("glass-surface glass-border glass-radius-md glass-glass-backdrop-blur-md glass-contrast-guard transition-all duration-1000", currentStressLevel > 0.7 ? "glass-surface-subtle glass-border-subtle" : "glass-surface-medium glass-border-medium", currentStressLevel > 0.7 && "calming-mode", className),
...restProps,
children: jsxs(OptimizedGlassCore, {
children: [jsxs("span", {
id: descriptionId,
className: 'sr-only',
children: ["Biometric adaptation interface responding to stress level", " ", Math.round(currentStressLevel * 100), "%.", adaptationType !== "all" ? ` Adaptation type: ${adaptationType}` : " All adaptations active."]
}), children, jsx("div", {
className: "glass-absolute glass-top-2 glass-right-2 glass-opacity-30",
children: jsx("div", {
className: cn("glass-w-2 glass-h-2 glass-radius-full glass-transition", currentStressLevel > 0.7 ? "glass-surface-danger" : currentStressLevel > 0.4 ? "glass-surface-warning" : "glass-surface-success"),
"aria-hidden": true
})
})]
})
});
});
// Biometric dashboard
const GlassBiometricDashboard = /*#__PURE__*/forwardRef(function GlassBiometricDashboard({
className,
show = true,
"aria-label": ariaLabel,
role,
...restProps
}, ref) {
const {
latestReading,
currentStressLevel,
connectHeartRateMonitor,
engine
} = useBiometricAdaptation();
const [history, setHistory] = useState([]);
const [showDetails, setShowDetails] = useState(false);
const dashboardId = useA11yId("biometric-dashboard");
useEffect(() => {
if (!engine) return;
const interval = setInterval(() => {
const readings = engine.getLatestReading();
if (readings) {
setHistory(prev => [...prev.slice(-19), readings]); // Keep last 20 readings
}
}, 2000);
return () => clearInterval(interval);
}, [engine]);
if (!show) return null;
return jsxs(OptimizedGlassCore, {
ref: ref,
intensity: "subtle",
glassBlur: "strong",
className: cn("fixed top-4 left-4 glass-p-4 glass-radius-lg", className),
id: dashboardId,
role: role || "region",
"aria-label": ariaLabel || "Biometric monitoring dashboard",
...restProps,
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between glass-mb-3",
children: [jsx("h3", {
className: 'glass-text-sm font-medium glass-text-secondary dark:glass-text-secondary',
children: "Biometrics"
}), jsx("button", {
onClick: () => setShowDetails(!showDetails),
className: 'glass-text-xs glass-text-secondary hover:glass-text-secondary glass-focus glass-touch-target glass-contrast-guard',
"aria-expanded": showDetails,
"aria-controls": `${dashboardId}-details`,
children: showDetails ? "−" : "+"
})]
}), jsxs("div", {
className: "glass-gap-2",
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsx("span", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary',
children: "Stress Level"
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("div", {
className: "glass-w-16 glass-h-2 glass-surface-subtle glass-radius-full glass-overflow-hidden",
children: jsx(motion.div, {
className: cn("glass-h-full glass-radius-full glass-transition", currentStressLevel > 0.7 ? "glass-surface-danger" : currentStressLevel > 0.4 ? "glass-surface-warning" : "glass-surface-success"),
animate: {
width: `${currentStressLevel * 100}%`
},
transition: {
duration: 0.5
}
})
}), jsxs("span", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary',
children: [(currentStressLevel * 100).toFixed(0), "%"]
})]
})]
}), latestReading?.heartRate && jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsx("span", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary',
children: "Heart Rate"
}), jsxs("span", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary',
children: [latestReading.heartRate, " bpm"]
})]
})]
}), jsx(AnimatePresence, {
children: showDetails && jsxs(motion.div, {
id: `${dashboardId}-details`,
className: 'glass-mt-4 pt-4 glass-border-t glass-border-white/10 glass-gap-3',
initial: {
opacity: 0,
height: 0
},
animate: {
opacity: 1,
height: "auto"
},
exit: {
opacity: 0,
height: 0
},
transition: {
duration: 0.3
},
children: [jsxs("div", {
children: [jsx("div", {
className: "glass-text-xs glass-text-secondary glass-mb-2",
children: "Stress History"
}), jsx("div", {
className: "glass-relative glass-h-12 glass-surface-primary/50 glass-radius-md",
children: history.map((reading, index) => jsx("div", {
ref: el => {
if (!el) return;
el.style.left = `${index / (history.length - 1) * 100}%`;
el.style.height = `${(reading.stressLevel || 0) * 100}%`;
el.style.backgroundColor = (reading.stressLevel || 0) > 0.7 ? "var(--glass-color-danger)" : (reading.stressLevel || 0) > 0.4 ? "var(--glass-color-warning)" : "var(--glass-color-success)";
},
className: "glass-absolute glass-bottom-0 glass-w-1 glass-radius-md"
}, index))
})]
}), jsx("div", {
children: jsx("button", {
onClick: connectHeartRateMonitor,
className: cn("glass-w-full glass-px-3 glass-py-2 glass-text-xs glass-surface-subtle glass-radius-md glass-focus glass-touch-target glass-contrast-guard", "glass-text-secondary hover:glass-surface-subtle glass-transition"),
children: "Connect Heart Rate Monitor"
})
}), engine && jsxs("div", {
children: [jsx("div", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary mb-2',
children: "Active Adaptations"
}), jsx("div", {
className: "glass-gap-1",
children: ["color", "motion", "layout", "audio"].map(type => {
const adaptation = engine.getCurrentAdaptation(type);
return adaptation ? jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between glass-text-xs",
children: [jsx("span", {
className: 'glass-text-secondary dark:glass-text-secondary capitalize',
children: type
}), jsx("span", {
className: 'glass-text-secondary dark:glass-text-secondary capitalize',
children: adaptation.type
})]
}, type) : null;
})
})]
}), latestReading && jsxs("div", {
children: [jsx("div", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary mb-1',
children: "Last Reading"
}), jsx("div", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary',
children: new Date(latestReading.timestamp).toLocaleTimeString()
}), jsxs("div", {
className: 'glass-text-xs glass-text-secondary dark:glass-text-secondary',
children: ["Confidence: ", (latestReading.confidence * 100).toFixed(0), "%"]
})]
})]
})
})]
});
});
// Presets for different adaptation modes
const biometricAdaptationPresets = {
subtle: {
sensitivity: 0.3,
responseSpeed: 2000,
stressThreshold: 0.8,
calmingThreshold: 0.2
},
standard: {
sensitivity: 0.7,
responseSpeed: 1000,
stressThreshold: 0.7,
calmingThreshold: 0.3
},
sensitive: {
sensitivity: 0.9,
responseSpeed: 500,
stressThreshold: 0.5,
calmingThreshold: 0.4
},
accessibility: {
sensitivity: 0.8,
responseSpeed: 1500,
enableColorAdaptation: true,
enableMotionAdaptation: true,
enableLayoutAdaptation: true,
stressThreshold: 0.6
}
};
function BiometricSummaryCard() {
const {
latestReading,
currentStressLevel,
isInitialized
} = useBiometricAdaptation();
return jsxs("div", {
className: cn("glass-surface-primary glass-radius-2xl glass-p-6 glass-space-y-4", "glass-border glass-border-white/10 glass-shadow-soft-lg"),
"data-testid": "glass-biometric-summary",
children: [jsxs("div", {
children: [jsx("p", {
className: "glass-text-xs glass-text-tertiary uppercase tracking-wide",
children: "Biometric adaptation"
}), jsx("h2", {
className: "glass-text-2xl glass-text-primary font-semibold",
children: isInitialized ? "Monitoring" : "Initializing"
}), jsxs("p", {
className: "glass-text-sm glass-text-secondary",
children: ["Stress level ", (currentStressLevel * 100).toFixed(0), "%"]
})]
}), jsxs("div", {
className: "glass-grid glass-grid-cols-2 glass-gap-3",
children: [jsxs("div", {
className: "glass-surface-subtle glass-radius-xl glass-p-4",
children: [jsx("p", {
className: "glass-text-xs glass-text-tertiary mb-1",
children: "Heart rate"
}), jsx("p", {
className: "glass-text-lg glass-text-primary font-semibold",
children: latestReading?.heartRate ? `${latestReading.heartRate.toFixed(0)} bpm` : "—"
})]
}), jsxs("div", {
className: "glass-surface-subtle glass-radius-xl glass-p-4",
children: [jsx("p", {
className: "glass-text-xs glass-text-tertiary mb-1",
children: "Respiratory"
}), jsx("p", {
className: "glass-text-lg glass-text-primary font-semibold",
children: latestReading?.respiratoryRate ? `${latestReading.respiratoryRate.toFixed(0)} rpm` : "—"
})]
})]
}), jsxs("div", {
className: "glass-text-xs glass-text-secondary",
children: ["Confidence", " ", latestReading ? `${Math.round((latestReading.confidence || 0) * 100)}%` : "Collecting signals"]
})]
});
}
const GlassBiometricAdaptation = ({
settings,
autoInitialize = true,
showDashboard = true,
className,
children,
...rest
}) => jsx(GlassBiometricAdaptationProvider, {
settings: settings,
autoInitialize: autoInitialize,
children: jsxs("div", {
className: cn("glass-biometric-adaptation glass-relative glass-space-y-4", className),
...rest,
children: [children ?? jsx(BiometricSummaryCard, {}), showDashboard && jsx(GlassBiometricDashboard, {})]
})
});
export { BiometricAdaptationEngine, BiometricStressDetector, GlassBiometricAdaptation, GlassBiometricAdaptationProvider, GlassBiometricDashboard, GlassStressResponsive, biometricAdaptationPresets, GlassBiometricAdaptation as default, useBiometricAdaptation };
//# sourceMappingURL=GlassBiometricAdaptation.js.map