mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-14 15:39:32 +00:00
Fix : mobile issues and git diff in the git panel
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
41
src/components/DiffViewer.jsx
Normal file
41
src/components/DiffViewer.jsx
Normal 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;
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user