17 Commits

Author SHA1 Message Date
viper151
7d0fd141ff Merge pull request #203 from SyedaAnshrahGillani/fix-accessibility-issues
feat: Improve accessibility and refactor settings management
2025-10-01 22:21:35 +02:00
SyedaAnshrahGillani
e5a05d9865 fix: Address coderabbitai feedback 2025-10-02 01:12:52 +05:00
SyedaAnshrahGillani
2d6c3b5755 refactor: Create useLocalStorage hook to reduce code duplication 2025-10-01 17:16:09 +05:00
SyedaAnshrahGillani
2a5d27ffc0 fix(accessibility): Use buttons for modal backdrops 2025-10-01 17:04:35 +05:00
viper151
3c9a4cab82 Fix: Prevent CLI option injection in --print argument
Fix: Prevent CLI option injection in --print argument
2025-09-23 11:31:51 +02:00
viper151
ce9ab0cd16 Merge branch 'main' into fix-injection 2025-09-23 11:29:43 +02:00
viper151
66fad9a6a2 Update .env.example 2025-09-23 11:29:15 +02:00
viper151
0fcf906ff0 Merge pull request #197 from johnhenry/johnhenry/env-claude-path
Feat: Use environment variable for Claude path
2025-09-23 11:26:44 +02:00
viper151
7e1f2940d3 Merge branch 'main' into johnhenry/env-claude-path 2025-09-23 11:25:56 +02:00
simos
533d589132 Release 1.8.10 2025-09-23 10:54:35 +02:00
simos
d1f310161f fixes on changelog prettify 2025-09-23 10:54:00 +02:00
viper151
3f743d8210 Merge branch 'main' into johnhenry/env-claude-path 2025-09-23 10:48:59 +02:00
John Henry
d74a99ef24 Merge branch 'main' into johnhenry/env-claude-path 2025-09-22 10:53:48 -07:00
John Henry
133af82935 Merge branch 'main' into johnhenry/env-claude-path 2025-09-15 14:30:59 -07:00
viper151
3daf21c3d1 Merge branch 'main' into johnhenry/env-claude-path 2025-09-15 17:40:05 +02:00
John
f52ca8e702 Use environment variable for Claude path 2025-09-14 09:35:58 -07:00
Terrasse
d82a004224 fix prompt injection bug 2025-08-27 18:23:00 +08:00
7 changed files with 78 additions and 49 deletions

View File

@@ -9,4 +9,7 @@
#API server
PORT=3001
#Frontend port
VITE_PORT=5173
VITE_PORT=5173
# Uncomment the following line if you have a custom claude cli path other than the default "claude"
# CLAUDE_CLI_PATH=claude

View File

@@ -11,7 +11,7 @@
"release": true,
"releaseName": "Claude Code UI v${version}",
"releaseNotes": {
"commit": "* ${commit.subject} (${commit.hash}) - thanks @${author.login}!",
"commit": "* ${commit.subject} (${sha}){ - thanks @${author.login}!}",
"excludeMatches": ["viper151"]
}
},

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@siteboon/claude-code-ui",
"version": "1.8.9",
"version": "1.8.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@siteboon/claude-code-ui",
"version": "1.8.9",
"version": "1.8.10",
"license": "MIT",
"dependencies": {
"@codemirror/lang-css": "^6.3.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@siteboon/claude-code-ui",
"version": "1.8.9",
"version": "1.8.10",
"description": "A web-based UI for Claude Code CLI",
"type": "module",
"main": "server/index.js",

View File

@@ -25,15 +25,6 @@ async function spawnClaude(command, options = {}, ws) {
// Build Claude CLI command - start with print/resume flags first
const args = [];
// Add print flag with command if we have a command
if (command && command.trim()) {
// Separate arguments for better cross-platform compatibility
// This prevents issues with spaces and quotes on Windows
args.push('--print');
args.push(command);
}
// Use cwd (actual project directory) instead of projectPath (Claude's metadata directory)
const workingDir = cwd || process.cwd();
@@ -225,6 +216,17 @@ async function spawnClaude(command, options = {}, ws) {
console.log('📝 Skip permissions disabled due to plan mode');
}
}
// Add print flag with command if we have a command
if (command && command.trim()) {
// Separate arguments for better cross-platform compatibility
// This prevents issues with spaces and quotes on Windows
args.push('--print');
// Use `--` so user input is always treated as text, not options
args.push('--');
args.push(command);
}
console.log('Spawning Claude CLI:', 'claude', args.map(arg => {
const cleanArg = arg.replace(/\n/g, '\\n').replace(/\r/g, '\\r');
@@ -235,7 +237,11 @@ async function spawnClaude(command, options = {}, ws) {
console.log('🔍 Full command args:', JSON.stringify(args, null, 2));
console.log('🔍 Final Claude command will be: claude ' + args.join(' '));
const claudeProcess = spawnFunction('claude', args, {
// Use Claude CLI from environment variable or default to 'claude'
const claudePath = process.env.CLAUDE_CLI_PATH || 'claude';
console.log('🔍 Using Claude CLI path:', claudePath);
const claudeProcess = spawnFunction(claudePath, args, {
cwd: workingDir,
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env } // Inherit all environment variables

View File

@@ -33,6 +33,7 @@ import { TasksSettingsProvider } from './contexts/TasksSettingsContext';
import { WebSocketProvider, useWebSocketContext } from './contexts/WebSocketContext';
import ProtectedRoute from './components/ProtectedRoute';
import { useVersionCheck } from './hooks/useVersionCheck';
import useLocalStorage from './hooks/useLocalStorage';
import { api, authenticatedFetch } from './utils/api';
@@ -54,22 +55,10 @@ function AppContent() {
const [isInputFocused, setIsInputFocused] = useState(false);
const [showSettings, setShowSettings] = useState(false);
const [showQuickSettings, setShowQuickSettings] = useState(false);
const [autoExpandTools, setAutoExpandTools] = useState(() => {
const saved = localStorage.getItem('autoExpandTools');
return saved !== null ? JSON.parse(saved) : false;
});
const [showRawParameters, setShowRawParameters] = useState(() => {
const saved = localStorage.getItem('showRawParameters');
return saved !== null ? JSON.parse(saved) : false;
});
const [autoScrollToBottom, setAutoScrollToBottom] = useState(() => {
const saved = localStorage.getItem('autoScrollToBottom');
return saved !== null ? JSON.parse(saved) : true;
});
const [sendByCtrlEnter, setSendByCtrlEnter] = useState(() => {
const saved = localStorage.getItem('sendByCtrlEnter');
return saved !== null ? JSON.parse(saved) : false;
});
const [autoExpandTools, setAutoExpandTools] = useLocalStorage('autoExpandTools', false);
const [showRawParameters, setShowRawParameters] = useLocalStorage('showRawParameters', false);
const [autoScrollToBottom, setAutoScrollToBottom] = useLocalStorage('autoScrollToBottom', true);
const [sendByCtrlEnter, setSendByCtrlEnter] = useLocalStorage('sendByCtrlEnter', false);
// Session Protection System: Track sessions with active conversations to prevent
// automatic project updates from interrupting ongoing chats. When a user sends
// a message, the session is marked as "active" and project updates are paused
@@ -491,9 +480,10 @@ function AppContent() {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div
<button
className="fixed inset-0 bg-black/50 backdrop-blur-sm"
onClick={() => setShowVersionModal(false)}
aria-label="Close version upgrade modal"
/>
{/* Modal */}
@@ -604,7 +594,7 @@ function AppContent() {
<div className={`fixed inset-0 z-50 flex transition-all duration-150 ease-out ${
sidebarOpen ? 'opacity-100 visible' : 'opacity-0 invisible'
}`}>
<div
<button
className="fixed inset-0 bg-background/80 backdrop-blur-sm transition-opacity duration-150 ease-out"
onClick={(e) => {
e.stopPropagation();
@@ -615,6 +605,7 @@ function AppContent() {
e.stopPropagation();
setSidebarOpen(false);
}}
aria-label="Close sidebar"
/>
<div
className={`relative w-[85vw] max-w-sm sm:w-80 bg-card border-r border-border transform transition-transform duration-150 ease-out ${
@@ -688,25 +679,13 @@ function AppContent() {
isOpen={showQuickSettings}
onToggle={setShowQuickSettings}
autoExpandTools={autoExpandTools}
onAutoExpandChange={(value) => {
setAutoExpandTools(value);
localStorage.setItem('autoExpandTools', JSON.stringify(value));
}}
onAutoExpandChange={setAutoExpandTools}
showRawParameters={showRawParameters}
onShowRawParametersChange={(value) => {
setShowRawParameters(value);
localStorage.setItem('showRawParameters', JSON.stringify(value));
}}
onShowRawParametersChange={setShowRawParameters}
autoScrollToBottom={autoScrollToBottom}
onAutoScrollChange={(value) => {
setAutoScrollToBottom(value);
localStorage.setItem('autoScrollToBottom', JSON.stringify(value));
}}
onAutoScrollChange={setAutoScrollToBottom}
sendByCtrlEnter={sendByCtrlEnter}
onSendByCtrlEnterChange={(value) => {
setSendByCtrlEnter(value);
localStorage.setItem('sendByCtrlEnter', JSON.stringify(value));
}}
onSendByCtrlEnterChange={setSendByCtrlEnter}
isMobile={isMobile}
/>
)}

View File

@@ -0,0 +1,41 @@
import { useState } from 'react';
/**
* Custom hook to persist state in localStorage.
*
* @param {string} key The key to use for localStorage.
* @param {any} initialValue The initial value to use if nothing is in localStorage.
* @returns {[any, Function]} A tuple containing the stored value and a setter function.
*/
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
if (typeof window === 'undefined') {
return initialValue;
}
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
const setValue = (value) => {
if (typeof window === 'undefined') {
return;
}
try {
const valueToStore =
value instanceof Function ? value(storedValue) : value;
window.localStorage.setItem(key, JSON.stringify(valueToStore));
setStoredValue(valueToStore);
} catch (error) {
console.log(error);
}
};
return [storedValue, setValue];
}
export default useLocalStorage;