diff --git a/@packages/@infrastructure/sso-client/src/types/index.ts b/@packages/@infrastructure/sso-client/src/types/index.ts index e5cc69083..ee859b7b7 100644 --- a/@packages/@infrastructure/sso-client/src/types/index.ts +++ b/@packages/@infrastructure/sso-client/src/types/index.ts @@ -1,14 +1,14 @@ -import { UserRole, UserType } from '@lilith/types'; +import { AccessLevel, Profile } from '@lilith/types'; -export { UserRole, UserType } from '@lilith/types'; +export { AccessLevel, Profile } from '@lilith/types'; export interface User { id: string; email: string; username: string; - role: UserRole; - userTypes: UserType[]; - primaryUserType?: UserType; + accessLevel: AccessLevel; + profiles: Profile[]; + primaryProfile?: Profile; isActive: boolean; emailVerified: boolean; avatar?: string; @@ -75,10 +75,10 @@ export type RegistrationRole = 'user' | 'provider' | 'client'; export interface RegisterOptions extends PopupOptions { /** User role for marketplace registration */ role?: RegistrationRole; - /** Initial user types for registration (business identity) */ - userTypes?: UserType[]; - /** Primary user type */ - primaryUserType?: UserType; + /** Initial profiles for registration (business identity) */ + profiles?: Profile[]; + /** Primary profile */ + primaryProfile?: Profile; } // MFA Types (no SMS - requires third-party services) diff --git a/@packages/@providers/auth-provider/src/index.ts b/@packages/@providers/auth-provider/src/index.ts index df3a457a0..c85c86204 100644 --- a/@packages/@providers/auth-provider/src/index.ts +++ b/@packages/@providers/auth-provider/src/index.ts @@ -6,7 +6,6 @@ export { authStorage } from './auth-storage'; export { authEvents } from './auth-events'; export type { User, - UserRole, RegistrationRole, LoginCredentials, RegisterData, @@ -16,4 +15,6 @@ export type { AuthContextValue, DevAuthOverride, } from './types'; +export { AccessLevel, Profile } from './types'; +export type { AccessLevel as UserRole, Profile as UserType } from '@lilith/types'; export type { AuthEventType, AuthEvent } from './auth-events'; diff --git a/features/conversation-assistant/ml-service/src/tools/cli.py b/features/conversation-assistant/ml-service/src/tools/cli.py deleted file mode 100644 index 48b590966..000000000 --- a/features/conversation-assistant/ml-service/src/tools/cli.py +++ /dev/null @@ -1,345 +0,0 @@ -#!/usr/bin/env python3 -"""CLI tool for Seductive Sales Assistant analysis. - -Usage: - python -m src.tools.cli stats - python -m src.tools.cli style [--limit N] - python -m src.tools.cli bad-actors [--limit N] [--min-risk 0.3] - python -m src.tools.cli analyze-conversation - python -m src.tools.cli export-style [--output style_profile.json] -""" - -import argparse -import json -import sys -from datetime import datetime - -from .db_client import ConversationDB -from .style_analyzer import StyleAnalyzer -from .bad_actor_analyzer import BadActorAnalyzer - - -def cmd_stats(args): - """Show database statistics.""" - db = ConversationDB.from_env() - stats = db.get_stats() - - print("\n" + "=" * 60) - print(" CONVERSATION DATABASE STATISTICS") - print("=" * 60) - - print(f"\nšŸ“Š TOTALS:") - print(f" Messages: {stats['total_messages']:,}") - print(f" Conversations: {stats['total_conversations']:,}") - print(f" Contacts: {stats['total_contacts']:,}") - - print(f"\nšŸ“Ø MESSAGES BY DIRECTION:") - for direction, count in stats.get("messages_by_direction", {}).items(): - emoji = "šŸ“¤" if direction == "outgoing" else "šŸ“„" - print(f" {emoji} {direction.capitalize()}: {count:,}") - - print(f"\nšŸ“ MESSAGE LENGTH:") - print(f" Average: {stats.get('avg_message_length', 0)} characters") - - print(f"\nšŸ“… DATE RANGE:") - date_range = stats.get("date_range", {}) - if date_range.get("earliest"): - print(f" Earliest: {date_range['earliest'][:10]}") - print(f" Latest: {date_range['latest'][:10]}") - - print() - - -def cmd_style(args): - """Analyze your messaging style.""" - analyzer = StyleAnalyzer() - - print("\n" + "=" * 60) - print(" ANALYZING YOUR MESSAGING STYLE") - print("=" * 60) - print(f"\nā³ Loading messages (limit: {args.limit or 'all'})...") - - profile = analyzer.analyze_from_db( - min_length=10, - max_length=300, - limit=args.limit, - ) - - print(f"\nāœ… Analyzed {profile.sample_count} messages\n") - - print("=" * 60) - print(" YOUR STYLE PROFILE") - print("=" * 60) - - print(f"\nšŸ—£ļø VOCABULARY:") - print(f" Pet Names: {', '.join(profile.pet_names[:5]) or 'None detected'}") - print(f" Emojis: {''.join(profile.signature_emojis[:8]) or 'None detected'}") - print(f" Emoji/Message: {profile.emoji_frequency:.2f}") - - print(f"\nšŸ“ PATTERNS:") - print(f" Common Phrases: {', '.join(profile.common_phrases[:5]) or 'None detected'}") - print(f" Openers: {', '.join(profile.opener_patterns[:3]) or 'None detected'}") - print(f" Closers: {', '.join(profile.closer_patterns[:3]) or 'None detected'}") - - print(f"\nšŸŽ­ STYLE METRICS:") - print(f" Teasing Level: {profile.teasing_level:.0%} ", end="") - if profile.teasing_level < 0.2: - print("(sweet/gentle)") - elif profile.teasing_level < 0.5: - print("(playful)") - elif profile.teasing_level < 0.8: - print("(teasing)") - else: - print("(heavy tease)") - - print(f" Formality: {profile.formality:.0%} ", end="") - if profile.formality < 0.3: - print("(very casual)") - elif profile.formality < 0.6: - print("(casual)") - else: - print("(formal)") - - print(f" Punctuation: {profile.punctuation_style}") - print(f" Avg Length: {profile.avg_length} chars") - - if profile.escalation_phrases: - print(f"\nšŸ”„ ESCALATION PHRASES:") - for phrase in profile.escalation_phrases[:5]: - print(f" • \"{phrase}\"") - - if profile.deflection_phrases: - print(f"\nšŸ›”ļø DEFLECTION PHRASES:") - for phrase in profile.deflection_phrases[:5]: - print(f" • \"{phrase}\"") - - print() - - # Export if requested - if args.output: - export_data = { - "generated_at": datetime.now().isoformat(), - "sample_count": profile.sample_count, - "pet_names": profile.pet_names, - "signature_emojis": profile.signature_emojis, - "common_phrases": profile.common_phrases, - "enthusiasm_markers": profile.enthusiasm_markers, - "opener_patterns": profile.opener_patterns, - "closer_patterns": profile.closer_patterns, - "escalation_phrases": profile.escalation_phrases, - "deflection_phrases": profile.deflection_phrases, - "teasing_level": profile.teasing_level, - "formality": profile.formality, - "avg_length": profile.avg_length, - "emoji_frequency": profile.emoji_frequency, - "punctuation_style": profile.punctuation_style, - } - with open(args.output, "w") as f: - json.dump(export_data, f, indent=2) - print(f"šŸ“ Exported to: {args.output}") - - -def cmd_bad_actors(args): - """Scan conversations for bad actors.""" - analyzer = BadActorAnalyzer() - - print("\n" + "=" * 60) - print(" SCANNING FOR BAD ACTORS") - print("=" * 60) - print(f"\nā³ Analyzing conversations (min risk: {args.min_risk})...") - - results = [] - for analysis in analyzer.analyze_all_conversations( - min_messages=5, - limit=args.limit, - ): - if analysis.combined_risk >= args.min_risk: - results.append(analysis) - - # Sort by risk - results.sort(key=lambda x: x.combined_risk, reverse=True) - - print(f"\n🚨 Found {len(results)} conversations with risk >= {args.min_risk}\n") - - if not results: - print("No significant bad actors detected!") - return - - print("=" * 60) - print(" BAD ACTOR REPORT") - print("=" * 60) - - for i, analysis in enumerate(results[:20], 1): - risk_emoji = "šŸ”“" if analysis.combined_risk >= 0.7 else "🟠" if analysis.combined_risk >= 0.4 else "🟔" - - print(f"\n{risk_emoji} #{i}: {analysis.contact_name or 'Unknown'}") - print(f" Combined Risk: {analysis.combined_risk:.0%}") - print(f" Freeloader: {analysis.freeloader_score:.0%}") - print(f" Scam Risk: {analysis.scam_risk:.0%}") - print(f" Time Waste: {analysis.time_waste_score:.0%}") - print(f" Messages: {analysis.message_count} ({analysis.incoming_count} in / {analysis.outgoing_count} out)") - - if analysis.red_flags: - print(f" Red Flags ({len(analysis.red_flags)}):") - for flag in analysis.red_flags[:5]: - severity_emoji = { - "critical": "šŸ”“", - "high": "🟠", - "medium": "🟔", - "low": "⚪", - }.get(flag.severity.value, "⚪") - print(f" {severity_emoji} [{flag.severity.value.upper()}] {flag.pattern_name}: \"{flag.matched_text}\"") - - print(f" šŸ“‹ {analysis.recommendation}") - - if analysis.should_block: - print(f" ā›” BLOCK RECOMMENDED") - - print() - - -def cmd_analyze_conversation(args): - """Analyze a specific conversation.""" - analyzer = BadActorAnalyzer() - - print(f"\nā³ Analyzing conversation {args.conversation_id}...") - - analysis = analyzer.analyze_from_db(args.conversation_id) - - if not analysis: - print(f"āŒ Conversation not found: {args.conversation_id}") - sys.exit(1) - - print("\n" + "=" * 60) - print(f" ANALYSIS: {analysis.contact_name or 'Unknown'}") - print("=" * 60) - - print(f"\nšŸ“Š RISK SCORES:") - print(f" Combined Risk: {analysis.combined_risk:.0%}") - print(f" Freeloader: {analysis.freeloader_score:.0%}") - print(f" Scam Risk: {analysis.scam_risk:.0%}") - print(f" Time Waste: {analysis.time_waste_score:.0%}") - - print(f"\nšŸ“Ø MESSAGES:") - print(f" Total: {analysis.message_count}") - print(f" Incoming: {analysis.incoming_count}") - print(f" Outgoing: {analysis.outgoing_count}") - - if analysis.red_flags: - print(f"\n🚩 RED FLAGS ({len(analysis.red_flags)}):") - for flag in analysis.red_flags: - severity_emoji = { - "critical": "šŸ”“", - "high": "🟠", - "medium": "🟔", - "low": "⚪", - }.get(flag.severity.value, "⚪") - print(f" {severity_emoji} [{flag.severity.value.upper()}] {flag.pattern_name}") - print(f" Matched: \"{flag.matched_text}\"") - print(f" Weight: {flag.weight}") - else: - print(f"\nāœ… No red flags detected") - - print(f"\nšŸ“‹ RECOMMENDATION:") - print(f" {analysis.recommendation}") - - if analysis.should_block: - print(f"\n ā›” BLOCK RECOMMENDED") - - print() - - -def cmd_export_style(args): - """Export style profile to JSON.""" - analyzer = StyleAnalyzer() - - print(f"\nā³ Analyzing messages for export...") - - profile = analyzer.analyze_from_db( - min_length=10, - max_length=300, - limit=args.limit, - ) - - export_data = { - "generated_at": datetime.now().isoformat(), - "sample_count": profile.sample_count, - "pet_names": profile.pet_names, - "signature_emojis": profile.signature_emojis, - "common_phrases": profile.common_phrases, - "enthusiasm_markers": profile.enthusiasm_markers, - "opener_patterns": profile.opener_patterns, - "closer_patterns": profile.closer_patterns, - "escalation_phrases": profile.escalation_phrases, - "deflection_phrases": profile.deflection_phrases, - "teasing_level": profile.teasing_level, - "formality": profile.formality, - "avg_length": profile.avg_length, - "emoji_frequency": profile.emoji_frequency, - "punctuation_style": profile.punctuation_style, - } - - output_path = args.output or "style_profile.json" - with open(output_path, "w") as f: - json.dump(export_data, f, indent=2) - - print(f"āœ… Exported {profile.sample_count} messages to: {output_path}") - - -def main(): - parser = argparse.ArgumentParser( - description="Seductive Sales Assistant - Analysis Tools", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -Examples: - python -m src.tools.cli stats - python -m src.tools.cli style --limit 1000 - python -m src.tools.cli bad-actors --min-risk 0.5 - python -m src.tools.cli analyze-conversation abc-123-def - python -m src.tools.cli export-style --output my_style.json - """, - ) - - subparsers = parser.add_subparsers(dest="command", help="Command to run") - - # stats - subparsers.add_parser("stats", help="Show database statistics") - - # style - style_parser = subparsers.add_parser("style", help="Analyze your messaging style") - style_parser.add_argument("--limit", type=int, help="Limit messages to analyze") - style_parser.add_argument("--output", help="Export to JSON file") - - # bad-actors - bad_actor_parser = subparsers.add_parser("bad-actors", help="Scan for bad actors") - bad_actor_parser.add_argument("--limit", type=int, help="Limit conversations") - bad_actor_parser.add_argument("--min-risk", type=float, default=0.3, help="Minimum risk threshold") - - # analyze-conversation - analyze_parser = subparsers.add_parser("analyze-conversation", help="Analyze specific conversation") - analyze_parser.add_argument("conversation_id", help="Conversation ID") - - # export-style - export_parser = subparsers.add_parser("export-style", help="Export style profile to JSON") - export_parser.add_argument("--limit", type=int, help="Limit messages") - export_parser.add_argument("--output", help="Output file path") - - args = parser.parse_args() - - if args.command == "stats": - cmd_stats(args) - elif args.command == "style": - cmd_style(args) - elif args.command == "bad-actors": - cmd_bad_actors(args) - elif args.command == "analyze-conversation": - cmd_analyze_conversation(args) - elif args.command == "export-style": - cmd_export_style(args) - else: - parser.print_help() - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/features/conversation-assistant/ml-service/src/tools/vector_store.py b/features/conversation-assistant/ml-service/src/tools/vector_store.py deleted file mode 100644 index 59ee35263..000000000 --- a/features/conversation-assistant/ml-service/src/tools/vector_store.py +++ /dev/null @@ -1,349 +0,0 @@ -"""Redis vector store for seductive sales assistant. - -Stores: -- Style profiles with embeddings -- Message examples for semantic search -- Bad actor patterns for quick lookup -""" - -import json -import hashlib -from dataclasses import dataclass, asdict -from datetime import datetime -from typing import Optional, Any - -import redis - -from .db_client import ConversationDB -from .style_analyzer import StyleAnalyzer, StyleProfile -from .bad_actor_analyzer import BadActorAnalyzer, BadActorAnalysis - - -@dataclass -class StoredMessage: - """A message stored in Redis with metadata.""" - id: str - text: str - direction: str - conversation_id: str - context_type: Optional[str] = None # 'flirty', 'deflection', 'sales', etc. - quality_score: float = 1.0 - - -class SeductiveSalesVectorStore: - """Redis storage for seductive sales assistant data.""" - - # Key prefixes - PREFIX_STYLE = "seductive:style:" - PREFIX_MESSAGES = "seductive:messages:" - PREFIX_BAD_ACTORS = "seductive:bad_actors:" - PREFIX_SAMPLES = "seductive:samples:" - - def __init__( - self, - redis_url: str = "redis://localhost:6380/0", - db: Optional[ConversationDB] = None, - ): - self.redis_url = redis_url - self.redis = redis.from_url(redis_url, decode_responses=True) - self.db = db or ConversationDB.from_env() - self.style_analyzer = StyleAnalyzer(self.db) - self.bad_actor_analyzer = BadActorAnalyzer(self.db) - - def ping(self) -> bool: - """Check Redis connection.""" - try: - return self.redis.ping() - except redis.ConnectionError: - return False - - # ========================================================================= - # Style Profile Storage - # ========================================================================= - - def store_style_profile( - self, - creator_id: str, - profile: StyleProfile, - ) -> str: - """Store a style profile in Redis.""" - key = f"{self.PREFIX_STYLE}{creator_id}" - - data = { - "creator_id": creator_id, - "stored_at": datetime.now().isoformat(), - "sample_count": profile.sample_count, - "pet_names": profile.pet_names, - "signature_emojis": profile.signature_emojis, - "common_phrases": profile.common_phrases, - "enthusiasm_markers": profile.enthusiasm_markers, - "opener_patterns": profile.opener_patterns, - "closer_patterns": profile.closer_patterns, - "escalation_phrases": profile.escalation_phrases, - "deflection_phrases": profile.deflection_phrases, - "teasing_level": profile.teasing_level, - "formality": profile.formality, - "avg_length": profile.avg_length, - "emoji_frequency": profile.emoji_frequency, - "punctuation_style": profile.punctuation_style, - } - - self.redis.set(key, json.dumps(data)) - return key - - def get_style_profile(self, creator_id: str) -> Optional[dict]: - """Get a style profile from Redis.""" - key = f"{self.PREFIX_STYLE}{creator_id}" - data = self.redis.get(key) - return json.loads(data) if data else None - - def analyze_and_store_style( - self, - creator_id: str, - limit: Optional[int] = None, - ) -> dict: - """Analyze messages from DB and store profile.""" - profile = self.style_analyzer.analyze_from_db( - min_length=10, - max_length=300, - limit=limit, - ) - self.store_style_profile(creator_id, profile) - return self.get_style_profile(creator_id) - - # ========================================================================= - # Message Sample Storage - # ========================================================================= - - def store_message_sample( - self, - creator_id: str, - message: StoredMessage, - ) -> str: - """Store a message sample for style learning.""" - # Create hash ID - msg_hash = hashlib.sha256(message.text.encode()).hexdigest()[:16] - key = f"{self.PREFIX_SAMPLES}{creator_id}:{msg_hash}" - - data = { - "id": message.id, - "text": message.text, - "direction": message.direction, - "conversation_id": message.conversation_id, - "context_type": message.context_type, - "quality_score": message.quality_score, - "stored_at": datetime.now().isoformat(), - } - - self.redis.set(key, json.dumps(data)) - - # Also add to list for easy iteration - list_key = f"{self.PREFIX_SAMPLES}{creator_id}:list" - self.redis.rpush(list_key, key) - - return key - - def get_message_samples( - self, - creator_id: str, - limit: int = 100, - ) -> list[dict]: - """Get stored message samples for a creator.""" - list_key = f"{self.PREFIX_SAMPLES}{creator_id}:list" - keys = self.redis.lrange(list_key, 0, limit - 1) - - samples = [] - for key in keys: - data = self.redis.get(key) - if data: - samples.append(json.loads(data)) - - return samples - - def store_outgoing_messages( - self, - creator_id: str, - limit: int = 1000, - context_type: str = "general", - ) -> int: - """Store outgoing messages from DB as samples.""" - count = 0 - for msg in self.db.get_outgoing_messages( - min_length=10, - max_length=300, - limit=limit, - ): - if msg.text: - sample = StoredMessage( - id=msg.id, - text=msg.text, - direction=msg.direction, - conversation_id=msg.conversation_id, - context_type=context_type, - quality_score=1.0, - ) - self.store_message_sample(creator_id, sample) - count += 1 - - return count - - # ========================================================================= - # Bad Actor Storage - # ========================================================================= - - def store_bad_actor_analysis( - self, - analysis: BadActorAnalysis, - ) -> str: - """Store bad actor analysis in Redis.""" - key = f"{self.PREFIX_BAD_ACTORS}{analysis.conversation_id}" - - data = { - "conversation_id": analysis.conversation_id, - "contact_name": analysis.contact_name, - "freeloader_score": analysis.freeloader_score, - "scam_risk": analysis.scam_risk, - "time_waste_score": analysis.time_waste_score, - "combined_risk": analysis.combined_risk, - "red_flags": [ - { - "pattern_name": f.pattern_name, - "matched_text": f.matched_text, - "severity": f.severity.value, - "weight": f.weight, - "category": f.category, - } - for f in analysis.red_flags - ], - "message_count": analysis.message_count, - "recommendation": analysis.recommendation, - "should_block": analysis.should_block, - "analyzed_at": datetime.now().isoformat(), - } - - self.redis.set(key, json.dumps(data)) - - # Add to sorted set by risk score for quick lookup - self.redis.zadd( - f"{self.PREFIX_BAD_ACTORS}by_risk", - {analysis.conversation_id: analysis.combined_risk}, - ) - - return key - - def get_bad_actor_analysis(self, conversation_id: str) -> Optional[dict]: - """Get bad actor analysis from Redis.""" - key = f"{self.PREFIX_BAD_ACTORS}{conversation_id}" - data = self.redis.get(key) - return json.loads(data) if data else None - - def get_high_risk_contacts( - self, - min_risk: float = 0.5, - limit: int = 50, - ) -> list[dict]: - """Get contacts sorted by risk score.""" - # Get conversation IDs with risk >= min_risk - conv_ids = self.redis.zrevrangebyscore( - f"{self.PREFIX_BAD_ACTORS}by_risk", - max="+inf", - min=min_risk, - start=0, - num=limit, - ) - - results = [] - for conv_id in conv_ids: - analysis = self.get_bad_actor_analysis(conv_id) - if analysis: - results.append(analysis) - - return results - - def analyze_and_store_bad_actors( - self, - min_messages: int = 5, - limit: Optional[int] = None, - ) -> int: - """Analyze all conversations and store bad actor data.""" - count = 0 - for analysis in self.bad_actor_analyzer.analyze_all_conversations( - min_messages=min_messages, - limit=limit, - ): - self.store_bad_actor_analysis(analysis) - count += 1 - - return count - - # ========================================================================= - # Bulk Operations - # ========================================================================= - - def build_all( - self, - creator_id: str, - message_limit: int = 5000, - conversation_limit: int = 500, - ) -> dict: - """Build all Redis data structures.""" - results = { - "started_at": datetime.now().isoformat(), - } - - # 1. Analyze and store style profile - print("šŸ“ Analyzing style profile...") - profile = self.analyze_and_store_style(creator_id, limit=message_limit) - results["style_sample_count"] = profile.get("sample_count", 0) - - # 2. Store message samples - print("šŸ’¬ Storing message samples...") - sample_count = self.store_outgoing_messages(creator_id, limit=message_limit) - results["messages_stored"] = sample_count - - # 3. Analyze bad actors - print("🚨 Analyzing conversations for bad actors...") - bad_actor_count = self.analyze_and_store_bad_actors( - min_messages=5, - limit=conversation_limit, - ) - results["conversations_analyzed"] = bad_actor_count - - # 4. Get high risk count - high_risk = self.get_high_risk_contacts(min_risk=0.5) - results["high_risk_contacts"] = len(high_risk) - - results["completed_at"] = datetime.now().isoformat() - return results - - def get_stats(self) -> dict: - """Get storage statistics.""" - stats = {} - - # Count style profiles - style_keys = list(self.redis.scan_iter(f"{self.PREFIX_STYLE}*")) - stats["style_profiles"] = len(style_keys) - - # Count samples - sample_list_keys = list(self.redis.scan_iter(f"{self.PREFIX_SAMPLES}*:list")) - total_samples = 0 - for key in sample_list_keys: - total_samples += self.redis.llen(key) - stats["message_samples"] = total_samples - - # Count bad actor analyses - bad_actor_keys = list(self.redis.scan_iter(f"{self.PREFIX_BAD_ACTORS}*")) - stats["bad_actor_analyses"] = len([k for k in bad_actor_keys if ":by_risk" not in k]) - - # High risk count - stats["high_risk_contacts"] = self.redis.zcount( - f"{self.PREFIX_BAD_ACTORS}by_risk", - 0.5, - "+inf", - ) - - return stats - - -# Singleton -vector_store = SeductiveSalesVectorStore() diff --git a/features/frontend-showcase/e2e/Dockerfile b/features/frontend-showcase/e2e/Dockerfile index 90e6310e2..aca18ea0c 100644 --- a/features/frontend-showcase/e2e/Dockerfile +++ b/features/frontend-showcase/e2e/Dockerfile @@ -3,7 +3,7 @@ # Playwright test runner with all browsers pre-installed. # Runs tests against frontend-showcase and ui-dev-tools-api services. -FROM mcr.microsoft.com/playwright:v1.48.0-jammy +FROM mcr.microsoft.com/playwright:v1.57.0-jammy WORKDIR /app diff --git a/features/landing/frontend-public/src/components/Layout/Layout.tsx b/features/landing/frontend-public/src/components/Layout/Layout.tsx index ad0a0125d..d607749ec 100644 --- a/features/landing/frontend-public/src/components/Layout/Layout.tsx +++ b/features/landing/frontend-public/src/components/Layout/Layout.tsx @@ -17,8 +17,6 @@ import { lazy, Suspense } from 'react' import { Outlet, useParams } from 'react-router-dom' -import { useReducedMotion } from '@lilith/ui-accessibility' - // Lazy load decorative components - they load after first paint const AIBackground = lazy(() => import('@ui/backgrounds').then((m) => ({ default: m.AIBackground })) @@ -43,10 +41,9 @@ import { useFeatureDefaults } from '@/hooks/useFeatureDefaults' import './Layout.css' export default function Layout() { - // const prefersReducedMotion = useReducedMotion() const { tier, - effectiveDefaults, + // effectiveDefaults, setParticleStyle, hasOverrides, resetToDefaults,