diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e75a4ff3e..05d583b6d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,18 @@ -import './index.css'; import React from 'react'; +import { MantineProvider } from '@mantine/core'; +import { mantineTheme } from './theme/mantineTheme'; import HomePage from './pages/HomePage'; + +// Import global styles +import './styles/theme.css'; +import './styles/tailwind.css'; +import './styles/components.css'; +import './index.css'; + export default function App() { - return ; + return ( + + + + ); } diff --git a/frontend/src/components/TopControls.tsx b/frontend/src/components/TopControls.tsx index 1b9822bf3..edd013f47 100644 --- a/frontend/src/components/TopControls.tsx +++ b/frontend/src/components/TopControls.tsx @@ -48,28 +48,8 @@ const TopControls: React.FC = ({ const { colorScheme, toggleColorScheme } = useMantineColorScheme(); return ( -
-
+
+
-
- +
+
+ +
); diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index 9ceec9948..e6d761cd5 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -78,14 +78,12 @@ export default function HomePage() { {/* Left: Tool Picker */} {sidebarsVisible && ( + {/* Top Controls */} - + {(currentView === "viewer" || currentView === "pageEditor") && !pdfFile ? ( setSidebarsVisible((v) => !v)} > {t("sidebar.toggle", sidebarsVisible ? "Hide Sidebars" : "Show Sidebars")} diff --git a/frontend/src/styles/HomePage.module.css b/frontend/src/styles/HomePage.module.css index 5ffd91e12..51c69670c 100644 --- a/frontend/src/styles/HomePage.module.css +++ b/frontend/src/styles/HomePage.module.css @@ -1,95 +1,13 @@ -/* Main container */ -.container { - min-height: 100vh; - width: 100vw; - overflow: hidden; - flex-wrap: nowrap; - display: flex; -} +/* Use Tailwind for most styles, keep only complex layouts in CSS modules */ -/* Left sidebar */ .leftSidebar { - min-width: 180px; - max-width: 240px; - width: 16vw; - height: 100vh; - z-index: 101; - display: flex; - flex-direction: column; + min-width: var(--sidebar-width-min); + max-width: var(--sidebar-width-max); + width: var(--sidebar-width); } -.leftSidebarLight { - border-right: 1px solid #e9ecef; - background: #fff; -} - -.leftSidebarDark { - border-right: 1px solid var(--mantine-color-dark-4); - background: var(--mantine-color-dark-7); -} - -/* Main content area */ -.mainContent { - flex: 1; - height: 100vh; - min-width: 20rem; - position: relative; - display: flex; - flex-direction: column; - transition: all 0.3s; -} - -.mainContentLight { - background: #f8f9fa; -} - -.mainContentDark { - background: var(--mantine-color-dark-6); -} - -/* Main paper container */ -.mainPaper { - flex: 1; - min-height: 0; - margin-top: 0; - box-sizing: border-box; - overflow: hidden; - display: flex; - flex-direction: column; -} - -.mainPaperInner { - flex: 1; - min-height: 0; -} - -/* Right sidebar */ .rightSidebar { min-width: 260px; max-width: 400px; width: 22vw; - height: 100vh; - padding: 24px; - gap: 16px; - z-index: 100; - display: flex; - flex-direction: column; } - -.rightSidebarLight { - border-left: 1px solid #e9ecef; - background: #fff; -} - -.rightSidebarDark { - border-left: 1px solid var(--mantine-color-dark-4); - background: var(--mantine-color-dark-7); -} - -/* Sidebar toggle button */ -.sidebarToggle { - position: fixed; - top: 16px; - right: 16px; - z-index: 200; -} \ No newline at end of file diff --git a/frontend/src/styles/components.css b/frontend/src/styles/components.css new file mode 100644 index 000000000..4eb82cb55 --- /dev/null +++ b/frontend/src/styles/components.css @@ -0,0 +1,123 @@ +/* Custom component styles that don't fit Tailwind patterns */ + +/* Mantine component overrides */ +.mantine-override { + /* Custom overrides for Mantine components when needed */ +} + +/* PDF-specific custom components */ +.pdf-page-thumbnail { + aspect-ratio: 3/4; + background: var(--bg-surface); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-sm); + overflow: hidden; + position: relative; +} + +.pdf-page-thumbnail:hover { + border-color: var(--color-primary-500); + box-shadow: var(--shadow-md); +} + +.pdf-page-thumbnail img { + width: 100%; + height: 100%; + object-fit: cover; +} + +/* Loading spinner for PDF operations */ +.pdf-loading { + display: flex; + align-items: center; + justify-content: center; + min-height: 200px; + background: var(--bg-surface); + border-radius: var(--radius-md); +} + +/* Custom scrollbar for PDF viewer */ +.pdf-viewer-container { + scrollbar-width: thin; + scrollbar-color: var(--border-strong) var(--bg-muted); +} + +.pdf-viewer-container::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +.pdf-viewer-container::-webkit-scrollbar-track { + background: var(--bg-muted); + border-radius: var(--radius-md); +} + +.pdf-viewer-container::-webkit-scrollbar-thumb { + background: var(--border-strong); + border-radius: var(--radius-md); +} + +.pdf-viewer-container::-webkit-scrollbar-thumb:hover { + background: var(--color-primary-500); +} + +/* File upload drag and drop animations */ +.file-drop-zone.drag-over { + animation: pulse 1s infinite; +} + +@keyframes pulse { + 0%, 100% { + transform: scale(1); + } + 50% { + transform: scale(1.02); + } +} + +/* Progress bar for operations */ +.progress-bar { + width: 100%; + height: 4px; + background: var(--bg-muted); + border-radius: var(--radius-full); + overflow: hidden; +} + +.progress-bar-fill { + height: 100%; + background: var(--color-primary-500); + border-radius: var(--radius-full); + transition: width 0.3s ease; +} + +/* Tool-specific layouts that need custom CSS */ +.split-preview-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: var(--space-md); + padding: var(--space-md); + background: var(--bg-surface); + border-radius: var(--radius-lg); + border: 1px solid var(--border-subtle); +} + +/* Error states */ +.error-state { + padding: var(--space-lg); + text-align: center; + background: var(--bg-surface); + border: 1px solid var(--color-error); + border-radius: var(--radius-md); + color: var(--color-error); +} + +/* Success states */ +.success-state { + padding: var(--space-lg); + text-align: center; + background: var(--bg-surface); + border: 1px solid var(--color-success); + border-radius: var(--radius-md); + color: var(--color-success); +} \ No newline at end of file diff --git a/frontend/src/styles/tailwind.css b/frontend/src/styles/tailwind.css new file mode 100644 index 000000000..3a32f3d32 --- /dev/null +++ b/frontend/src/styles/tailwind.css @@ -0,0 +1,66 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Custom component classes using Tailwind (with app- prefix to avoid Mantine conflicts) */ +@layer components { + .app-card { + @apply bg-bg-surface border border-border-subtle rounded-app-lg p-app-lg shadow-app-md; + } + + .app-surface { + @apply bg-bg-surface border border-border-subtle rounded-app-md shadow-app-sm; + } + + .app-raised { + @apply bg-bg-raised shadow-app-sm rounded-app-md; + } + + .app-interactive { + @apply cursor-pointer transition-all duration-200 hover:bg-hover-bg active:bg-active-bg; + } + + .app-file-drop-zone { + @apply border-2 border-dashed border-file-drop rounded-app-lg bg-bg-surface transition-all duration-200; + } + + .app-file-drop-zone:hover, + .app-file-drop-zone.drag-over { + @apply border-app-primary-500 bg-file-drop-hover; + } + + /* PDF-specific component classes */ + .app-pdf-viewer-bg { + @apply bg-pdf-viewer; + } + + .app-pdf-toolbar { + @apply bg-pdf-toolbar border-b border-border-subtle; + } + + /* Button variants */ + .app-btn-pdf-tool { + @apply bg-bg-surface border border-border-default text-text-primary px-app-md py-app-sm rounded-app-md font-medium transition-all duration-200 hover:bg-hover-bg hover:border-app-primary-500; + } + + /* Focus styles */ + .app-focus-ring { + @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2; + } +} + +/* Custom utilities */ +@layer utilities { + .text-balance { + text-wrap: balance; + } + + .scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; + } + + .scrollbar-hide::-webkit-scrollbar { + display: none; + } +} \ No newline at end of file diff --git a/frontend/src/styles/theme.css b/frontend/src/styles/theme.css new file mode 100644 index 000000000..42345f479 --- /dev/null +++ b/frontend/src/styles/theme.css @@ -0,0 +1,160 @@ +/* Design System CSS Variables - Single Source of Truth */ + +:root { + /* Color System */ + --color-primary-50: #eff6ff; + --color-primary-100: #dbeafe; + --color-primary-200: #bfdbfe; + --color-primary-300: #93c5fd; + --color-primary-400: #60a5fa; + --color-primary-500: #3b82f6; + --color-primary-600: #2563eb; + --color-primary-700: #1d4ed8; + --color-primary-800: #1e40af; + --color-primary-900: #1e3a8a; + + --color-gray-50: #f9fafb; + --color-gray-100: #f3f4f6; + --color-gray-200: #e5e7eb; + --color-gray-300: #d1d5db; + --color-gray-400: #9ca3af; + --color-gray-500: #6b7280; + --color-gray-600: #4b5563; + --color-gray-700: #374151; + --color-gray-800: #1f2937; + --color-gray-900: #111827; + + /* Semantic Colors */ + --color-success: #10b981; + --color-warning: #f59e0b; + --color-error: #ef4444; + --color-info: #3b82f6; + + /* Spacing System */ + --space-xs: 4px; + --space-sm: 8px; + --space-md: 16px; + --space-lg: 24px; + --space-xl: 32px; + --space-2xl: 48px; + + /* Radius System */ + --radius-xs: 2px; + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-2xl: 24px; + --radius-full: 9999px; + + /* Shadow System */ + --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05); + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1); + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); + --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1); + + /* Layout */ + --sidebar-width-min: 180px; + --sidebar-width-max: 240px; + --sidebar-width: 16vw; + --header-height: 60px; + --border-width: 1px; + + /* Typography */ + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + + /* Z-Index Scale */ + --z-dropdown: 1000; + --z-sticky: 1020; + --z-fixed: 1030; + --z-modal-backdrop: 1040; + --z-modal: 1050; + --z-popover: 1060; + --z-tooltip: 1070; +} + +/* Light Theme */ +:root, [data-mantine-color-scheme="light"] { + /* Background Colors */ + --bg-app: var(--color-gray-50); + --bg-surface: #ffffff; + --bg-raised: var(--color-gray-50); + --bg-overlay: rgba(255, 255, 255, 0.8); + --bg-muted: var(--color-gray-100); + + /* Text Colors */ + --text-primary: var(--color-gray-900); + --text-secondary: var(--color-gray-600); + --text-muted: var(--color-gray-500); + --text-inverse: #ffffff; + + /* Border Colors */ + --border-subtle: var(--color-gray-200); + --border-default: var(--color-gray-300); + --border-strong: var(--color-gray-400); + + /* Interactive States */ + --hover-bg: var(--color-gray-50); + --active-bg: var(--color-gray-100); + --focus-ring: var(--color-primary-500); + + /* PDF Tool Specific */ + --pdf-viewer-bg: #f8fafc; + --pdf-toolbar-bg: var(--bg-surface); + --file-drop-border: var(--color-gray-300); + --file-drop-hover: var(--color-primary-100); +} + +/* Dark Theme */ +[data-mantine-color-scheme="dark"] { + /* Background Colors */ + --bg-app: var(--color-gray-900); + --bg-surface: var(--color-gray-800); + --bg-raised: var(--color-gray-700); + --bg-overlay: rgba(17, 24, 39, 0.8); + --bg-muted: var(--color-gray-700); + + /* Text Colors */ + --text-primary: var(--color-gray-50); + --text-secondary: var(--color-gray-300); + --text-muted: var(--color-gray-400); + --text-inverse: var(--color-gray-900); + + /* Border Colors */ + --border-subtle: var(--color-gray-700); + --border-default: var(--color-gray-600); + --border-strong: var(--color-gray-500); + + /* Interactive States */ + --hover-bg: var(--color-gray-700); + --active-bg: var(--color-gray-600); + --focus-ring: var(--color-primary-400); + + /* PDF Tool Specific */ + --pdf-viewer-bg: var(--color-gray-800); + --pdf-toolbar-bg: var(--bg-surface); + --file-drop-border: var(--color-gray-600); + --file-drop-hover: var(--color-primary-900); + + /* Shadow adjustments for dark mode */ + --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4); + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4); + --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.4); + --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.4); +} + +/* Smooth transitions for theme switching */ +* { + transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease; +} + +/* Focus styles */ +*:focus-visible { + outline: 2px solid var(--focus-ring); + outline-offset: 2px; +} \ No newline at end of file diff --git a/frontend/src/theme/mantineTheme.ts b/frontend/src/theme/mantineTheme.ts new file mode 100644 index 000000000..017b7d737 --- /dev/null +++ b/frontend/src/theme/mantineTheme.ts @@ -0,0 +1,281 @@ +import { createTheme, MantineColorsTuple } from '@mantine/core'; + +// Define color tuples using CSS variables +const primary: MantineColorsTuple = [ + 'var(--color-primary-50)', + 'var(--color-primary-100)', + 'var(--color-primary-200)', + 'var(--color-primary-300)', + 'var(--color-primary-400)', + 'var(--color-primary-500)', + 'var(--color-primary-600)', + 'var(--color-primary-700)', + 'var(--color-primary-800)', + 'var(--color-primary-900)', +]; + +const gray: MantineColorsTuple = [ + 'var(--color-gray-50)', + 'var(--color-gray-100)', + 'var(--color-gray-200)', + 'var(--color-gray-300)', + 'var(--color-gray-400)', + 'var(--color-gray-500)', + 'var(--color-gray-600)', + 'var(--color-gray-700)', + 'var(--color-gray-800)', + 'var(--color-gray-900)', +]; + +export const mantineTheme = createTheme({ + // Primary color + primaryColor: 'primary', + + // Color palette + colors: { + primary, + gray, + }, + + // Spacing system - uses CSS variables + spacing: { + xs: 'var(--space-xs)', + sm: 'var(--space-sm)', + md: 'var(--space-md)', + lg: 'var(--space-lg)', + xl: 'var(--space-xl)', + }, + + // Border radius system + radius: { + xs: 'var(--radius-xs)', + sm: 'var(--radius-sm)', + md: 'var(--radius-md)', + lg: 'var(--radius-lg)', + xl: 'var(--radius-xl)', + }, + + // Shadow system + shadows: { + xs: 'var(--shadow-xs)', + sm: 'var(--shadow-sm)', + md: 'var(--shadow-md)', + lg: 'var(--shadow-lg)', + xl: 'var(--shadow-xl)', + }, + + // Font weights + fontWeights: { + normal: 'var(--font-weight-normal)', + medium: 'var(--font-weight-medium)', + semibold: 'var(--font-weight-semibold)', + bold: 'var(--font-weight-bold)', + }, + + // Component customizations + components: { + Button: { + styles: { + root: { + fontWeight: 'var(--font-weight-medium)', + transition: 'all 0.2s ease', + }, + }, + variants: { + // Custom button variant for PDF tools + pdfTool: (theme) => ({ + root: { + backgroundColor: 'var(--bg-surface)', + border: '1px solid var(--border-default)', + color: 'var(--text-primary)', + '&:hover': { + backgroundColor: 'var(--hover-bg)', + borderColor: 'var(--color-primary-500)', + }, + }, + }), + }, + }, + + Paper: { + styles: { + root: { + backgroundColor: 'var(--bg-surface)', + border: '1px solid var(--border-subtle)', + }, + }, + }, + + Card: { + styles: { + root: { + backgroundColor: 'var(--bg-surface)', + border: '1px solid var(--border-subtle)', + boxShadow: 'var(--shadow-sm)', + }, + }, + }, + + TextInput: { + styles: { + input: { + backgroundColor: 'var(--bg-surface)', + borderColor: 'var(--border-default)', + color: 'var(--text-primary)', + '&:focus': { + borderColor: 'var(--color-primary-500)', + boxShadow: '0 0 0 1px var(--color-primary-500)', + }, + }, + label: { + color: 'var(--text-secondary)', + fontWeight: 'var(--font-weight-medium)', + }, + }, + }, + + Select: { + styles: { + input: { + backgroundColor: 'var(--bg-surface)', + borderColor: 'var(--border-default)', + color: 'var(--text-primary)', + '&:focus': { + borderColor: 'var(--color-primary-500)', + boxShadow: '0 0 0 1px var(--color-primary-500)', + }, + }, + label: { + color: 'var(--text-secondary)', + fontWeight: 'var(--font-weight-medium)', + }, + dropdown: { + backgroundColor: 'var(--bg-surface)', + borderColor: 'var(--border-subtle)', + boxShadow: 'var(--shadow-lg)', + }, + option: { + color: 'var(--text-primary)', + '&[data-hovered]': { + backgroundColor: 'var(--hover-bg)', + }, + '&[data-selected]': { + backgroundColor: 'var(--color-primary-100)', + color: 'var(--color-primary-900)', + }, + }, + }, + }, + + Checkbox: { + styles: { + input: { + borderColor: 'var(--border-default)', + '&:checked': { + backgroundColor: 'var(--color-primary-500)', + borderColor: 'var(--color-primary-500)', + }, + }, + label: { + color: 'var(--text-primary)', + }, + }, + }, + + Slider: { + styles: { + track: { + backgroundColor: 'var(--bg-muted)', + }, + bar: { + backgroundColor: 'var(--color-primary-500)', + }, + thumb: { + backgroundColor: 'var(--color-primary-500)', + borderColor: 'var(--color-primary-500)', + }, + mark: { + borderColor: 'var(--border-default)', + }, + markLabel: { + color: 'var(--text-muted)', + }, + }, + }, + + Modal: { + styles: { + content: { + backgroundColor: 'var(--bg-surface)', + border: '1px solid var(--border-subtle)', + boxShadow: 'var(--shadow-xl)', + }, + header: { + backgroundColor: 'var(--bg-surface)', + borderBottom: '1px solid var(--border-subtle)', + }, + title: { + color: 'var(--text-primary)', + fontWeight: 'var(--font-weight-semibold)', + }, + }, + }, + + Notification: { + styles: { + root: { + backgroundColor: 'var(--bg-surface)', + border: '1px solid var(--border-subtle)', + boxShadow: 'var(--shadow-lg)', + }, + title: { + color: 'var(--text-primary)', + }, + description: { + color: 'var(--text-secondary)', + }, + }, + }, + + SegmentedControl: { + styles: { + root: { + backgroundColor: 'var(--bg-muted)', + border: '1px solid var(--border-subtle)', + }, + control: { + color: 'var(--text-secondary)', + '&[data-active]': { + backgroundColor: 'var(--bg-surface)', + color: 'var(--text-primary)', + boxShadow: 'var(--shadow-sm)', + }, + }, + }, + }, + }, + + // Global styles + globalStyles: () => ({ + // Ensure smooth color transitions + '*': { + transition: 'background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease', + }, + + // Custom scrollbar styling + '*::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '*::-webkit-scrollbar-track': { + backgroundColor: 'var(--bg-muted)', + }, + '*::-webkit-scrollbar-thumb': { + backgroundColor: 'var(--border-strong)', + borderRadius: 'var(--radius-md)', + }, + '*::-webkit-scrollbar-thumb:hover': { + backgroundColor: 'var(--color-primary-500)', + }, + }), +}); \ No newline at end of file diff --git a/frontend/src/tools/Split.tsx b/frontend/src/tools/Split.tsx index 37b4efe8c..2e3dbbe24 100644 --- a/frontend/src/tools/Split.tsx +++ b/frontend/src/tools/Split.tsx @@ -143,7 +143,7 @@ const SplitPdfPanel: React.FC = ({ }; return ( -
+