diff --git a/src/app/app.component.html b/src/app/app.component.html index 7aeee52..67820d3 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,7 +1,26 @@ + +@if (currentTheme() === 'mist') { + +} +
+ + @if (sidebarOpen()) { +
+ } + -
+ + + + + + +
+

Error State

+

+ Structured error displays with retry, help links, and severity levels. +

+
+
+

Card Variant (Warning)

+
+ +
+
+
+

Inline Variant

+
+ +
+
+
+
+ + +
+

Smart Notifications

+

+ Context-aware notifications with priority, categories, and AI summaries. +

+
+

Notification List

+
+ +
+
+
+ + + + + + +
+

File Upload

+

+ Drag-and-drop file upload with preview, progress, and validation. +

+
+

Multi-file Upload

+
+ +
+
+
+ + + + + + +
+

Agent Selector

+

+ Choose from available AI agents with search, categories, favorites, and status indicators. +

+
+
+

Grid Variant

+
+ +
+
+
+

List Variant

+
+ +
+
+
+

Dropdown Variant

+
+ +
+
+
+
+ + +
+

Agent Profile

+

+ Detailed agent profile cards showing capabilities, model info, and suggested prompts. +

+
+
+

Full Variant

+
+ +
+
+
+

Compact Variant

+
+ + +
+
+
+
+ + +
+

Agent Message

+

+ Multi-agent attributed message bubbles with user, assistant, and system event rendering. +

+
+

Conversation Flow

+
+ @for (msg of agentMessages; track msg.id) { + + } +
+
+
+ + +
+

Agent Switcher

+

+ Horizontal tab/pill bar for switching between active agent conversations. +

+
+
+

Pills Variant

+
+ +
+
+
+

Tabs Variant

+
+ +
+
+
+
+ + +
+

Agent Handoff

+

+ Visual indicator for agent-to-agent delegation with animated connectors and status tracking. +

+
+
+

In-Progress (Inline)

+
+ +
+
+
+

Complete (Card)

+
+ +
+
+
+
+ + +
+

Agent Activity

+

+ Real-time activity feed showing what agents are doing, with status tracking and duration. +

+
+
+

Compact Variant

+
+ +
+
+
+

Detailed Variant

+
+ +
+
+
+
+ } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3d6981c..e2cebed 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -7,9 +7,59 @@ import { AiConfidenceMeterComponent, AiSkeletonLoaderComponent, AiCodeBlockComponent, - AiConversationHistoryComponent + AiConversationHistoryComponent, + AiFeedbackComponent, + AiProgressStepperComponent, + AiTokenCounterComponent, + AiRateLimitIndicatorComponent, + AiSessionHeaderComponent, + AiModelSelectorComponent, + AiSourceCitationComponent, + AiDiffViewerComponent, + AiErrorStateComponent, + AiResponseActionsComponent, + AiToolCallDisplayComponent, + AiSuggestedPromptsComponent, + AiCardComponent, + AiCardGridComponent, + AiFileUploadComponent, + AiPromptTemplatesComponent, + AiMarkdownRendererComponent, + AiAnnotationLayerComponent, + AiImageGalleryComponent, + VoiceCommandComponent, + PredictiveInputComponent, + SmartNotificationComponent, + AiChatComponent, + AiAgentActivityComponent, + AiAgentHandoffComponent, + AiAgentMessageComponent, + AiAgentProfileComponent, + AiAgentSelectorComponent, + AiAgentSwitcherComponent +} from 'ai-elements-ui'; +import type { + Conversation, + AiStep, + TokenUsage, + RateLimitInfo, + SessionInfo, + AiModel, + AiSource, + AiError, + ToolCall, + SuggestedPrompt, + CanvasCard, + PromptTemplate, + Annotation, + GeneratedImage, + AiNotification, + AiAgent, + AgentMessage, + AgentConversation, + AgentHandoff, + AgentActivityEntry } from 'ai-elements-ui'; -import type { Conversation } from 'ai-elements-ui'; @Component({ selector: 'app-root', @@ -22,7 +72,36 @@ import type { Conversation } from 'ai-elements-ui'; AiConfidenceMeterComponent, AiSkeletonLoaderComponent, AiCodeBlockComponent, - AiConversationHistoryComponent + AiConversationHistoryComponent, + AiFeedbackComponent, + AiProgressStepperComponent, + AiTokenCounterComponent, + AiRateLimitIndicatorComponent, + AiSessionHeaderComponent, + AiModelSelectorComponent, + AiSourceCitationComponent, + AiDiffViewerComponent, + AiErrorStateComponent, + AiResponseActionsComponent, + AiToolCallDisplayComponent, + AiSuggestedPromptsComponent, + AiCardComponent, + AiCardGridComponent, + AiFileUploadComponent, + AiPromptTemplatesComponent, + AiMarkdownRendererComponent, + AiAnnotationLayerComponent, + AiImageGalleryComponent, + VoiceCommandComponent, + PredictiveInputComponent, + SmartNotificationComponent, + AiChatComponent, + AiAgentActivityComponent, + AiAgentHandoffComponent, + AiAgentMessageComponent, + AiAgentProfileComponent, + AiAgentSelectorComponent, + AiAgentSwitcherComponent ], templateUrl: './app.component.html', styleUrl: './app.component.scss' @@ -31,6 +110,9 @@ export class AppComponent { // Demo state currentView = signal<'canvas' | 'components'>('components'); avatarState = signal<'idle' | 'thinking' | 'speaking'>('idle'); + darkMode = signal(false); + sidebarOpen = signal(false); + currentTheme = signal<'apple' | 'mist'>('apple'); // Conversations conversations: Conversation[] = [ @@ -49,6 +131,226 @@ export class AppComponent { const results = Array.from({ length: 10 }, (_, i) => fibonacci(i)); console.log(results);`; + // Feedback + feedbackMessageId = 'msg-demo-001'; + + // Progress Stepper + steps: AiStep[] = [ + { id: '1', label: 'Analyzing query', description: 'Parsing your request', status: 'complete', substeps: [ + { id: '1a', label: 'Tokenizing input', status: 'complete' }, + { id: '1b', label: 'Extracting intent', status: 'complete' } + ]}, + { id: '2', label: 'Searching knowledge base', description: 'Finding relevant information', status: 'complete' }, + { id: '3', label: 'Generating response', description: 'Composing answer', status: 'active', substeps: [ + { id: '3a', label: 'Drafting content', status: 'active' }, + { id: '3b', label: 'Fact checking', status: 'pending' } + ]}, + { id: '4', label: 'Review & citations', description: 'Adding source references', status: 'pending' } + ]; + + // Token Counter + tokenUsage: TokenUsage = { used: 3420, limit: 8192, promptTokens: 1200, completionTokens: 2220 }; + + // Rate Limit + rateLimit: RateLimitInfo = { + requestsUsed: 42, + requestsLimit: 60, + tokensUsed: 28500, + tokensLimit: 100000, + resetsAt: new Date(Date.now() + 45000), + tier: 'Pro' + }; + + // Session Header + session: SessionInfo = { + id: 'sess-abc123', + model: 'Claude Opus 4.6', + context: 'Code Review', + tokenUsage: { used: 3420, limit: 8192, promptTokens: 1200, completionTokens: 2220 }, + startedAt: new Date(Date.now() - 1800000), + lastActivity: new Date() + }; + + // Model Selector + models: AiModel[] = [ + { id: 'opus-4.6', name: 'Claude Opus 4.6', provider: 'Anthropic', description: 'Most capable model', contextWindow: 200000, capabilities: ['reasoning', 'code', 'analysis', 'vision'], badge: 'Latest' }, + { id: 'sonnet-4.5', name: 'Claude Sonnet 4.5', provider: 'Anthropic', description: 'Balanced performance', contextWindow: 200000, capabilities: ['reasoning', 'code', 'analysis'] }, + { id: 'haiku-4.5', name: 'Claude Haiku 4.5', provider: 'Anthropic', description: 'Fast and efficient', contextWindow: 200000, capabilities: ['code', 'analysis'] }, + { id: 'gpt-4o', name: 'GPT-4o', provider: 'OpenAI', description: 'Multimodal flagship', contextWindow: 128000, capabilities: ['reasoning', 'code', 'vision'] }, + { id: 'gemini-2', name: 'Gemini 2.0 Flash', provider: 'Google', description: 'Fast multimodal model', contextWindow: 1000000, capabilities: ['reasoning', 'code', 'vision'] } + ]; + selectedModelId = 'opus-4.6'; + + // Source Citations + sources: AiSource[] = [ + { id: '1', title: 'Angular Signals Documentation', url: 'https://angular.dev/guide/signals', type: 'web', snippet: 'Angular Signals is a system that granularly tracks how and where your state is used...', confidence: 0.95 }, + { id: '2', title: 'Component Design Patterns', author: 'Angular Team', type: 'document', snippet: 'Best practices for building reusable Angular components...', confidence: 0.88 }, + { id: '3', title: 'TypeScript 5.7 Release Notes', url: 'https://devblogs.microsoft.com/typescript/', type: 'web', snippet: 'New features include improved type inference and performance...', confidence: 0.82 } + ]; + + // Diff Viewer + diffBefore = `function greet(name: string) { + console.log("Hello " + name); + return name; +}`; + + diffAfter = `function greet(name: string, greeting = 'Hello') { + const message = \`\${greeting}, \${name}!\`; + console.log(message); + return message; +}`; + + // Error State + sampleError: AiError = { + code: 'RATE_LIMIT_EXCEEDED', + message: 'You\'ve exceeded the rate limit for this model.', + severity: 'warning', + details: 'Current usage: 62/60 requests per minute. Limit resets in 45 seconds.', + retryable: true, + helpUrl: 'https://docs.example.com/rate-limits' + }; + + // Response Actions + responseMessageId = 'msg-resp-001'; + + // Tool Calls + toolCalls: ToolCall[] = [ + { id: 'tc-1', name: 'search_codebase', description: 'Searching for relevant files', status: 'complete', startedAt: new Date(Date.now() - 3000), completedAt: new Date(Date.now() - 1500), result: '3 files found' }, + { id: 'tc-2', name: 'read_file', description: 'Reading src/app/app.component.ts', status: 'complete', startedAt: new Date(Date.now() - 1500), completedAt: new Date(Date.now() - 800) }, + { id: 'tc-3', name: 'run_tests', description: 'Executing test suite', status: 'running', startedAt: new Date(Date.now() - 500) }, + { id: 'tc-4', name: 'deploy', description: 'Deploy to staging', status: 'pending' } + ]; + + // Suggested Prompts + suggestedPrompts: SuggestedPrompt[] = [ + { id: '1', text: 'Explain this code', icon: 'code', category: 'Code', description: 'Get a detailed explanation of selected code' }, + { id: '2', text: 'Find bugs', icon: 'bug', category: 'Code', description: 'Scan for potential issues' }, + { id: '3', text: 'Write tests', icon: 'test', category: 'Testing', description: 'Generate unit tests' }, + { id: '4', text: 'Refactor this', icon: 'refactor', category: 'Code', description: 'Suggest improvements' }, + { id: '5', text: 'Summarize changes', icon: 'diff', category: 'Git', description: 'Summarize recent changes' } + ]; + + // Cards + demoCards: CanvasCard[] = [ + { id: '1', title: 'Neural Network Basics', subtitle: 'Machine Learning', description: 'Introduction to neural network architectures and training.', badges: [{ label: 'Popular', variant: 'success' }], actions: [{ id: 'view', label: 'View', variant: 'primary' }, { id: 'save', label: 'Save', variant: 'secondary' }] }, + { id: '2', title: 'Transformer Architecture', subtitle: 'Deep Learning', description: 'How attention mechanisms revolutionized NLP.', badges: [{ label: 'Advanced', variant: 'warning' }], actions: [{ id: 'view', label: 'View', variant: 'primary' }] }, + { id: '3', title: 'Prompt Engineering', subtitle: 'AI Techniques', description: 'Best practices for crafting effective prompts.', badges: [{ label: 'New', variant: 'info' }], actions: [{ id: 'view', label: 'View', variant: 'primary' }] } + ]; + + // Prompt Templates + promptTemplates: PromptTemplate[] = [ + { id: '1', name: 'Code Review', description: 'Review code for bugs and improvements', content: 'Review the following {{language}} code for bugs, performance issues, and best practices:\n\n{{code}}', category: 'Development', variables: [{ name: 'language', label: 'Language', type: 'select', options: ['TypeScript', 'Python', 'Go', 'Rust'] }, { name: 'code', label: 'Code', type: 'textarea', required: true }], isFavorite: true, usageCount: 24 }, + { id: '2', name: 'Explain Concept', description: 'Get a clear explanation of a topic', content: 'Explain {{topic}} in simple terms, suitable for a {{level}} audience.', category: 'Learning', variables: [{ name: 'topic', label: 'Topic', type: 'text', required: true }, { name: 'level', label: 'Level', type: 'select', options: ['beginner', 'intermediate', 'advanced'] }], usageCount: 18 }, + { id: '3', name: 'Write Tests', description: 'Generate test cases', content: 'Write comprehensive unit tests for:\n\n{{code}}', category: 'Development', variables: [{ name: 'code', label: 'Code to test', type: 'textarea', required: true }], usageCount: 15 } + ]; + + // Markdown + markdownContent = `## AI Response + +Here's a summary of the changes: + +- **Added** new authentication middleware +- **Fixed** the race condition in \`useEffect\` +- **Removed** deprecated API calls + +> Note: These changes require a database migration. + +\`\`\`typescript +const result = await auth.verify(token); +\`\`\` + +Visit the [documentation](https://example.com) for more details.`; + + // Annotations + annotationText = 'Angular Signals provide a way to define reactive state. When a signal value changes, any computed signals or effects that depend on it are automatically updated. This makes it easy to build responsive UIs without manual change detection.'; + annotations: Annotation[] = [ + { id: '1', startOffset: 0, endOffset: 15, text: 'Angular Signals', note: 'Core reactivity primitive in Angular 16+', color: '#ffeb3b', createdAt: new Date() }, + { id: '2', startOffset: 96, endOffset: 113, text: 'computed signals', note: 'Derived values that auto-update', color: '#4caf50', createdAt: new Date() }, + { id: '3', startOffset: 198, endOffset: 214, text: 'change detection', note: 'Zone.js-based detection is the alternative', color: '#2196f3', createdAt: new Date() } + ]; + + // Image Gallery + demoImages: GeneratedImage[] = [ + { id: '1', url: 'https://picsum.photos/seed/ai1/400/300', thumbnailUrl: 'https://picsum.photos/seed/ai1/200/150', prompt: 'A futuristic city skyline at sunset', model: 'DALL-E 3', size: '1024x1024', createdAt: new Date(), isFavorite: true }, + { id: '2', url: 'https://picsum.photos/seed/ai2/400/300', thumbnailUrl: 'https://picsum.photos/seed/ai2/200/150', prompt: 'An abstract neural network visualization', model: 'DALL-E 3', size: '1024x1024', createdAt: new Date() }, + { id: '3', url: 'https://picsum.photos/seed/ai3/400/300', thumbnailUrl: 'https://picsum.photos/seed/ai3/200/150', prompt: 'A robot learning to paint', model: 'Midjourney', size: '1024x1024', createdAt: new Date() } + ]; + + // Notifications + demoNotifications: AiNotification[] = [ + { id: '1', title: 'Model Updated', message: 'Claude Opus 4.6 is now available', priority: 'high', category: 'System', timestamp: new Date(), read: false, aiSummary: 'New model release with improved reasoning' }, + { id: '2', title: 'Rate Limit Warning', message: 'Approaching 80% of hourly limit', priority: 'medium', category: 'Usage', timestamp: new Date(Date.now() - 300000), read: false }, + { id: '3', title: 'Task Complete', message: 'Code review finished with 3 suggestions', priority: 'low', category: 'Tasks', timestamp: new Date(Date.now() - 600000), read: true } + ]; + + // Multi-Agent: Sample Agents + sampleAgents: AiAgent[] = [ + { id: 'research', name: 'Research Agent', description: 'Searches the web, reads documents, and synthesizes findings into concise reports.', status: 'online', capabilities: ['web-search', 'summarization', 'fact-checking'], category: 'Research', model: 'Claude Opus 4.6', accentColor: '#6366f1', fallbackInitials: 'RA', isFavorite: true }, + { id: 'code', name: 'Code Agent', description: 'Writes, reviews, and debugs code across multiple languages and frameworks.', status: 'online', capabilities: ['code-generation', 'debugging', 'refactoring', 'testing'], category: 'Development', model: 'Claude Sonnet 4.5', accentColor: '#10b981', fallbackInitials: 'CA' }, + { id: 'writing', name: 'Writing Agent', description: 'Crafts polished prose, edits drafts, and adapts tone for different audiences.', status: 'busy', capabilities: ['copywriting', 'editing', 'translation'], category: 'Creative', model: 'Claude Opus 4.6', accentColor: '#f59e0b', fallbackInitials: 'WA' }, + { id: 'data', name: 'Data Agent', description: 'Analyzes datasets, creates visualizations, and extracts actionable insights.', status: 'online', capabilities: ['data-analysis', 'visualization', 'statistics'], category: 'Research', model: 'Claude Sonnet 4.5', accentColor: '#ec4899', fallbackInitials: 'DA', isFavorite: true }, + { id: 'creative', name: 'Creative Agent', description: 'Generates images, brainstorms ideas, and designs creative concepts.', status: 'offline', capabilities: ['ideation', 'image-generation', 'storytelling'], category: 'Creative', model: 'Claude Haiku 4.5', accentColor: '#8b5cf6', fallbackInitials: 'CR' } + ]; + selectedAgentId = signal('research'); + + // Multi-Agent: Messages + agentMessages: AgentMessage[] = [ + { id: 'm1', role: 'user', content: 'Can you research the latest trends in AI agents?', timestamp: new Date(Date.now() - 120000) }, + { id: 'm2', role: 'assistant', content: 'I\'ll look into that for you. The key trends in AI agents for 2026 include multi-agent collaboration, tool-use orchestration, and memory-augmented reasoning.', timestamp: new Date(Date.now() - 90000), agent: { agentId: 'research', agentName: 'Research Agent', agentFallbackInitials: 'RA', accentColor: '#6366f1' } }, + { id: 'm3', role: 'system', content: '', timestamp: new Date(Date.now() - 60000), systemEvent: { type: 'handoff', agentId: 'research', agentName: 'Research Agent', targetAgentId: 'code', targetAgentName: 'Code Agent', reason: 'Implementation requested', timestamp: new Date(Date.now() - 60000) } }, + { id: 'm4', role: 'assistant', content: 'I can help implement an agent orchestration system. Here\'s a basic architecture using an event-driven pattern with a central coordinator.', timestamp: new Date(Date.now() - 30000), agent: { agentId: 'code', agentName: 'Code Agent', agentFallbackInitials: 'CA', accentColor: '#10b981' } }, + { id: 'm5', role: 'system', content: '', timestamp: new Date(Date.now() - 15000), systemEvent: { type: 'joined', agentId: 'writing', agentName: 'Writing Agent', timestamp: new Date(Date.now() - 15000) } } + ]; + + // Multi-Agent: Conversations + agentConversations: AgentConversation[] = [ + { id: 'conv-1', agent: this.sampleAgents[0], unreadCount: 0, lastMessage: 'Here are the key findings...', lastActivity: new Date(), isActive: true }, + { id: 'conv-2', agent: this.sampleAgents[1], unreadCount: 3, lastMessage: 'Code review complete.', lastActivity: new Date(Date.now() - 30000), isActive: false }, + { id: 'conv-3', agent: this.sampleAgents[2], unreadCount: 1, lastMessage: 'Draft is ready for review.', lastActivity: new Date(Date.now() - 60000), isActive: false }, + { id: 'conv-4', agent: this.sampleAgents[3], unreadCount: 0, lastMessage: 'Analysis exported to CSV.', lastActivity: new Date(Date.now() - 300000), isActive: false } + ]; + activeConversationId = signal('conv-1'); + + // Multi-Agent: Handoff + sampleHandoff: AgentHandoff = { + id: 'handoff-1', + sourceAgent: this.sampleAgents[0], + targetAgent: this.sampleAgents[1], + reason: 'Implementation of agent orchestration system requested by user', + context: 'The user wants to build an event-driven multi-agent architecture with a central coordinator pattern.', + status: 'in-progress', + initiatedAt: new Date(Date.now() - 5000) + }; + + sampleHandoffComplete: AgentHandoff = { + id: 'handoff-2', + sourceAgent: this.sampleAgents[1], + targetAgent: this.sampleAgents[2], + reason: 'Code complete, documentation needed', + status: 'complete', + initiatedAt: new Date(Date.now() - 60000), + completedAt: new Date(Date.now() - 30000) + }; + + // Multi-Agent: Activity + activityEntries: AgentActivityEntry[] = [ + { id: 'act-1', agentId: 'research', agentName: 'Research Agent', agentFallbackInitials: 'RA', action: 'Searching web for AI agent trends', detail: 'Querying multiple sources including academic papers and tech blogs.', status: 'complete', startedAt: new Date(Date.now() - 120000), completedAt: new Date(Date.now() - 90000) }, + { id: 'act-2', agentId: 'research', agentName: 'Research Agent', agentFallbackInitials: 'RA', action: 'Synthesizing findings', status: 'complete', startedAt: new Date(Date.now() - 90000), completedAt: new Date(Date.now() - 60000) }, + { id: 'act-3', agentId: 'code', agentName: 'Code Agent', agentFallbackInitials: 'CA', action: 'Generating orchestration architecture', detail: 'Building event-driven coordinator with TypeScript interfaces.', status: 'active', startedAt: new Date(Date.now() - 30000) }, + { id: 'act-4', agentId: 'code', agentName: 'Code Agent', agentFallbackInitials: 'CA', action: 'Running test suite', status: 'active', startedAt: new Date(Date.now() - 10000) }, + { id: 'act-5', agentId: 'writing', agentName: 'Writing Agent', agentFallbackInitials: 'WA', action: 'Failed to access document store', status: 'error', startedAt: new Date(Date.now() - 15000), completedAt: new Date(Date.now() - 14000) } + ]; + + onAgentSelect(agent: AiAgent) { + this.selectedAgentId.set(agent.id); + console.log('[Demo] Agent selected:', agent.name); + } + + onConversationSwitch(conversation: AgentConversation) { + this.activeConversationId.set(conversation.id); + console.log('[Demo] Conversation switched:', conversation.agent.name); + } + setView(view: 'canvas' | 'components') { this.currentView.set(view); } @@ -57,10 +359,30 @@ console.log(results);`; console.log('Selected conversation:', conversation); } + toggleTheme() { + const next = this.currentTheme() === 'apple' ? 'mist' : 'apple'; + document.body.classList.remove(`theme-${this.currentTheme()}`); + document.body.classList.add(`theme-${next}`); + this.currentTheme.set(next); + } + + toggleDarkMode() { + this.darkMode.update(v => !v); + document.body.classList.toggle('theme-dark', this.darkMode()); + } + + toggleSidebar() { + this.sidebarOpen.update(v => !v); + } + cycleAvatarState() { const states: ('idle' | 'thinking' | 'speaking')[] = ['idle', 'thinking', 'speaking']; const currentIndex = states.indexOf(this.avatarState()); const nextIndex = (currentIndex + 1) % states.length; this.avatarState.set(states[nextIndex]); } + + onLog(event: string, data?: unknown) { + console.log(`[Demo] ${event}`, data); + } } diff --git a/src/styles.scss b/src/styles.scss index ecab38f..66a3fb1 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -3,8 +3,9 @@ @use '../../sda-frontend/libs/base-ui/src/styles/tokens/semantic'; @use '../../sda-frontend/libs/base-ui/src/styles/tokens/component'; -// Import Apple theme (applies via class) +// Import themes (applied via class) @use '../../sda-frontend/libs/base-ui/src/styles/themes/apple'; +@use '../../sda-frontend/libs/base-ui/src/styles/themes/mist'; // Base resets *, @@ -15,9 +16,12 @@ padding: 0; } -html, -body { +html { height: 100%; +} + +body { + min-height: 100%; font-family: var(--font-family-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif); font-size: var(--font-size-base, 16px); line-height: var(--line-height-base, 1.5); @@ -27,7 +31,7 @@ body { -moz-osx-font-smoothing: grayscale; } -// Apply theme class to body +// Default theme class (overridden by JS theme switching) body { @extend .theme-apple; } @@ -57,11 +61,51 @@ body { color: var(--color-text-primary); } +.app__sidebar-toggle { + display: none; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border: none; + background: transparent; + border-radius: var(--radius-md, 8px); + cursor: pointer; + color: var(--color-text-secondary); + transition: all 0.2s ease; + + &:hover { + background: var(--color-bg-tertiary, #ebebeb); + color: var(--color-text-primary); + } +} + .app__nav { display: flex; + align-items: center; gap: var(--spacing-md, 16px); } +.app__theme-toggle { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border: 1px solid var(--color-border-primary, #e0e0e0); + background: var(--color-bg-tertiary, #ebebeb); + border-radius: var(--radius-md, 8px); + cursor: pointer; + font-size: 18px; + line-height: 1; + transition: all 0.2s ease; + margin-left: var(--spacing-sm, 8px); + + &:hover { + background: var(--color-bg-primary, #fff); + } +} + .app__nav-link { padding: var(--spacing-sm, 8px) var(--spacing-md, 16px); color: var(--color-text-secondary); @@ -90,17 +134,24 @@ body { overflow: hidden; } +.app__sidebar-backdrop { + display: none; +} + .app__sidebar { width: 280px; background: var(--color-bg-secondary, #f5f5f5); border-right: 1px solid var(--color-border-primary, #e0e0e0); overflow-y: auto; + flex-shrink: 0; + transition: transform 0.3s ease; } .app__content { flex: 1; overflow-y: auto; padding: var(--spacing-lg, 24px); + background-color: var(--color-bg-primary); } // Demo sections @@ -146,3 +197,67 @@ body { border-bottom: 1px solid var(--color-border-primary, #e0e0e0); } } + +// ============================================================================= +// RESPONSIVE — Tablet & Mobile +// ============================================================================= + +// Tablet: sidebar becomes a slide-over drawer +@media (max-width: 1024px) { + .app__sidebar-toggle { + display: flex; + } + + .app__sidebar { + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: 200; + transform: translateX(-100%); + box-shadow: none; + + &--open { + transform: translateX(0); + box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15); + } + } + + .app__sidebar-backdrop { + display: block; + position: fixed; + inset: 0; + z-index: 199; + background: rgba(0, 0, 0, 0.4); + animation: fadeIn 0.2s ease; + } +} + +// Mobile: tighter spacing, single-column demo grid +@media (max-width: 640px) { + .app__header { + padding: var(--spacing-sm, 8px) var(--spacing-md, 16px); + } + + .app__nav-link { + padding: var(--spacing-xs, 4px) var(--spacing-sm, 8px); + font-size: var(--font-size-sm, 14px); + } + + .app__content { + padding: var(--spacing-md, 16px); + } + + .demo-section { + padding: var(--spacing-md, 16px); + } + + .demo-grid { + grid-template-columns: 1fr; + } +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +}