mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 18:10:18 +05:30
feat(renderer): setup React, Vite, Tailwind CSS and Zustand state management
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
Generated
+1812
-38
File diff suppressed because it is too large
Load Diff
+19
-2
@@ -44,6 +44,12 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/dom": "^10.4.1",
|
"@testing-library/dom": "^10.4.1",
|
||||||
|
"@types/react": "^19.2.16",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"@vitejs/plugin-react": "^6.0.2",
|
||||||
|
"autoprefixer": "^10.5.0",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
"cross-env": "^10.0.0",
|
"cross-env": "^10.0.0",
|
||||||
"electron": "^41.1.1",
|
"electron": "^41.1.1",
|
||||||
"electron-builder": "^26.0.12",
|
"electron-builder": "^26.0.12",
|
||||||
@@ -52,8 +58,15 @@
|
|||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
"jest": "^30.2.0",
|
"jest": "^30.2.0",
|
||||||
"jest-environment-jsdom": "^30.2.0",
|
"jest-environment-jsdom": "^30.2.0",
|
||||||
|
"lucide-react": "^1.17.0",
|
||||||
|
"postcss": "^8.5.15",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.7.4",
|
||||||
"sharp": "^0.34.3"
|
"sharp": "^0.34.3",
|
||||||
|
"tailwind-merge": "^3.6.0",
|
||||||
|
"tailwindcss": "^3.4.19",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"typescript": "^6.0.3",
|
||||||
|
"vite": "^8.0.16"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.20.1",
|
"@codemirror/autocomplete": "^6.20.1",
|
||||||
@@ -70,6 +83,7 @@
|
|||||||
"@codemirror/state": "^6.5.4",
|
"@codemirror/state": "^6.5.4",
|
||||||
"@codemirror/theme-one-dark": "^6.1.3",
|
"@codemirror/theme-one-dark": "^6.1.3",
|
||||||
"@codemirror/view": "^6.39.16",
|
"@codemirror/view": "^6.39.16",
|
||||||
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"codemirror": "^6.0.2",
|
"codemirror": "^6.0.2",
|
||||||
"core-util-is": "^1.0.3",
|
"core-util-is": "^1.0.3",
|
||||||
"docx": "^9.6.0",
|
"docx": "^9.6.0",
|
||||||
@@ -87,8 +101,11 @@
|
|||||||
"pdfjs-dist": "^5.5.207",
|
"pdfjs-dist": "^5.5.207",
|
||||||
"pdfkit": "^0.17.2",
|
"pdfkit": "^0.17.2",
|
||||||
"pizzip": "^3.2.0",
|
"pizzip": "^3.2.0",
|
||||||
|
"react": "^19.2.7",
|
||||||
|
"react-dom": "^19.2.7",
|
||||||
"simple-git": "^3.32.3",
|
"simple-git": "^3.32.3",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1",
|
||||||
|
"zustand": "^5.0.14"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { AppHeader } from './components/layout/AppHeader';
|
||||||
|
import { TabBar } from './components/layout/TabBar';
|
||||||
|
import { Toolbar } from './components/layout/Toolbar';
|
||||||
|
import { Breadcrumb } from './components/layout/Breadcrumb';
|
||||||
|
import { Sidebar } from './components/sidebar/Sidebar';
|
||||||
|
import { EditorPane } from './components/editor/EditorPane';
|
||||||
|
import { PreviewPane } from './components/preview/PreviewPane';
|
||||||
|
import { StatusBar } from './components/layout/StatusBar';
|
||||||
|
import { CommandPalette } from './components/modals/CommandPalette';
|
||||||
|
import { ExportDialog } from './components/modals/ExportDialog';
|
||||||
|
import { SettingsDialog } from './components/modals/SettingsDialog';
|
||||||
|
import { useAppStore } from './stores/appStore';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const { sidebarVisible, previewVisible, activeTabId } = useAppStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-screen bg-background text-foreground overflow-hidden">
|
||||||
|
<AppHeader />
|
||||||
|
<TabBar />
|
||||||
|
<Toolbar />
|
||||||
|
<Breadcrumb />
|
||||||
|
|
||||||
|
<main className="flex flex-1 overflow-hidden">
|
||||||
|
{sidebarVisible && <Sidebar />}
|
||||||
|
|
||||||
|
<div className="flex flex-1 overflow-hidden">
|
||||||
|
<EditorPane className={previewVisible ? 'flex-1' : 'flex-1'} />
|
||||||
|
{previewVisible && (
|
||||||
|
<>
|
||||||
|
<div className="w-px bg-border cursor-col-resize hover:bg-brand transition-colors" />
|
||||||
|
<PreviewPane className="flex-1" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<StatusBar />
|
||||||
|
<CommandPalette />
|
||||||
|
<ExportDialog />
|
||||||
|
<SettingsDialog />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self';">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MarkdownConverter</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="font-sans antialiased">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="./main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
import App from './App';
|
||||||
|
import './styles/globals.css';
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 0 0% 9%;
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 0 0% 9%;
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 0 0% 9%;
|
||||||
|
--primary: 11 79% 51%;
|
||||||
|
--primary-foreground: 0 0% 100%;
|
||||||
|
--secondary: 0 0% 96%;
|
||||||
|
--secondary-foreground: 0 0% 9%;
|
||||||
|
--muted: 0 0% 96%;
|
||||||
|
--muted-foreground: 0 0% 45%;
|
||||||
|
--accent: 0 0% 96%;
|
||||||
|
--accent-foreground: 0 0% 9%;
|
||||||
|
--destructive: 0 84% 60%;
|
||||||
|
--destructive-foreground: 0 0% 100%;
|
||||||
|
--border: 0 0% 90%;
|
||||||
|
--input: 0 0% 90%;
|
||||||
|
--ring: 11 79% 51%;
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 30 14% 4%;
|
||||||
|
--foreground: 0 0% 98%;
|
||||||
|
--card: 30 14% 6%;
|
||||||
|
--card-foreground: 0 0% 98%;
|
||||||
|
--popover: 30 14% 6%;
|
||||||
|
--popover-foreground: 0 0% 98%;
|
||||||
|
--primary: 11 79% 51%;
|
||||||
|
--primary-foreground: 0 0% 100%;
|
||||||
|
--secondary: 30 6% 15%;
|
||||||
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
--muted: 30 6% 15%;
|
||||||
|
--muted-foreground: 0 0% 63%;
|
||||||
|
--accent: 30 6% 15%;
|
||||||
|
--accent-foreground: 0 0% 98%;
|
||||||
|
--destructive: 0 62% 30%;
|
||||||
|
--destructive-foreground: 0 0% 100%;
|
||||||
|
--border: 30 6% 15%;
|
||||||
|
--input: 30 6% 15%;
|
||||||
|
--ring: 11 79% 51%;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom scrollbar */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: hsl(var(--muted-foreground) / 0.3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: hsl(var(--muted-foreground) / 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CodeMirror integration */
|
||||||
|
.cm-editor {
|
||||||
|
@apply h-full;
|
||||||
|
}
|
||||||
|
.cm-editor .cm-scroller {
|
||||||
|
@apply font-mono text-sm;
|
||||||
|
}
|
||||||
Vendored
+90
@@ -0,0 +1,90 @@
|
|||||||
|
export interface ElectronAPI {
|
||||||
|
// App info
|
||||||
|
getAppVersion: () => Promise<string>;
|
||||||
|
|
||||||
|
// File operations
|
||||||
|
openFile: () => Promise<{ canceled: boolean; filePaths: string[] }>;
|
||||||
|
saveFile: (data: { path: string; content: string }) => Promise<void>;
|
||||||
|
readFile: (path: string) => Promise<string>;
|
||||||
|
setCurrentFile: (path: string | null) => void;
|
||||||
|
|
||||||
|
// Export
|
||||||
|
exportDocument: (format: string, options?: Record<string, any>) => Promise<void>;
|
||||||
|
|
||||||
|
// Theme
|
||||||
|
getTheme: () => void;
|
||||||
|
onThemeChanged: (callback: (theme: string) => void) => () => void;
|
||||||
|
|
||||||
|
// File events
|
||||||
|
onFileOpened: (callback: (data: { path: string; content: string }) => void) => () => void;
|
||||||
|
onFileNew: (callback: () => void) => () => void;
|
||||||
|
onFileSave: (callback: () => void) => () => void;
|
||||||
|
|
||||||
|
// Conversion status
|
||||||
|
onConversionStatus: (callback: (message: string) => void) => () => void;
|
||||||
|
onConversionComplete: (callback: (data: { format: string; outputPath: string }) => void) => () => void;
|
||||||
|
|
||||||
|
// Dialogs
|
||||||
|
showExportDialog: (format: string) => void;
|
||||||
|
showBatchDialog: () => void;
|
||||||
|
showUniversalConverterDialog: () => void;
|
||||||
|
showTableGenerator: () => void;
|
||||||
|
showPdfEditorDialog: () => void;
|
||||||
|
showHeaderFooterDialog: () => void;
|
||||||
|
showFieldPickerDialog: () => void;
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
getSettings: (key: string) => Promise<any>;
|
||||||
|
setSettings: (key: string, value: any) => Promise<void>;
|
||||||
|
|
||||||
|
// Plugin settings
|
||||||
|
getPluginSetting: (key: string) => Promise<any>;
|
||||||
|
setPluginSetting: (key: string, value: any) => Promise<void>;
|
||||||
|
|
||||||
|
// Sidebar
|
||||||
|
toggleSidebar: () => void;
|
||||||
|
toggleBottomPanel: () => void;
|
||||||
|
|
||||||
|
// Print
|
||||||
|
doPrint: (options?: any) => void;
|
||||||
|
|
||||||
|
// PDF
|
||||||
|
openPdfViewer: (filePath: string) => void;
|
||||||
|
processPdfOperation: (data: any) => void;
|
||||||
|
|
||||||
|
// Images
|
||||||
|
selectFolder: () => Promise<string | null>;
|
||||||
|
savePastedImage: (data: { base64: string; ext: string }) => Promise<{ relativePath: string } | null>;
|
||||||
|
|
||||||
|
// Templates
|
||||||
|
loadTemplate: (file: string) => Promise<string>;
|
||||||
|
getSnippets: () => Promise<any[]>;
|
||||||
|
saveSnippet: (snippet: any) => Promise<void>;
|
||||||
|
deleteSnippet: (id: string) => Promise<void>;
|
||||||
|
|
||||||
|
// Git
|
||||||
|
gitStatus: () => Promise<any>;
|
||||||
|
gitDiff: (file: string) => Promise<any>;
|
||||||
|
gitStage: (files: string[]) => Promise<void>;
|
||||||
|
gitCommit: (message: string) => Promise<void>;
|
||||||
|
gitLog: () => Promise<any[]>;
|
||||||
|
|
||||||
|
// Directory
|
||||||
|
listDirectory: (dir: string) => Promise<any[] | null>;
|
||||||
|
openFilePath: (path: string) => void;
|
||||||
|
|
||||||
|
// Custom CSS
|
||||||
|
selectCustomCSS: () => Promise<string | null>;
|
||||||
|
loadCustomCSS: () => void;
|
||||||
|
clearCustomCSS: () => void;
|
||||||
|
|
||||||
|
// Generic invoke/on
|
||||||
|
invoke: (channel: string, data?: any) => Promise<any>;
|
||||||
|
on: (channel: string, callback: (...args: any[]) => void) => () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
electronAPI: ElectronAPI;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
darkMode: ['class'],
|
||||||
|
content: [
|
||||||
|
'./src/renderer/**/*.{js,ts,jsx,tsx}',
|
||||||
|
'./src/renderer/index.html',
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
brand: {
|
||||||
|
DEFAULT: '#e5461f',
|
||||||
|
dark: '#c93a18',
|
||||||
|
light: '#ff6b47',
|
||||||
|
},
|
||||||
|
success: '#1a7a56',
|
||||||
|
warning: '#eab308',
|
||||||
|
danger: '#ef4444',
|
||||||
|
info: '#0ea5e9',
|
||||||
|
warm: {
|
||||||
|
50: '#fafbfc',
|
||||||
|
100: '#f5f5f5',
|
||||||
|
200: '#e3e3e3',
|
||||||
|
300: '#d1d1d1',
|
||||||
|
400: '#b0b0b0',
|
||||||
|
500: '#9a9696',
|
||||||
|
600: '#7a7878',
|
||||||
|
700: '#5a5858',
|
||||||
|
800: '#4e4e4e',
|
||||||
|
900: '#464646',
|
||||||
|
950: '#0d0b09',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['"Plus Jakarta Sans"', 'system-ui', 'sans-serif'],
|
||||||
|
display: ['"Barlow Condensed"', 'system-ui', 'sans-serif'],
|
||||||
|
mono: ['"JetBrains Mono"', '"Fira Code"', '"SF Mono"', 'Consolas', 'monospace'],
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: '0.75rem',
|
||||||
|
md: '0.5rem',
|
||||||
|
sm: '0.25rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [require('tailwindcss-animate')],
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
root: path.join(__dirname, 'src/renderer'),
|
||||||
|
base: './',
|
||||||
|
build: {
|
||||||
|
outDir: path.join(__dirname, 'dist/renderer'),
|
||||||
|
emptyOutDir: true,
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.join(__dirname, 'src/renderer'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 5173,
|
||||||
|
strictPort: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user