Fix : mobile issues and git diff in the git panel

This commit is contained in:
simos
2025-09-15 20:57:49 +00:00
parent fb1117a999
commit 79981693f3
6 changed files with 84 additions and 48 deletions

View File

@@ -899,23 +899,17 @@ async function addProjectManually(projectPath, displayName = null) {
// Generate project name (encode path for use as directory name) // Generate project name (encode path for use as directory name)
const projectName = absolutePath.replace(/\//g, '-'); const projectName = absolutePath.replace(/\//g, '-');
// Check if project already exists in config or as a folder // Check if project already exists in config
const config = await loadProjectConfig(); const config = await loadProjectConfig();
const projectDir = path.join(process.env.HOME, '.claude', 'projects', projectName); const projectDir = path.join(process.env.HOME, '.claude', 'projects', projectName);
try {
await fs.access(projectDir);
throw new Error(`Project already exists for path: ${absolutePath}`);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
if (config[projectName]) { if (config[projectName]) {
throw new Error(`Project already configured for path: ${absolutePath}`); throw new Error(`Project already configured for path: ${absolutePath}`);
} }
// Allow adding projects even if the directory exists - this enables tracking
// existing Claude Code or Cursor projects in the UI
// Add to config as manually added project // Add to config as manually added project
config[projectName] = { config[projectName] = {
manuallyAdded: true, manuallyAdded: true,

View File

@@ -89,10 +89,12 @@ function AppContent() {
document.referrer.includes('android-app://'); document.referrer.includes('android-app://');
setIsPWA(isStandalone); setIsPWA(isStandalone);
// Add class to body for CSS targeting // Add class to html and body for CSS targeting
if (isStandalone) { if (isStandalone) {
document.documentElement.classList.add('pwa-mode');
document.body.classList.add('pwa-mode'); document.body.classList.add('pwa-mode');
} else { } else {
document.documentElement.classList.remove('pwa-mode');
document.body.classList.remove('pwa-mode'); document.body.classList.remove('pwa-mode');
} }
}; };

View File

@@ -0,0 +1,41 @@
import React from 'react';
function DiffViewer({ diff, fileName, isMobile, wrapText }) {
if (!diff) {
return (
<div className="p-4 text-center text-gray-500 dark:text-gray-400 text-sm">
No diff available
</div>
);
}
const renderDiffLine = (line, index) => {
const isAddition = line.startsWith('+') && !line.startsWith('+++');
const isDeletion = line.startsWith('-') && !line.startsWith('---');
const isHeader = line.startsWith('@@');
return (
<div
key={index}
className={`font-mono text-xs p-2 ${
isMobile && wrapText ? 'whitespace-pre-wrap break-all' : 'whitespace-pre overflow-x-auto'
} ${
isAddition ? 'bg-green-50 dark:bg-green-950 text-green-700 dark:text-green-300' :
isDeletion ? 'bg-red-50 dark:bg-red-950 text-red-700 dark:text-red-300' :
isHeader ? 'bg-blue-50 dark:bg-blue-950 text-blue-700 dark:text-blue-300' :
'text-gray-600 dark:text-gray-400'
}`}
>
{line}
</div>
);
};
return (
<div className="diff-viewer">
{diff.split('\n').map((line, index) => renderDiffLine(line, index))}
</div>
);
}
export default DiffViewer;

View File

@@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import { GitBranch, GitCommit, Plus, Minus, RefreshCw, Check, X, ChevronDown, ChevronRight, Info, History, FileText, Mic, MicOff, Sparkles, Download, RotateCcw, Trash2, AlertTriangle, Upload } from 'lucide-react'; import { GitBranch, GitCommit, Plus, Minus, RefreshCw, Check, X, ChevronDown, ChevronRight, Info, History, FileText, Mic, MicOff, Sparkles, Download, RotateCcw, Trash2, AlertTriangle, Upload } from 'lucide-react';
import { MicButton } from './MicButton.jsx'; import { MicButton } from './MicButton.jsx';
import { authenticatedFetch } from '../utils/api'; import { authenticatedFetch } from '../utils/api';
import DiffViewer from './DiffViewer.jsx';
function GitPanel({ selectedProject, isMobile }) { function GitPanel({ selectedProject, isMobile }) {
const [gitStatus, setGitStatus] = useState(null); const [gitStatus, setGitStatus] = useState(null);
@@ -523,27 +524,6 @@ function GitPanel({ selectedProject, isMobile }) {
} }
}; };
const renderDiffLine = (line, index) => {
const isAddition = line.startsWith('+') && !line.startsWith('+++');
const isDeletion = line.startsWith('-') && !line.startsWith('---');
const isHeader = line.startsWith('@@');
return (
<div
key={index}
className={`font-mono text-xs ${
isMobile && wrapText ? 'whitespace-pre-wrap break-all' : 'whitespace-pre overflow-x-auto'
} ${
isAddition ? 'bg-green-50 dark:bg-green-950 text-green-700 dark:text-green-300' :
isDeletion ? 'bg-red-50 dark:bg-red-950 text-red-700 dark:text-red-300' :
isHeader ? 'bg-blue-50 dark:bg-blue-950 text-blue-700 dark:text-blue-300' :
'text-gray-600 dark:text-gray-400'
}`}
>
{line}
</div>
);
};
const getStatusLabel = (status) => { const getStatusLabel = (status) => {
switch (status) { switch (status) {
@@ -590,7 +570,7 @@ function GitPanel({ selectedProject, isMobile }) {
<div className="text-xs font-mono text-gray-600 dark:text-gray-400 mb-2"> <div className="text-xs font-mono text-gray-600 dark:text-gray-400 mb-2">
{commit.stats} {commit.stats}
</div> </div>
{diff.split('\n').map((line, index) => renderDiffLine(line, index))} <DiffViewer diff={diff} fileName="commit" isMobile={isMobile} wrapText={wrapText} />
</div> </div>
</div> </div>
)} )}
@@ -705,8 +685,8 @@ function GitPanel({ selectedProject, isMobile }) {
</button> </button>
)} )}
</div> </div>
<div className="max-h-96 overflow-y-auto p-2"> <div className="max-h-96 overflow-y-auto">
{diff && diff.split('\n').map((line, index) => renderDiffLine(line, index))} {diff && <DiffViewer diff={diff} fileName={filePath} isMobile={isMobile} wrapText={wrapText} />}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -155,7 +155,6 @@ function MainContent({
{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-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"
style={isPWA && isMobile ? { paddingTop: '56px' } : {}}
> >
<button <button
onClick={onMenuClick} onClick={onMenuClick}
@@ -194,7 +193,6 @@ function MainContent({
{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-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"
style={isPWA && isMobile ? { paddingTop: '56px' } : {}}
> >
<button <button
onClick={onMenuClick} onClick={onMenuClick}
@@ -233,7 +231,6 @@ function MainContent({
{/* 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-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"
style={isPWA && isMobile ? { paddingTop: '56px' } : {}}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center space-x-2 sm:space-x-3"> <div className="flex items-center space-x-2 sm:space-x-3">

View File

@@ -124,18 +124,32 @@
} }
/* PWA mode detected by JavaScript - more reliable */ /* PWA mode detected by JavaScript - more reliable */
body.pwa-mode #root { html.pwa-mode,
padding-top: calc(env(safe-area-inset-top, 0px) + 20px); body.pwa-mode {
padding-left: env(safe-area-inset-left); height: 100%;
padding-right: env(safe-area-inset-right); overflow: hidden;
background-color: rgb(255 255 255); /* white - same as bg-white for safe area */
} }
/* iOS specific PWA adjustments */
@supports (-webkit-touch-callout: none) {
body.pwa-mode #root { body.pwa-mode #root {
/* iOS status bar is typically 20-44px */ padding-top: calc(env(safe-area-inset-top, 0px) + 8px);
padding-top: max(env(safe-area-inset-top, 44px), 44px); padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
height: 100vh;
overflow: hidden;
} }
/* Adjust fixed inset positioning in PWA mode */
body.pwa-mode .fixed.inset-0 {
top: calc(env(safe-area-inset-top, 0px) + 8px);
left: env(safe-area-inset-left);
right: env(safe-area-inset-right);
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 */
@@ -635,7 +649,15 @@
/* When PWA mode is detected by JavaScript */ /* When PWA mode is detected by JavaScript */
body.pwa-mode .pwa-header-safe { body.pwa-mode .pwa-header-safe {
/* Reset padding since #root already handles safe area */ /* Reset padding since #root already handles safe area */
padding-top: 12px; padding-top: 0px !important;
}
/* For mobile PWA, ensure proper header spacing */
@media screen and (max-width: 768px) {
body.pwa-mode .pwa-header-safe {
padding-top: 4px !important;
padding-bottom: 12px;
}
} }
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {