feat: Transform to Tauri desktop app

- Initialize Tauri v2 project with Rust backend
- Restructure project: move source files to src/ directory
- Add Tauri configuration for Windows, macOS, and Linux builds
- Update Vite config for Tauri development workflow
- Add file system and dialog permissions for native features
- Update package.json with desktop build scripts
- Update tsconfig.json paths for new src structure
- Add Tauri and desktop badges to README
- Document desktop build process and architecture
This commit is contained in:
TypoGenie
2026-01-29 18:28:35 +02:00
parent ae5ce9d243
commit 2c22b0fce6
51 changed files with 7556 additions and 58 deletions

View File

@@ -0,0 +1,92 @@
import React, { useCallback, useState } from 'react';
import { Upload, FileText, AlertCircle } from 'lucide-react';
interface FileUploadProps {
onFileLoaded: (content: string) => void;
}
export const FileUpload: React.FC<FileUploadProps> = ({ onFileLoaded }) => {
const [dragActive, setDragActive] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleFile = (file: File) => {
setError(null);
if (!file.name.endsWith('.md') && !file.name.endsWith('.txt') && !file.name.endsWith('.markdown')) {
setError('Please upload a Markdown (.md) or Text (.txt) file.');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const text = e.target?.result;
if (typeof text === 'string') {
onFileLoaded(text);
}
};
reader.onerror = () => setError('Error reading file.');
reader.readAsText(file);
};
const handleDrag = useCallback((e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
if (e.type === 'dragenter' || e.type === 'dragover') {
setDragActive(true);
} else if (e.type === 'dragleave') {
setDragActive(false);
}
}, []);
const handleDrop = useCallback((e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setDragActive(false);
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
handleFile(e.dataTransfer.files[0]);
}
}, []);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
if (e.target.files && e.target.files[0]) {
handleFile(e.target.files[0]);
}
};
return (
<div className="w-full max-w-xl mx-auto">
<div
className={`relative group flex flex-col items-center justify-center w-full h-64 border-2 border-dashed rounded-2xl transition-all duration-300 ease-in-out cursor-pointer overflow-hidden
${dragActive ? 'border-indigo-500 bg-indigo-500/10' : 'border-zinc-700 bg-zinc-900/50 hover:border-zinc-500 hover:bg-zinc-800/50'}`}
onDragEnter={handleDrag}
onDragLeave={handleDrag}
onDragOver={handleDrag}
onDrop={handleDrop}
>
<input
type="file"
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10"
onChange={handleChange}
accept=".md,.txt,.markdown"
/>
<div className="flex flex-col items-center justify-center pt-5 pb-6 text-zinc-400 group-hover:text-zinc-200 transition-colors">
<div className={`p-4 rounded-full mb-4 ${dragActive ? 'bg-indigo-500/20 text-indigo-400' : 'bg-zinc-800 text-zinc-500'}`}>
<Upload size={32} />
</div>
<p className="mb-2 text-lg font-medium">
<span className="font-semibold text-indigo-400">Click to upload</span> or drag and drop
</p>
<p className="text-sm text-zinc-500">Markdown or Plain Text files</p>
</div>
</div>
{error && (
<div className="mt-4 p-4 bg-red-900/20 border border-red-800 rounded-lg flex items-center gap-3 text-red-200 animate-in fade-in slide-in-from-top-2">
<AlertCircle size={20} />
<span>{error}</span>
</div>
)}
</div>
);
};