Compare commits

..

5 Commits

Author SHA1 Message Date
simos
9079326ac5 feat: Inform users about slash commands on chat interface 2025-10-30 14:37:09 +00:00
Andrew Garrett
7a087039c9 Make authentication database path configurable via DATABASE_PATH environment variable (#205)
* Make database path configurable via DATABASE_PATH environment variable

- Add DATABASE_PATH environment variable support in db.js
- Automatically create database directory if custom path is provided
- Update .env.example with DATABASE_PATH documentation for container deployments
- Maintain backward compatibility with default path (server/database/auth.db)

Co-authored-by: werdnum <271070+werdnum@users.noreply.github.com>

* Add error handling for creating DATABASE_PATH.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: werdnum <271070+werdnum@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: viper151 <simosmik@gmail.com>
2025-10-30 15:28:59 +01:00
viper151
de1f5d36f3 Update README.md 2025-10-30 15:20:51 +01:00
simos
d9eef6dcfe Release 1.9.1 2025-10-30 15:19:14 +01:00
simos
c9afc2e851 fix: resolve NPX redirect issue and improve startup documentation 2025-10-30 15:18:44 +01:00
7 changed files with 94 additions and 25 deletions

View File

@@ -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

View File

@@ -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
View File

@@ -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",

View File

@@ -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",

View File

@@ -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 () => {

View File

@@ -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}`);

View File

@@ -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>