From 1f6c0c38996334cd2f670a4c17c9484bb4e0ed27 Mon Sep 17 00:00:00 2001 From: Valics Lehel Date: Fri, 11 Jul 2025 22:16:57 +0300 Subject: [PATCH 1/3] feat: Add thinking mode selector to chat interface - Added ThinkingModeSelector component with 5 thinking modes - Integrated selector into chat header next to permission mode - Automatically prefixes messages with thinking instructions - Resets to default mode after sending message --- src/components/ChatInterface.jsx | 17 +++ src/components/ThinkingModeSelector.jsx | 164 ++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 src/components/ThinkingModeSelector.jsx diff --git a/src/components/ChatInterface.jsx b/src/components/ChatInterface.jsx index a9d5110..f0a26cf 100644 --- a/src/components/ChatInterface.jsx +++ b/src/components/ChatInterface.jsx @@ -33,6 +33,7 @@ import ClaudeStatus from './ClaudeStatus'; import TokenUsagePie from './TokenUsagePie'; import { MicButton } from './MicButton.jsx'; import { api, authenticatedFetch } from '../utils/api'; +import ThinkingModeSelector, { thinkingModes } from './ThinkingModeSelector.jsx'; import Fuse from 'fuse.js'; import CommandMenu from './CommandMenu'; import { CLAUDE_MODELS, CURSOR_MODELS, CODEX_MODELS } from '../../shared/modelConstants'; @@ -1884,6 +1885,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess const [slashPosition, setSlashPosition] = useState(-1); const [visibleMessageCount, setVisibleMessageCount] = useState(100); const [claudeStatus, setClaudeStatus] = useState(null); + const [thinkingMode, setThinkingMode] = useState('none'); const [provider, setProvider] = useState(() => { return localStorage.getItem('selected-provider') || 'claude'; }); @@ -4226,6 +4228,13 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess e.preventDefault(); if (!input.trim() || isLoading || !selectedProject) return; + // Apply thinking mode prefix if selected + let messageContent = input; + const selectedThinkingMode = thinkingModes.find(mode => mode.id === thinkingMode); + if (selectedThinkingMode && selectedThinkingMode.prefix) { + messageContent = `${selectedThinkingMode.prefix}: ${input}`; + } + // Upload images first if any let uploadedImages = []; if (attachedImages.length > 0) { @@ -4365,6 +4374,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess setUploadingImages(new Map()); setImageErrors(new Map()); setIsTextareaExpanded(false); + setThinkingMode('none'); // Reset thinking mode after sending // Reset textarea height if (textareaRef.current) { @@ -5180,6 +5190,13 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess + + {/* Thinking Mode Selector */} + {/* Token usage pie chart - positioned next to mode indicator */} { + const handleClickOutside = (event) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setIsOpen(false); + if (onClose) onClose(); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [onClose]); + + const currentMode = thinkingModes.find(mode => mode.id === selectedMode) || thinkingModes[0]; + const IconComponent = currentMode.icon || Brain; + + return ( +
+ + + {isOpen && ( +
+
+
+

+ Thinking Mode +

+ +
+

+ Extended thinking gives Claude more time to evaluate alternatives +

+
+ +
+ {thinkingModes.map((mode) => { + const ModeIcon = mode.icon; + const isSelected = mode.id === selectedMode; + + return ( + + ); + })} +
+ +
+

+ Tip: Higher thinking modes take more time but provide more thorough analysis +

+
+
+ )} +
+ ); +} + +export default ThinkingModeSelector; +export { thinkingModes }; \ No newline at end of file From e73960ae783b9f087bef3ae5d8992a6d9eb09d10 Mon Sep 17 00:00:00 2001 From: Haileyesus Dessie <118998054+blackmammoth@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:08:30 +0300 Subject: [PATCH 2/3] feat: Conditionally render Thinking Mode Selector for Claude provider --- src/components/ChatInterface.jsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/ChatInterface.jsx b/src/components/ChatInterface.jsx index f0a26cf..3d2ee24 100644 --- a/src/components/ChatInterface.jsx +++ b/src/components/ChatInterface.jsx @@ -5191,12 +5191,16 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess - {/* Thinking Mode Selector */} - + {/* Thinking Mode Selector */} + { + provider === 'claude' && ( + + + )} {/* Token usage pie chart - positioned next to mode indicator */} Date: Sun, 18 Jan 2026 06:05:34 +0100 Subject: [PATCH 3/3] feat: add folder browser to ProjectCreationWizard - Add folder browser modal to navigate and select project folders - Sort folders alphabetically (case-insensitive) - Add toggle to show/hide hidden folders (hidden by default) - Auto-advance to confirmation step when selecting a folder - Place "Use this folder" button next to Cancel --- server/index.js | 8 +- src/components/ProjectCreationWizard.jsx | 210 ++++++++++++++++++++--- 2 files changed, 193 insertions(+), 25 deletions(-) diff --git a/server/index.js b/server/index.js index 25b25fd..44ebb95 100755 --- a/server/index.js +++ b/server/index.js @@ -496,7 +496,13 @@ app.get('/api/browse-filesystem', authenticateToken, async (req, res) => { name: item.name, type: 'directory' })) - .slice(0, 20); // Limit results + .sort((a, b) => { + const aHidden = a.name.startsWith('.'); + const bHidden = b.name.startsWith('.'); + if (aHidden && !bHidden) return 1; + if (!aHidden && bHidden) return -1; + return a.name.localeCompare(b.name); + }); // Add common directories if browsing home directory const suggestions = []; diff --git a/src/components/ProjectCreationWizard.jsx b/src/components/ProjectCreationWizard.jsx index 38564dc..24bda03 100644 --- a/src/components/ProjectCreationWizard.jsx +++ b/src/components/ProjectCreationWizard.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { X, FolderPlus, GitBranch, Key, ChevronRight, ChevronLeft, Check, Loader2, AlertCircle } from 'lucide-react'; +import { X, FolderPlus, GitBranch, Key, ChevronRight, ChevronLeft, Check, Loader2, AlertCircle, FolderOpen, Eye, EyeOff } from 'lucide-react'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { api } from '../utils/api'; @@ -7,7 +7,7 @@ import { api } from '../utils/api'; const ProjectCreationWizard = ({ onClose, onProjectCreated }) => { // Wizard state const [step, setStep] = useState(1); // 1: Choose type, 2: Configure, 3: Confirm - const [workspaceType, setWorkspaceType] = useState(null); // 'existing' or 'new' + const [workspaceType, setWorkspaceType] = useState('existing'); // 'existing' or 'new' - default to 'existing' // Form state const [workspacePath, setWorkspacePath] = useState(''); @@ -23,6 +23,11 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => { const [loadingTokens, setLoadingTokens] = useState(false); const [pathSuggestions, setPathSuggestions] = useState([]); const [showPathDropdown, setShowPathDropdown] = useState(false); + const [showFolderBrowser, setShowFolderBrowser] = useState(false); + const [browserCurrentPath, setBrowserCurrentPath] = useState('~'); + const [browserFolders, setBrowserFolders] = useState([]); + const [loadingFolders, setLoadingFolders] = useState(false); + const [showHiddenFolders, setShowHiddenFolders] = useState(false); // Load available GitHub tokens when needed useEffect(() => { @@ -155,6 +160,37 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => { setShowPathDropdown(false); }; + const openFolderBrowser = async () => { + setShowFolderBrowser(true); + await loadBrowserFolders('~'); + }; + + const loadBrowserFolders = async (path) => { + try { + setLoadingFolders(true); + setBrowserCurrentPath(path); + const response = await api.browseFilesystem(path); + const data = await response.json(); + setBrowserFolders(data.suggestions || []); + } catch (error) { + console.error('Error loading folders:', error); + } finally { + setLoadingFolders(false); + } + }; + + const selectFolder = (folderPath, advanceToConfirm = false) => { + setWorkspacePath(folderPath); + setShowFolderBrowser(false); + if (advanceToConfirm) { + setStep(3); + } + }; + + const navigateToFolder = async (folderPath) => { + await loadBrowserFolders(folderPath); + }; + return (
@@ -290,28 +326,39 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => { -
- setWorkspacePath(e.target.value)} - placeholder={workspaceType === 'existing' ? '/path/to/existing/workspace' : '/path/to/new/workspace'} - className="w-full" - /> - {showPathDropdown && pathSuggestions.length > 0 && ( -
- {pathSuggestions.map((suggestion, index) => ( - - ))} -
- )} +
+
+ setWorkspacePath(e.target.value)} + placeholder={workspaceType === 'existing' ? '/path/to/existing/workspace' : '/path/to/new/workspace'} + className="w-full" + /> + {showPathDropdown && pathSuggestions.length > 0 && ( +
+ {pathSuggestions.map((suggestion, index) => ( + + ))} +
+ )} +
+

{workspaceType === 'existing' @@ -563,6 +610,121 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {

+ + {/* Folder Browser Modal */} + {showFolderBrowser && ( +
+
+ {/* Browser Header */} +
+
+
+ +
+

+ Select Folder +

+
+
+ + +
+
+ + {/* Folder List */} +
+ {loadingFolders ? ( +
+ +
+ ) : browserFolders.length === 0 ? ( +
+ No folders found +
+ ) : ( +
+ {/* Parent Directory */} + {browserCurrentPath !== '~' && browserCurrentPath !== '/' && ( + + )} + + {/* Folders */} + {browserFolders + .filter(folder => showHiddenFolders || !folder.name.startsWith('.')) + .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())) + .map((folder, index) => ( +
+ + +
+ ))} +
+ )} +
+ + {/* Browser Footer with Current Path */} +
+
+ Path: + + {browserCurrentPath} + +
+
+ + +
+
+
+
+ )}
); };