mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-11 12:29:37 +00:00
Compare commits
3 Commits
289c2334e0
...
69b7b59f00
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69b7b59f00 | ||
|
|
51431832d8 | ||
|
|
1e50cfdad6 |
@@ -744,7 +744,7 @@ function AppContent() {
|
|||||||
{/* Fixed Desktop Sidebar */}
|
{/* Fixed Desktop Sidebar */}
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<div
|
<div
|
||||||
className={`flex-shrink-0 border-r border-border bg-card transition-all duration-300 ${
|
className={`h-full flex-shrink-0 border-r border-border bg-card transition-all duration-300 ${
|
||||||
sidebarVisible ? 'w-80' : 'w-14'
|
sidebarVisible ? 'w-80' : 'w-14'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -838,10 +838,9 @@ function AppContent() {
|
|||||||
aria-label="Close sidebar"
|
aria-label="Close sidebar"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={`relative w-[85vw] max-w-sm sm:w-80 bg-card border-r border-border transform transition-transform duration-150 ease-out ${
|
className={`relative w-[85vw] max-w-sm sm:w-80 h-full bg-card border-r border-border transform transition-transform duration-150 ease-out ${
|
||||||
sidebarOpen ? 'translate-x-0' : '-translate-x-full'
|
sidebarOpen ? 'translate-x-0' : '-translate-x-full'
|
||||||
}`}
|
}`}
|
||||||
style={{ height: 'calc(100vh - 80px)' }}
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
onTouchStart={(e) => e.stopPropagation()}
|
onTouchStart={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
@@ -871,7 +870,7 @@ function AppContent() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Main Content Area - Flexible */}
|
{/* Main Content Area - Flexible */}
|
||||||
<div className={`flex-1 flex flex-col min-w-0 ${isMobile && !isInputFocused ? 'pb-16' : ''}`}>
|
<div className={`flex-1 flex flex-col min-w-0 ${isMobile && !isInputFocused ? 'pb-mobile-nav' : ''}`}>
|
||||||
<MainContent
|
<MainContent
|
||||||
selectedProject={selectedProject}
|
selectedProject={selectedProject}
|
||||||
selectedSession={selectedSession}
|
selectedSession={selectedSession}
|
||||||
|
|||||||
@@ -4743,7 +4743,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Hint text inside input box at bottom */}
|
{/* Hint text inside input box at bottom - Desktop only */}
|
||||||
<div className={`absolute bottom-1 left-12 right-14 sm:right-40 text-xs text-gray-400 dark:text-gray-500 pointer-events-none hidden sm:block transition-opacity duration-200 ${
|
<div className={`absolute bottom-1 left-12 right-14 sm:right-40 text-xs text-gray-400 dark:text-gray-500 pointer-events-none hidden sm:block transition-opacity duration-200 ${
|
||||||
input.trim() ? 'opacity-0' : 'opacity-100'
|
input.trim() ? 'opacity-0' : 'opacity-100'
|
||||||
}`}>
|
}`}>
|
||||||
@@ -4751,13 +4751,6 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
|
|||||||
? "Ctrl+Enter to send • Shift+Enter for new line • Tab to change modes • / for slash commands"
|
? "Ctrl+Enter to send • Shift+Enter for new line • Tab to change modes • / for slash commands"
|
||||||
: "Enter to send • Shift+Enter for new line • Tab to change modes • / for slash commands"}
|
: "Enter to send • Shift+Enter for new line • Tab to change modes • / for slash commands"}
|
||||||
</div>
|
</div>
|
||||||
<div className={`absolute bottom-1 left-12 right-14 text-xs text-gray-400 dark:text-gray-500 pointer-events-none sm:hidden transition-opacity duration-200 ${
|
|
||||||
isInputFocused && !input.trim() ? 'opacity-100' : 'opacity-0'
|
|
||||||
}`}>
|
|
||||||
{sendByCtrlEnter
|
|
||||||
? "Ctrl+Enter to send • Tab for modes • / for commands"
|
|
||||||
: "Enter to send • Tab for modes • / for commands"}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -57,36 +57,31 @@ function ClaudeStatus({ status, onAbort, isLoading, provider = 'claude' }) {
|
|||||||
const currentSpinner = spinners[animationPhase];
|
const currentSpinner = spinners[animationPhase];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full mb-6 animate-in slide-in-from-bottom duration-300">
|
<div className="w-full mb-3 sm:mb-6 animate-in slide-in-from-bottom duration-300">
|
||||||
<div className="flex items-center justify-between max-w-4xl mx-auto bg-gray-900 dark:bg-gray-950 text-white rounded-lg shadow-lg px-4 py-3">
|
<div className="flex items-center justify-between max-w-4xl mx-auto bg-gray-800 dark:bg-gray-900 text-white rounded-lg shadow-lg px-2.5 py-2 sm:px-4 sm:py-3 border border-gray-700 dark:border-gray-800">
|
||||||
<div className="flex-1">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2 sm:gap-3">
|
||||||
{/* Animated spinner */}
|
{/* Animated spinner */}
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"text-xl transition-all duration-500",
|
"text-base sm:text-xl transition-all duration-500 flex-shrink-0",
|
||||||
animationPhase % 2 === 0 ? "text-blue-400 scale-110" : "text-blue-300"
|
animationPhase % 2 === 0 ? "text-blue-400 scale-110" : "text-blue-300"
|
||||||
)}>
|
)}>
|
||||||
{currentSpinner}
|
{currentSpinner}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Status text - first line */}
|
{/* Status text - compact for mobile */}
|
||||||
<div className="flex-1">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1.5 sm:gap-2">
|
||||||
<span className="font-medium text-sm">{statusText}...</span>
|
<span className="font-medium text-xs sm:text-sm truncate">{statusText}...</span>
|
||||||
<span className="text-gray-400 text-sm">({elapsedTime}s)</span>
|
<span className="text-gray-400 text-xs sm:text-sm flex-shrink-0">({elapsedTime}s)</span>
|
||||||
{tokens > 0 && (
|
{tokens > 0 && (
|
||||||
<>
|
<>
|
||||||
<span className="text-gray-400">·</span>
|
<span className="text-gray-500 hidden sm:inline">·</span>
|
||||||
<span className="text-gray-300 text-sm hidden sm:inline">⚒ {tokens.toLocaleString()} tokens</span>
|
<span className="text-gray-300 text-xs sm:text-sm hidden sm:inline flex-shrink-0">⚒ {tokens.toLocaleString()}</span>
|
||||||
<span className="text-gray-300 text-sm sm:hidden">⚒ {tokens.toLocaleString()}</span>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<span className="text-gray-400 hidden sm:inline">·</span>
|
<span className="text-gray-500 hidden sm:inline">·</span>
|
||||||
<span className="text-gray-300 text-sm hidden sm:inline">esc to interrupt</span>
|
<span className="text-gray-400 text-xs sm:text-sm hidden sm:inline">esc to stop</span>
|
||||||
</div>
|
|
||||||
{/* Second line for mobile */}
|
|
||||||
<div className="text-xs text-gray-400 sm:hidden mt-1">
|
|
||||||
esc to interrupt
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -96,7 +91,7 @@ function ClaudeStatus({ status, onAbort, isLoading, provider = 'claude' }) {
|
|||||||
{canInterrupt && onAbort && (
|
{canInterrupt && onAbort && (
|
||||||
<button
|
<button
|
||||||
onClick={onAbort}
|
onClick={onAbort}
|
||||||
className="ml-3 text-xs bg-red-600 hover:bg-red-700 text-white px-2.5 py-1 sm:px-3 sm:py-1.5 rounded-md transition-colors flex items-center gap-1.5 flex-shrink-0"
|
className="ml-2 sm:ml-3 text-xs bg-red-600 hover:bg-red-700 active:bg-red-800 text-white px-2 py-1 sm:px-3 sm:py-1.5 rounded-md transition-colors flex items-center gap-1 sm:gap-1.5 flex-shrink-0 font-medium"
|
||||||
>
|
>
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ function CodeEditor({ file, onClose, projectPath, isSidebar = false, isExpanded
|
|||||||
`}
|
`}
|
||||||
</style>
|
</style>
|
||||||
{isSidebar ? (
|
{isSidebar ? (
|
||||||
<div className="w-full h-full flex items-center justify-center bg-white dark:bg-gray-900">
|
<div className="w-full h-full flex items-center justify-center bg-background">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
|
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
|
||||||
<span className="text-gray-900 dark:text-white">Loading {file.name}...</span>
|
<span className="text-gray-900 dark:text-white">Loading {file.name}...</span>
|
||||||
@@ -560,14 +560,14 @@ function CodeEditor({ file, onClose, projectPath, isSidebar = false, isExpanded
|
|||||||
'md:bg-black/50 md:flex md:items-center md:justify-center md:p-4'
|
'md:bg-black/50 md:flex md:items-center md:justify-center md:p-4'
|
||||||
} ${isFullscreen ? 'md:p-0' : ''}`}>
|
} ${isFullscreen ? 'md:p-0' : ''}`}>
|
||||||
<div className={isSidebar ?
|
<div className={isSidebar ?
|
||||||
'bg-white dark:bg-gray-900 flex flex-col w-full h-full' :
|
'bg-background flex flex-col w-full h-full' :
|
||||||
`bg-white shadow-2xl flex flex-col ${
|
`bg-background shadow-2xl flex flex-col ${
|
||||||
// Mobile: always fullscreen, Desktop: modal sizing
|
// Mobile: always fullscreen, Desktop: modal sizing
|
||||||
'w-full h-full md:rounded-lg md:shadow-2xl' +
|
'w-full h-full md:rounded-lg md:shadow-2xl' +
|
||||||
(isFullscreen ? ' md:w-full md:h-full md:rounded-none' : ' md:w-full md:max-w-6xl md:h-[80vh] md:max-h-[80vh]')
|
(isFullscreen ? ' md:w-full md:h-full md:rounded-none' : ' md:w-full md:max-w-6xl md:h-[80vh] md:max-h-[80vh]')
|
||||||
}`}>
|
}`}>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700 flex-shrink-0 min-w-0">
|
<div className="flex items-center justify-between p-4 border-b border-border flex-shrink-0 min-w-0">
|
||||||
<div className="flex items-center gap-3 min-w-0 flex-1">
|
<div className="flex items-center gap-3 min-w-0 flex-1">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="flex items-center gap-2 min-w-0">
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
@@ -684,7 +684,7 @@ function CodeEditor({ file, onClose, projectPath, isSidebar = false, isExpanded
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex items-center justify-between p-3 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 flex-shrink-0">
|
<div className="flex items-center justify-between p-3 border-t border-border bg-muted flex-shrink-0">
|
||||||
<div className="flex items-center gap-4 text-sm text-gray-600 dark:text-gray-400">
|
<div className="flex items-center gap-4 text-sm text-gray-600 dark:text-gray-400">
|
||||||
<span>Lines: {content.split('\n').length}</span>
|
<span>Lines: {content.split('\n').length}</span>
|
||||||
<span>Characters: {content.length}</span>
|
<span>Characters: {content.length}</span>
|
||||||
|
|||||||
@@ -769,7 +769,7 @@ function GitPanel({ selectedProject, isMobile, onFileOpen }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col bg-white dark:bg-gray-900">
|
<div className="h-full flex flex-col bg-background">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`flex items-center justify-between border-b border-gray-200 dark:border-gray-700 ${isMobile ? 'px-3 py-2' : 'px-4 py-3'}`}>
|
<div className={`flex items-center justify-between border-b border-gray-200 dark:border-gray-700 ${isMobile ? 'px-3 py-2' : 'px-4 py-3'}`}>
|
||||||
<div className="relative" ref={dropdownRef}>
|
<div className="relative" ref={dropdownRef}>
|
||||||
@@ -1156,7 +1156,7 @@ function GitPanel({ selectedProject, isMobile, onFileOpen }) {
|
|||||||
|
|
||||||
{/* File List - Changes View - Only show when git is available */}
|
{/* File List - Changes View - Only show when git is available */}
|
||||||
{activeView === 'changes' && !gitStatus?.error && (
|
{activeView === 'changes' && !gitStatus?.error && (
|
||||||
<div className={`flex-1 overflow-y-auto ${isMobile ? 'pb-20' : ''}`}>
|
<div className={`flex-1 overflow-y-auto ${isMobile ? 'pb-mobile-nav' : ''}`}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center h-32">
|
<div className="flex items-center justify-center h-32">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-gray-400" />
|
<RefreshCw className="w-6 h-6 animate-spin text-gray-400" />
|
||||||
@@ -1179,7 +1179,7 @@ function GitPanel({ selectedProject, isMobile, onFileOpen }) {
|
|||||||
|
|
||||||
{/* History View - Only show when git is available */}
|
{/* History View - Only show when git is available */}
|
||||||
{activeView === 'history' && !gitStatus?.error && (
|
{activeView === 'history' && !gitStatus?.error && (
|
||||||
<div className={`flex-1 overflow-y-auto ${isMobile ? 'pb-20' : ''}`}>
|
<div className={`flex-1 overflow-y-auto ${isMobile ? 'pb-mobile-nav' : ''}`}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center h-32">
|
<div className="flex items-center justify-center h-32">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-gray-400" />
|
<RefreshCw className="w-6 h-6 animate-spin text-gray-400" />
|
||||||
|
|||||||
@@ -214,11 +214,11 @@ function MainContent({
|
|||||||
{/* Header with menu button for mobile */}
|
{/* Header with menu button for mobile */}
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<div
|
<div
|
||||||
className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 p-3 sm:p-4 pwa-header-safe flex-shrink-0"
|
className="bg-background border-b border-border p-2 sm:p-3 pwa-header-safe flex-shrink-0"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={onMenuClick}
|
onClick={onMenuClick}
|
||||||
className="p-1.5 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
className="p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
||||||
@@ -252,11 +252,11 @@ function MainContent({
|
|||||||
{/* Header with menu button for mobile */}
|
{/* Header with menu button for mobile */}
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<div
|
<div
|
||||||
className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 p-3 sm:p-4 pwa-header-safe flex-shrink-0"
|
className="bg-background border-b border-border p-2 sm:p-3 pwa-header-safe flex-shrink-0"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={onMenuClick}
|
onClick={onMenuClick}
|
||||||
className="p-1.5 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
className="p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
||||||
@@ -290,10 +290,10 @@ function MainContent({
|
|||||||
<div className="h-full flex flex-col">
|
<div className="h-full flex flex-col">
|
||||||
{/* Header with tabs */}
|
{/* Header with tabs */}
|
||||||
<div
|
<div
|
||||||
className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 p-3 sm:p-4 pwa-header-safe flex-shrink-0"
|
className="bg-background border-b border-border p-2 sm:p-3 pwa-header-safe flex-shrink-0"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between relative">
|
<div className="flex items-center justify-between relative">
|
||||||
<div className="flex items-center space-x-2 sm:space-x-3">
|
<div className="flex items-center space-x-2 min-w-0 flex-1">
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<button
|
<button
|
||||||
onClick={onMenuClick}
|
onClick={onMenuClick}
|
||||||
@@ -301,36 +301,36 @@ function MainContent({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onMenuClick();
|
onMenuClick();
|
||||||
}}
|
}}
|
||||||
className="p-2.5 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 touch-manipulation active:scale-95 pwa-menu-button"
|
className="p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 touch-manipulation active:scale-95 pwa-menu-button flex-shrink-0"
|
||||||
>
|
>
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<div className="min-w-0 flex items-center gap-2">
|
<div className="min-w-0 flex items-center gap-2 flex-1 overflow-x-auto scrollbar-hide">
|
||||||
{activeTab === 'chat' && selectedSession && (
|
{activeTab === 'chat' && selectedSession && (
|
||||||
<div className="w-6 h-6 flex-shrink-0 flex items-center justify-center">
|
<div className="w-5 h-5 flex-shrink-0 flex items-center justify-center">
|
||||||
{selectedSession.__provider === 'cursor' ? (
|
{selectedSession.__provider === 'cursor' ? (
|
||||||
<CursorLogo className="w-5 h-5" />
|
<CursorLogo className="w-4 h-4" />
|
||||||
) : (
|
) : (
|
||||||
<ClaudeLogo className="w-5 h-5" />
|
<ClaudeLogo className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="min-w-0 flex-1">
|
||||||
{activeTab === 'chat' && selectedSession ? (
|
{activeTab === 'chat' && selectedSession ? (
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<h2 className="text-base sm:text-lg font-semibold text-gray-900 dark:text-white truncate">
|
<h2 className="text-sm sm:text-base font-semibold text-gray-900 dark:text-white whitespace-nowrap overflow-x-auto scrollbar-hide">
|
||||||
{selectedSession.__provider === 'cursor' ? (selectedSession.name || 'Untitled Session') : (selectedSession.summary || 'New Session')}
|
{selectedSession.__provider === 'cursor' ? (selectedSession.name || 'Untitled Session') : (selectedSession.summary || 'New Session')}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="text-xs text-gray-500 dark:text-gray-400 truncate">
|
<div className="text-xs text-gray-500 dark:text-gray-400 truncate">
|
||||||
{selectedProject.displayName} <span className="hidden sm:inline">• {selectedSession.id}</span>
|
{selectedProject.displayName}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : activeTab === 'chat' && !selectedSession ? (
|
) : activeTab === 'chat' && !selectedSession ? (
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<h2 className="text-base sm:text-lg font-semibold text-gray-900 dark:text-white">
|
<h2 className="text-sm sm:text-base font-semibold text-gray-900 dark:text-white">
|
||||||
New Session
|
New Session
|
||||||
</h2>
|
</h2>
|
||||||
<div className="text-xs text-gray-500 dark:text-gray-400 truncate">
|
<div className="text-xs text-gray-500 dark:text-gray-400 truncate">
|
||||||
@@ -338,8 +338,8 @@ function MainContent({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
<h2 className="text-base sm:text-lg font-semibold text-gray-900 dark:text-white">
|
<h2 className="text-sm sm:text-base font-semibold text-gray-900 dark:text-white">
|
||||||
{activeTab === 'files' ? 'Project Files' :
|
{activeTab === 'files' ? 'Project Files' :
|
||||||
activeTab === 'git' ? 'Source Control' :
|
activeTab === 'git' ? 'Source Control' :
|
||||||
(activeTab === 'tasks' && shouldShowTasksTab) ? 'TaskMaster' :
|
(activeTab === 'tasks' && shouldShowTasksTab) ? 'TaskMaster' :
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import { useTasksSettings } from '../contexts/TasksSettingsContext';
|
|||||||
|
|
||||||
function MobileNav({ activeTab, setActiveTab, isInputFocused }) {
|
function MobileNav({ activeTab, setActiveTab, isInputFocused }) {
|
||||||
const { tasksEnabled } = useTasksSettings();
|
const { tasksEnabled } = useTasksSettings();
|
||||||
// Detect dark mode
|
|
||||||
const isDarkMode = document.documentElement.classList.contains('dark');
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{
|
{
|
||||||
id: 'chat',
|
id: 'chat',
|
||||||
@@ -36,22 +34,11 @@ function MobileNav({ activeTab, setActiveTab, isInputFocused }) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<style>
|
className={`fixed bottom-0 left-0 right-0 bg-background border-t border-border z-50 ios-bottom-safe transform transition-transform duration-300 ease-in-out shadow-lg ${
|
||||||
{`
|
isInputFocused ? 'translate-y-full' : 'translate-y-0'
|
||||||
.mobile-nav-container {
|
}`}
|
||||||
background-color: ${isDarkMode ? '#1f2937' : '#ffffff'} !important;
|
>
|
||||||
}
|
|
||||||
.mobile-nav-container:hover {
|
|
||||||
background-color: ${isDarkMode ? '#1f2937' : '#ffffff'} !important;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
</style>
|
|
||||||
<div
|
|
||||||
className={`mobile-nav-container fixed bottom-0 left-0 right-0 border-t border-gray-200 dark:border-gray-700 z-50 ios-bottom-safe transform transition-transform duration-300 ease-in-out shadow-lg ${
|
|
||||||
isInputFocused ? 'translate-y-full' : 'translate-y-0'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-around py-1">
|
<div className="flex items-center justify-around py-1">
|
||||||
{navItems.map((item) => {
|
{navItems.map((item) => {
|
||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
@@ -81,7 +68,6 @@ function MobileNav({ activeTab, setActiveTab, isInputFocused }) {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const QuickSettingsPanel = ({
|
|||||||
|
|
||||||
{/* Panel */}
|
{/* Panel */}
|
||||||
<div
|
<div
|
||||||
className={`fixed top-0 right-0 h-full w-64 bg-white dark:bg-gray-900 border-l border-gray-200 dark:border-gray-700 shadow-xl transform transition-transform duration-150 ease-out z-40 ${
|
className={`fixed top-0 right-0 h-full w-64 bg-background border-l border-border shadow-xl transform transition-transform duration-150 ease-out z-40 ${
|
||||||
localIsOpen ? 'translate-x-0' : 'translate-x-full'
|
localIsOpen ? 'translate-x-0' : 'translate-x-full'
|
||||||
} ${isMobile ? 'h-screen' : ''}`}
|
} ${isMobile ? 'h-screen' : ''}`}
|
||||||
>
|
>
|
||||||
@@ -85,7 +85,7 @@ const QuickSettingsPanel = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Settings Content */}
|
{/* Settings Content */}
|
||||||
<div className={`flex-1 overflow-y-auto overflow-x-hidden p-4 space-y-6 bg-white dark:bg-gray-900 ${isMobile ? 'pb-20' : ''}`}>
|
<div className={`flex-1 overflow-y-auto overflow-x-hidden p-4 space-y-6 bg-background ${isMobile ? 'pb-mobile-nav' : ''}`}>
|
||||||
{/* Appearance Settings */}
|
{/* Appearance Settings */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h4 className="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 mb-2">Appearance</h4>
|
<h4 className="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 mb-2">Appearance</h4>
|
||||||
|
|||||||
@@ -467,7 +467,6 @@ function Sidebar({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className="h-full flex flex-col bg-card md:select-none"
|
className="h-full flex flex-col bg-card md:select-none"
|
||||||
style={isPWA && isMobile ? { paddingTop: '44px' } : {}}
|
|
||||||
>
|
>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="md:p-4 md:border-b md:border-border">
|
<div className="md:p-4 md:border-b md:border-border">
|
||||||
@@ -505,7 +504,6 @@ function Sidebar({
|
|||||||
{/* Mobile Header */}
|
{/* Mobile Header */}
|
||||||
<div
|
<div
|
||||||
className="md:hidden p-3 border-b border-border"
|
className="md:hidden p-3 border-b border-border"
|
||||||
style={isPWA && isMobile ? { paddingTop: '16px' } : {}}
|
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@@ -1308,7 +1306,7 @@ function Sidebar({
|
|||||||
{/* Settings Section */}
|
{/* Settings Section */}
|
||||||
<div className="md:p-2 md:border-t md:border-border flex-shrink-0">
|
<div className="md:p-2 md:border-t md:border-border flex-shrink-0">
|
||||||
{/* Mobile Settings */}
|
{/* Mobile Settings */}
|
||||||
<div className="md:hidden p-4 pb-20 border-t border-border/50">
|
<div className="md:hidden p-4 pb-mobile-nav border-t border-border/50">
|
||||||
<button
|
<button
|
||||||
className="w-full h-14 bg-muted/50 hover:bg-muted/70 rounded-2xl flex items-center justify-start gap-4 px-4 active:scale-[0.98] transition-all duration-150"
|
className="w-full h-14 bg-muted/50 hover:bg-muted/70 rounded-2xl flex items-center justify-start gap-4 px-4 active:scale-[0.98] transition-all duration-150"
|
||||||
onClick={onShowSettings}
|
onClick={onShowSettings}
|
||||||
|
|||||||
@@ -49,6 +49,16 @@
|
|||||||
--safe-area-inset-right: env(safe-area-inset-right);
|
--safe-area-inset-right: env(safe-area-inset-right);
|
||||||
--safe-area-inset-bottom: env(safe-area-inset-bottom);
|
--safe-area-inset-bottom: env(safe-area-inset-bottom);
|
||||||
--safe-area-inset-left: env(safe-area-inset-left);
|
--safe-area-inset-left: env(safe-area-inset-left);
|
||||||
|
|
||||||
|
/* Mobile navigation dimensions - Single source of truth */
|
||||||
|
--mobile-nav-height: 60px;
|
||||||
|
--mobile-nav-padding: 12px;
|
||||||
|
--mobile-nav-total: calc(var(--mobile-nav-height) + max(env(safe-area-inset-bottom, 0px), var(--mobile-nav-padding)));
|
||||||
|
|
||||||
|
/* Header safe area dimensions */
|
||||||
|
--header-safe-area-top: env(safe-area-inset-top, 0px);
|
||||||
|
--header-base-padding: 8px;
|
||||||
|
--header-total-padding: calc(var(--header-safe-area-top) + var(--header-base-padding));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fallback for older iOS versions */
|
/* Fallback for older iOS versions */
|
||||||
@@ -128,30 +138,24 @@
|
|||||||
body.pwa-mode {
|
body.pwa-mode {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: rgb(255 255 255); /* white - same as bg-white for safe area */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body.pwa-mode #root {
|
body.pwa-mode #root {
|
||||||
padding-top: calc(env(safe-area-inset-top, 0px) + 8px);
|
padding-top: var(--header-total-padding);
|
||||||
padding-left: env(safe-area-inset-left);
|
padding-left: var(--safe-area-inset-left);
|
||||||
padding-right: env(safe-area-inset-right);
|
padding-right: var(--safe-area-inset-right);
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust fixed inset positioning in PWA mode */
|
/* Adjust fixed inset positioning in PWA mode */
|
||||||
body.pwa-mode .fixed.inset-0 {
|
body.pwa-mode .fixed.inset-0 {
|
||||||
top: calc(env(safe-area-inset-top, 0px) + 8px);
|
top: var(--header-total-padding);
|
||||||
left: env(safe-area-inset-left);
|
left: var(--safe-area-inset-left);
|
||||||
right: env(safe-area-inset-right);
|
right: var(--safe-area-inset-right);
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark mode safe area background */
|
|
||||||
html.dark body.pwa-mode {
|
|
||||||
background-color: rgb(31 41 55); /* gray-800 - matches header color */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Global transition defaults */
|
/* Global transition defaults */
|
||||||
button,
|
button,
|
||||||
a,
|
a,
|
||||||
@@ -641,9 +645,9 @@
|
|||||||
padding-bottom: max(env(safe-area-inset-bottom), 12px);
|
padding-bottom: max(env(safe-area-inset-bottom), 12px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PWA specific header adjustments for iOS */
|
/* PWA specific header adjustments - uses CSS variables for consistency */
|
||||||
.pwa-header-safe {
|
.pwa-header-safe {
|
||||||
padding-top: 16px;
|
padding-top: var(--header-base-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When PWA mode is detected by JavaScript */
|
/* When PWA mode is detected by JavaScript */
|
||||||
@@ -652,11 +656,10 @@
|
|||||||
padding-top: 0px !important;
|
padding-top: 0px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For mobile PWA, ensure proper header spacing */
|
/* For mobile PWA, add bottom padding for better spacing */
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
body.pwa-mode .pwa-header-safe {
|
body.pwa-mode .pwa-header-safe {
|
||||||
padding-top: 4px !important;
|
padding-bottom: 8px;
|
||||||
padding-bottom: 12px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,6 +689,16 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hide scrollbar utility for horizontal scroll */
|
||||||
|
.scrollbar-hide {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollbar-hide::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide markdown backticks in prose content */
|
/* Hide markdown backticks in prose content */
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export default {
|
|||||||
},
|
},
|
||||||
spacing: {
|
spacing: {
|
||||||
'safe-area-inset-bottom': 'env(safe-area-inset-bottom)',
|
'safe-area-inset-bottom': 'env(safe-area-inset-bottom)',
|
||||||
|
'mobile-nav': 'var(--mobile-nav-total)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user