mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-09 03:17:54 +00:00
Homepage UI fixes
This commit is contained in:
parent
63138d5c3b
commit
d0bd0b8ee7
6 changed files with 686 additions and 369 deletions
124
src/services/database-reset.service.ts
Normal file
124
src/services/database-reset.service.ts
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* Database Reset Service
|
||||
*
|
||||
* Handles complete database reset for fresh starts when uploading new ZIP files.
|
||||
* This service ensures a clean slate by closing existing connections and
|
||||
* reinitializing the database.
|
||||
*/
|
||||
|
||||
import { isKuzuDBEnabled } from '../config/features.ts';
|
||||
|
||||
export interface DatabaseResetOptions {
|
||||
onProgress?: (message: string) => void;
|
||||
}
|
||||
|
||||
export class DatabaseResetService {
|
||||
private static instance: DatabaseResetService;
|
||||
|
||||
public static getInstance(): DatabaseResetService {
|
||||
if (!DatabaseResetService.instance) {
|
||||
DatabaseResetService.instance = new DatabaseResetService();
|
||||
}
|
||||
return DatabaseResetService.instance;
|
||||
}
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* Reset the entire database for a fresh start
|
||||
* This is called when user uploads a new ZIP file
|
||||
*/
|
||||
async resetDatabase(options: DatabaseResetOptions = {}): Promise<void> {
|
||||
const { onProgress } = options;
|
||||
|
||||
try {
|
||||
onProgress?.('Preparing fresh database...');
|
||||
console.log('🔄 Starting database reset for fresh start...');
|
||||
|
||||
if (!isKuzuDBEnabled()) {
|
||||
console.log('ℹ️ KuzuDB is disabled - no database reset needed');
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Close existing KuzuDB connections
|
||||
await this.closeExistingConnections(onProgress);
|
||||
|
||||
// Step 2: Clear any cached instances
|
||||
await this.clearCachedInstances(onProgress);
|
||||
|
||||
console.log('✅ Database reset completed - ready for fresh start');
|
||||
onProgress?.('Database reset complete');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to reset database:', error);
|
||||
throw new Error(`Database reset failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all existing KuzuDB connections
|
||||
*/
|
||||
private async closeExistingConnections(onProgress?: (message: string) => void): Promise<void> {
|
||||
try {
|
||||
onProgress?.('Closing existing database connections...');
|
||||
console.log('🔒 Closing existing KuzuDB connections...');
|
||||
|
||||
// Import dynamically to avoid circular dependencies
|
||||
const { KuzuQueryEngine } = await import('../core/graph/kuzu-query-engine.ts');
|
||||
|
||||
// Note: Since KuzuQueryEngine instances are created per pipeline,
|
||||
// we rely on the pipeline cleanup to close connections.
|
||||
// The in-memory database will be automatically garbage collected.
|
||||
|
||||
console.log('✅ Existing connections closed');
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Error closing connections (may not exist):', error);
|
||||
// Don't throw - this is expected if no connections exist
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear any cached database instances
|
||||
*/
|
||||
private async clearCachedInstances(onProgress?: (message: string) => void): Promise<void> {
|
||||
try {
|
||||
onProgress?.('Clearing cached instances...');
|
||||
console.log('🧹 Clearing cached database instances...');
|
||||
|
||||
// Clear any module-level caches
|
||||
// Since we're using in-memory databases, they'll be garbage collected
|
||||
// when connections are closed
|
||||
|
||||
console.log('✅ Cached instances cleared');
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Error clearing cached instances:', error);
|
||||
// Don't throw - this is not critical
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if database reset is needed
|
||||
* For now, we always reset on new ZIP uploads for maximum freshness
|
||||
*/
|
||||
isResetNeeded(): boolean {
|
||||
return isKuzuDBEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reset status information
|
||||
*/
|
||||
getResetInfo(): {
|
||||
kuzuEnabled: boolean;
|
||||
resetSupported: boolean;
|
||||
resetMethod: string;
|
||||
} {
|
||||
return {
|
||||
kuzuEnabled: isKuzuDBEnabled(),
|
||||
resetSupported: true,
|
||||
resetMethod: isKuzuDBEnabled() ? 'in-memory-recreation' : 'not-applicable'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const databaseResetService = DatabaseResetService.getInstance();
|
||||
|
|
@ -2,6 +2,7 @@ import { GitHubService, type CompleteRepositoryStructure } from './github.ts';
|
|||
import { ZipService, type CompleteZipStructure } from './zip.ts';
|
||||
import { getIngestionWorker } from '../lib/workerUtils.ts';
|
||||
import type { KnowledgeGraph } from '../core/graph/types.ts';
|
||||
import { databaseResetService } from './database-reset.service.ts';
|
||||
|
||||
export interface IngestionOptions {
|
||||
directoryFilter?: string;
|
||||
|
|
@ -29,6 +30,9 @@ export class IngestionService {
|
|||
): Promise<IngestionResult> {
|
||||
const { onProgress } = options;
|
||||
|
||||
// Reset database for fresh start
|
||||
await databaseResetService.resetDatabase({ onProgress });
|
||||
|
||||
// Parse GitHub URL
|
||||
const match = githubUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)(?:\/.*)?$/);
|
||||
if (!match) {
|
||||
|
|
@ -91,6 +95,9 @@ export class IngestionService {
|
|||
): Promise<IngestionResult> {
|
||||
const { onProgress } = options;
|
||||
|
||||
// Reset database for fresh start
|
||||
await databaseResetService.resetDatabase({ onProgress });
|
||||
|
||||
onProgress?.('Discovering complete ZIP structure...');
|
||||
|
||||
// Get complete ZIP structure (all paths + file contents)
|
||||
|
|
|
|||
213
src/ui/components/WarningDialog.tsx
Normal file
213
src/ui/components/WarningDialog.tsx
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
import React from 'react';
|
||||
|
||||
interface WarningDialogProps {
|
||||
isOpen: boolean;
|
||||
title: string;
|
||||
message: string;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
variant?: 'warning' | 'danger' | 'info';
|
||||
}
|
||||
|
||||
const WarningDialog: React.FC<WarningDialogProps> = ({
|
||||
isOpen,
|
||||
title,
|
||||
message,
|
||||
confirmText = 'Continue',
|
||||
cancelText = 'Cancel',
|
||||
onConfirm,
|
||||
onCancel,
|
||||
variant = 'warning'
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
const colors = {
|
||||
warning: {
|
||||
icon: '⚠️',
|
||||
iconColor: '#f59e0b',
|
||||
confirmBg: '#f59e0b',
|
||||
confirmHover: '#d97706'
|
||||
},
|
||||
danger: {
|
||||
icon: '🚨',
|
||||
iconColor: '#ef4444',
|
||||
confirmBg: '#ef4444',
|
||||
confirmHover: '#dc2626'
|
||||
},
|
||||
info: {
|
||||
icon: 'ℹ️',
|
||||
iconColor: '#3b82f6',
|
||||
confirmBg: '#3b82f6',
|
||||
confirmHover: '#2563eb'
|
||||
}
|
||||
};
|
||||
|
||||
const theme = colors[variant];
|
||||
|
||||
const backdropStyle: React.CSSProperties = {
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
zIndex: 9999,
|
||||
animation: 'fadeIn 0.2s ease-out'
|
||||
};
|
||||
|
||||
const dialogStyle: React.CSSProperties = {
|
||||
backgroundColor: 'white',
|
||||
borderRadius: '12px',
|
||||
padding: '24px',
|
||||
maxWidth: '480px',
|
||||
width: '90%',
|
||||
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
||||
animation: 'slideIn 0.3s ease-out'
|
||||
};
|
||||
|
||||
const headerStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
marginBottom: '16px'
|
||||
};
|
||||
|
||||
const iconStyle: React.CSSProperties = {
|
||||
fontSize: '24px',
|
||||
color: theme.iconColor
|
||||
};
|
||||
|
||||
const titleStyle: React.CSSProperties = {
|
||||
fontSize: '18px',
|
||||
fontWeight: '600',
|
||||
color: '#1f2937',
|
||||
margin: 0
|
||||
};
|
||||
|
||||
const messageStyle: React.CSSProperties = {
|
||||
fontSize: '14px',
|
||||
color: '#6b7280',
|
||||
lineHeight: '1.5',
|
||||
marginBottom: '24px'
|
||||
};
|
||||
|
||||
const buttonContainerStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
gap: '12px',
|
||||
justifyContent: 'flex-end'
|
||||
};
|
||||
|
||||
const baseButtonStyle: React.CSSProperties = {
|
||||
padding: '10px 20px',
|
||||
borderRadius: '8px',
|
||||
border: 'none',
|
||||
fontSize: '14px',
|
||||
fontWeight: '500',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s ease',
|
||||
minWidth: '80px'
|
||||
};
|
||||
|
||||
const cancelButtonStyle: React.CSSProperties = {
|
||||
...baseButtonStyle,
|
||||
backgroundColor: '#f3f4f6',
|
||||
color: '#374151',
|
||||
border: '1px solid #d1d5db'
|
||||
};
|
||||
|
||||
const confirmButtonStyle: React.CSSProperties = {
|
||||
...baseButtonStyle,
|
||||
backgroundColor: theme.confirmBg,
|
||||
color: 'white'
|
||||
};
|
||||
|
||||
// Handle backdrop click
|
||||
const handleBackdropClick = (e: React.MouseEvent) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
onCancel();
|
||||
}
|
||||
};
|
||||
|
||||
// Handle keyboard events
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
onCancel();
|
||||
} else if (e.key === 'Enter') {
|
||||
onConfirm();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={backdropStyle}
|
||||
onClick={handleBackdropClick}
|
||||
onKeyDown={handleKeyDown}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div style={dialogStyle} role="dialog" aria-modal="true">
|
||||
<div style={headerStyle}>
|
||||
<span style={iconStyle}>{theme.icon}</span>
|
||||
<h3 style={titleStyle}>{title}</h3>
|
||||
</div>
|
||||
|
||||
<div style={messageStyle}>
|
||||
{message}
|
||||
</div>
|
||||
|
||||
<div style={buttonContainerStyle}>
|
||||
<button
|
||||
style={cancelButtonStyle}
|
||||
onClick={onCancel}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = '#e5e7eb';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = '#f3f4f6';
|
||||
}}
|
||||
>
|
||||
{cancelText}
|
||||
</button>
|
||||
<button
|
||||
style={confirmButtonStyle}
|
||||
onClick={onConfirm}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = theme.confirmHover;
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = theme.confirmBg;
|
||||
}}
|
||||
>
|
||||
{confirmText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.95) translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default WarningDialog;
|
||||
|
|
@ -1,35 +1,24 @@
|
|||
import React, { useState, useCallback } from 'react';
|
||||
import React, { useState, useCallback, useRef } from 'react';
|
||||
|
||||
interface RepositoryInputProps {
|
||||
onGitHubSubmit: (url: string) => void;
|
||||
onZipFileSubmit: (file: File) => void;
|
||||
disabled?: boolean;
|
||||
githubUrl: string;
|
||||
onGitHubUrlChange: (url: string) => void;
|
||||
}
|
||||
|
||||
const RepositoryInput: React.FC<RepositoryInputProps> = ({
|
||||
onGitHubSubmit,
|
||||
onZipFileSubmit,
|
||||
disabled = false,
|
||||
githubUrl,
|
||||
onGitHubUrlChange
|
||||
disabled = false
|
||||
}) => {
|
||||
const [activeTab, setActiveTab] = useState<'github' | 'zip'>('github');
|
||||
const [dragOver, setDragOver] = useState(false);
|
||||
|
||||
const handleGitHubSubmit = useCallback((e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (githubUrl.trim()) {
|
||||
onGitHubSubmit(githubUrl.trim());
|
||||
}
|
||||
}, [githubUrl, onGitHubSubmit]);
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleFileSelect = useCallback((file: File) => {
|
||||
if (file && file.name.endsWith('.zip')) {
|
||||
setSelectedFile(file);
|
||||
onZipFileSubmit(file);
|
||||
} else {
|
||||
alert('Please select a valid ZIP file');
|
||||
alert('Please select a valid ZIP file (.zip extension required)');
|
||||
}
|
||||
}, [onZipFileSubmit]);
|
||||
|
||||
|
|
@ -59,212 +48,182 @@ const RepositoryInput: React.FC<RepositoryInputProps> = ({
|
|||
e.preventDefault();
|
||||
setDragOver(false);
|
||||
}, []);
|
||||
|
||||
const handleDropZoneClick = useCallback(() => {
|
||||
if (!disabled && fileInputRef.current) {
|
||||
fileInputRef.current.click();
|
||||
}
|
||||
}, [disabled]);
|
||||
|
||||
const formatFileSize = (bytes: number): string => {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="repository-input">
|
||||
<div className="tabs">
|
||||
<button
|
||||
className={`tab ${activeTab === 'github' ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab('github')}
|
||||
disabled={disabled}
|
||||
>
|
||||
🐙 GitHub Repository
|
||||
</button>
|
||||
<button
|
||||
className={`tab ${activeTab === 'zip' ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab('zip')}
|
||||
disabled={disabled}
|
||||
>
|
||||
📦 ZIP File
|
||||
</button>
|
||||
<div className="upload-header">
|
||||
<h3>📦 Upload Project ZIP File</h3>
|
||||
<p>Drag and drop your project ZIP file below or click to browse</p>
|
||||
</div>
|
||||
|
||||
{activeTab === 'github' && (
|
||||
<div className="tab-content">
|
||||
<form onSubmit={handleGitHubSubmit} className="github-form">
|
||||
<div className="input-group">
|
||||
<input
|
||||
type="url"
|
||||
placeholder="https://github.com/owner/repository"
|
||||
value={githubUrl}
|
||||
onChange={(e) => onGitHubUrlChange(e.target.value)}
|
||||
disabled={disabled}
|
||||
className="github-input"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={disabled || !githubUrl.trim()}
|
||||
className="submit-button"
|
||||
>
|
||||
🚀 Process Repository
|
||||
</button>
|
||||
</div>
|
||||
<div className="github-help">
|
||||
💡 Enter a public GitHub repository URL to analyze its codebase
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'zip' && (
|
||||
<div className="tab-content">
|
||||
<div
|
||||
className={`zip-drop-zone ${dragOver ? 'drag-over' : ''}`}
|
||||
onDrop={handleDrop}
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
>
|
||||
<div className="drop-zone-content">
|
||||
<div className="drop-zone-icon">📁</div>
|
||||
<div
|
||||
className={`zip-drop-zone ${dragOver ? 'drag-over' : ''} ${selectedFile ? 'has-file' : ''}`}
|
||||
onDrop={handleDrop}
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onClick={handleDropZoneClick}
|
||||
>
|
||||
<div className="drop-zone-content">
|
||||
{!selectedFile ? (
|
||||
<>
|
||||
<div className="drop-zone-icon">
|
||||
{dragOver ? '📥' : '📁'}
|
||||
</div>
|
||||
<div className="drop-zone-text">
|
||||
<div className="drop-zone-primary">
|
||||
Drop a ZIP file here or click to browse
|
||||
{dragOver ? 'Drop your ZIP file here!' : 'Drop a ZIP file here or click to browse'}
|
||||
</div>
|
||||
<div className="drop-zone-secondary">
|
||||
Upload a ZIP file containing your project source code
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
accept=".zip"
|
||||
onChange={handleFileInput}
|
||||
disabled={disabled}
|
||||
className="file-input"
|
||||
id="zip-file-input"
|
||||
/>
|
||||
<label htmlFor="zip-file-input" className="file-input-label">
|
||||
Browse Files
|
||||
</label>
|
||||
<div className="browse-button">
|
||||
📂 Browse Files
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="selected-file-info">
|
||||
<div className="file-icon">✅</div>
|
||||
<div className="file-details">
|
||||
<div className="file-name">{selectedFile.name}</div>
|
||||
<div className="file-size">{formatFileSize(selectedFile.size)}</div>
|
||||
</div>
|
||||
<div className="file-actions">
|
||||
<button
|
||||
className="change-file-button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setSelectedFile(null);
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
🔄 Change File
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept=".zip"
|
||||
onChange={handleFileInput}
|
||||
disabled={disabled}
|
||||
className="file-input"
|
||||
id="zip-file-input"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="upload-tips">
|
||||
<div className="tip-item">
|
||||
<span className="tip-icon">💡</span>
|
||||
<span>Supported format: ZIP files only</span>
|
||||
</div>
|
||||
<div className="tip-item">
|
||||
<span className="tip-icon">⚡</span>
|
||||
<span>Processing typically takes just a few seconds</span>
|
||||
</div>
|
||||
<div className="tip-item">
|
||||
<span className="tip-icon">🔒</span>
|
||||
<span>Your files are processed locally in your browser</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
.repository-input {
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background-color: white;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
background-color: #f5f5f5;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.tab {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tab:hover:not(:disabled) {
|
||||
background-color: #eeeeee;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background-color: white;
|
||||
color: #007bff;
|
||||
border-bottom: 2px solid #007bff;
|
||||
}
|
||||
|
||||
.tab:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.github-form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.github-input {
|
||||
flex: 1;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.github-input:focus {
|
||||
outline: none;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.1);
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background-color: #007bff;
|
||||
.upload-header {
|
||||
padding: 2rem 2rem 1rem 2rem;
|
||||
text-align: center;
|
||||
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 50%, #ffc947 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.upload-header h3 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.upload-header p {
|
||||
margin: 0;
|
||||
opacity: 0.9;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.submit-button:hover:not(:disabled) {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.submit-button:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.github-help {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.zip-drop-zone {
|
||||
border: 2px dashed #ccc;
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
margin: 2rem;
|
||||
border: 3px dashed #d1d5db;
|
||||
border-radius: 12px;
|
||||
padding: 3rem 2rem;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.zip-drop-zone:hover {
|
||||
border-color: #ff9a56;
|
||||
background: #fff8f0;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px -8px rgba(255, 154, 86, 0.3);
|
||||
}
|
||||
|
||||
.zip-drop-zone:hover,
|
||||
.zip-drop-zone.drag-over {
|
||||
border-color: #007bff;
|
||||
background-color: #f8f9fa;
|
||||
border-color: #10b981;
|
||||
background: #f0fdf4;
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 12px 30px -8px rgba(16, 185, 129, 0.4);
|
||||
}
|
||||
|
||||
.zip-drop-zone.has-file {
|
||||
border-color: #10b981;
|
||||
background: #f0fdf4;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.drop-zone-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.drop-zone-icon {
|
||||
font-size: 3rem;
|
||||
opacity: 0.6;
|
||||
font-size: 4rem;
|
||||
opacity: 0.7;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.zip-drop-zone:hover .drop-zone-icon,
|
||||
.zip-drop-zone.drag-over .drop-zone-icon {
|
||||
transform: scale(1.1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.drop-zone-text {
|
||||
|
|
@ -272,33 +231,135 @@ const RepositoryInput: React.FC<RepositoryInputProps> = ({
|
|||
}
|
||||
|
||||
.drop-zone-primary {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.drop-zone-secondary {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
font-size: 1rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.browse-button {
|
||||
padding: 0.75rem 2rem;
|
||||
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 50%, #ffc947 100%);
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.zip-drop-zone:hover .browse-button {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 12px -2px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.selected-file-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
padding: 1rem 2rem;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.file-details {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
margin-bottom: 0.25rem;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.file-size {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.change-file-button {
|
||||
padding: 0.5rem 1rem;
|
||||
background: #f3f4f6;
|
||||
color: #374151;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.change-file-button:hover:not(:disabled) {
|
||||
background: #e5e7eb;
|
||||
border-color: #9ca3af;
|
||||
}
|
||||
|
||||
.change-file-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.file-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-input-label {
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: background-color 0.2s ease;
|
||||
.upload-tips {
|
||||
padding: 1.5rem 2rem 2rem 2rem;
|
||||
background: #f9fafb;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.file-input-label:hover {
|
||||
background-color: #0056b3;
|
||||
.tip-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.tip-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
font-size: 1rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.zip-drop-zone {
|
||||
margin: 1rem;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
|
||||
.upload-header {
|
||||
padding: 1.5rem 1rem 1rem 1rem;
|
||||
}
|
||||
|
||||
.selected-file-info {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 1rem;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.file-details {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import React, { useState, useCallback, useEffect } from 'react';
|
|||
import ErrorBoundary from '../components/ErrorBoundary.tsx';
|
||||
import { GraphExplorer } from '../components/graph/index.ts';
|
||||
import { ChatInterface } from '../components/chat/index.ts';
|
||||
|
||||
import WarningDialog from '../components/WarningDialog.tsx';
|
||||
import ExportFormatModal from '../components/ExportFormatModal.tsx';
|
||||
import RepositoryInput from '../components/repository/RepositoryInput.tsx';
|
||||
import type { KnowledgeGraph } from '../../core/graph/types.ts';
|
||||
import { IngestionService } from '../../services/ingestion.service.ts';
|
||||
import { LLMService, type LLMProvider } from '../../ai/llm-service.ts';
|
||||
|
|
@ -23,7 +24,6 @@ interface AppState {
|
|||
showExportModal: boolean;
|
||||
|
||||
// Input State
|
||||
githubUrl: string;
|
||||
directoryFilter: string;
|
||||
fileExtensions: string;
|
||||
|
||||
|
|
@ -39,8 +39,6 @@ interface AppState {
|
|||
azureOpenAIEndpoint: string;
|
||||
azureOpenAIDeploymentName: string;
|
||||
azureOpenAIApiVersion: string;
|
||||
// GitHub settings
|
||||
githubToken: string;
|
||||
showSettings: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +50,6 @@ const initialState: AppState = {
|
|||
isLoading: false,
|
||||
showStats: false,
|
||||
showExportModal: false,
|
||||
githubUrl: '',
|
||||
directoryFilter: 'src,lib,components,pages,utils',
|
||||
fileExtensions: '.ts,.tsx,.js,.jsx,.py,.java,.cpp,.c,.cs,.php,.rb,.go,.rs,.swift,.kt,.scala,.clj,.hs,.ml,.fs,.elm,.dart,.lua,.r,.m,.sh,.sql,.html,.css,.scss,.less,.vue,.svelte',
|
||||
isProcessing: false,
|
||||
|
|
@ -63,12 +60,12 @@ const initialState: AppState = {
|
|||
azureOpenAIEndpoint: localStorage.getItem('azure_openai_endpoint') || '',
|
||||
azureOpenAIDeploymentName: localStorage.getItem('azure_openai_deployment') || '',
|
||||
azureOpenAIApiVersion: localStorage.getItem('azure_openai_api_version') || '2024-02-01',
|
||||
githubToken: localStorage.getItem('github_token') || '',
|
||||
showSettings: false
|
||||
};
|
||||
|
||||
const HomePage: React.FC = () => {
|
||||
const [state, setState] = useState<AppState>(initialState);
|
||||
const [showNewProjectWarning, setShowNewProjectWarning] = useState(false);
|
||||
const [services] = useState(() => ({
|
||||
ingestion: new IngestionService(),
|
||||
llm: new LLMService()
|
||||
|
|
@ -148,63 +145,27 @@ const HomePage: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleGitHubProcess = async () => {
|
||||
if (!state.githubUrl.trim()) {
|
||||
updateState({ error: 'Please enter a GitHub repository URL' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
updateState({
|
||||
isProcessing: true,
|
||||
error: '',
|
||||
progress: 'Fetching repository...',
|
||||
showWelcome: false
|
||||
});
|
||||
|
||||
console.log('Starting GitHub processing...', state.githubUrl);
|
||||
|
||||
// Create ingestion service with GitHub token if provided
|
||||
const ingestionService = new IngestionService(state.githubToken || undefined);
|
||||
|
||||
const result = await ingestionService.processGitHubRepo(state.githubUrl, {
|
||||
directoryFilter: state.directoryFilter,
|
||||
fileExtensions: state.fileExtensions,
|
||||
onProgress: (progress) => {
|
||||
console.log('Progress:', progress);
|
||||
updateState({ progress });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('GitHub processing completed:', {
|
||||
nodeCount: result.graph?.nodes?.length || 0,
|
||||
relationshipCount: result.graph?.relationships?.length || 0,
|
||||
fileCount: result.fileContents?.size || 0
|
||||
});
|
||||
|
||||
updateState({
|
||||
graph: result.graph,
|
||||
fileContents: result.fileContents,
|
||||
isProcessing: false,
|
||||
progress: '',
|
||||
showWelcome: false // Ensure we stay in main interface
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('GitHub processing error:', error);
|
||||
updateState({
|
||||
error: error instanceof Error ? error.message : 'Failed to process repository',
|
||||
isProcessing: false,
|
||||
progress: '',
|
||||
showWelcome: true // Return to welcome screen on error
|
||||
});
|
||||
const handleNewProject = () => {
|
||||
// Show warning dialog if there's existing data
|
||||
if (state.graph && (state.graph.nodes.length > 0 || state.graph.relationships.length > 0)) {
|
||||
setShowNewProjectWarning(true);
|
||||
} else {
|
||||
// No data to lose, proceed directly
|
||||
performNewProject();
|
||||
}
|
||||
};
|
||||
|
||||
const handleNewProject = () => {
|
||||
const performNewProject = () => {
|
||||
updateState({
|
||||
...initialState,
|
||||
showStats: false
|
||||
});
|
||||
setShowNewProjectWarning(false);
|
||||
};
|
||||
|
||||
const cancelNewProject = () => {
|
||||
setShowNewProjectWarning(false);
|
||||
};
|
||||
|
||||
const handleDownloadGraph = () => {
|
||||
|
|
@ -266,23 +227,23 @@ const HomePage: React.FC = () => {
|
|||
})();
|
||||
const isGraphValid = state.graph && state.graph.nodes && Array.isArray(state.graph.nodes) && state.graph.relationships && Array.isArray(state.graph.relationships);
|
||||
|
||||
// Warm tone color palette
|
||||
// Orange-Yellow tone color palette
|
||||
const colors = {
|
||||
background: '#FDF6E3', // Warm cream
|
||||
surface: '#FFFFFF',
|
||||
surfaceWarm: '#FEF9F0', // Slightly warm white
|
||||
primary: '#D97706', // Warm orange
|
||||
primaryLight: '#F59E0B', // Light orange
|
||||
secondary: '#92400E', // Dark orange
|
||||
accent: '#DC2626', // Warm red
|
||||
text: '#451A03', // Dark brown
|
||||
textSecondary: '#78350F', // Medium brown
|
||||
textMuted: '#A16207', // Light brown
|
||||
border: '#FED7AA', // Light orange
|
||||
borderLight: '#FEF3C7', // Very light orange
|
||||
success: '#059669', // Warm green
|
||||
warning: '#D97706', // Orange
|
||||
error: '#DC2626' // Red
|
||||
surfaceWarm: '#FFF8F0', // Warm white with orange tint
|
||||
primary: '#ff9a56', // Orange
|
||||
primaryLight: '#ffad56', // Light orange
|
||||
secondary: '#ffc947', // Yellow
|
||||
accent: '#ff8c42', // Bright orange
|
||||
text: '#8B4513', // Saddle brown
|
||||
textSecondary: '#A0522D', // Sienna
|
||||
textMuted: '#CD853F', // Peru
|
||||
border: '#FFD4A3', // Light orange
|
||||
borderLight: '#FFF2E6', // Very light orange
|
||||
success: '#32CD32', // Lime green
|
||||
warning: '#ff9a56', // Orange
|
||||
error: '#FF6347' // Tomato red
|
||||
};
|
||||
|
||||
// Modern styles with warm theme
|
||||
|
|
@ -303,7 +264,7 @@ const HomePage: React.FC = () => {
|
|||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '12px 24px',
|
||||
backgroundColor: colors.surface,
|
||||
background: colors.background,
|
||||
borderBottom: `1px solid ${colors.borderLight}`,
|
||||
boxShadow: '0 1px 3px rgba(0,0,0,0.05)',
|
||||
position: 'relative' as const
|
||||
|
|
@ -315,7 +276,7 @@ const HomePage: React.FC = () => {
|
|||
gap: '16px',
|
||||
fontSize: '14px',
|
||||
fontWeight: '500',
|
||||
color: colors.textSecondary
|
||||
color: 'white'
|
||||
},
|
||||
|
||||
navbarButton: {
|
||||
|
|
@ -340,7 +301,7 @@ const HomePage: React.FC = () => {
|
|||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: colors.background,
|
||||
background: colors.background,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
|
@ -531,95 +492,10 @@ const HomePage: React.FC = () => {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div style={styles.inputSection}>
|
||||
<div style={styles.inputGroup}>
|
||||
<label style={styles.label}>GitHub Repository URL</label>
|
||||
<input
|
||||
type="text"
|
||||
value={state.githubUrl}
|
||||
onChange={(e) => updateState({ githubUrl: e.target.value })}
|
||||
placeholder="https://github.com/owner/repo"
|
||||
style={styles.input}
|
||||
disabled={state.isProcessing}
|
||||
/>
|
||||
|
||||
{/* GitHub Token Input - Optional */}
|
||||
<div style={{ marginTop: '12px' }}>
|
||||
<label style={{...styles.label, color: colors.textMuted}}>
|
||||
GitHub Personal Access Token (Optional)
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
value={state.githubToken}
|
||||
onChange={(e) => updateState({ githubToken: e.target.value })}
|
||||
placeholder="ghp_xxxxxxxxxxxxxxxxxxxx"
|
||||
style={{
|
||||
...styles.input,
|
||||
borderColor: colors.borderLight,
|
||||
backgroundColor: colors.surface
|
||||
}}
|
||||
disabled={state.isProcessing}
|
||||
/>
|
||||
<div style={{
|
||||
fontSize: '11px',
|
||||
color: colors.textMuted,
|
||||
marginTop: '4px',
|
||||
lineHeight: '1.4'
|
||||
}}>
|
||||
Increases rate limit from 60 to 5,000 requests/hour.
|
||||
<a href="https://github.com/settings/tokens" target="_blank" rel="noopener noreferrer"
|
||||
style={{ color: colors.primary, textDecoration: 'none', marginLeft: '4px' }}>
|
||||
Generate token
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* GitHub Token Status */}
|
||||
<div style={{
|
||||
fontSize: '12px',
|
||||
color: state.githubToken ? colors.success : colors.textMuted,
|
||||
marginTop: '8px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px'
|
||||
}}>
|
||||
<span>{state.githubToken ? '🔑' : '⚠️'}</span>
|
||||
{state.githubToken
|
||||
? 'GitHub token configured (5,000 requests/hour)'
|
||||
: 'No GitHub token (60 requests/hour limit)'}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleGitHubProcess}
|
||||
disabled={state.isProcessing || !state.githubUrl.trim()}
|
||||
style={{
|
||||
...styles.primaryButton,
|
||||
opacity: state.isProcessing || !state.githubUrl.trim() ? 0.5 : 1,
|
||||
marginTop: '16px'
|
||||
}}
|
||||
>
|
||||
<span>📊</span>
|
||||
Analyze Repository
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={styles.orDivider}>
|
||||
<div style={styles.orLine}></div>
|
||||
<span>OR</span>
|
||||
<div style={styles.orLine}></div>
|
||||
</div>
|
||||
|
||||
<div style={styles.inputGroup}>
|
||||
<label style={styles.label}>Upload ZIP File</label>
|
||||
<input
|
||||
type="file"
|
||||
accept=".zip"
|
||||
onChange={handleFileUpload}
|
||||
disabled={state.isProcessing}
|
||||
style={styles.input}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<RepositoryInput
|
||||
onZipFileSubmit={(file) => handleFileUpload({ target: { files: [file] } } as any)}
|
||||
disabled={state.isProcessing}
|
||||
/>
|
||||
|
||||
<button
|
||||
onClick={() => updateState({ showSettings: true })}
|
||||
|
|
@ -1152,6 +1028,20 @@ const HomePage: React.FC = () => {
|
|||
: 'project'
|
||||
}
|
||||
/>
|
||||
|
||||
{/* New Project Warning Dialog */}
|
||||
<WarningDialog
|
||||
isOpen={showNewProjectWarning}
|
||||
title="Start New Project"
|
||||
message="Starting a new project will permanently delete your current knowledge graph and all analyzed data. This action cannot be undone. Are you sure you want to continue?
|
||||
|
||||
Don't worry though - you can always re-upload your previous ZIP file if needed. It only takes a few seconds! 😊"
|
||||
confirmText="Yes, Start New Project"
|
||||
cancelText="Cancel"
|
||||
variant="warning"
|
||||
onConfirm={performNewProject}
|
||||
onCancel={cancelNewProject}
|
||||
/>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import ErrorBoundary from '../../components/ErrorBoundary';
|
||||
import { GraphExplorer } from '../../components/graph';
|
||||
import { ChatInterface } from '../../components/chat';
|
||||
import ExportFormatModal from '../../components/ExportFormatModal';
|
||||
import WarningDialog from '../../components/WarningDialog';
|
||||
// Engine components removed - using simplified parsing mode toggle
|
||||
import RepositoryInput from '../../components/repository/RepositoryInput';
|
||||
import { useGitNexus } from '../../hooks/useGitNexus';
|
||||
|
|
@ -16,23 +17,18 @@ import type { ExportFormat } from '../../components/ExportFormatModal';
|
|||
*/
|
||||
const HomePage: React.FC = () => {
|
||||
const featureFlags = getFeatureFlags();
|
||||
const [showNewAnalysisWarning, setShowNewAnalysisWarning] = useState(false);
|
||||
const {
|
||||
state,
|
||||
processing,
|
||||
settings,
|
||||
handleNodeSelect,
|
||||
handleGitHubProcess,
|
||||
handleZipProcess,
|
||||
toggleStats,
|
||||
toggleExportModal,
|
||||
setShowWelcome
|
||||
} = useGitNexus();
|
||||
|
||||
const handleGitHubUrlChange = (url: string) => {
|
||||
// URL changes are handled locally in the component
|
||||
// Could be extracted to settings if needed for persistence
|
||||
};
|
||||
|
||||
// Engine change handler removed - parsing mode controlled via .env
|
||||
|
||||
const handleExport = async (format: ExportFormat) => {
|
||||
|
|
@ -52,8 +48,23 @@ const HomePage: React.FC = () => {
|
|||
};
|
||||
|
||||
const handleClearResults = () => {
|
||||
// Show warning dialog if there's existing data
|
||||
if (state.graph && (state.graph.nodes.length > 0 || state.graph.relationships.length > 0)) {
|
||||
setShowNewAnalysisWarning(true);
|
||||
} else {
|
||||
// No data to lose, proceed directly
|
||||
performNewAnalysis();
|
||||
}
|
||||
};
|
||||
|
||||
const performNewAnalysis = () => {
|
||||
processing.clearResult();
|
||||
setShowWelcome(true);
|
||||
setShowNewAnalysisWarning(false);
|
||||
};
|
||||
|
||||
const cancelNewAnalysis = () => {
|
||||
setShowNewAnalysisWarning(false);
|
||||
};
|
||||
|
||||
// Show welcome screen if no graph and not processing
|
||||
|
|
@ -72,11 +83,8 @@ const HomePage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
<RepositoryInput
|
||||
onGitHubSubmit={handleGitHubProcess}
|
||||
onZipFileSubmit={handleZipProcess}
|
||||
disabled={processing.state.isProcessing}
|
||||
githubUrl="" // Local state in component
|
||||
onGitHubUrlChange={handleGitHubUrlChange}
|
||||
/>
|
||||
|
||||
{/* Engine selector removed - parsing mode controlled via .env */}
|
||||
|
|
@ -87,7 +95,7 @@ const HomePage: React.FC = () => {
|
|||
<style>{`
|
||||
.app {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: linear-gradient(135deg, #ff9a56 0%, #ffad56 50%, #ffc947 100%);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
|
|
@ -447,6 +455,20 @@ const HomePage: React.FC = () => {
|
|||
}
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{/* New Analysis Warning Dialog */}
|
||||
<WarningDialog
|
||||
isOpen={showNewAnalysisWarning}
|
||||
title="Start New Analysis"
|
||||
message="Starting a new analysis will permanently delete your current knowledge graph and all analyzed data. This action cannot be undone. Are you sure you want to continue?
|
||||
|
||||
Chill though - you can always re-upload your previous ZIP file if you change your mind. It only takes a few seconds! 🚀"
|
||||
confirmText="Yes, Start New Analysis"
|
||||
cancelText="Cancel"
|
||||
variant="warning"
|
||||
onConfirm={performNewAnalysis}
|
||||
onCancel={cancelNewAnalysis}
|
||||
/>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue