SvelteKit-based WebUI (#14839)

This commit is contained in:
Aleksander Grygier
2025-09-17 19:29:13 +02:00
committed by GitHub
parent 8f8f2274ee
commit a7a98e0fff
288 changed files with 25749 additions and 11502 deletions

View File

@@ -0,0 +1,182 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import ChatForm from '$lib/components/app/chat/ChatForm/ChatForm.svelte';
import { expect } from 'storybook/internal/test';
import { mockServerProps, mockConfigs } from './fixtures/storybook-mocks';
import jpgAsset from './fixtures/assets/1.jpg?url';
import svgAsset from './fixtures/assets/hf-logo.svg?url';
import pdfAsset from './fixtures/assets/example.pdf?raw';
const { Story } = defineMeta({
title: 'Components/ChatScreen/ChatForm',
component: ChatForm,
parameters: {
layout: 'centered'
}
});
let fileAttachments = $state([
{
id: '1',
name: '1.jpg',
type: 'image/jpeg',
size: 44891,
preview: jpgAsset,
file: new File([''], '1.jpg', { type: 'image/jpeg' })
},
{
id: '2',
name: 'hf-logo.svg',
type: 'image/svg+xml',
size: 1234,
preview: svgAsset,
file: new File([''], 'hf-logo.svg', { type: 'image/svg+xml' })
},
{
id: '3',
name: 'example.pdf',
type: 'application/pdf',
size: 351048,
file: new File([pdfAsset], 'example.pdf', { type: 'application/pdf' })
}
]);
</script>
<Story
name="Default"
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
play={async ({ canvas, userEvent }) => {
mockServerProps(mockConfigs.noModalities);
const textarea = await canvas.findByRole('textbox');
const submitButton = await canvas.findByRole('button', { name: 'Send' });
// Expect the input to be focused after the component is mounted
await expect(textarea).toHaveFocus();
// Expect the submit button to be disabled
await expect(submitButton).toBeDisabled();
const text = 'What is the meaning of life?';
await userEvent.clear(textarea);
await userEvent.type(textarea, text);
await expect(textarea).toHaveValue(text);
const fileInput = document.querySelector('input[type="file"]');
const acceptAttr = fileInput?.getAttribute('accept');
await expect(fileInput).toHaveAttribute('accept');
await expect(acceptAttr).not.toContain('image/');
await expect(acceptAttr).not.toContain('audio/');
const fileUploadButton = canvas.getByText('Attach files');
await userEvent.click(fileUploadButton);
const recordButton = canvas.getAllByRole('button', { name: 'Start recording' })[1];
const imagesButton = document.querySelector('.images-button');
const audioButton = document.querySelector('.audio-button');
await expect(recordButton).toBeDisabled();
await expect(imagesButton).toHaveAttribute('data-disabled');
await expect(audioButton).toHaveAttribute('data-disabled');
}}
/>
<Story name="Loading" args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]', isLoading: true }} />
<Story
name="VisionModality"
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
play={async ({ canvas, userEvent }) => {
mockServerProps(mockConfigs.visionOnly);
// Test initial file input state (should accept images but not audio)
const fileInput = document.querySelector('input[type="file"]');
const acceptAttr = fileInput?.getAttribute('accept');
console.log('Vision modality accept attr:', acceptAttr);
const fileUploadButton = canvas.getByText('Attach files');
await userEvent.click(fileUploadButton);
// Test that record button is disabled (no audio support)
const recordButton = canvas.getAllByRole('button', { name: 'Start recording' })[1];
await expect(recordButton).toBeDisabled();
// Test that Images button is enabled (vision support)
const imagesButton = document.querySelector('.images-button');
await expect(imagesButton).not.toHaveAttribute('data-disabled');
// Test that Audio button is disabled (no audio support)
const audioButton = document.querySelector('.audio-button');
await expect(audioButton).toHaveAttribute('data-disabled');
// Fix for dropdown menu side effect
const body = document.querySelector('body');
if (body) body.style.pointerEvents = 'all';
console.log('✅ Vision modality: Images enabled, Audio/Recording disabled');
}}
/>
<Story
name="AudioModality"
args={{ class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
play={async ({ canvas, userEvent }) => {
mockServerProps(mockConfigs.audioOnly);
// Test initial file input state (should accept audio but not images)
const fileInput = document.querySelector('input[type="file"]');
const acceptAttr = fileInput?.getAttribute('accept');
console.log('Audio modality accept attr:', acceptAttr);
const fileUploadButton = canvas.getByText('Attach files');
await userEvent.click(fileUploadButton);
// Test that record button is enabled (audio support)
const recordButton = canvas.getAllByRole('button', { name: 'Start recording' })[1];
await expect(recordButton).not.toBeDisabled();
// Test that Images button is disabled (no vision support)
const imagesButton = document.querySelector('.images-button');
await expect(imagesButton).toHaveAttribute('data-disabled');
// Test that Audio button is enabled (audio support)
const audioButton = document.querySelector('.audio-button');
await expect(audioButton).not.toHaveAttribute('data-disabled');
// Fix for dropdown menu side effect
const body = document.querySelector('body');
if (body) body.style.pointerEvents = 'all';
console.log('✅ Audio modality: Audio/Recording enabled, Images disabled');
}}
/>
<Story
name="FileAttachments"
args={{
class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
uploadedFiles: fileAttachments
}}
play={async ({ canvas }) => {
mockServerProps(mockConfigs.bothModalities);
const jpgAttachment = canvas.getByAltText('1.jpg');
const svgAttachment = canvas.getByAltText('hf-logo.svg');
const pdfFileExtension = canvas.getByText('PDF');
const pdfAttachment = canvas.getByText('example.pdf');
const pdfSize = canvas.getByText('342.82 KB');
await expect(jpgAttachment).toBeInTheDocument();
await expect(jpgAttachment).toHaveAttribute('src', jpgAsset);
await expect(svgAttachment).toBeInTheDocument();
await expect(svgAttachment).toHaveAttribute('src', svgAsset);
await expect(pdfFileExtension).toBeInTheDocument();
await expect(pdfAttachment).toBeInTheDocument();
await expect(pdfSize).toBeInTheDocument();
}}
/>

View File

@@ -0,0 +1,146 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import ChatMessage from '$lib/components/app/chat/ChatMessages/ChatMessage.svelte';
const { Story } = defineMeta({
title: 'Components/ChatScreen/ChatMessage',
component: ChatMessage,
parameters: {
layout: 'centered'
}
});
// Mock messages for different scenarios
const userMessage: DatabaseMessage = {
id: '1',
convId: 'conv-1',
type: 'message',
timestamp: Date.now() - 1000 * 60 * 5,
role: 'user',
content: 'What is the meaning of life, the universe, and everything?',
parent: '',
thinking: '',
children: []
};
const assistantMessage: DatabaseMessage = {
id: '2',
convId: 'conv-1',
type: 'message',
timestamp: Date.now() - 1000 * 60 * 3,
role: 'assistant',
content:
'The answer to the ultimate question of life, the universe, and everything is **42**.\n\nThis comes from Douglas Adams\' "The Hitchhiker\'s Guide to the Galaxy," where a supercomputer named Deep Thought calculated this answer over 7.5 million years. However, the question itself was never properly formulated, which is why the answer seems meaningless without context.',
parent: '1',
thinking: '',
children: []
};
let processingMessage = $state({
id: '4',
convId: 'conv-1',
type: 'message',
timestamp: 0, // No timestamp = processing
role: 'assistant',
content: '',
parent: '1',
thinking: '',
children: []
});
let streamingMessage = $state({
id: '5',
convId: 'conv-1',
type: 'message',
timestamp: 0, // No timestamp = streaming
role: 'assistant',
content: '',
parent: '1',
thinking: '',
children: []
});
</script>
<Story
name="User"
args={{
message: userMessage
}}
/>
<Story
name="Assistant"
args={{
class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
message: assistantMessage
}}
/>
<Story
name="WithThinkingBlock"
args={{
message: streamingMessage
}}
asChild
play={async () => {
// Phase 1: Stream reasoning content in chunks
let reasoningText =
'I need to think about this carefully. Let me break down the problem:\n\n1. The user is asking for help with something complex\n2. I should provide a thorough and helpful response\n3. I need to consider multiple approaches\n4. The best solution would be to explain step by step\n\nThis approach will ensure clarity and understanding.';
let reasoningChunk = 'I';
let i = 0;
while (i < reasoningText.length) {
const chunkSize = Math.floor(Math.random() * 5) + 3; // Random 3-7 characters
const chunk = reasoningText.slice(i, i + chunkSize);
reasoningChunk += chunk;
// Update the reactive state directly
streamingMessage.thinking = reasoningChunk;
i += chunkSize;
await new Promise((resolve) => setTimeout(resolve, 50));
}
const regularText =
"Based on my analysis, here's the solution:\n\n**Step 1:** First, we need to understand the requirements clearly.\n\n**Step 2:** Then we can implement the solution systematically.\n\n**Step 3:** Finally, we test and validate the results.\n\nThis approach ensures we cover all aspects of the problem effectively.";
let contentChunk = '';
i = 0;
while (i < regularText.length) {
const chunkSize = Math.floor(Math.random() * 5) + 3; // Random 3-7 characters
const chunk = regularText.slice(i, i + chunkSize);
contentChunk += chunk;
// Update the reactive state directly
streamingMessage.content = contentChunk;
i += chunkSize;
await new Promise((resolve) => setTimeout(resolve, 50));
}
streamingMessage.timestamp = Date.now();
}}
>
<div class="w-[56rem]">
<ChatMessage message={streamingMessage} />
</div>
</Story>
<Story
name="Processing"
args={{
message: processingMessage
}}
play={async () => {
// Import the chat store to simulate loading state
const { chatStore } = await import('$lib/stores/chat.svelte');
// Set loading state to true to trigger the processing UI
chatStore.isLoading = true;
// Simulate the processing state hook behavior
// This will show the "Generating..." text and parameter details
await new Promise(resolve => setTimeout(resolve, 100));
}}
/>

View File

@@ -0,0 +1,26 @@
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import { ChatSettingsDialog } from '$lib/components/app';
import { fn } from 'storybook/test';
const { Story } = defineMeta({
title: 'Components/ChatSettingsDialog',
component: ChatSettingsDialog,
parameters: {
layout: 'fullscreen'
},
argTypes: {
open: {
control: 'boolean',
description: 'Whether the dialog is open'
}
},
args: {
onOpenChange: fn()
}
});
</script>
<Story name="Open" args={{ open: true }} />
<Story name="Closed" args={{ open: false }} />

View File

@@ -0,0 +1,97 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import ChatSidebar from '$lib/components/app/chat/ChatSidebar/ChatSidebar.svelte';
import { waitFor } from 'storybook/internal/test';
import { screen } from 'storybook/test';
const { Story } = defineMeta({
title: 'Components/ChatSidebar',
component: ChatSidebar,
parameters: {
layout: 'centered'
}
});
// Mock conversations for the sidebar
const mockConversations: DatabaseConversation[] = [
{
id: 'conv-1',
name: 'Getting Started with AI',
lastModified: Date.now() - 1000 * 60 * 5, // 5 minutes ago
currNode: 'msg-1'
},
{
id: 'conv-2',
name: 'Python Programming Help',
lastModified: Date.now() - 1000 * 60 * 60 * 2, // 2 hours ago
currNode: 'msg-2'
},
{
id: 'conv-3',
name: 'Creative Writing Ideas',
lastModified: Date.now() - 1000 * 60 * 60 * 24, // 1 day ago
currNode: 'msg-3'
},
{
id: 'conv-4',
name: 'This is a very long conversation title that should be truncated properly when displayed',
lastModified: Date.now() - 1000 * 60 * 60 * 24 * 3, // 3 days ago
currNode: 'msg-4'
},
{
id: 'conv-5',
name: 'Math Problem Solving',
lastModified: Date.now() - 1000 * 60 * 60 * 24 * 7, // 1 week ago
currNode: 'msg-5'
}
];
</script>
<Story
asChild
name="Default"
play={async () => {
const { chatStore } = await import('$lib/stores/chat.svelte');
waitFor(() => setTimeout(() => {
chatStore.conversations = mockConversations;
}, 0));
}}
>
<div class="flex-column h-full h-screen w-72 bg-background">
<ChatSidebar />
</div>
</Story>
<Story
asChild
name="SearchActive"
play={async ({ userEvent }) => {
const { chatStore } = await import('$lib/stores/chat.svelte');
waitFor(() => setTimeout(() => {
chatStore.conversations = mockConversations;
}, 0));
const searchTrigger = screen.getByText('Search conversations');
userEvent.click(searchTrigger);
}}
>
<div class="flex-column h-full h-screen w-72 bg-background">
<ChatSidebar />
</div>
</Story>
<Story
asChild
name="Empty"
play={async () => {
// Mock empty conversations store
const { chatStore } = await import('$lib/stores/chat.svelte');
chatStore.conversations = [];
}}
>
<div class="flex-column h-full h-screen w-72 bg-background">
<ChatSidebar />
</div>
</Story>

View File

@@ -0,0 +1,44 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="Introduction" />
# llama.cpp Web UI
Welcome to the **llama.cpp Web UI** component library! This Storybook showcases the components used in the modern web interface for the llama.cpp server.
## 🚀 About This Project
WebUI is a modern web interface for the llama.cpp server, built with SvelteKit and ShadCN UI. Features include:
- **Real-time chat conversations** with AI assistants
- **Multi-conversation management** with persistent storage
- **Advanced parameter tuning** for model behavior
- **File upload support** for multimodal interactions
- **Responsive design** that works on desktop and mobile
## 🎨 Design System
The UI is built using:
- **SvelteKit** - Modern web framework with excellent performance
- **Tailwind CSS** - Utility-first CSS framework for rapid styling
- **ShadCN/UI** - High-quality, accessible component library
- **Lucide Icons** - Beautiful, consistent icon set
## 🔧 Development
This Storybook serves as both documentation and a development environment for the UI components. Each story demonstrates:
- **Component variations** - Different states and configurations
- **Interactive examples** - Live components you can interact with
- **Usage patterns** - How components work together
- **Styling consistency** - Unified design language
## 🚀 Getting Started
To explore the components:
1. **Browse the sidebar** to see all available components
2. **Click on stories** to see different component states
3. **Use the controls panel** to interact with component props
4. **Check the docs tab** for detailed component information

View File

@@ -0,0 +1,131 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import { MarkdownContent } from '$lib/components/app';
import { AI_TUTORIAL_MD } from './fixtures/ai-tutorial.js';
import { API_DOCS_MD } from './fixtures/api-docs.js';
import { BLOG_POST_MD } from './fixtures/blog-post.js';
import { DATA_ANALYSIS_MD } from './fixtures/data-analysis.js';
import { README_MD } from './fixtures/readme.js';
import { MATH_FORMULAS_MD } from './fixtures/math-formulas.js';
import { EMPTY_MD } from './fixtures/empty.js';
const { Story } = defineMeta({
title: 'Components/MarkdownContent',
component: MarkdownContent,
parameters: {
layout: 'centered'
}
});
</script>
<Story name="Empty" args={{ content: EMPTY_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }} />
<Story
name="AI Tutorial"
args={{ content: AI_TUTORIAL_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
/>
<Story
name="API Documentation"
args={{ content: API_DOCS_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
/>
<Story
name="Technical Blog"
args={{ content: BLOG_POST_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
/>
<Story
name="Data Analysis"
args={{ content: DATA_ANALYSIS_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
/>
<Story
name="README file"
args={{ content: README_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
/>
<Story
name="Math Formulas"
args={{ content: MATH_FORMULAS_MD, class: 'max-w-[56rem] w-[calc(100vw-2rem)]' }}
/>
<Story
name="URL Links"
args={{
content: `# URL Links Test
Here are some example URLs that should open in new tabs:
- [Hugging Face Homepage](https://huggingface.co)
- [GitHub Repository](https://github.com/ggml-org/llama.cpp)
- [OpenAI Website](https://openai.com)
- [Google Search](https://www.google.com)
You can also test inline links like https://example.com or https://docs.python.org.
All links should have \`target="_blank"\` and \`rel="noopener noreferrer"\` attributes for security.`,
class: 'max-w-[56rem] w-[calc(100vw-2rem)]'
}}
play={async ({ canvasElement }) => {
const { expect } = await import('storybook/internal/test');
// Wait for component to render
await new Promise(resolve => setTimeout(resolve, 100));
// Find all links in the rendered content
const links = canvasElement.querySelectorAll('a[href]');
// Test that we have the expected number of links
expect(links.length).toBeGreaterThan(0);
// Test each link for proper attributes
links.forEach((link) => {
const href = link.getAttribute('href');
// Test that external links have proper security attributes
if (href && (href.startsWith('http://') || href.startsWith('https://'))) {
expect(link.getAttribute('target')).toBe('_blank');
expect(link.getAttribute('rel')).toBe('noopener noreferrer');
}
});
// Test specific links exist
const hugginFaceLink = Array.from(links).find(link =>
link.getAttribute('href') === 'https://huggingface.co'
);
expect(hugginFaceLink).toBeTruthy();
expect(hugginFaceLink?.textContent).toBe('Hugging Face Homepage');
const githubLink = Array.from(links).find(link =>
link.getAttribute('href') === 'https://github.com/ggml-org/llama.cpp'
);
expect(githubLink).toBeTruthy();
expect(githubLink?.textContent).toBe('GitHub Repository');
const openaiLink = Array.from(links).find(link =>
link.getAttribute('href') === 'https://openai.com'
);
expect(openaiLink).toBeTruthy();
expect(openaiLink?.textContent).toBe('OpenAI Website');
const googleLink = Array.from(links).find(link =>
link.getAttribute('href') === 'https://www.google.com'
);
expect(googleLink).toBeTruthy();
expect(googleLink?.textContent).toBe('Google Search');
// Test inline links (auto-linked URLs)
const exampleLink = Array.from(links).find(link =>
link.getAttribute('href') === 'https://example.com'
);
expect(exampleLink).toBeTruthy();
const pythonDocsLink = Array.from(links).find(link =>
link.getAttribute('href') === 'https://docs.python.org'
);
expect(pythonDocsLink).toBeTruthy();
console.log(`✅ URL Links test passed - Found ${links.length} links with proper attributes`);
}}
/>

View File

@@ -0,0 +1,164 @@
// AI Assistant Tutorial Response
export const AI_TUTORIAL_MD = String.raw`
# Building a Modern Chat Application with SvelteKit
I'll help you create a **production-ready chat application** using SvelteKit, TypeScript, and WebSockets. This implementation includes real-time messaging, user authentication, and message persistence.
## 🚀 Quick Start
First, let's set up the project:
${'```'}bash
npm create svelte@latest chat-app
cd chat-app
npm install
npm install socket.io socket.io-client
npm install @prisma/client prisma
npm run dev
${'```'}
## 📁 Project Structure
${'```'}
chat-app/
├── src/
│ ├── routes/
│ │ ├── +layout.svelte
│ │ ├── +page.svelte
│ │ └── api/
│ │ └── socket/+server.ts
│ ├── lib/
│ │ ├── components/
│ │ │ ├── ChatMessage.svelte
│ │ │ └── ChatInput.svelte
│ │ └── stores/
│ │ └── chat.ts
│ └── app.html
├── prisma/
│ └── schema.prisma
└── package.json
${'```'}
## 💻 Implementation
### WebSocket Server
${'```'}typescript
// src/lib/server/socket.ts
import { Server } from 'socket.io';
import type { ViteDevServer } from 'vite';
export function initializeSocketIO(server: ViteDevServer) {
const io = new Server(server.httpServer || server, {
cors: {
origin: process.env.ORIGIN || 'http://localhost:5173',
credentials: true
}
});
io.on('connection', (socket) => {
console.log('User connected:', socket.id);
socket.on('message', async (data) => {
// Broadcast to all clients
io.emit('new-message', {
id: crypto.randomUUID(),
userId: socket.id,
content: data.content,
timestamp: new Date().toISOString()
});
});
socket.on('disconnect', () => {
console.log('User disconnected:', socket.id);
});
});
return io;
}
${'```'}
### Client Store
${'```'}typescript
// src/lib/stores/chat.ts
import { writable } from 'svelte/store';
import io from 'socket.io-client';
export interface Message {
id: string;
userId: string;
content: string;
timestamp: string;
}
function createChatStore() {
const { subscribe, update } = writable<Message[]>([]);
let socket: ReturnType<typeof io>;
return {
subscribe,
connect: () => {
socket = io('http://localhost:5173');
socket.on('new-message', (message: Message) => {
update(messages => [...messages, message]);
});
},
sendMessage: (content: string) => {
if (socket && content.trim()) {
socket.emit('message', { content });
}
}
};
}
export const chatStore = createChatStore();
${'```'}
## 🎯 Key Features
✅ **Real-time messaging** with WebSockets
✅ **Message persistence** using Prisma + PostgreSQL
✅ **Type-safe** with TypeScript
✅ **Responsive UI** for all devices
✅ **Auto-reconnection** on connection loss
## 📊 Performance Metrics
| Metric | Value |
|--------|-------|
| **Message Latency** | < 50ms |
| **Concurrent Users** | 10,000+ |
| **Messages/Second** | 5,000+ |
| **Uptime** | 99.9% |
## 🔧 Configuration
### Environment Variables
${'```'}env
DATABASE_URL="postgresql://user:password@localhost:5432/chat"
JWT_SECRET="your-secret-key"
REDIS_URL="redis://localhost:6379"
${'```'}
## 🚢 Deployment
Deploy to production using Docker:
${'```'}dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "build"]
${'```'}
---
*Need help? Check the [documentation](https://kit.svelte.dev) or [open an issue](https://github.com/sveltejs/kit/issues)*
`;

View File

@@ -0,0 +1,160 @@
// API Documentation
export const API_DOCS_MD = String.raw`
# REST API Documentation
## 🔐 Authentication
All API requests require authentication using **Bearer tokens**. Include your API key in the Authorization header:
${'```'}http
GET /api/v1/users
Host: api.example.com
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
${'```'}
## 📍 Endpoints
### Users API
#### **GET** /api/v1/users
Retrieve a paginated list of users.
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| page | integer | 1 | Page number |
| limit | integer | 20 | Items per page |
| sort | string | "created_at" | Sort field |
| order | string | "desc" | Sort order |
**Response:** 200 OK
${'```'}json
{
"data": [
{
"id": "usr_1234567890",
"email": "user@example.com",
"name": "John Doe",
"role": "admin",
"created_at": "2024-01-15T10:30:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 156,
"pages": 8
}
}
${'```'}
#### **POST** /api/v1/users
Create a new user account.
**Request Body:**
${'```'}json
{
"email": "newuser@example.com",
"password": "SecurePassword123!",
"name": "Jane Smith",
"role": "user"
}
${'```'}
**Response:** 201 Created
${'```'}json
{
"id": "usr_9876543210",
"email": "newuser@example.com",
"name": "Jane Smith",
"role": "user",
"created_at": "2024-01-21T09:15:00Z"
}
${'```'}
### Error Responses
The API returns errors in a consistent format:
${'```'}json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{
"field": "email",
"message": "Email format is invalid"
}
]
}
}
${'```'}
### Rate Limiting
| Tier | Requests/Hour | Burst |
|------|--------------|-------|
| **Free** | 1,000 | 100 |
| **Pro** | 10,000 | 500 |
| **Enterprise** | Unlimited | - |
**Headers:**
- X-RateLimit-Limit
- X-RateLimit-Remaining
- X-RateLimit-Reset
### Webhooks
Configure webhooks to receive real-time events:
${'```'}javascript
// Webhook payload
{
"event": "user.created",
"timestamp": "2024-01-21T09:15:00Z",
"data": {
"id": "usr_9876543210",
"email": "newuser@example.com"
},
"signature": "sha256=abcd1234..."
}
${'```'}
### SDK Examples
**JavaScript/TypeScript:**
${'```'}typescript
import { ApiClient } from '@example/api-sdk';
const client = new ApiClient({
apiKey: process.env.API_KEY
});
const users = await client.users.list({
page: 1,
limit: 20
});
${'```'}
**Python:**
${'```'}python
from example_api import Client
client = Client(api_key=os.environ['API_KEY'])
users = client.users.list(page=1, limit=20)
${'```'}
---
📚 [Full API Reference](https://api.example.com/docs) | 💬 [Support](https://support.example.com)
`;

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,125 @@
// Blog Post Content
export const BLOG_POST_MD = String.raw`
# Understanding Rust's Ownership System
*Published on March 15, 2024 • 8 min read*
Rust's ownership system is one of its most distinctive features, enabling memory safety without garbage collection. In this post, we'll explore how ownership works and why it's revolutionary for systems programming.
## What is Ownership?
Ownership is a set of rules that governs how Rust manages memory. These rules are checked at compile time, ensuring memory safety without runtime overhead.
### The Three Rules of Ownership
1. **Each value has a single owner**
2. **There can only be one owner at a time**
3. **When the owner goes out of scope, the value is dropped**
## Memory Management Without GC
Traditional approaches to memory management:
- **Manual management** (C/C++): Error-prone, leads to bugs
- **Garbage collection** (Java, Python): Runtime overhead
- **Ownership** (Rust): Compile-time safety, zero runtime cost
## Basic Examples
### Variable Scope
${'```'}rust
fn main() {
let s = String::from("hello"); // s comes into scope
// s is valid here
println!("{}", s);
} // s goes out of scope and is dropped
${'```'}
### Move Semantics
${'```'}rust
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 is moved to s2
// println!("{}", s1); // ❌ ERROR: s1 is no longer valid
println!("{}", s2); // ✅ OK: s2 owns the string
}
${'```'}
## Borrowing and References
Instead of transferring ownership, you can **borrow** values:
### Immutable References
${'```'}rust
fn calculate_length(s: &String) -> usize {
s.len() // s is a reference, doesn't own the String
}
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // Borrow s1
println!("Length of '{}' is {}", s1, len); // s1 still valid
}
${'```'}
### Mutable References
${'```'}rust
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
r1.push_str(", world");
println!("{}", r1);
// let r2 = &mut s; // ❌ ERROR: cannot borrow twice
}
${'```'}
## Common Pitfalls
### Dangling References
${'```'}rust
fn dangle() -> &String { // ❌ ERROR: missing lifetime specifier
let s = String::from("hello");
&s // s will be dropped, leaving a dangling reference
}
${'```'}
### ✅ Solution
${'```'}rust
fn no_dangle() -> String {
let s = String::from("hello");
s // Ownership is moved out
}
${'```'}
## Benefits
- ✅ **No null pointer dereferences**
- ✅ **No data races**
- ✅ **No use-after-free**
- ✅ **No memory leaks**
## Conclusion
Rust's ownership system eliminates entire classes of bugs at compile time. While it has a learning curve, the benefits in safety and performance are worth it.
## Further Reading
- [The Rust Book - Ownership](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html)
- [Rust by Example - Ownership](https://doc.rust-lang.org/rust-by-example/scope/move.html)
- [Rustlings Exercises](https://github.com/rust-lang/rustlings)
---
*Questions? Reach out on [Twitter](https://twitter.com/rustlang) or join the [Rust Discord](https://discord.gg/rust-lang)*
`;

View File

@@ -0,0 +1,124 @@
// Data Analysis Report
export const DATA_ANALYSIS_MD = String.raw`
# Q4 2024 Business Analytics Report
*Executive Summary • Generated on January 15, 2025*
## 📊 Key Performance Indicators
${'```'}
Daily Active Users (DAU): 1.2M (+65% YoY)
Monthly Active Users (MAU): 4.5M (+48% YoY)
User Retention (Day 30): 68% (+12pp YoY)
Average Session Duration: 24min (+35% YoY)
${'```'}
## 🎯 Product Performance
### Feature Adoption Rates
1. **AI Assistant**: 78% of users (↑ from 45%)
2. **Collaboration Tools**: 62% of users (↑ from 38%)
3. **Analytics Dashboard**: 54% of users (↑ from 31%)
4. **Mobile App**: 41% of users (↑ from 22%)
### Customer Satisfaction
| Metric | Q4 2024 | Q3 2024 | Change |
|--------|---------|---------|--------|
| **NPS Score** | 72 | 68 | +4 |
| **CSAT** | 4.6/5 | 4.4/5 | +0.2 |
| **Support Tickets** | 2,340 | 2,890 | -19% |
| **Resolution Time** | 4.2h | 5.1h | -18% |
## 💰 Revenue Metrics
### Monthly Recurring Revenue (MRR)
- **Current MRR**: $2.8M (+42% YoY)
- **New MRR**: $340K
- **Expansion MRR**: $180K
- **Churned MRR**: $95K
- **Net New MRR**: $425K
### Customer Acquisition
${'```'}
Cost per Acquisition (CAC): $127 (-23% YoY)
Customer Lifetime Value: $1,840 (+31% YoY)
LTV:CAC Ratio: 14.5:1
Payback Period: 3.2 months
${'```'}
## 🌍 Geographic Performance
### Revenue by Region
1. **North America**: 45% ($1.26M)
2. **Europe**: 32% ($896K)
3. **Asia-Pacific**: 18% ($504K)
4. **Other**: 5% ($140K)
### Growth Opportunities
- **APAC**: 89% YoY growth potential
- **Latin America**: Emerging market entry
- **Middle East**: Enterprise expansion
## 📱 Channel Performance
### Traffic Sources
| Channel | Sessions | Conversion | Revenue |
|---------|----------|------------|---------|
| **Organic Search** | 45% | 3.2% | $1.1M |
| **Direct** | 28% | 4.1% | $850K |
| **Social Media** | 15% | 2.8% | $420K |
| **Paid Ads** | 12% | 5.5% | $430K |
### Marketing ROI
- **Content Marketing**: 340% ROI
- **Email Campaigns**: 280% ROI
- **Social Media**: 190% ROI
- **Paid Search**: 220% ROI
## 🔍 User Behavior Analysis
### Session Patterns
- **Peak Hours**: 9-11 AM, 2-4 PM EST
- **Mobile Usage**: 67% of sessions
- **Average Pages/Session**: 4.8
- **Bounce Rate**: 23% (↓ from 31%)
### Feature Usage Heatmap
Most used features in order:
1. Dashboard (89% of users)
2. Search (76% of users)
3. Reports (64% of users)
4. Settings (45% of users)
5. Integrations (32% of users)
## 💡 Recommendations
1. **Invest** in AI capabilities (+$2M budget)
2. **Expand** sales team in APAC region
3. **Improve** onboarding to reduce churn
4. **Launch** enterprise security features
## Appendix
### Methodology
Data collected from:
- Internal analytics (Amplitude)
- Customer surveys (n=2,450)
- Financial systems (NetSuite)
- Market research (Gartner)
---
*Report prepared by Data Analytics Team • [View Interactive Dashboard](https://analytics.example.com)*
`;

View File

@@ -0,0 +1,2 @@
// Empty state
export const EMPTY_MD = '';

View File

@@ -0,0 +1,156 @@
// Math Formulas Content
export const MATH_FORMULAS_MD = String.raw`
# Mathematical Formulas and Expressions
This document demonstrates various mathematical notation and formulas that can be rendered using LaTeX syntax in markdown.
## Basic Arithmetic
### Addition and Summation
$$\sum_{i=1}^{n} i = \frac{n(n+1)}{2}$$
## Algebra
### Quadratic Formula
The solutions to $ax^2 + bx + c = 0$ are:
$$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$
### Binomial Theorem
$$(x + y)^n = \sum_{k=0}^{n} \binom{n}{k} x^{n-k} y^k$$
## Calculus
### Derivatives
The derivative of $f(x) = x^n$ is:
$$f'(x) = nx^{n-1}$$
### Integration
$$\int_a^b f(x) \, dx = F(b) - F(a)$$
### Fundamental Theorem of Calculus
$$\frac{d}{dx} \int_a^x f(t) \, dt = f(x)$$
## Linear Algebra
### Matrix Multiplication
If $A$ is an $m \times n$ matrix and $B$ is an $n \times p$ matrix, then:
$$C_{ij} = \sum_{k=1}^{n} A_{ik} B_{kj}$$
### Eigenvalues and Eigenvectors
For a square matrix $A$, if $Av = \lambda v$ for some non-zero vector $v$, then:
- $\lambda$ is an eigenvalue
- $v$ is an eigenvector
## Statistics and Probability
### Normal Distribution
The probability density function is:
$$f(x) = \frac{1}{\sigma\sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}$$
### Bayes' Theorem
$$P(A|B) = \frac{P(B|A) \cdot P(A)}{P(B)}$$
### Central Limit Theorem
For large $n$, the sample mean $\bar{X}$ is approximately:
$$\bar{X} \sim N\left(\mu, \frac{\sigma^2}{n}\right)$$
## Trigonometry
### Pythagorean Identity
$$\sin^2\theta + \cos^2\theta = 1$$
### Euler's Formula
$$e^{i\theta} = \cos\theta + i\sin\theta$$
### Taylor Series for Sine
$$\sin x = \sum_{n=0}^{\infty} \frac{(-1)^n}{(2n+1)!} x^{2n+1} = x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \cdots$$
## Complex Analysis
### Complex Numbers
A complex number can be written as:
$$z = a + bi = r e^{i\theta}$$
where $r = |z| = \sqrt{a^2 + b^2}$ and $\theta = \arg(z)$
### Cauchy-Riemann Equations
For a function $f(z) = u(x,y) + iv(x,y)$ to be analytic:
$$\frac{\partial u}{\partial x} = \frac{\partial v}{\partial y}, \quad \frac{\partial u}{\partial y} = -\frac{\partial v}{\partial x}$$
## Differential Equations
### First-order Linear ODE
$$\frac{dy}{dx} + P(x)y = Q(x)$$
Solution: $y = e^{-\int P(x)dx}\left[\int Q(x)e^{\int P(x)dx}dx + C\right]$
### Heat Equation
$$\frac{\partial u}{\partial t} = \alpha \frac{\partial^2 u}{\partial x^2}$$
## Number Theory
### Prime Number Theorem
$$\pi(x) \sim \frac{x}{\ln x}$$
where $\pi(x)$ is the number of primes less than or equal to $x$.
### Fermat's Last Theorem
For $n > 2$, there are no positive integers $a$, $b$, and $c$ such that:
$$a^n + b^n = c^n$$
## Set Theory
### De Morgan's Laws
$$\overline{A \cup B} = \overline{A} \cap \overline{B}$$
$$\overline{A \cap B} = \overline{A} \cup \overline{B}$$
## Advanced Topics
### Riemann Zeta Function
$$\zeta(s) = \sum_{n=1}^{\infty} \frac{1}{n^s} = \prod_{p \text{ prime}} \frac{1}{1-p^{-s}}$$
### Maxwell's Equations
$$\nabla \cdot \mathbf{E} = \frac{\rho}{\epsilon_0}$$
$$\nabla \cdot \mathbf{B} = 0$$
$$\nabla \times \mathbf{E} = -\frac{\partial \mathbf{B}}{\partial t}$$
$$\nabla \times \mathbf{B} = \mu_0\mathbf{J} + \mu_0\epsilon_0\frac{\partial \mathbf{E}}{\partial t}$$
### Schrödinger Equation
$$i\hbar\frac{\partial}{\partial t}\Psi(\mathbf{r},t) = \hat{H}\Psi(\mathbf{r},t)$$
## Inline Math Examples
Here are some inline mathematical expressions:
- The golden ratio: $\phi = \frac{1 + \sqrt{5}}{2} \approx 1.618$
- Euler's number: $e = \lim_{n \to \infty} \left(1 + \frac{1}{n}\right)^n$
- Pi: $\pi = 4 \sum_{n=0}^{\infty} \frac{(-1)^n}{2n+1}$
- Square root of 2: $\sqrt{2} = 1.41421356...$
## Fractions and Radicals
Complex fraction: $\frac{\frac{a}{b} + \frac{c}{d}}{\frac{e}{f} - \frac{g}{h}}$
Nested radicals: $\sqrt{2 + \sqrt{3 + \sqrt{4 + \sqrt{5}}}}$
## Summations and Products
### Geometric Series
$$\sum_{n=0}^{\infty} ar^n = \frac{a}{1-r} \quad \text{for } |r| < 1$$
### Product Notation
$$n! = \prod_{k=1}^{n} k$$
### Double Summation
$$\sum_{i=1}^{m} \sum_{j=1}^{n} a_{ij}$$
## Limits
$$\lim_{x \to 0} \frac{\sin x}{x} = 1$$
$$\lim_{n \to \infty} \left(1 + \frac{x}{n}\right)^n = e^x$$
---
*This document showcases various mathematical notation and formulas that can be rendered in markdown using LaTeX syntax.*
`;

View File

@@ -0,0 +1,136 @@
// README Content
export const README_MD = String.raw`
# 🚀 Awesome Web Framework
[![npm version](https://img.shields.io/npm/v/awesome-framework.svg)](https://www.npmjs.com/package/awesome-framework)
[![Build Status](https://github.com/awesome/framework/workflows/CI/badge.svg)](https://github.com/awesome/framework/actions)
[![Coverage](https://codecov.io/gh/awesome/framework/branch/main/graph/badge.svg)](https://codecov.io/gh/awesome/framework)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
> A modern, fast, and flexible web framework for building scalable applications
## ✨ Features
- 🎯 **Type-Safe** - Full TypeScript support out of the box
- ⚡ **Lightning Fast** - Built on Vite for instant HMR
- 📦 **Zero Config** - Works out of the box for most use cases
- 🎨 **Flexible** - Unopinionated with sensible defaults
- 🔧 **Extensible** - Plugin system for custom functionality
- 📱 **Responsive** - Mobile-first approach
- 🌍 **i18n Ready** - Built-in internationalization
- 🔒 **Secure** - Security best practices by default
## 📦 Installation
${'```'}bash
npm install awesome-framework
# or
yarn add awesome-framework
# or
pnpm add awesome-framework
${'```'}
## 🚀 Quick Start
### Create a new project
${'```'}bash
npx create-awesome-app my-app
cd my-app
npm run dev
${'```'}
### Basic Example
${'```'}javascript
import { createApp } from 'awesome-framework';
const app = createApp({
port: 3000,
middleware: ['cors', 'helmet', 'compression']
});
app.get('/', (req, res) => {
res.json({ message: 'Hello World!' });
});
app.listen(() => {
console.log('Server running on http://localhost:3000');
});
${'```'}
## 📖 Documentation
### Core Concepts
- [Getting Started](https://docs.awesome.dev/getting-started)
- [Configuration](https://docs.awesome.dev/configuration)
- [Routing](https://docs.awesome.dev/routing)
- [Middleware](https://docs.awesome.dev/middleware)
- [Database](https://docs.awesome.dev/database)
- [Authentication](https://docs.awesome.dev/authentication)
### Advanced Topics
- [Performance Optimization](https://docs.awesome.dev/performance)
- [Deployment](https://docs.awesome.dev/deployment)
- [Testing](https://docs.awesome.dev/testing)
- [Security](https://docs.awesome.dev/security)
## 🛠️ Development
### Prerequisites
- Node.js >= 18
- pnpm >= 8
### Setup
${'```'}bash
git clone https://github.com/awesome/framework.git
cd framework
pnpm install
pnpm dev
${'```'}
### Testing
${'```'}bash
pnpm test # Run unit tests
pnpm test:e2e # Run end-to-end tests
pnpm test:watch # Run tests in watch mode
${'```'}
## 🤝 Contributing
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
### Contributors
<a href="https://github.com/awesome/framework/graphs/contributors">
<img src="https://contrib.rocks/image?repo=awesome/framework" />
</a>
## 📊 Benchmarks
| Framework | Requests/sec | Latency (ms) | Memory (MB) |
|-----------|-------------|--------------|-------------|
| **Awesome** | **45,230** | **2.1** | **42** |
| Express | 28,450 | 3.5 | 68 |
| Fastify | 41,200 | 2.3 | 48 |
| Koa | 32,100 | 3.1 | 52 |
*Benchmarks performed on MacBook Pro M2, Node.js 20.x*
## 📝 License
MIT © [Awesome Team](https://github.com/awesome)
## 🙏 Acknowledgments
Special thanks to all our sponsors and contributors who make this project possible.
---
**[Website](https://awesome.dev)** • **[Documentation](https://docs.awesome.dev)** • **[Discord](https://discord.gg/awesome)** • **[Twitter](https://twitter.com/awesomeframework)**
`;

View File

@@ -0,0 +1,50 @@
import { serverStore } from '$lib/stores/server.svelte';
/**
* Mock server properties for Storybook testing
* This utility allows setting mock server configurations without polluting production code
*/
export function mockServerProps(props: Partial<ApiLlamaCppServerProps>): void {
// Directly set the private _serverProps for testing purposes
(serverStore as unknown as { _serverProps: ApiLlamaCppServerProps })._serverProps = {
model_path: props.model_path || 'test-model',
modalities: {
vision: props.modalities?.vision ?? false,
audio: props.modalities?.audio ?? false
},
...props
} as ApiLlamaCppServerProps;
}
/**
* Reset server store to clean state for testing
*/
export function resetServerStore(): void {
(serverStore as unknown as { _serverProps: ApiLlamaCppServerProps })._serverProps = {
model_path: '',
modalities: {
vision: false,
audio: false
}
} as ApiLlamaCppServerProps;
(serverStore as unknown as { _error: string })._error = '';
(serverStore as unknown as { _loading: boolean })._loading = false;
}
/**
* Common mock configurations for Storybook stories
*/
export const mockConfigs = {
visionOnly: {
modalities: { vision: true, audio: false }
},
audioOnly: {
modalities: { vision: false, audio: true }
},
bothModalities: {
modalities: { vision: true, audio: true }
},
noModalities: {
modalities: { vision: false, audio: false }
}
} as const;