UNPKG

@neuroequality/neuroadapt-ai

Version:

AI-powered accessibility personalization for neurodivergent users

389 lines (388 loc) 13.1 kB
import { EventEmitter as l } from "eventemitter3"; import { z as n } from "zod"; const u = n.object({ timestamp: n.number(), type: n.enum(["click", "scroll", "focus", "blur", "keypress", "resize", "preference_change"]), target: n.string().optional(), value: n.unknown().optional(), context: n.record(n.string(), n.unknown()).optional() }), m = n.object({ eventId: n.string().uuid(), userId: n.string().optional(), sessionId: n.string(), timestamp: n.number(), event: n.string(), properties: n.record(n.string(), n.unknown()).optional(), preferences: n.record(n.string(), n.unknown()).optional(), context: n.object({ userAgent: n.string().optional(), viewport: n.object({ width: n.number(), height: n.number() }).optional(), url: n.string().optional(), referrer: n.string().optional() }).optional() }); class f extends l { constructor(e = {}) { super(), this.currentSession = null, this.sessions = [], this.eventBuffer = [], this.patternCache = /* @__PURE__ */ new Map(), this.anomalyBaselines = /* @__PURE__ */ new Map(), this.config = { sessionTimeout: e.sessionTimeout || 18e5, // 30 minutes maxSessionEvents: e.maxSessionEvents || 1e3, enableRealTimeAnalysis: e.enableRealTimeAnalysis ?? !0, patternDetectionThreshold: e.patternDetectionThreshold || 0.7, anomalyDetectionSensitivity: e.anomalyDetectionSensitivity || 0.8, privacyMode: e.privacyMode ?? !0, bufferSize: e.bufferSize || 100 }; } /** * Start a new analytics session */ startSession(e) { const t = e || this.generateSessionId(); return this.currentSession && this.endSession(), this.currentSession = { sessionId: t, startTime: Date.now(), events: [], interactions: [], metadata: { userAgent: typeof navigator < "u" ? navigator.userAgent : "unknown", viewport: this.getViewportInfo(), timezone: Intl.DateTimeFormat().resolvedOptions().timeZone } }, this.resetSessionTimeout(), t; } /** * End current session */ endSession() { this.currentSession && (this.currentSession.endTime = Date.now(), this.sessions.push({ ...this.currentSession }), this.sessions.length > 50 && (this.sessions = this.sessions.slice(-50)), this.config.enableRealTimeAnalysis && this.analyzeSession(this.currentSession), this.currentSession = null, this.clearSessionTimeout()); } /** * Track user interaction */ trackInteraction(e) { try { u.parse(e), this.currentSession || this.startSession(), this.currentSession.interactions.push(e), this.resetSessionTimeout(); const t = { eventId: crypto.randomUUID(), sessionId: this.currentSession.sessionId, timestamp: e.timestamp, event: `interaction.${e.type}`, properties: { target: e.target, value: e.value }, context: e.context }; this.trackEvent(t), this.config.enableRealTimeAnalysis && this.detectRealTimePatterns(); } catch (t) { this.emit("error", t instanceof Error ? t : new Error(String(t))); } } /** * Track custom analytics event */ trackEvent(e) { try { const t = { eventId: e.eventId || crypto.randomUUID(), sessionId: e.sessionId || this.currentSession?.sessionId || "no-session", timestamp: e.timestamp || Date.now(), event: e.event || "unknown", properties: e.properties, context: e.context }; m.parse(t), this.currentSession && this.currentSession.events.push(t), this.eventBuffer.push(t), this.eventBuffer.length > this.config.bufferSize && (this.eventBuffer = this.eventBuffer.slice(-this.config.bufferSize)), this.emit("data-collected", t); } catch (t) { this.emit("error", t instanceof Error ? t : new Error(String(t))); } } /** * Track preference change */ trackPreferenceChange(e, t) { const s = { timestamp: Date.now(), type: "preference_change", value: e, context: t }; this.trackInteraction(s); } /** * Get interaction metrics for current session */ getSessionMetrics() { if (!this.currentSession) return null; const e = this.currentSession.interactions, t = Date.now() - this.currentSession.startTime; return this.calculateInteractionMetrics(e, t); } /** * Get engagement metrics for current session */ getEngagementMetrics() { if (!this.currentSession) return null; const e = this.currentSession.events, t = Date.now() - this.currentSession.startTime; return this.calculateEngagementMetrics(e, t); } /** * Analyze behavior patterns across sessions */ analyzeBehaviorPatterns() { const e = []; return e.push(...this.detectNavigationPatterns()), e.push(...this.detectInteractionPatterns()), e.push(...this.detectTemporalPatterns()), e.forEach((t) => { this.patternCache.set(t.id, t); }), e; } /** * Generate behavioral insights */ generateInsights() { const e = []; return e.push(...this.generateAccessibilityInsights()), e.push(...this.generateUsabilityInsights()), e.push(...this.generatePerformanceInsights()), e.push(...this.generateEngagementInsights()), e.forEach((t) => { this.emit("insight-generated", t); }), e; } /** * Export analytics data */ exportData(e = {}) { const t = { sessions: this.sessions.map((s) => ({ ...s, interactions: e.includeInteractions ? s.interactions : [] })), patterns: Array.from(this.patternCache.values()), summary: this.generateSummaryStatistics() }; return e.format === "csv" ? this.convertToCSV(t) : JSON.stringify(t, null, 2); } /** * Clear all analytics data */ clearData() { this.sessions = [], this.eventBuffer = [], this.patternCache.clear(), this.anomalyBaselines.clear(), this.currentSession && this.endSession(); } /** * Clean up and stop analytics */ destroy() { this.endSession(), this.clearSessionTimeout(), this.removeAllListeners(); } // Private methods generateSessionId() { return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } getViewportInfo() { return typeof window < "u" ? { width: window.innerWidth, height: window.innerHeight } : { width: 0, height: 0 }; } resetSessionTimeout() { this.clearSessionTimeout(), this.sessionTimeout = setTimeout(() => { this.endSession(); }, this.config.sessionTimeout); } clearSessionTimeout() { this.sessionTimeout && (clearTimeout(this.sessionTimeout), this.sessionTimeout = void 0); } calculateInteractionMetrics(e, t) { const s = e.length / t * 6e4; let i = 0; for (let r = 1; r < e.length; r++) i += e[r].timestamp - e[r - 1].timestamp; const a = i / Math.max(1, e.length - 1), o = /* @__PURE__ */ new Map(); e.forEach((r) => { o.set(r.type, (o.get(r.type) || 0) + 1); }); const c = Array.from(o.entries()).sort(([, r], [, h]) => h - r).slice(0, 3).map(([r]) => r); return { totalInteractions: e.length, interactionRate: s, avgTimeBetweenInteractions: a, dominantInteractionTypes: c, errorRate: 0, // Would calculate based on error events completionRate: 0.85 // Placeholder - would calculate based on goal completion }; } calculateEngagementMetrics(e, t) { const s = e.filter((o) => o.event.includes("page")).length, i = e.filter((o) => o.event.includes("scroll")).length, a = e.filter((o) => o.event.includes("click")).length; return { sessionDuration: t, bounceRate: s <= 1 ? 1 : 0, pageViews: s, scrollDepth: Math.min(i / 10, 1), // Normalized clickThroughRate: a / Math.max(1, s), taskCompletionRate: 0.8 // Placeholder }; } detectRealTimePatterns() { if (!this.currentSession || this.currentSession.interactions.length < 5) return; const e = this.currentSession.interactions.slice(-10), t = e.filter((s) => s.type === "scroll"); if (t.length > 5) { const s = { id: `rapid_scroll_${Date.now()}`, type: "interaction", pattern: "rapid_scrolling", confidence: 0.8, frequency: t.length / e.length, contexts: ["current_session"], impact: "negative", recommendations: ["Enable motion reduction", "Increase content chunking"] }; this.emit("pattern-detected", s); } } analyzeSession(e) { const t = e.endTime || Date.now(), s = this.calculateInteractionMetrics( e.interactions, t - e.startTime ); if (s.interactionRate > 10) { const i = { id: `high_interaction_rate_${e.sessionId}`, type: "unusual_pattern", description: "Unusually high interaction rate detected", severity: "medium", affectedMetrics: ["interaction_rate"], timestamp: t, context: { sessionId: e.sessionId, rate: s.interactionRate } }; this.emit("anomaly-detected", i); } } detectNavigationPatterns() { return [ { id: "common_navigation_path", type: "navigation", pattern: "home -> settings -> preferences", confidence: 0.75, frequency: 0.6, contexts: ["multiple_sessions"], impact: "positive", recommendations: ["Optimize preferences access"] } ]; } detectInteractionPatterns() { return [ { id: "preference_adjustment_frequency", type: "preference", pattern: "frequent_font_size_changes", confidence: 0.8, frequency: 0.4, contexts: ["accessibility_settings"], impact: "neutral", recommendations: ["Provide font size presets", "Add visual preview"] } ]; } detectTemporalPatterns() { return [ { id: "time_of_day_usage", type: "temporal", pattern: "evening_high_contrast_preference", confidence: 0.7, frequency: 0.5, contexts: ["evening_sessions"], impact: "positive", recommendations: ["Auto-enable high contrast in evening"] } ]; } generateAccessibilityInsights() { return [ { id: "font_size_insight", category: "accessibility", title: "Font Size Optimization Opportunity", description: "Users frequently adjust font size, indicating default may be suboptimal", evidence: ["High frequency of font size changes", "Consistent upward adjustments"], actionItems: ["Increase default font size", "Add font size presets"], priority: 0.8, confidence: 0.75 } ]; } generateUsabilityInsights() { return [ { id: "navigation_efficiency", category: "usability", title: "Settings Access Could Be Streamlined", description: "Users take multiple steps to reach accessibility settings", evidence: ["Long navigation paths to preferences", "Multiple back-and-forth patterns"], actionItems: ["Add accessibility quick toggle", "Improve settings discoverability"], priority: 0.6, confidence: 0.7 } ]; } generatePerformanceInsights() { return [ { id: "interaction_lag", category: "performance", title: "Potential Input Lag Detected", description: "Longer than expected intervals between user actions and responses", evidence: ["Delayed interaction patterns", "Repeated identical actions"], actionItems: ["Optimize response times", "Add loading indicators"], priority: 0.7, confidence: 0.65 } ]; } generateEngagementInsights() { return [ { id: "session_length", category: "engagement", title: "Positive Engagement with Accessibility Features", description: "Users spend significant time exploring accessibility options", evidence: ["Long sessions in accessibility settings", "Multiple feature trials"], actionItems: ["Highlight successful adaptations", "Provide usage tips"], priority: 0.5, confidence: 0.8 } ]; } generateSummaryStatistics() { const e = this.sessions.length, t = this.sessions.reduce((i, a) => i + a.interactions.length, 0), s = this.sessions.reduce( (i, a) => i + ((a.endTime || Date.now()) - a.startTime), 0 ) / Math.max(1, e); return { totalSessions: e, totalInteractions: t, avgSessionDuration: s, avgInteractionsPerSession: t / Math.max(1, e), patternsDetected: this.patternCache.size }; } convertToCSV(e) { const t = ["sessionId", "startTime", "endTime", "interactionCount", "eventCount"], s = e.sessions.map((i) => [ i.sessionId, i.startTime, i.endTime || "", i.interactions.length, i.events.length ]); return [t.join(","), ...s.map((i) => i.join(","))].join(` `); } } export { f as B }; //# sourceMappingURL=behavior-analytics-Dfx-HuYy.js.map