Skip to content

SvelteKit-based WebUI #14839

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 148 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
a9aadf7
feat: Initializes SvelteKit web UI project
allozaur Jul 21, 2025
efc058c
feat: Adds theme mode watcher
allozaur Jul 21, 2025
6e6c09f
Merge remote-tracking branch 'upstream/master' into allozaur/svelte-w…
allozaur Jul 22, 2025
2fe2c9f
feat: Adds webui development script
allozaur Jul 22, 2025
3150fa4
feat: Switches to static adapter
allozaur Jul 22, 2025
e334bb8
feat: Adds basic chat UI
allozaur Jul 23, 2025
ec28b88
refactor: Rename old webui app and use SvelteKit app as the main
allozaur Jul 23, 2025
b6e7e46
feat: Rename local llama-server script
allozaur Jul 23, 2025
fa3dce4
feat: Update header logotype
allozaur Jul 23, 2025
2e74774
chore: Update public webui build
allozaur Jul 23, 2025
d1112d8
feat: Add `-j` flag for concurrent jobs in CPU Build
allozaur Jul 23, 2025
9d496ed
refactor: Remove unused code
allozaur Jul 23, 2025
896e4ac
feat: Creat Dummy UI
allozaur Jul 23, 2025
da8fb1a
refactor: Naming
allozaur Jul 23, 2025
0c80bac
feat: UI components & Storybook WIP
allozaur Jul 23, 2025
69ef331
feat: UI components & Storybook WIP
allozaur Jul 23, 2025
82f1b8b
feat: Essential Chat UI & logic wired with IndexedDB
allozaur Jul 24, 2025
db7295f
feat: UI improvements
allozaur Jul 24, 2025
a6d85e6
feat: UI & Architecture improvements
allozaur Jul 24, 2025
a555a58
feat: UI & logic improvements
allozaur Jul 25, 2025
4fb2cc4
feat: Thinking response UI, copy to clipboard, sonner toast alerts
allozaur Jul 25, 2025
d5ccf2e
feat: UI improvements
allozaur Jul 26, 2025
f9f062e
feat: Implement Alert Dialog before deleting conversations
allozaur Jul 26, 2025
5c228be
feat: Generation logic improvements
allozaur Jul 26, 2025
a12fef4
chore: Remove unneeded file
allozaur Jul 26, 2025
d18718e
feat: Single file bundling
allozaur Jul 26, 2025
a5ee47e
feat: Save generated output before navigating away
allozaur Jul 27, 2025
59f645f
feat: Inline HTML compilation
allozaur Jul 27, 2025
7e96e19
fix: Rendering condition
allozaur Jul 27, 2025
0039d54
feat: Gzip the output HTML file
allozaur Jul 27, 2025
c653687
feat: Markdown Content styling WIP
allozaur Jul 28, 2025
d5106e2
chore: Windsurf rule update
allozaur Jul 28, 2025
957c492
feat: Markdown content formatting
allozaur Jul 28, 2025
34c812a
refactor: Data structure compatibility WIP
allozaur Jul 29, 2025
11d1c4d
Merge remote-tracking branch 'ggml-org/master' into allozaur/svelte-w…
allozaur Jul 29, 2025
3d115dc
refactor: Cleanup
allozaur Jul 29, 2025
b708653
debug: Build config
allozaur Jul 29, 2025
608fa93
Merge remote-tracking branch 'ggml-org/master' into allozaur/svelte-w…
allozaur Jul 29, 2025
aa64372
fix: Public assets config
allozaur Jul 29, 2025
da78a64
feat: Add loading.html to static files
allozaur Jul 29, 2025
8f7ac8a
debug: Leave HTML file in build output to check bundle size
allozaur Jul 29, 2025
b33fb8e
refactor: Remove Shiki code highlighting
allozaur Jul 29, 2025
00d3a16
feat: Use `rehype-highlight` and `highlight.js` for markdown code hig…
allozaur Jul 29, 2025
d514ceb
feat: Implement enhanced code blocks for Markdown Content
allozaur Jul 29, 2025
f45f9c8
Merge remote-tracking branch 'ggml-org/master' into allozaur/svelte-w…
allozaur Jul 29, 2025
6af3542
feat: Structural improvements + auto-scroll fixes
allozaur Jul 30, 2025
10f0559
fix: UI fix
allozaur Jul 30, 2025
3f6468b
refactor: Cleanup
allozaur Jul 30, 2025
3a642cc
feat: Setting first message as chat title
allozaur Jul 30, 2025
5d067f6
chore: Update public build
allozaur Jul 30, 2025
5e72f25
refactor: Cleanup chat completion streaming logic & DRY code
allozaur Jul 30, 2025
5ba666e
refactor: Cleanup
allozaur Jul 30, 2025
af8b1cd
refactor: Chat components and data structures
allozaur Jul 31, 2025
4cb616a
Merge remote-tracking branch 'origin/allozaur/svelte-webui' into allo…
allozaur Jul 31, 2025
833118e
chore: Update package-lock.json
allozaur Aug 1, 2025
48f64f9
feat: Adjusting types & request for handling the files WIP
allozaur Aug 1, 2025
fef2fe1
feat: File Upload UI
allozaur Aug 1, 2025
d9fee52
refactor: Stories cleanup
allozaur Aug 1, 2025
028fb8c
feat: Adds chat attachments functionality
allozaur Aug 1, 2025
009f77b
refactor: Decouple chat attachments display into components
allozaur Aug 1, 2025
fc1a74e
feat: Improves file handling and paste functionality
allozaur Aug 1, 2025
f3a55ed
refactor: Rename post-build script for web UI
allozaur Aug 1, 2025
e01ae49
Merge remote-tracking branch 'ggml-org/master' into allozaur/svelte-w…
allozaur Aug 2, 2025
839140b
feat: Adds file attachment previews to chat
allozaur Aug 4, 2025
2b1b1f8
feat: Improves chat form and file handling
allozaur Aug 4, 2025
6830c25
feat: Adds converting SVG images to PNG
allozaur Aug 4, 2025
ecf1f04
feat: Adds PDF file support to chat
allozaur Aug 4, 2025
932f31e
feat: Enables previewing attachments in a dialog
allozaur Aug 4, 2025
2d0b873
chore: Update `index.html.gz` build
allozaur Aug 4, 2025
5b668bf
feat: UI/UX improvements
allozaur Aug 4, 2025
e9528f6
feat: Mobile UI & UI elements improvements
allozaur Aug 4, 2025
6ffa578
feat: UI improvements
allozaur Aug 4, 2025
93fb1dc
feat: Chat Sidebar Conversation Items UI improvements
allozaur Aug 4, 2025
b592954
feat: UI improvements
allozaur Aug 4, 2025
14e8ac8
chore: Update public build
allozaur Aug 4, 2025
34f3709
feat: Sets focus to `ChatForm`'s `<textarea>` after load
allozaur Aug 5, 2025
b5d9f55
feat: Formats message timestamps for readability
allozaur Aug 5, 2025
13c30d6
refactor: Components structure
allozaur Aug 6, 2025
377b808
feat: UI improvements for Chat Settings Dialog
allozaur Aug 6, 2025
fa0e729
fix: Close search results after clicking conversation item
allozaur Aug 6, 2025
e0c4f6b
chore: Update static webui build
allozaur Aug 6, 2025
f3a8758
feat: Updates server properties handling
allozaur Aug 6, 2025
54cfe15
feat: Adds context length check before sending message (PoC)
allozaur Aug 6, 2025
e714579
chore: Adds VS Code workspace file
allozaur Aug 6, 2025
2a3d057
feat: Improves chat settings and UI layout
allozaur Aug 6, 2025
d6ec058
feat: Binds Vite dev server to all interfaces
allozaur Aug 6, 2025
b54989e
refactor: Better structure for components
allozaur Aug 6, 2025
82996b8
docs: Adds JSDoc comments and improves utils
allozaur Aug 6, 2025
8908088
chore: Windsurf rules
allozaur Aug 6, 2025
ce2f9d1
feat: Refactors settings dialog with Svelte runes
allozaur Aug 6, 2025
3a45b7d
refactor: Component imports
allozaur Aug 6, 2025
3cde171
refactor: Centralize type definitions
allozaur Aug 6, 2025
f0af1ef
feat: Improves chat service architecture, starts implementing Setting…
allozaur Aug 6, 2025
2601cbf
feat: Add WebP image conversion utilities
allozaur Aug 7, 2025
dbb292d
refactor: Extract file upload processing to utilities
allozaur Aug 7, 2025
8242776
feat(app): Extract maximum context alert dialog into component
allozaur Aug 7, 2025
be5ba77
refactor: Updates order of imports
allozaur Aug 7, 2025
bd03cb2
Refactor: Consolidate ChatScreen imports, constants, and naming setup
allozaur Aug 7, 2025
111684c
Feat: Improve chat auto-scrolling behavior
allozaur Aug 7, 2025
7a63680
refactor: Changes order of imports
allozaur Aug 7, 2025
a14d3f6
chore: Updates static build
allozaur Aug 7, 2025
7d5cd2c
Build: Enhance frontend bundle compression and size validation
allozaur Aug 7, 2025
4f8bd9b
Refactor: Decouple utility imports from index.ts
allozaur Aug 7, 2025
75afbdc
Refactor: Rename context error state to maxContextError
allozaur Aug 7, 2025
e5d263a
chore: Update static build
allozaur Aug 7, 2025
afcc79c
feat: Adds proxy and headers for local development
allozaur Aug 7, 2025
82495d9
chore: Update static build
allozaur Aug 7, 2025
23510ae
feat: Enables processing math formulas for markdown content
allozaur Aug 8, 2025
05f976a
feat: Chat Form improvements
allozaur Aug 8, 2025
5e7efef
docs: MarkdownContent component stories
allozaur Aug 8, 2025
0a42402
fix: ScrollArea Height
allozaur Aug 8, 2025
2e0f1d7
chore: Creates `fixtures` folder and adds test files
allozaur Aug 9, 2025
4cad2b6
feat: Adds support for audio file uploads
allozaur Aug 9, 2025
4ba2e04
fix: Fixes chat attachment preview interactivity & displaying issues
allozaur Aug 9, 2025
c5b5812
Merge remote-tracking branch 'ggml-org/master' into allozaur/svelte-w…
allozaur Aug 9, 2025
6c9992a
refactor: Cleans up redundant console logs
allozaur Aug 9, 2025
ed4716c
fix: Improves scroll interval on active responses stream
allozaur Aug 9, 2025
ccf4bef
feat: Improve sidebar scrollarea
allozaur Aug 9, 2025
b026a17
feat: Improves chat sidebar and header layout & structure
allozaur Aug 11, 2025
5de7bc6
Merge remote-tracking branch 'ggml-org/master' into allozaur/svelte-w…
allozaur Aug 11, 2025
20174b1
feat: Enables editing of conversation names
allozaur Aug 11, 2025
b8c6795
feat: Implement basic `Processing...` UI
allozaur Aug 11, 2025
0b71fdf
fix: Chat Sidebar interactions
allozaur Aug 11, 2025
7919123
feat: Improves UI appearance
allozaur Aug 11, 2025
e23d39d
feat: Allows sending messages with attachments only
allozaur Aug 12, 2025
787f203
refactor: Simplifies API base URL handling
allozaur Aug 12, 2025
d5735bd
fix: Add missing validation logic updates
allozaur Aug 12, 2025
ceb3d81
fix: Data binding
allozaur Aug 12, 2025
01537bc
refactor: Refactors file type handling using enums
allozaur Aug 12, 2025
8d8372b
fix: Fix types & svelte issues
allozaur Aug 12, 2025
94a06de
feat: Validates file types before upload
allozaur Aug 12, 2025
5d1d146
refactor: DRYs text file extension handling
allozaur Aug 12, 2025
a6d846e
feat: Enables audio recording in chat form
allozaur Aug 12, 2025
78219cd
faet: Displays real-time processing details (WIP)
allozaur Aug 12, 2025
b67f888
fix: Wrong file extension
allozaur Aug 12, 2025
f02925d
fix: Missing import update
allozaur Aug 12, 2025
47ec32d
refactor: Removes debug logs from chat and attachment preview
allozaur Aug 12, 2025
7e2ab2a
feat: Improves file upload handling with modality support
allozaur Aug 12, 2025
f8fc579
feat: Adds SPA fallback route to server
allozaur Aug 12, 2025
4632deb
chore: Update static build
allozaur Aug 12, 2025
be0dfce
feat: UI improvements & tests
allozaur Aug 12, 2025
d16fb23
Merge remote-tracking branch 'ggml-org/master' into allozaur/svelte-w…
allozaur Aug 12, 2025
6c99820
chore: Remove old file
allozaur Aug 12, 2025
8699fd9
feat: Improves context handling and error reporting (WIP)
allozaur Aug 13, 2025
0465636
chore: Static build update
allozaur Aug 13, 2025
2fc3a8e
fix: Stops slots polling on context errors
allozaur Aug 13, 2025
5ff1575
chore: Lint
allozaur Aug 13, 2025
a5277b0
feat: Improves error handling and UI feedback
allozaur Aug 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .windsurf/rules/css-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
trigger: manual
---

#### Tailwind & CSS

- We are using Tailwind v4 which uses oklch colors so we now want to refer to the CSS vars directly, without wrapping it with any color function like `hsla/hsl`, `rgba` etc.
44 changes: 44 additions & 0 deletions .windsurf/rules/sveltekit-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
trigger: manual
---

# Coding rules

## Svelte & SvelteKit

### Services vs Stores Separation Pattern

#### `lib/services/` - Pure Business Logic

- **Purpose**: Stateless business logic and external communication
- **Contains**:
- Database operations (DatabaseService)
- API calls to external services (ApiService)
- Pure business logic functions (ChatService, etc.)
- **Rules**:
- NO Svelte runes ($state, $derived, $effect)
- NO reactive state management
- Pure functions and classes only
- Can import types but not stores
- Focus on "how" - implementation details

#### `lib/stores/` - Reactive State Management

- **Purpose**: Svelte-specific reactive state with runes
- **Contains**:
- Reactive state classes with $state, $derived, $effect
- UI-focused state management
- Store orchestration logic
- **Rules**:
- USE Svelte runes for reactivity
- Import and use services for business logic
- NO direct database operations
- NO direct API calls (use services)
- Focus on "what" - reactive state for UI

#### Enforcement

- Services should be testable without Svelte
- Stores should leverage Svelte's reactivity system
- Clear separation: services handle data, stores handle state
- Services can be reused across multiple stores
7 changes: 7 additions & 0 deletions .windsurf/rules/typescript-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
trigger: manual
---

## TypeScript

- Add JSDocs for functions
117 changes: 117 additions & 0 deletions scripts/llama-server-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/bin/bash

# llama.cpp WebUI Development Script
# Usage: ./scripts/webui-dev.sh -hf <model> [-b|--build] [-p|--port <port>]

set -e

# Default values
MODEL=""
PORT="8080"
BUILD=false
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"

# Function to display usage
usage() {
echo "Usage: $0 (-hf|-m) <model> [-b|--build] [-p|--port <port>]"
echo ""
echo "Options:"
echo " -b, --build Build llama-server before running"
echo " -h, --help Show this help message"
echo " -hf <model> Hugging Face model to use (required)"
echo " -m <model> Path to local model file (required)"
echo " -p, --port <port> Port to run the server on (default: 8080)"
echo ""
echo "Note: -hf and -m are interchangeable and can accept either HF models or local paths"
echo ""
echo "Examples:"
echo " $0 -hf ggml-org/SmolLM3-3B-GGUF"
echo " $0 -m /path/to/model.gguf"
echo " $0 -hf ggml-org/SmolLM3-3B-GGUF -b -p 8081"
exit 1
}

# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-hf)
MODEL="$2"
shift 2
;;
-b|--build)
BUILD=true
shift
;;
-m)
MODEL="$2"
shift 2
;;
-p|--port)
PORT="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1"
usage
;;
esac
done

if [[ -z "$MODEL" ]]; then
echo "Error: Model (-hf or -m) is required"
usage
fi

if ! [[ "$PORT" =~ ^[0-9]+$ ]]; then
echo "Error: Port must be a number"
exit 1
fi

echo "🚀 Starting llama.cpp WebUI Development Environment"
echo "📁 Project root: $PROJECT_ROOT"
echo "🤖 Model: $MODEL"
echo "🌐 Port: $PORT"
echo "🔨 Build: $BUILD"
echo ""

# Change to project root
cd "$PROJECT_ROOT"

# Build if requested
if [[ "$BUILD" == true ]]; then
echo "🔨 Building llama-server..."
cmake -B build
cmake --build build --config Release -t llama-server
echo "✅ Build completed"
echo "Building webui..."
cd tools/server/webui
npm run build
cd $PROJECT_ROOT
echo ""
fi

# Check if binary exists
if [[ ! -f "./build/bin/llama-server" ]]; then
echo "❌ Error: llama-server binary not found at ./build/bin/llama-server"
echo "💡 Try running with the -b flag to build first"
exit 1
fi

echo "🎯 Starting llama-server..."
echo "📡 Server will be available at: http://localhost:$PORT"
echo "🛑 Press Ctrl+C to stop the server"
echo ""

# Start the server
# Check if model looks like a HuggingFace model or local path
if [[ "$MODEL" == *"/"* && ! "$MODEL" == *"ggml-org/"* && ! "$MODEL" == *"microsoft/"* && ! "$MODEL" == *"meta-llama/"* ]]; then
# Looks like a local path
./build/bin/llama-server --path tools/server/public -m "$MODEL" --port "$PORT"
else
# Treat as HuggingFace model
./build/bin/llama-server --path tools/server/public -hf "$MODEL" --port "$PORT"
fi
Binary file modified tools/server/public/index.html.gz
Binary file not shown.
36 changes: 36 additions & 0 deletions tools/server/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4911,6 +4911,42 @@ int main(int argc, char ** argv) {
svr->Get (params.api_prefix + "/slots", handle_slots);
svr->Post(params.api_prefix + "/slots/:id_slot", handle_slots_action);

// SPA fallback route - serve index.html for any route that doesn't match API endpoints
// This enables client-side routing for dynamic routes like /chat/[id]
if (params.public_path.empty()) {
// Only add fallback when using embedded static files
svr->Get(".*", [](const httplib::Request & req, httplib::Response & res) {
// Skip API routes - they should have been handled above
if (req.path.find("/v1/") != std::string::npos ||
req.path.find("/health") != std::string::npos ||
req.path.find("/metrics") != std::string::npos ||
req.path.find("/props") != std::string::npos ||
req.path.find("/models") != std::string::npos ||
req.path.find("/api/tags") != std::string::npos ||
req.path.find("/completions") != std::string::npos ||
req.path.find("/chat/completions") != std::string::npos ||
req.path.find("/embeddings") != std::string::npos ||
req.path.find("/tokenize") != std::string::npos ||
req.path.find("/detokenize") != std::string::npos ||
req.path.find("/lora-adapters") != std::string::npos ||
req.path.find("/slots") != std::string::npos) {
return false; // Let other handlers process API routes
}

// Serve index.html for all other routes (SPA fallback)
if (req.get_header_value("Accept-Encoding").find("gzip") == std::string::npos) {
res.set_content("Error: gzip is not supported by this browser", "text/plain");
} else {
res.set_header("Content-Encoding", "gzip");
// COEP and COOP headers, required by pyodide (python interpreter)
res.set_header("Cross-Origin-Embedder-Policy", "require-corp");
res.set_header("Cross-Origin-Opener-Policy", "same-origin");
res.set_content(reinterpret_cast<const char*>(index_html_gz), index_html_gz_len, "text/html; charset=utf-8");
}
return false;
});
}

//
// Start the server
//
Expand Down
24 changes: 24 additions & 0 deletions tools/server/webui-old/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
10 changes: 10 additions & 0 deletions tools/server/webui-old/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
**/.vscode
**/.github
**/.git
**/.svn
**/.hg
**/node_modules
**/dist
**/build

*.config.js
26 changes: 26 additions & 0 deletions tools/server/webui-old/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': 'off',
'@typescript-eslint/no-unused-vars': 'off',
},
},
)
File renamed without changes.
Loading