Merge branch 'V2' into settingsPageEnhanced

Resolved conflicts by:
- Kept invite link feature (inviteLinkExpiryHours in ApplicationProperties)
- Kept enhanced email invite validation (both SMTP and invites enabled check)
- Adopted V2's Posthog/Scarf tracking additions
- Adopted V2's password length validation (min 6 chars) on both backend and frontend
- Converted email templates to Java text blocks
- Kept success message functionality in Login flow
- Used @app path aliases consistently across frontend files
- Fixed remaining relative imports in Workbench, RestartConfirmationModal, useRestartServer, and InviteAccept
- Accepted deletion of refactored files (App.tsx, Landing.tsx, etc.)
- Kept InviteAccept.tsx for invite link feature
This commit is contained in:
Anthony Stirling
2025-10-28 20:45:29 +00:00
769 changed files with 4650 additions and 3220 deletions

View File

@@ -1,38 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@@ -1,119 +0,0 @@
import { Suspense } from "react";
import { Routes, Route } from "react-router-dom";
import { RainbowThemeProvider } from "./components/shared/RainbowThemeProvider";
import { FileContextProvider } from "./contexts/FileContext";
import { NavigationProvider } from "./contexts/NavigationContext";
import { ToolRegistryProvider } from "./contexts/ToolRegistryProvider";
import { FilesModalProvider } from "./contexts/FilesModalContext";
import { ToolWorkflowProvider } from "./contexts/ToolWorkflowContext";
import { HotkeyProvider } from "./contexts/HotkeyContext";
import { SidebarProvider } from "./contexts/SidebarContext";
import { PreferencesProvider } from "./contexts/PreferencesContext";
import { OnboardingProvider } from "./contexts/OnboardingContext";
import { TourOrchestrationProvider } from "./contexts/TourOrchestrationContext";
import ErrorBoundary from "./components/shared/ErrorBoundary";
import OnboardingTour from "./components/onboarding/OnboardingTour";
// Import auth components
import { AuthProvider } from "./auth/UseSession";
import Landing from "./routes/Landing";
import Login from "./routes/Login";
import Signup from "./routes/Signup";
import AuthCallback from "./routes/AuthCallback";
import InviteAccept from "./routes/InviteAccept";
// Import global styles
import "./styles/tailwind.css";
import "./index.css";
// Load cookieconsent.css optionally - won't block UI if ad blocker blocks it
const loadOptionalCSS = () => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '/src/styles/cookieconsent.css';
link.onerror = () => {
console.debug('Cookie consent styles blocked by ad blocker - continuing without them');
};
document.head.appendChild(link);
};
// Load it once when app initializes
if (typeof document !== 'undefined') {
loadOptionalCSS();
}
import { RightRailProvider } from "./contexts/RightRailContext";
import { ViewerProvider } from "./contexts/ViewerContext";
import { SignatureProvider } from "./contexts/SignatureContext";
// Import file ID debugging helpers (development only)
import "./utils/fileIdSafety";
// Loading component for i18next suspense
const LoadingFallback = () => (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
fontSize: "18px",
color: "#666",
}}
>
Loading...
</div>
);
export default function App() {
return (
<Suspense fallback={<LoadingFallback />}>
<PreferencesProvider>
<RainbowThemeProvider>
<ErrorBoundary>
<AuthProvider>
<Routes>
{/* Auth routes - no FileContext or other providers needed */}
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
<Route path="/invite" element={<InviteAccept />} />
<Route path="/auth/callback" element={<AuthCallback />} />
{/* Main app routes - wrapped with all providers */}
<Route
path="/*"
element={
<OnboardingProvider>
<FileContextProvider enableUrlSync={true} enablePersistence={true}>
<ToolRegistryProvider>
<NavigationProvider>
<FilesModalProvider>
<ToolWorkflowProvider>
<HotkeyProvider>
<SidebarProvider>
<ViewerProvider>
<SignatureProvider>
<RightRailProvider>
<TourOrchestrationProvider>
<Landing />
<OnboardingTour />
</TourOrchestrationProvider>
</RightRailProvider>
</SignatureProvider>
</ViewerProvider>
</SidebarProvider>
</HotkeyProvider>
</ToolWorkflowProvider>
</FilesModalProvider>
</NavigationProvider>
</ToolRegistryProvider>
</FileContextProvider>
</OnboardingProvider>
}
/>
</Routes>
</AuthProvider>
</ErrorBoundary>
</RainbowThemeProvider>
</PreferencesProvider>
</Suspense>
);
}

24
frontend/src/core/App.tsx Normal file
View File

@@ -0,0 +1,24 @@
import { Suspense } from "react";
import { AppProviders } from "@app/components/AppProviders";
import { LoadingFallback } from "@app/components/shared/LoadingFallback";
import HomePage from "@app/pages/HomePage";
import OnboardingTour from "@app/components/onboarding/OnboardingTour";
// Import global styles
import "@app/styles/tailwind.css";
import "@app/styles/cookieconsent.css";
import "@app/styles/index.css";
// Import file ID debugging helpers (development only)
import "@app/utils/fileIdSafety";
export default function App() {
return (
<Suspense fallback={<LoadingFallback />}>
<AppProviders>
<HomePage />
<OnboardingTour />
</AppProviders>
</Suspense>
);
}

View File

@@ -0,0 +1,67 @@
import { ReactNode } from "react";
import { RainbowThemeProvider } from "@app/components/shared/RainbowThemeProvider";
import { FileContextProvider } from "@app/contexts/FileContext";
import { NavigationProvider } from "@app/contexts/NavigationContext";
import { ToolRegistryProvider } from "@app/contexts/ToolRegistryProvider";
import { FilesModalProvider } from "@app/contexts/FilesModalContext";
import { ToolWorkflowProvider } from "@app/contexts/ToolWorkflowContext";
import { HotkeyProvider } from "@app/contexts/HotkeyContext";
import { SidebarProvider } from "@app/contexts/SidebarContext";
import { PreferencesProvider } from "@app/contexts/PreferencesContext";
import { AppConfigProvider } from "@app/contexts/AppConfigContext";
import { RightRailProvider } from "@app/contexts/RightRailContext";
import { ViewerProvider } from "@app/contexts/ViewerContext";
import { SignatureProvider } from "@app/contexts/SignatureContext";
import { OnboardingProvider } from "@app/contexts/OnboardingContext";
import { TourOrchestrationProvider } from "@app/contexts/TourOrchestrationContext";
import ErrorBoundary from "@app/components/shared/ErrorBoundary";
import { useScarfTracking } from "@app/hooks/useScarfTracking";
// Component to initialize scarf tracking (must be inside AppConfigProvider)
function ScarfTrackingInitializer() {
useScarfTracking();
return null;
}
/**
* Core application providers
* Contains all providers needed for the core
*/
export function AppProviders({ children }: { children: ReactNode }) {
return (
<PreferencesProvider>
<RainbowThemeProvider>
<ErrorBoundary>
<OnboardingProvider>
<AppConfigProvider>
<ScarfTrackingInitializer />
<FileContextProvider enableUrlSync={true} enablePersistence={true}>
<ToolRegistryProvider>
<NavigationProvider>
<FilesModalProvider>
<ToolWorkflowProvider>
<HotkeyProvider>
<SidebarProvider>
<ViewerProvider>
<SignatureProvider>
<RightRailProvider>
<TourOrchestrationProvider>
{children}
</TourOrchestrationProvider>
</RightRailProvider>
</SignatureProvider>
</ViewerProvider>
</SidebarProvider>
</HotkeyProvider>
</ToolWorkflowProvider>
</FilesModalProvider>
</NavigationProvider>
</ToolRegistryProvider>
</FileContextProvider>
</AppConfigProvider>
</OnboardingProvider>
</ErrorBoundary>
</RainbowThemeProvider>
</PreferencesProvider>
);
}

View File

@@ -1,17 +1,17 @@
import React, { useState, useCallback, useEffect } from 'react';
import { Modal } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { StirlingFileStub } from '../types/fileContext';
import { useFileManager } from '../hooks/useFileManager';
import { useFilesModalContext } from '../contexts/FilesModalContext';
import { Tool } from '../types/tool';
import MobileLayout from './fileManager/MobileLayout';
import DesktopLayout from './fileManager/DesktopLayout';
import DragOverlay from './fileManager/DragOverlay';
import { FileManagerProvider } from '../contexts/FileManagerContext';
import { Z_INDEX_FILE_MANAGER_MODAL } from '../styles/zIndex';
import { isGoogleDriveConfigured } from '../services/googleDrivePickerService';
import { loadScript } from '../utils/scriptLoader';
import { StirlingFileStub } from '@app/types/fileContext';
import { useFileManager } from '@app/hooks/useFileManager';
import { useFilesModalContext } from '@app/contexts/FilesModalContext';
import { Tool } from '@app/types/tool';
import MobileLayout from '@app/components/fileManager/MobileLayout';
import DesktopLayout from '@app/components/fileManager/DesktopLayout';
import DragOverlay from '@app/components/fileManager/DragOverlay';
import { FileManagerProvider } from '@app/contexts/FileManagerContext';
import { Z_INDEX_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
import { isGoogleDriveConfigured } from '@app/services/googleDrivePickerService';
import { loadScript } from '@app/utils/scriptLoader';
interface FileManagerProps {
selectedTool?: Tool | null;

View File

@@ -3,9 +3,9 @@ import { Card, Group, Text, Button, Progress } from "@mantine/core";
import { useTranslation } from "react-i18next";
import StorageIcon from "@mui/icons-material/Storage";
import DeleteIcon from "@mui/icons-material/Delete";
import { StorageStats } from "../services/fileStorage";
import { formatFileSize } from "../utils/fileUtils";
import { getStorageUsagePercent } from "../utils/storageUtils";
import { StorageStats } from "@app/services/fileStorage";
import { formatFileSize } from "@app/utils/fileUtils";
import { getStorageUsagePercent } from "@app/utils/storageUtils";
interface StorageStatsCardProps {
storageStats: StorageStats | null;

View File

@@ -1,9 +1,9 @@
import React, { useState } from 'react';
import { Stack, Alert, Text } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { DrawingControls } from './DrawingControls';
import { ColorPicker } from './ColorPicker';
import { usePDFAnnotation } from '../providers/PDFAnnotationProvider';
import { DrawingControls } from '@app/components/annotation/shared/DrawingControls';
import { ColorPicker } from '@app/components/annotation/shared/ColorPicker';
import { usePDFAnnotation } from '@app/components/annotation/providers/PDFAnnotationProvider';
export interface AnnotationToolConfig {
enableDrawing?: boolean;

View File

@@ -1,7 +1,7 @@
import React, { useRef, useState } from 'react';
import { Paper, Button, Modal, Stack, Text, Popover, ColorPicker as MantineColorPicker } from '@mantine/core';
import { ColorSwatchButton } from './ColorPicker';
import PenSizeSelector from '../../tools/sign/PenSizeSelector';
import { ColorSwatchButton } from '@app/components/annotation/shared/ColorPicker';
import PenSizeSelector from '@app/components/tools/sign/PenSizeSelector';
import SignaturePad from 'signature_pad';
interface DrawingCanvasProps {

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { Stack, TextInput, Select, Combobox, useCombobox, Group, Box } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { ColorPicker } from './ColorPicker';
import { ColorPicker } from '@app/components/annotation/shared/ColorPicker';
interface TextInputWithFontProps {
text: string;

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Stack } from '@mantine/core';
import { BaseAnnotationTool } from '../shared/BaseAnnotationTool';
import { DrawingCanvas } from '../shared/DrawingCanvas';
import { BaseAnnotationTool } from '@app/components/annotation/shared/BaseAnnotationTool';
import { DrawingCanvas } from '@app/components/annotation/shared/DrawingCanvas';
interface DrawingToolProps {
onDrawingChange?: (data: string | null) => void;

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Stack } from '@mantine/core';
import { BaseAnnotationTool } from '../shared/BaseAnnotationTool';
import { ImageUploader } from '../shared/ImageUploader';
import { BaseAnnotationTool } from '@app/components/annotation/shared/BaseAnnotationTool';
import { ImageUploader } from '@app/components/annotation/shared/ImageUploader';
interface ImageToolProps {
onImageChange?: (data: string | null) => void;

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Stack } from '@mantine/core';
import { BaseAnnotationTool } from '../shared/BaseAnnotationTool';
import { TextInputWithFont } from '../shared/TextInputWithFont';
import { BaseAnnotationTool } from '@app/components/annotation/shared/BaseAnnotationTool';
import { TextInputWithFont } from '@app/components/annotation/shared/TextInputWithFont';
interface TextToolProps {
onTextChange?: (text: string) => void;

View File

@@ -2,10 +2,10 @@ import React, { useRef, useState } from 'react';
import { Button, Group, useMantineColorScheme } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import AddIcon from '@mui/icons-material/Add';
import { useFilesModalContext } from '../../contexts/FilesModalContext';
import LocalIcon from '../shared/LocalIcon';
import { BASE_PATH } from '../../constants/app';
import styles from './FileEditor.module.css';
import { useFilesModalContext } from '@app/contexts/FilesModalContext';
import LocalIcon from '@app/components/shared/LocalIcon';
import { BASE_PATH } from '@app/constants/app';
import styles from '@app/components/fileEditor/FileEditor.module.css';
interface AddFileCardProps {
onFileSelect: (files: File[]) => void;

View File

@@ -1,19 +1,19 @@
import React, { useState, useCallback, useRef, useMemo, useEffect } from 'react';
import { useState, useCallback, useRef, useMemo, useEffect } from 'react';
import {
Text, Center, Box, LoadingOverlay, Stack
} from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import { useFileSelection, useFileState, useFileManagement, useFileActions, useFileContext } from '../../contexts/FileContext';
import { useNavigationActions } from '../../contexts/NavigationContext';
import { zipFileService } from '../../services/zipFileService';
import { detectFileExtension } from '../../utils/fileUtils';
import FileEditorThumbnail from './FileEditorThumbnail';
import AddFileCard from './AddFileCard';
import FilePickerModal from '../shared/FilePickerModal';
import { FileId, StirlingFile } from '../../types/fileContext';
import { alert } from '../toast';
import { downloadBlob } from '../../utils/downloadUtils';
import { useFileEditorRightRailButtons } from './fileEditorRightRailButtons';
import { useFileSelection, useFileState, useFileManagement, useFileActions, useFileContext } from '@app/contexts/FileContext';
import { useNavigationActions } from '@app/contexts/NavigationContext';
import { zipFileService } from '@app/services/zipFileService';
import { detectFileExtension } from '@app/utils/fileUtils';
import FileEditorThumbnail from '@app/components/fileEditor/FileEditorThumbnail';
import AddFileCard from '@app/components/fileEditor/AddFileCard';
import FilePickerModal from '@app/components/shared/FilePickerModal';
import { FileId, StirlingFile } from '@app/types/fileContext';
import { alert } from '@app/components/toast';
import { downloadBlob } from '@app/utils/downloadUtils';
import { useFileEditorRightRailButtons } from '@app/components/fileEditor/fileEditorRightRailButtons';
interface FileEditorProps {

View File

@@ -1,7 +1,7 @@
import React, { useState, useCallback, useRef, useMemo } from 'react';
import { Text, ActionIcon, CheckboxIndicator, Tooltip, Modal, Button, Group, Stack } from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
import { alert } from '../toast';
import { alert } from '@app/components/toast';
import { useTranslation } from 'react-i18next';
import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined';
import CloseIcon from '@mui/icons-material/Close';
@@ -11,16 +11,16 @@ import PushPinIcon from '@mui/icons-material/PushPin';
import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined';
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { StirlingFileStub } from '../../types/fileContext';
import { zipFileService } from '../../services/zipFileService';
import { StirlingFileStub } from '@app/types/fileContext';
import { zipFileService } from '@app/services/zipFileService';
import styles from './FileEditor.module.css';
import { useFileContext } from '../../contexts/FileContext';
import { useFileState } from '../../contexts/file/fileHooks';
import { FileId } from '../../types/file';
import { formatFileSize } from '../../utils/fileUtils';
import ToolChain from '../shared/ToolChain';
import HoverActionMenu, { HoverAction } from '../shared/HoverActionMenu';
import styles from '@app/components/fileEditor/FileEditor.module.css';
import { useFileContext } from '@app/contexts/FileContext';
import { useFileState } from '@app/contexts/file/fileHooks';
import { FileId } from '@app/types/file';
import { formatFileSize } from '@app/utils/fileUtils';
import ToolChain from '@app/components/shared/ToolChain';
import HoverActionMenu, { HoverAction } from '@app/components/shared/HoverActionMenu';

View File

@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useRightRailButtons, RightRailButtonWithAction } from '../../hooks/useRightRailButtons';
import LocalIcon from '../shared/LocalIcon';
import { useRightRailButtons, RightRailButtonWithAction } from '@app/hooks/useRightRailButtons';
import LocalIcon from '@app/components/shared/LocalIcon';
interface FileEditorRightRailButtonsParams {
totalItems: number;

View File

@@ -4,8 +4,8 @@ import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import { useTranslation } from 'react-i18next';
import { getFileSize } from '../../utils/fileUtils';
import { StirlingFileStub } from '../../types/fileContext';
import { getFileSize } from '@app/utils/fileUtils';
import { StirlingFileStub } from '@app/types/fileContext';
interface CompactFileDetailsProps {
currentFile: StirlingFileStub | null;

View File

@@ -1,12 +1,12 @@
import React from 'react';
import { Grid } from '@mantine/core';
import FileSourceButtons from './FileSourceButtons';
import FileDetails from './FileDetails';
import SearchInput from './SearchInput';
import FileListArea from './FileListArea';
import FileActions from './FileActions';
import HiddenFileInput from './HiddenFileInput';
import { useFileManagerContext } from '../../contexts/FileManagerContext';
import FileSourceButtons from '@app/components/fileManager/FileSourceButtons';
import FileDetails from '@app/components/fileManager/FileDetails';
import SearchInput from '@app/components/fileManager/SearchInput';
import FileListArea from '@app/components/fileManager/FileListArea';
import FileActions from '@app/components/fileManager/FileActions';
import HiddenFileInput from '@app/components/fileManager/HiddenFileInput';
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
const DesktopLayout: React.FC = () => {
const {

View File

@@ -2,9 +2,9 @@ import React, { useState } from 'react';
import { Button, Group, Text, Stack, useMantineColorScheme } from '@mantine/core';
import HistoryIcon from '@mui/icons-material/History';
import { useTranslation } from 'react-i18next';
import { useFileManagerContext } from '../../contexts/FileManagerContext';
import LocalIcon from '../shared/LocalIcon';
import { BASE_PATH } from '../../constants/app';
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
import LocalIcon from '@app/components/shared/LocalIcon';
import { BASE_PATH } from '@app/constants/app';
const EmptyFilesState: React.FC = () => {
const { t } = useTranslation();

View File

@@ -4,7 +4,7 @@ import SelectAllIcon from "@mui/icons-material/SelectAll";
import DeleteIcon from "@mui/icons-material/Delete";
import DownloadIcon from "@mui/icons-material/Download";
import { useTranslation } from "react-i18next";
import { useFileManagerContext } from "../../contexts/FileManagerContext";
import { useFileManagerContext } from "@app/contexts/FileManagerContext";
const FileActions: React.FC = () => {
const { t } = useTranslation();

View File

@@ -1,11 +1,11 @@
import React, { useEffect, useState } from 'react';
import { Stack, Button, Box } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { useIndexedDBThumbnail } from '../../hooks/useIndexedDBThumbnail';
import { useFileManagerContext } from '../../contexts/FileManagerContext';
import FilePreview from '../shared/FilePreview';
import FileInfoCard from './FileInfoCard';
import CompactFileDetails from './CompactFileDetails';
import { useIndexedDBThumbnail } from '@app/hooks/useIndexedDBThumbnail';
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
import FilePreview from '@app/components/shared/FilePreview';
import FileInfoCard from '@app/components/fileManager/FileInfoCard';
import CompactFileDetails from '@app/components/fileManager/CompactFileDetails';
interface FileDetailsProps {
compact?: boolean;

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { Box, Text, Collapse, Group } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { StirlingFileStub } from '../../types/fileContext';
import FileListItem from './FileListItem';
import { StirlingFileStub } from '@app/types/fileContext';
import FileListItem from '@app/components/fileManager/FileListItem';
interface FileHistoryGroupProps {
leafFile: StirlingFileStub;

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { Stack, Card, Box, Text, Badge, Group, Divider, ScrollArea } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { detectFileExtension, getFileSize } from '../../utils/fileUtils';
import { StirlingFileStub } from '../../types/fileContext';
import ToolChain from '../shared/ToolChain';
import { detectFileExtension, getFileSize } from '@app/utils/fileUtils';
import { StirlingFileStub } from '@app/types/fileContext';
import ToolChain from '@app/components/shared/ToolChain';
interface FileInfoCardProps {
currentFile: StirlingFileStub | null;

View File

@@ -2,10 +2,10 @@ import React from 'react';
import { Center, ScrollArea, Text, Stack } from '@mantine/core';
import CloudIcon from '@mui/icons-material/Cloud';
import { useTranslation } from 'react-i18next';
import FileListItem from './FileListItem';
import FileHistoryGroup from './FileHistoryGroup';
import EmptyFilesState from './EmptyFilesState';
import { useFileManagerContext } from '../../contexts/FileManagerContext';
import FileListItem from '@app/components/fileManager/FileListItem';
import FileHistoryGroup from '@app/components/fileManager/FileHistoryGroup';
import EmptyFilesState from '@app/components/fileManager/EmptyFilesState';
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
interface FileListAreaProps {
scrollAreaHeight: string;

View File

@@ -7,12 +7,12 @@ import HistoryIcon from '@mui/icons-material/History';
import RestoreIcon from '@mui/icons-material/Restore';
import UnarchiveIcon from '@mui/icons-material/Unarchive';
import { useTranslation } from 'react-i18next';
import { getFileSize, getFileDate } from '../../utils/fileUtils';
import { FileId, StirlingFileStub } from '../../types/fileContext';
import { useFileManagerContext } from '../../contexts/FileManagerContext';
import { zipFileService } from '../../services/zipFileService';
import ToolChain from '../shared/ToolChain';
import { Z_INDEX_OVER_FILE_MANAGER_MODAL } from '../../styles/zIndex';
import { getFileSize, getFileDate } from '@app/utils/fileUtils';
import { FileId, StirlingFileStub } from '@app/types/fileContext';
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
import { zipFileService } from '@app/services/zipFileService';
import ToolChain from '@app/components/shared/ToolChain';
import { Z_INDEX_OVER_FILE_MANAGER_MODAL } from '@app/styles/zIndex';
interface FileListItemProps {
file: StirlingFileStub;

View File

@@ -4,8 +4,8 @@ import HistoryIcon from '@mui/icons-material/History';
import UploadIcon from '@mui/icons-material/Upload';
import CloudIcon from '@mui/icons-material/Cloud';
import { useTranslation } from 'react-i18next';
import { useFileManagerContext } from '../../contexts/FileManagerContext';
import { useGoogleDrivePicker } from '../../hooks/useGoogleDrivePicker';
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
import { useGoogleDrivePicker } from '@app/hooks/useGoogleDrivePicker';
interface FileSourceButtonsProps {
horizontal?: boolean;

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { useFileManagerContext } from '../../contexts/FileManagerContext';
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
const HiddenFileInput: React.FC = () => {
const { fileInputRef, onFileInputChange } = useFileManagerContext();

View File

@@ -1,12 +1,12 @@
import React from 'react';
import { Box } from '@mantine/core';
import FileSourceButtons from './FileSourceButtons';
import FileDetails from './FileDetails';
import SearchInput from './SearchInput';
import FileListArea from './FileListArea';
import FileActions from './FileActions';
import HiddenFileInput from './HiddenFileInput';
import { useFileManagerContext } from '../../contexts/FileManagerContext';
import FileSourceButtons from '@app/components/fileManager/FileSourceButtons';
import FileDetails from '@app/components/fileManager/FileDetails';
import SearchInput from '@app/components/fileManager/SearchInput';
import FileListArea from '@app/components/fileManager/FileListArea';
import FileActions from '@app/components/fileManager/FileActions';
import HiddenFileInput from '@app/components/fileManager/HiddenFileInput';
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
const MobileLayout: React.FC = () => {
const {

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { TextInput } from '@mantine/core';
import SearchIcon from '@mui/icons-material/Search';
import { useTranslation } from 'react-i18next';
import { useFileManagerContext } from '../../contexts/FileManagerContext';
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
interface SearchInputProps {
style?: React.CSSProperties;

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { HotkeyBinding } from '../../utils/hotkeys';
import { useHotkeys } from '../../contexts/HotkeyContext';
import { HotkeyBinding } from '@app/utils/hotkeys';
import { useHotkeys } from '@app/contexts/HotkeyContext';
interface HotkeyDisplayProps {
binding: HotkeyBinding | null | undefined;

View File

@@ -1,13 +1,13 @@
import React from 'react';
import { Box } from '@mantine/core';
import { useRainbowThemeContext } from '../shared/RainbowThemeProvider';
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import { useFileHandler } from '../../hooks/useFileHandler';
import { useFileState } from '../../contexts/FileContext';
import { useNavigationState, useNavigationActions } from '../../contexts/NavigationContext';
import { isBaseWorkbench } from '../../types/workbench';
import { useViewer } from '../../contexts/ViewerContext';
import { useAppConfig } from '../../hooks/useAppConfig';
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
import { useFileHandler } from '@app/hooks/useFileHandler';
import { useFileState } from '@app/contexts/FileContext';
import { useNavigationState, useNavigationActions } from '@app/contexts/NavigationContext';
import { isBaseWorkbench } from '@app/types/workbench';
import { useViewer } from '@app/contexts/ViewerContext';
import { useAppConfig } from '@app/contexts/AppConfigContext';
import './Workbench.css';
import TopControls from '../shared/TopControls';

View File

@@ -1,14 +1,14 @@
import React from "react";
import { TourProvider, useTour, type StepType } from '@reactour/tour';
import { useOnboarding } from '../../contexts/OnboardingContext';
import { useOnboarding } from '@app/contexts/OnboardingContext';
import { useTranslation } from 'react-i18next';
import { CloseButton, ActionIcon } from '@mantine/core';
import { useFilesModalContext } from '../../contexts/FilesModalContext';
import { useTourOrchestration } from '../../contexts/TourOrchestrationContext';
import { useFilesModalContext } from '@app/contexts/FilesModalContext';
import { useTourOrchestration } from '@app/contexts/TourOrchestrationContext';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import CheckIcon from '@mui/icons-material/Check';
import TourWelcomeModal from './TourWelcomeModal';
import './OnboardingTour.css';
import TourWelcomeModal from '@app/components/onboarding/TourWelcomeModal';
import '@app/components/onboarding/OnboardingTour.css';
// Enum case order defines order steps will appear
enum TourStep {

View File

@@ -1,6 +1,6 @@
import { Modal, Title, Text, Button, Stack, Group } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '../../styles/zIndex';
import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '@app/styles/zIndex';
interface TourWelcomeModalProps {
opened: boolean;

View File

@@ -1,9 +1,9 @@
import { useState, useEffect } from 'react';
import classes from './bulkSelectionPanel/BulkSelectionPanel.module.css';
import { parseSelectionWithDiagnostics } from '../../utils/bulkselection/parseSelection';
import PageSelectionInput from './bulkSelectionPanel/PageSelectionInput';
import SelectedPagesDisplay from './bulkSelectionPanel/SelectedPagesDisplay';
import AdvancedSelectionPanel from './bulkSelectionPanel/AdvancedSelectionPanel';
import classes from '@app/components/pageEditor/bulkSelectionPanel/BulkSelectionPanel.module.css';
import { parseSelectionWithDiagnostics } from '@app/utils/bulkselection/parseSelection';
import PageSelectionInput from '@app/components/pageEditor/bulkSelectionPanel/PageSelectionInput';
import SelectedPagesDisplay from '@app/components/pageEditor/bulkSelectionPanel/SelectedPagesDisplay';
import AdvancedSelectionPanel from '@app/components/pageEditor/bulkSelectionPanel/AdvancedSelectionPanel';
interface BulkSelectionPanelProps {
csvInput: string;

View File

@@ -1,7 +1,7 @@
import React, { useRef, useEffect, useState, useCallback } from 'react';
import { Box } from '@mantine/core';
import { useVirtualizer } from '@tanstack/react-virtual';
import { GRID_CONSTANTS } from './constants';
import { GRID_CONSTANTS } from '@app/components/pageEditor/constants';
interface DragDropItem {
id: string;

View File

@@ -9,9 +9,9 @@ import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined';
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import styles from './PageEditor.module.css';
import { useFileContext } from '../../contexts/FileContext';
import { FileId } from '../../types/file';
import styles from '@app/components/pageEditor/PageEditor.module.css';
import { useFileContext } from '@app/contexts/FileContext';
import { FileId } from '@app/types/file';
interface FileItem {
id: FileId;

View File

@@ -1,19 +1,19 @@
import { useState, useCallback, useRef, useEffect } from "react";
import { Text, Center, Box, LoadingOverlay, Stack } from "@mantine/core";
import { useFileState, useFileActions } from "../../contexts/FileContext";
import { useNavigationGuard } from "../../contexts/NavigationContext";
import { PDFDocument, PageEditorFunctions } from "../../types/pageEditor";
import { pdfExportService } from "../../services/pdfExportService";
import { documentManipulationService } from "../../services/documentManipulationService";
import { exportProcessedDocumentsToFiles } from "../../services/pdfExportHelpers";
import { createStirlingFilesAndStubs } from "../../services/fileStubHelpers";
import { useFileState, useFileActions } from "@app/contexts/FileContext";
import { useNavigationGuard } from "@app/contexts/NavigationContext";
import { PDFDocument, PageEditorFunctions } from "@app/types/pageEditor";
import { pdfExportService } from "@app/services/pdfExportService";
import { documentManipulationService } from "@app/services/documentManipulationService";
import { exportProcessedDocumentsToFiles } from "@app/services/pdfExportHelpers";
import { createStirlingFilesAndStubs } from "@app/services/fileStubHelpers";
// Thumbnail generation is now handled by individual PageThumbnail components
import './PageEditor.module.css';
import PageThumbnail from './PageThumbnail';
import DragDropGrid from './DragDropGrid';
import SkeletonLoader from '../shared/SkeletonLoader';
import NavigationWarningModal from '../shared/NavigationWarningModal';
import { FileId } from "../../types/file";
import '@app/components/pageEditor/PageEditor.module.css';
import PageThumbnail from '@app/components/pageEditor/PageThumbnail';
import DragDropGrid from '@app/components/pageEditor/DragDropGrid';
import SkeletonLoader from '@app/components/shared/SkeletonLoader';
import NavigationWarningModal from '@app/components/shared/NavigationWarningModal';
import { FileId } from "@app/types/file";
import {
DeletePagesCommand,
@@ -22,12 +22,12 @@ import {
BulkRotateCommand,
PageBreakCommand,
UndoManager
} from './commands/pageCommands';
import { GRID_CONSTANTS } from './constants';
import { usePageDocument } from './hooks/usePageDocument';
import { usePageEditorState } from './hooks/usePageEditorState';
import { parseSelection } from "../../utils/bulkselection/parseSelection";
import { usePageEditorRightRailButtons } from "./pageEditorRightRailButtons";
} from '@app/components/pageEditor/commands/pageCommands';
import { GRID_CONSTANTS } from '@app/components/pageEditor/constants';
import { usePageDocument } from '@app/components/pageEditor/hooks/usePageDocument';
import { usePageEditorState } from '@app/components/pageEditor/hooks/usePageEditorState';
import { parseSelection } from "@app/utils/bulkselection/parseSelection";
import { usePageEditorRightRailButtons } from "@app/components/pageEditor/pageEditorRightRailButtons";
export interface PageEditorProps {
onFunctionsReady?: (functions: PageEditorFunctions) => void;

View File

@@ -1,7 +1,7 @@
import { ActionIcon, Popover } from '@mantine/core';
import LocalIcon from '../shared/LocalIcon';
import { Tooltip } from '../shared/Tooltip';
import BulkSelectionPanel from './BulkSelectionPanel';
import LocalIcon from '@app/components/shared/LocalIcon';
import { Tooltip } from '@app/components/shared/Tooltip';
import BulkSelectionPanel from '@app/components/pageEditor/BulkSelectionPanel';
interface PageSelectByNumberButtonProps {
disabled: boolean;

View File

@@ -9,11 +9,11 @@ import DeleteIcon from '@mui/icons-material/Delete';
import ContentCutIcon from '@mui/icons-material/ContentCut';
import AddIcon from '@mui/icons-material/Add';
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { PDFPage, PDFDocument } from '../../types/pageEditor';
import { useThumbnailGeneration } from '../../hooks/useThumbnailGeneration';
import { useFilesModalContext } from '../../contexts/FilesModalContext';
import styles from './PageEditor.module.css';
import HoverActionMenu, { HoverAction } from '../shared/HoverActionMenu';
import { PDFPage, PDFDocument } from '@app/types/pageEditor';
import { useThumbnailGeneration } from '@app/hooks/useThumbnailGeneration';
import { useFilesModalContext } from '@app/contexts/FilesModalContext';
import styles from '@app/components/pageEditor/PageEditor.module.css';
import HoverActionMenu, { HoverAction } from '@app/components/shared/HoverActionMenu';
interface PageThumbnailProps {

View File

@@ -1,7 +1,7 @@
import { useState } from 'react';
import { Flex } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import classes from './BulkSelectionPanel.module.css';
import classes from '@app/components/pageEditor/bulkSelectionPanel/BulkSelectionPanel.module.css';
import {
appendExpression,
insertOperatorSmart,
@@ -10,9 +10,9 @@ import {
everyNthExpression,
rangeExpression,
LogicalOperator,
} from './BulkSelection';
import SelectPages from './SelectPages';
import OperatorsSection from './OperatorsSection';
} from '@app/components/pageEditor/bulkSelectionPanel/BulkSelection';
import SelectPages from '@app/components/pageEditor/bulkSelectionPanel/SelectPages';
import OperatorsSection from '@app/components/pageEditor/bulkSelectionPanel/OperatorsSection';
interface AdvancedSelectionPanelProps {
csvInput: string;

View File

@@ -1,7 +1,7 @@
import { Button, Text, Group, Divider } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import classes from './BulkSelectionPanel.module.css';
import { LogicalOperator } from './BulkSelection';
import classes from '@app/components/pageEditor/bulkSelectionPanel/BulkSelectionPanel.module.css';
import { LogicalOperator } from '@app/components/pageEditor/bulkSelectionPanel/BulkSelection';
interface OperatorsSectionProps {
csvInput: string;

View File

@@ -1,9 +1,9 @@
import { TextInput, Button, Text, Flex, Switch } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import LocalIcon from '../../shared/LocalIcon';
import { Tooltip } from '../../shared/Tooltip';
import { usePageSelectionTips } from '../../tooltips/usePageSelectionTips';
import classes from './BulkSelectionPanel.module.css';
import LocalIcon from '@app/components/shared/LocalIcon';
import { Tooltip } from '@app/components/shared/Tooltip';
import { usePageSelectionTips } from '@app/components/tooltips/usePageSelectionTips';
import classes from '@app/components/pageEditor/bulkSelectionPanel/BulkSelectionPanel.module.css';
interface PageSelectionInputProps {
csvInput: string;

View File

@@ -1,6 +1,6 @@
import { useState } from 'react';
import { Button, Text, NumberInput, Group } from '@mantine/core';
import classes from './BulkSelectionPanel.module.css';
import classes from '@app/components/pageEditor/bulkSelectionPanel/BulkSelectionPanel.module.css';
interface SelectPagesProps {
title: string;

View File

@@ -1,5 +1,5 @@
import { Text } from '@mantine/core';
import classes from './BulkSelectionPanel.module.css';
import classes from '@app/components/pageEditor/bulkSelectionPanel/BulkSelectionPanel.module.css';
interface SelectedPagesDisplayProps {
selectedPageIds: string[];

View File

@@ -1,5 +1,5 @@
import { FileId } from '../../../types/file';
import { PDFDocument, PDFPage } from '../../../types/pageEditor';
import { FileId } from '@app/types/file';
import { PDFDocument, PDFPage } from '@app/types/pageEditor';
// V1-style DOM-first command system (replaces the old React state commands)
export abstract class DOMCommand {
@@ -694,7 +694,7 @@ export class InsertFilesCommand extends DOMCommand {
private async generateThumbnailsForInsertedPages(updatedDocument: PDFDocument): Promise<void> {
try {
const { thumbnailGenerationService } = await import('../../../services/thumbnailGenerationService');
const { thumbnailGenerationService } = await import('@app/services/thumbnailGenerationService');
// Group pages by file ID to generate thumbnails efficiently
const pagesByFileId = new Map<FileId, PDFPage[]>();
@@ -776,7 +776,7 @@ export class InsertFilesCommand extends DOMCommand {
const clonedArrayBuffer = arrayBuffer.slice(0);
// Use PDF.js via the worker manager to extract pages
const { pdfWorkerManager } = await import('../../../services/pdfWorkerManager');
const { pdfWorkerManager } = await import('@app/services/pdfWorkerManager');
const pdf = await pdfWorkerManager.createDocument(clonedArrayBuffer);
const pageCount = pdf.numPages;

View File

@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { useFileState } from '../../../contexts/FileContext';
import { PDFDocument, PDFPage } from '../../../types/pageEditor';
import { FileId } from '../../../types/file';
import { useFileState } from '@app/contexts/FileContext';
import { PDFDocument, PDFPage } from '@app/types/pageEditor';
import { FileId } from '@app/types/file';
export interface PageDocumentHook {
document: PDFDocument | null;

View File

@@ -1,8 +1,8 @@
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useRightRailButtons, RightRailButtonWithAction } from '../../hooks/useRightRailButtons';
import LocalIcon from '../shared/LocalIcon';
import PageSelectByNumberButton from './PageSelectByNumberButton';
import { useRightRailButtons, RightRailButtonWithAction } from '@app/hooks/useRightRailButtons';
import LocalIcon from '@app/components/shared/LocalIcon';
import PageSelectByNumberButton from '@app/components/pageEditor/PageSelectByNumberButton';
interface PageEditorRightRailButtonsParams {
totalPages: number;

View File

@@ -0,0 +1,112 @@
import { Modal, Stack, Button, Text, Title, Anchor } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { Z_ANALYTICS_MODAL } from '@app/styles/zIndex';
import { useAppConfig } from '@app/contexts/AppConfigContext';
import apiClient from '@app/services/apiClient';
interface AdminAnalyticsChoiceModalProps {
opened: boolean;
onClose: () => void;
}
export default function AdminAnalyticsChoiceModal({ opened, onClose }: AdminAnalyticsChoiceModalProps) {
const { t } = useTranslation();
const { refetch } = useAppConfig();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleChoice = async (enableAnalytics: boolean) => {
setLoading(true);
setError(null);
try {
const formData = new FormData();
formData.append('enabled', enableAnalytics.toString());
await apiClient.post('/api/v1/settings/update-enable-analytics', formData);
// Refetch config to apply new settings without page reload
await refetch();
// Close the modal after successful save
onClose();
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error occurred');
setLoading(false);
}
};
const handleEnable = () => {
handleChoice(true);
};
const handleDisable = () => {
handleChoice(false);
};
return (
<Modal
opened={opened}
onClose={() => {}} // Prevent closing
closeOnClickOutside={false}
closeOnEscape={false}
withCloseButton={false}
size="lg"
centered
zIndex={Z_ANALYTICS_MODAL}
>
<Stack gap="md">
<Title order={2}>{t('analytics.title', 'Do you want make Stirling PDF better?')}</Title>
<Text size="sm" c="dimmed">
{t('analytics.paragraph1', 'Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents.')}
</Text>
<Text size="sm" c="dimmed">
{t('analytics.paragraph2', 'Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better.')}{' '}
<Anchor
href="https://docs.stirlingpdf.com/analytics-telemetry"
target="_blank"
rel="noopener noreferrer"
size="sm"
>
{t('analytics.learnMore', 'Learn more')}
</Anchor>
</Text>
{error && (
<Text c="red" size="sm">
{error}
</Text>
)}
<Stack gap="sm">
<Button
onClick={handleEnable}
loading={loading}
fullWidth
size="md"
>
{t('analytics.enable', 'Enable analytics')}
</Button>
<Button
onClick={handleDisable}
loading={loading}
fullWidth
size="md"
variant="subtle"
c="gray"
>
{t('analytics.disable', 'Disable analytics')}
</Button>
</Stack>
<Text size="xs" c="dimmed" ta="center">
{t('analytics.settings', 'You can change the settings for analytics in the config/settings.yml file')}
</Text>
</Stack>
</Modal>
);
}

View File

@@ -1,11 +1,11 @@
import React from 'react';
import { ActionIcon } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { Tooltip } from './Tooltip';
import { Tooltip } from '@app/components/shared/Tooltip';
import AppsIcon from '@mui/icons-material/AppsRounded';
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import { useSidebarNavigation } from '../../hooks/useSidebarNavigation';
import { handleUnlessSpecialClick } from '../../utils/clickHandlers';
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
import { useSidebarNavigation } from '@app/hooks/useSidebarNavigation';
import { handleUnlessSpecialClick } from '@app/utils/clickHandlers';
interface AllToolsNavButtonProps {
activeButton: string;

View File

@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useAppConfig } from '../../hooks/useAppConfig';
import { updateSupportedLanguages } from '../../i18n';
import { useAppConfig } from '@app/contexts/AppConfigContext';
import { updateSupportedLanguages } from '@app/i18n';
/**
* Component that loads app configuration and applies it to the application.

View File

@@ -1,13 +1,13 @@
import React, { useMemo, useState, useEffect } from 'react';
import { Modal, Text, ActionIcon } from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
import LocalIcon from './LocalIcon';
import Overview from './config/configSections/Overview';
import { createConfigNavSections } from './config/configNavSections';
import { NavKey } from './config/types';
import { useAppConfig } from '../../hooks/useAppConfig';
import './AppConfigModal.css';
import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '../../styles/zIndex';
import LocalIcon from '@app/components/shared/LocalIcon';
import Overview from '@app/components/shared/config/configSections/Overview';
import { createConfigNavSections } from '@app/components/shared/config/configNavSections';
import { NavKey } from '@app/components/shared/config/types';
import { useAppConfig } from '@app/contexts/AppConfigContext';
import '@app/components/shared/AppConfigModal.css';
import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '@app/styles/zIndex';
interface AppConfigModalProps {
opened: boolean;

View File

@@ -1,7 +1,7 @@
import { describe, expect, test, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { MantineProvider } from '@mantine/core';
import ButtonSelector from './ButtonSelector';
import ButtonSelector from '@app/components/shared/ButtonSelector';
// Wrapper component to provide Mantine context
const TestWrapper = ({ children }: { children: React.ReactNode }) => (

View File

@@ -1,5 +1,5 @@
import { Button, Group, Stack, Text } from "@mantine/core";
import FitText from "./FitText";
import FitText from "@app/components/shared/FitText";
export interface ButtonOption<T> {
value: T;

View File

@@ -1,5 +1,5 @@
import { Stack, Card, Text, Flex } from '@mantine/core';
import { Tooltip } from './Tooltip';
import { Tooltip } from '@app/components/shared/Tooltip';
import { useTranslation } from 'react-i18next';
export interface CardOption<T = string> {

View File

@@ -1,10 +1,10 @@
import React from 'react';
import { Button, Group } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { useFileState } from '../../contexts/FileContext';
import { useFileActions } from '../../contexts/file/fileHooks';
import { useFileState } from '@app/contexts/FileContext';
import { useFileActions } from '@app/contexts/file/fileHooks';
import CloseIcon from '@mui/icons-material/Close';
import { Z_INDEX_TOAST } from '../../styles/zIndex';
import { Z_INDEX_TOAST } from '@app/styles/zIndex';
interface DismissAllErrorsButtonProps {
className?: string;

View File

@@ -6,9 +6,9 @@ import StorageIcon from "@mui/icons-material/Storage";
import VisibilityIcon from "@mui/icons-material/Visibility";
import EditIcon from "@mui/icons-material/Edit";
import { StirlingFileStub } from "../../types/fileContext";
import { getFileSize, getFileDate } from "../../utils/fileUtils";
import { useIndexedDBThumbnail } from "../../hooks/useIndexedDBThumbnail";
import { StirlingFileStub } from "@app/types/fileContext";
import { getFileSize, getFileDate } from "@app/utils/fileUtils";
import { useIndexedDBThumbnail } from "@app/hooks/useIndexedDBThumbnail";
interface FileCardProps {
file: File;

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { Menu, Loader, Group, Text } from '@mantine/core';
import VisibilityIcon from '@mui/icons-material/Visibility';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import FitText from './FitText';
import FitText from '@app/components/shared/FitText';
interface FileDropdownMenuProps {
displayName: string;

View File

@@ -3,9 +3,9 @@ import { Box, Flex, Group, Text, Button, TextInput, Select } from "@mantine/core
import { useTranslation } from "react-i18next";
import SearchIcon from "@mui/icons-material/Search";
import SortIcon from "@mui/icons-material/Sort";
import FileCard from "./FileCard";
import { StirlingFileStub } from "../../types/fileContext";
import { FileId } from "../../types/file";
import FileCard from "@app/components/shared/FileCard";
import { StirlingFileStub } from "@app/types/fileContext";
import { FileId } from "@app/types/file";
interface FileGridProps {
files: Array<{ file: File; record?: StirlingFileStub }>;

View File

@@ -15,7 +15,7 @@ import {
} from '@mantine/core';
import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
import { useTranslation } from 'react-i18next';
import { FileId } from '../../types/file';
import { FileId } from '@app/types/file';
interface FilePickerModalProps {
opened: boolean;

View File

@@ -1,11 +1,11 @@
import React from 'react';
import { Box, Center } from '@mantine/core';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
import { StirlingFileStub } from '../../types/fileContext';
import DocumentThumbnail from './filePreview/DocumentThumbnail';
import DocumentStack from './filePreview/DocumentStack';
import HoverOverlay from './filePreview/HoverOverlay';
import NavigationArrows from './filePreview/NavigationArrows';
import { StirlingFileStub } from '@app/types/fileContext';
import DocumentThumbnail from '@app/components/shared/filePreview/DocumentThumbnail';
import DocumentStack from '@app/components/shared/filePreview/DocumentStack';
import HoverOverlay from '@app/components/shared/filePreview/HoverOverlay';
import NavigationArrows from '@app/components/shared/filePreview/NavigationArrows';
export interface FilePreviewProps {
// Core file data

View File

@@ -1,15 +1,14 @@
import { useState } from 'react';
import { Modal, Stack, Text, PasswordInput, Button, Alert } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import LocalIcon from './LocalIcon';
import { accountService } from '../../services/accountService';
import { alert } from '../toast';
import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '../../styles/zIndex';
import { useAuth } from '../../auth/UseSession';
import LocalIcon from '@app/components/shared/LocalIcon';
import { accountService } from '@app/services/accountService';
import { alert } from '@app/components/toast';
import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '@app/styles/zIndex';
interface FirstLoginModalProps {
opened: boolean;
onPasswordChanged: () => void;
username: string;
}
@@ -19,10 +18,8 @@ interface FirstLoginModalProps {
* Forces first-time users to change their password.
* Cannot be dismissed until password is successfully changed.
*/
export default function FirstLoginModal({ opened, username }: FirstLoginModalProps) {
export default function FirstLoginModal({ opened, onPasswordChanged, username }: FirstLoginModalProps) {
const { t } = useTranslation();
const navigate = useNavigate();
const { signOut } = useAuth();
const [currentPassword, setCurrentPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
@@ -41,6 +38,11 @@ export default function FirstLoginModal({ opened, username }: FirstLoginModalPro
return;
}
if (newPassword.length < 8) {
setError(t('firstLogin.passwordTooShort', 'Password must be at least 8 characters'));
return;
}
if (newPassword === currentPassword) {
setError(t('firstLogin.passwordMustBeDifferent', 'New password must be different from current password'));
return;
@@ -50,8 +52,7 @@ export default function FirstLoginModal({ opened, username }: FirstLoginModalPro
setLoading(true);
setError('');
// Use changePasswordOnLogin to clear the first-use flag
await accountService.changePasswordOnLogin(currentPassword, newPassword);
await accountService.changePassword(currentPassword, newPassword);
alert({
alertType: 'success',
@@ -63,9 +64,11 @@ export default function FirstLoginModal({ opened, username }: FirstLoginModalPro
setNewPassword('');
setConfirmPassword('');
// Backend has logged us out, so clear frontend auth state and redirect to login
await signOut();
navigate('/login?messageType=passwordChanged');
// Wait a moment for the user to see the success message
// Then the backend will have logged them out, and onPasswordChanged will handle redirect
setTimeout(() => {
onPasswordChanged();
}, 1500);
} catch (err: any) {
console.error('Failed to change password:', err);
setError(
@@ -127,7 +130,7 @@ export default function FirstLoginModal({ opened, username }: FirstLoginModalPro
<PasswordInput
label={t('firstLogin.newPassword', 'New Password')}
placeholder={t('firstLogin.enterNewPassword', 'Enter new password')}
placeholder={t('firstLogin.enterNewPassword', 'Enter new password (min 8 characters)')}
value={newPassword}
onChange={(e) => setNewPassword(e.currentTarget.value)}
required

View File

@@ -1,5 +1,5 @@
import React, { CSSProperties, useMemo, useRef } from 'react';
import { useAdjustFontSizeToFit } from './fitText/textFit';
import { useAdjustFontSizeToFit } from '@app/components/shared/fitText/textFit';
type FitTextProps = {
text: string;

View File

@@ -1,6 +1,6 @@
import { Flex } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { useCookieConsent } from '../../hooks/useCookieConsent';
import { useCookieConsent } from '@app/hooks/useCookieConsent';
interface FooterProps {
privacyPolicy?: string;

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { ActionIcon, Tooltip } from '@mantine/core';
import styles from './HoverActionMenu.module.css';
import styles from '@app/components/shared/HoverActionMenu.module.css';
export interface HoverAction {
id: string;

View File

@@ -1,11 +1,11 @@
import React from 'react';
import { Container, Button, Group, useMantineColorScheme } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
import LocalIcon from './LocalIcon';
import LocalIcon from '@app/components/shared/LocalIcon';
import { useTranslation } from 'react-i18next';
import { useFileHandler } from '../../hooks/useFileHandler';
import { useFilesModalContext } from '../../contexts/FilesModalContext';
import { BASE_PATH } from '../../constants/app';
import { useFileHandler } from '@app/hooks/useFileHandler';
import { useFilesModalContext } from '@app/contexts/FilesModalContext';
import { BASE_PATH } from '@app/constants/app';
const LandingPage = () => {
const { addFiles } = useFileHandler();

View File

@@ -1,10 +1,10 @@
import React, { useState, useEffect } from 'react';
import { Menu, Button, ScrollArea, ActionIcon, Tooltip } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { supportedLanguages } from '../../i18n';
import LocalIcon from './LocalIcon';
import styles from './LanguageSelector.module.css';
import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '../../styles/zIndex';
import { supportedLanguages } from '@app/i18n';
import LocalIcon from '@app/components/shared/LocalIcon';
import styles from '@app/components/shared/LanguageSelector.module.css';
import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '@app/styles/zIndex';
// Types
interface LanguageSelectorProps {

View File

@@ -0,0 +1,19 @@
/**
* Loading fallback component for i18next suspense
*/
export function LoadingFallback() {
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
fontSize: "18px",
color: "#666",
}}
>
Loading...
</div>
);
}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { addCollection, Icon } from '@iconify/react';
import iconSet from '../../assets/material-symbols-icons.json';
import iconSet from '../../../assets/material-symbols-icons.json';
// Load icons synchronously at import time - guaranteed to be ready on first render
let iconsLoaded = false;

View File

@@ -1,5 +1,5 @@
import { Modal, Text, Button, Group, Stack } from "@mantine/core";
import { useNavigationGuard } from "../../contexts/NavigationContext";
import { useNavigationGuard } from "@app/contexts/NavigationContext";
import { useTranslation } from "react-i18next";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";

View File

@@ -1,5 +1,5 @@
import React from 'react';
import styles from './ObscuredOverlay/ObscuredOverlay.module.css';
import styles from '@app/components/shared/ObscuredOverlay/ObscuredOverlay.module.css';
type ObscuredOverlayProps = {
obscured: boolean;

View File

@@ -1,25 +1,25 @@
import React, { useState, useRef, forwardRef, useEffect } from "react";
import { ActionIcon, Stack, Divider } from "@mantine/core";
import { useTranslation } from 'react-i18next';
import LocalIcon from './LocalIcon';
import { useRainbowThemeContext } from "./RainbowThemeProvider";
import { useIsOverflowing } from '../../hooks/useIsOverflowing';
import { useFilesModalContext } from '../../contexts/FilesModalContext';
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import { useSidebarNavigation } from '../../hooks/useSidebarNavigation';
import { handleUnlessSpecialClick } from '../../utils/clickHandlers';
import { ButtonConfig } from '../../types/sidebar';
import './quickAccessBar/QuickAccessBar.css';
import AllToolsNavButton from './AllToolsNavButton';
import ActiveToolButton from "./quickAccessBar/ActiveToolButton";
import AppConfigModal from './AppConfigModal';
import { useAppConfig } from '../../hooks/useAppConfig';
import { useOnboarding } from '../../contexts/OnboardingContext';
import LocalIcon from '@app/components/shared/LocalIcon';
import { useRainbowThemeContext } from "@app/components/shared/RainbowThemeProvider";
import { useIsOverflowing } from '@app/hooks/useIsOverflowing';
import { useFilesModalContext } from '@app/contexts/FilesModalContext';
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
import { useSidebarNavigation } from '@app/hooks/useSidebarNavigation';
import { handleUnlessSpecialClick } from '@app/utils/clickHandlers';
import { ButtonConfig } from '@app/types/sidebar';
import '@app/components/shared/quickAccessBar/QuickAccessBar.css';
import AllToolsNavButton from '@app/components/shared/AllToolsNavButton';
import ActiveToolButton from "@app/components/shared/quickAccessBar/ActiveToolButton";
import AppConfigModal from '@app/components/shared/AppConfigModal';
import { useAppConfig } from '@app/contexts/AppConfigContext';
import { useOnboarding } from '@app/contexts/OnboardingContext';
import {
isNavButtonActive,
getNavButtonStyle,
getActiveNavButton,
} from './quickAccessBar/QuickAccessBar';
} from '@app/components/shared/quickAccessBar/QuickAccessBar';
const QuickAccessBar = forwardRef<HTMLDivElement>((_, ref) => {
const { t } = useTranslation();

View File

@@ -1,12 +1,12 @@
import { createContext, useContext, ReactNode } from 'react';
import { MantineProvider } from '@mantine/core';
import { useRainbowTheme } from '../../hooks/useRainbowTheme';
import { mantineTheme } from '../../theme/mantineTheme';
import rainbowStyles from '../../styles/rainbow.module.css';
import { ToastProvider } from '../toast';
import ToastRenderer from '../toast/ToastRenderer';
import { ToastPortalBinder } from '../toast';
import type { ThemeMode } from '../../constants/theme';
import { useRainbowTheme } from '@app/hooks/useRainbowTheme';
import { mantineTheme } from '@app/theme/mantineTheme';
import rainbowStyles from '@app/styles/rainbow.module.css';
import { ToastProvider } from '@app/components/toast';
import ToastRenderer from '@app/components/toast/ToastRenderer';
import { ToastPortalBinder } from '@app/components/toast';
import type { ThemeMode } from '@app/constants/theme';
interface RainbowThemeContextType {
themeMode: ThemeMode;

View File

@@ -1,21 +1,21 @@
import React, { useCallback, useMemo } from 'react';
import { ActionIcon, Divider } from '@mantine/core';
import './rightRail/RightRail.css';
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import { useRightRail } from '../../contexts/RightRailContext';
import { useFileState, useFileSelection } from '../../contexts/FileContext';
import { useNavigationState } from '../../contexts/NavigationContext';
import '@app/components/shared/rightRail/RightRail.css';
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
import { useRightRail } from '@app/contexts/RightRailContext';
import { useFileState, useFileSelection } from '@app/contexts/FileContext';
import { useNavigationState } from '@app/contexts/NavigationContext';
import { useTranslation } from 'react-i18next';
import LanguageSelector from '../shared/LanguageSelector';
import { useRainbowThemeContext } from '../shared/RainbowThemeProvider';
import { Tooltip } from '../shared/Tooltip';
import { ViewerContext } from '../../contexts/ViewerContext';
import { useSignature } from '../../contexts/SignatureContext';
import LocalIcon from './LocalIcon';
import LanguageSelector from '@app/components/shared/LanguageSelector';
import { useRainbowThemeContext } from '@app/components/shared/RainbowThemeProvider';
import { Tooltip } from '@app/components/shared/Tooltip';
import { ViewerContext } from '@app/contexts/ViewerContext';
import { useSignature } from '@app/contexts/SignatureContext';
import LocalIcon from '@app/components/shared/LocalIcon';
import { useSidebarContext } from '../../contexts/SidebarContext';
import { RightRailButtonConfig, RightRailRenderContext, RightRailSection } from '../../types/rightRail';
import { useSidebarContext } from '@app/contexts/SidebarContext';
import { RightRailButtonConfig, RightRailRenderContext, RightRailSection } from '@app/types/rightRail';
const SECTION_ORDER: RightRailSection[] = ['top', 'middle', 'bottom'];

View File

@@ -1,12 +1,16 @@
import React, { forwardRef } from 'react';
import { useMantineColorScheme } from '@mantine/core';
import LocalIcon from './LocalIcon';
import styles from './textInput/TextInput.module.css';
import LocalIcon from '@app/components/shared/LocalIcon';
import styles from '@app/components/shared/textInput/TextInput.module.css';
/**
* Props for the TextInput component
*/
export interface TextInputProps {
/** The input ID (required) */
id: string;
/** The input name (required) */
name: string;
/** The input value (required) */
value: string;
/** Callback when input value changes (required) */
@@ -36,6 +40,8 @@ export interface TextInputProps {
}
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(({
id,
name,
value,
onChange,
placeholder,
@@ -76,6 +82,8 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(({
<input
ref={ref}
type="text"
id={id}
name={name}
placeholder={placeholder}
value={value}
onChange={(e) => onChange(e.currentTarget.value)}

View File

@@ -5,9 +5,9 @@
import React from 'react';
import { Text, Tooltip, Badge, Group } from '@mantine/core';
import { ToolOperation } from '../../types/file';
import { ToolOperation } from '@app/types/file';
import { useTranslation } from 'react-i18next';
import { ToolId } from '../../types/toolId';
import { ToolId } from '@app/types/toolId';
interface ToolChainProps {
toolChain: ToolOperation[];

Some files were not shown because too many files have changed in this diff Show More