mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-01-23 18:07:34 +00:00
Compare commits
5 Commits
v1.9.0
...
9079326ac5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9079326ac5 | ||
|
|
7a087039c9 | ||
|
|
de1f5d36f3 | ||
|
|
d9eef6dcfe | ||
|
|
c9afc2e851 |
@@ -14,6 +14,15 @@ VITE_PORT=5173
|
||||
# Uncomment the following line if you have a custom claude cli path other than the default "claude"
|
||||
# CLAUDE_CLI_PATH=claude
|
||||
|
||||
# =============================================================================
|
||||
# DATABASE CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Path to the authentication database file
|
||||
# This should be set to a persistent volume path when running in containers
|
||||
# Default: server/database/auth.db (relative to project root)
|
||||
# Example for Docker: /data/auth.db
|
||||
# DATABASE_PATH=/data/auth.db
|
||||
# Claude Code context window size (maximum tokens per session)
|
||||
# Note: VITE_ prefix makes it available to frontend
|
||||
VITE_CONTEXT_WINDOW=160000
|
||||
|
||||
46
README.md
46
README.md
@@ -67,7 +67,49 @@ No installation required, direct operation:
|
||||
npx @siteboon/claude-code-ui
|
||||
```
|
||||
|
||||
Your default browser will automatically open the Claude Code UI interface.
|
||||
The server will start and be accessible at `http://localhost:3001` (or your configured PORT).
|
||||
|
||||
**To restart**: Simply run the same `npx` command again after stopping the server (Ctrl+C or Cmd+C).
|
||||
|
||||
### Global Installation (For Regular Use)
|
||||
|
||||
For frequent use, install globally once:
|
||||
|
||||
```bash
|
||||
npm install -g @siteboon/claude-code-ui
|
||||
```
|
||||
|
||||
Then start with a simple command:
|
||||
|
||||
```bash
|
||||
claude-code-ui
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Faster startup (no download/cache check)
|
||||
- Simple command to remember
|
||||
- Same experience every time
|
||||
|
||||
**To restart**: Stop with Ctrl+C and run `claude-code-ui` again.
|
||||
|
||||
### Run as Background Service (Optional)
|
||||
|
||||
To keep the server running in the background, use PM2:
|
||||
|
||||
```bash
|
||||
# Install PM2 globally (one-time)
|
||||
npm install -g pm2
|
||||
|
||||
# Start the server
|
||||
pm2 start claude-code-ui --name "claude-ui"
|
||||
|
||||
# Manage the service
|
||||
pm2 list # View status
|
||||
pm2 restart claude-ui # Restart
|
||||
pm2 stop claude-ui # Stop
|
||||
pm2 logs claude-ui # View logs
|
||||
pm2 startup # Auto-start on system boot
|
||||
```
|
||||
|
||||
### Local Development Installation
|
||||
|
||||
@@ -236,13 +278,13 @@ We welcome contributions! Please follow these guidelines:
|
||||
|
||||
### Common Issues & Solutions
|
||||
|
||||
|
||||
#### "No Claude projects found"
|
||||
**Problem**: The UI shows no projects or empty project list
|
||||
**Solutions**:
|
||||
- Ensure [Claude CLI](https://docs.anthropic.com/en/docs/claude-code) is properly installed
|
||||
- Run `claude` command in at least one project directory to initialize
|
||||
- Verify `~/.claude/projects/` directory exists and has proper permissions
|
||||
d
|
||||
|
||||
#### File Explorer Issues
|
||||
**Problem**: Files not loading, permission errors, empty directories
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@siteboon/claude-code-ui",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@siteboon/claude-code-ui",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.29",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@siteboon/claude-code-ui",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.1",
|
||||
"description": "A web-based UI for Claude Code CLI",
|
||||
"type": "module",
|
||||
"main": "server/index.js",
|
||||
|
||||
@@ -7,12 +7,27 @@ import { dirname } from 'path';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const DB_PATH = path.join(__dirname, 'auth.db');
|
||||
// Use DATABASE_PATH environment variable if set, otherwise use default location
|
||||
const DB_PATH = process.env.DATABASE_PATH || path.join(__dirname, 'auth.db');
|
||||
const INIT_SQL_PATH = path.join(__dirname, 'init.sql');
|
||||
|
||||
// Ensure database directory exists if custom path is provided
|
||||
if (process.env.DATABASE_PATH) {
|
||||
const dbDir = path.dirname(DB_PATH);
|
||||
try {
|
||||
if (!fs.existsSync(dbDir)) {
|
||||
fs.mkdirSync(dbDir, { recursive: true });
|
||||
console.log(`Created database directory: ${dbDir}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to create database directory ${dbDir}:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Create database connection
|
||||
const db = new Database(DB_PATH);
|
||||
console.log('Connected to SQLite database');
|
||||
console.log(`Connected to SQLite database at: ${DB_PATH}`);
|
||||
|
||||
// Initialize database with schema
|
||||
const initializeDatabase = async () => {
|
||||
|
||||
@@ -1234,14 +1234,17 @@ app.get('*', (req, res) => {
|
||||
|
||||
// Only serve index.html for HTML routes, not for static assets
|
||||
// Static assets should already be handled by express.static middleware above
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const indexPath = path.join(__dirname, '../dist/index.html');
|
||||
|
||||
// Check if dist/index.html exists (production build available)
|
||||
if (fs.existsSync(indexPath)) {
|
||||
// Set no-cache headers for HTML to prevent service worker issues
|
||||
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
||||
res.setHeader('Pragma', 'no-cache');
|
||||
res.setHeader('Expires', '0');
|
||||
res.sendFile(path.join(__dirname, '../dist/index.html'));
|
||||
res.sendFile(indexPath);
|
||||
} else {
|
||||
// In development, redirect to Vite dev server
|
||||
// In development, redirect to Vite dev server only if dist doesn't exist
|
||||
res.redirect(`http://localhost:${process.env.VITE_PORT || 5173}`);
|
||||
}
|
||||
});
|
||||
@@ -1336,8 +1339,17 @@ async function startServer() {
|
||||
await initializeDatabase();
|
||||
console.log('✅ Database initialization skipped (testing)');
|
||||
|
||||
// Check if running in production mode (dist folder exists)
|
||||
const distIndexPath = path.join(__dirname, '../dist/index.html');
|
||||
const isProduction = fs.existsSync(distIndexPath);
|
||||
|
||||
// Log Claude implementation mode
|
||||
console.log('🚀 Using Claude Agents SDK for Claude integration');
|
||||
console.log(`📦 Running in ${isProduction ? 'PRODUCTION' : 'DEVELOPMENT'} mode`);
|
||||
|
||||
if (!isProduction) {
|
||||
console.log(`⚠️ Note: Requests will be proxied to Vite dev server at http://localhost:${process.env.VITE_PORT || 5173}`);
|
||||
}
|
||||
|
||||
server.listen(PORT, '0.0.0.0', async () => {
|
||||
console.log(`Claude Code UI server running on http://0.0.0.0:${PORT}`);
|
||||
|
||||
@@ -4228,20 +4228,11 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
|
||||
const isExpanded = e.target.scrollHeight > lineHeight * 2;
|
||||
setIsTextareaExpanded(isExpanded);
|
||||
}}
|
||||
placeholder={`Ask ${provider === 'cursor' ? 'Cursor' : 'Claude'} to help with your code...`}
|
||||
placeholder={`Type / for commands, @ for files, or ask ${provider === 'cursor' ? 'Cursor' : 'Claude'} anything...`}
|
||||
disabled={isLoading}
|
||||
className="chat-input-placeholder block w-full pl-12 pr-20 sm:pr-40 py-1.5 sm:py-4 bg-transparent rounded-2xl focus:outline-none text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 disabled:opacity-50 resize-none min-h-[50px] sm:min-h-[80px] max-h-[40vh] sm:max-h-[300px] overflow-y-auto text-sm sm:text-base leading-[21px] sm:leading-6 transition-all duration-200"
|
||||
style={{ height: '50px' }}
|
||||
/>
|
||||
{/* Custom placeholder overlay that can wrap */}
|
||||
{!input.trim() && !isInputFocused && (
|
||||
<div
|
||||
className="absolute inset-0 pl-12 pr-20 sm:pr-40 py-1.5 sm:py-4 pointer-events-none text-gray-400 dark:text-gray-500 text-sm sm:text-base"
|
||||
style={{ whiteSpace: 'normal', wordWrap: 'break-word' }}
|
||||
>
|
||||
Type / for commands, @ for files, or ask {provider === 'cursor' ? 'Cursor' : 'Claude'} anything...
|
||||
</div>
|
||||
)}
|
||||
{/* Image upload button */}
|
||||
<button
|
||||
type="button"
|
||||
@@ -4339,15 +4330,15 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
|
||||
{/* Hint text */}
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 text-center mt-2 hidden sm:block">
|
||||
{sendByCtrlEnter
|
||||
? "Ctrl+Enter to send (IME safe) • Shift+Enter for new line • Tab to change modes"
|
||||
: "Press Enter to send • Shift+Enter for new line • Tab to change modes"}
|
||||
? "Ctrl+Enter to send (IME safe) • Shift+Enter for new line • Tab to change modes • Type / for slash commands"
|
||||
: "Press Enter to send • Shift+Enter for new line • Tab to change modes • Type / for slash commands"}
|
||||
</div>
|
||||
<div className={`text-xs text-gray-500 dark:text-gray-400 text-center mt-2 sm:hidden transition-opacity duration-200 ${
|
||||
isInputFocused ? 'opacity-100' : 'opacity-0'
|
||||
}`}>
|
||||
{sendByCtrlEnter
|
||||
? "Ctrl+Enter to send (IME safe) • Tab for modes • @ for files"
|
||||
: "Enter to send • Tab for modes • @ for files"}
|
||||
{sendByCtrlEnter
|
||||
? "Ctrl+Enter to send (IME safe) • Tab for modes • @ for files • / for commands"
|
||||
: "Enter to send • Tab for modes • @ for files • / for commands"}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user