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') {
+
+}
+
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; }
+}