diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx
index fe404d1..358f161 100644
--- a/frontend/src/components/Sidebar.jsx
+++ b/frontend/src/components/Sidebar.jsx
@@ -44,6 +44,8 @@ export function Sidebar({ open, onToggle }) {
stopClaudeSession,
clearMessages,
updateSessionConfig,
+ settings,
+ updateSettings,
} = useSessionManager();
const { user, authEnabled, logout, isAdmin } = useAuth();
@@ -102,6 +104,22 @@ export function Sidebar({ open, onToggle }) {
updateSessionConfig(focusedSessionId, { resumeOnStart: !resumeSession });
}, [focusedSessionId, resumeSession, updateSessionConfig]);
+ // Handle autoConnect toggle
+ const handleToggleAutoConnect = useCallback(() => {
+ const newAutoConnect = !settings?.autoConnect;
+ // If disabling autoConnect, also disable autoStart
+ if (!newAutoConnect) {
+ updateSettings({ autoConnect: false, autoStart: false });
+ } else {
+ updateSettings({ autoConnect: true });
+ }
+ }, [settings?.autoConnect, updateSettings]);
+
+ // Handle autoStart toggle
+ const handleToggleAutoStart = useCallback(() => {
+ updateSettings({ autoStart: !settings?.autoStart });
+ }, [settings?.autoStart, updateSettings]);
+
// Browse directories on host
const browsePath = useCallback(async (path) => {
if (!currentHost) return;
@@ -320,7 +338,7 @@ export function Sidebar({ open, onToggle }) {
)}
{/* Resume toggle */}
-
+
diff --git a/frontend/src/contexts/SessionContext.jsx b/frontend/src/contexts/SessionContext.jsx
index 6c44dea..17e296d 100644
--- a/frontend/src/contexts/SessionContext.jsx
+++ b/frontend/src/contexts/SessionContext.jsx
@@ -8,8 +8,28 @@ function getWsUrl() {
}
const SESSIONS_STORAGE_KEY = 'claude-webui-sessions';
+const SETTINGS_STORAGE_KEY = 'claude-webui-settings';
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
+// Load global settings from localStorage
+function loadSettings() {
+ try {
+ const stored = localStorage.getItem(SETTINGS_STORAGE_KEY);
+ return stored ? JSON.parse(stored) : { autoConnect: true, autoStart: false };
+ } catch {
+ return { autoConnect: true, autoStart: false };
+ }
+}
+
+// Save global settings to localStorage
+function saveSettings(settings) {
+ try {
+ localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settings));
+ } catch (e) {
+ console.error('Failed to save settings:', e);
+ }
+}
+
// Generate unique session ID
function generateSessionId() {
return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
@@ -64,6 +84,9 @@ export function SessionProvider({ children }) {
// Tab order (array of session IDs)
const [tabOrder, setTabOrder] = useState([]);
+ // Global settings (autoConnect, etc.)
+ const [settings, setSettings] = useState(() => loadSettings());
+
// WebSocket refs keyed by session ID
const wsRefs = useRef({});
@@ -74,9 +97,14 @@ export function SessionProvider({ children }) {
const sessionsRef = useRef(sessions);
sessionsRef.current = sessions;
+ // Ref to current settings (for stable callbacks)
+ const settingsRef = useRef(settings);
+ settingsRef.current = settings;
+
// Track if initial load is done (for auto-connecting restored sessions)
const initialLoadDone = useRef(false);
const sessionsToConnect = useRef([]);
+ const sessionsToAutoStart = useRef(new Set()); // Sessions that should auto-start after connecting
// Load sessions from localStorage on mount
useEffect(() => {
@@ -690,16 +718,73 @@ export function SessionProvider({ children }) {
};
}, [updateSession, handleWsMessage]);
- // Auto-connect restored sessions
+ // Auto-connect restored sessions (only if autoConnect is enabled)
useEffect(() => {
- if (sessionsToConnect.current.length > 0) {
+ if (sessionsToConnect.current.length > 0 && settings.autoConnect) {
const toConnect = [...sessionsToConnect.current];
sessionsToConnect.current = [];
+
+ // Mark sessions for auto-start if autoStart is also enabled
+ if (settings.autoStart) {
+ toConnect.forEach(sessionId => {
+ sessionsToAutoStart.current.add(sessionId);
+ });
+ }
+
toConnect.forEach(sessionId => {
connectSession(sessionId);
});
}
- }, [connectSession]);
+ }, [connectSession, settings.autoConnect, settings.autoStart]);
+
+ // Auto-start sessions after they connect (if marked for auto-start)
+ useEffect(() => {
+ if (sessionsToAutoStart.current.size === 0) return;
+
+ // Check each session marked for auto-start
+ sessionsToAutoStart.current.forEach(sessionId => {
+ const session = sessions[sessionId];
+ // If session is connected but not yet active, start it
+ if (session?.connected && !session?.active) {
+ console.log(`[${sessionId}] Auto-starting session`);
+ sessionsToAutoStart.current.delete(sessionId);
+
+ // Use the websocket directly to start the session
+ const ws = wsRefs.current[sessionId];
+ if (ws?.readyState === WebSocket.OPEN) {
+ // Load history first if resumeOnStart is enabled
+ const startSession = async () => {
+ if (session.resumeOnStart) {
+ try {
+ const res = await fetch(
+ `/api/history/${encodeURIComponent(session.project)}?host=${session.host}`,
+ { credentials: 'include' }
+ );
+ const data = await res.json();
+ if (data.messages && Array.isArray(data.messages)) {
+ setSessionMessages(prev => ({
+ ...prev,
+ [sessionId]: data.messages,
+ }));
+ }
+ } catch (e) {
+ console.error('Failed to load history:', e);
+ }
+ }
+
+ ws.send(JSON.stringify({
+ type: 'start_session',
+ project: session.project,
+ resume: session.resumeOnStart,
+ host: session.host,
+ }));
+ };
+
+ startSession();
+ }
+ }
+ });
+ }, [sessions]);
// Disconnect WebSocket for a session
const disconnectSession = useCallback((sessionId) => {
@@ -955,6 +1040,15 @@ export function SessionProvider({ children }) {
updateSession(sessionId, config);
}, [updateSession]);
+ // Update global settings (autoConnect, etc.)
+ const updateSettings = useCallback((newSettings) => {
+ setSettings(prev => {
+ const updated = { ...prev, ...newSettings };
+ saveSettings(updated);
+ return updated;
+ });
+ }, []);
+
// Memoize focused session to prevent unnecessary re-renders
const focusedSession = useMemo(() => {
return focusedSessionId ? sessions[focusedSessionId] : null;
@@ -969,6 +1063,7 @@ export function SessionProvider({ children }) {
splitSessions,
focusedSessionId,
focusedSession,
+ settings,
// Session management
createSession,
@@ -976,6 +1071,7 @@ export function SessionProvider({ children }) {
removeSession,
renameSession,
updateSessionConfig,
+ updateSettings,
// Focus & view
setFocusedSessionId,
@@ -1001,8 +1097,8 @@ export function SessionProvider({ children }) {
changePermissionMode,
respondToPermission,
}), [
- sessions, sessionMessages, tabOrder, splitSessions, focusedSessionId, focusedSession,
- createSession, closeSession, removeSession, renameSession, updateSessionConfig,
+ sessions, sessionMessages, tabOrder, splitSessions, focusedSessionId, focusedSession, settings,
+ createSession, closeSession, removeSession, renameSession, updateSessionConfig, updateSettings,
setFocusedSessionId, markAsRead, reorderTabs, addToSplit, removeFromSplit, clearSplit,
connectSession, disconnectSession, startClaudeSession, stopClaudeSession,
sendMessage, stopGeneration, clearMessages, setCompacting, changePermissionMode, respondToPermission,