mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-12-18 20:04:17 +01:00
Various fixes for desktop (#4976)
# Description of Changes - ~Force classic logo~ - Refer to email instead of username in SaaS sign in flow - Allow drag-and-drop files into desktop app - Convert terminology & icons from upload/download to open/save in desktop version
This commit is contained in:
parent
991e03970e
commit
2d8b0ff08c
@ -130,6 +130,8 @@
|
||||
"save": "Save",
|
||||
"saveToBrowser": "Save to Browser",
|
||||
"download": "Download",
|
||||
"downloadUnavailable": "Download unavailable for this item",
|
||||
"saveUnavailable": "Save unavailable for this item",
|
||||
"pin": "Pin File (keep active after tool run)",
|
||||
"unpin": "Unpin File (replace after tool run)",
|
||||
"undoOperationTooltip": "Click to undo the last operation and restore the original files",
|
||||
@ -872,7 +874,8 @@
|
||||
},
|
||||
"landing": {
|
||||
"addFiles": "Add Files",
|
||||
"uploadFromComputer": "Upload from computer"
|
||||
"uploadFromComputer": "Upload from computer",
|
||||
"openFromComputer": "Open from computer"
|
||||
},
|
||||
"viewPdf": {
|
||||
"tags": "view,read,annotate,text,image,highlight,edit",
|
||||
@ -3877,6 +3880,7 @@
|
||||
"exportAll": "Export PDF",
|
||||
"downloadSelected": "Download Selected Files",
|
||||
"downloadAll": "Download All",
|
||||
"saveAll": "Save All",
|
||||
"toggleTheme": "Toggle Theme",
|
||||
"toggleBookmarks": "Toggle Bookmarks",
|
||||
"language": "Language",
|
||||
@ -4570,12 +4574,17 @@
|
||||
"or": "or",
|
||||
"dropFileHere": "Drop file here or click to upload",
|
||||
"dropFilesHere": "Drop files here or click the upload button",
|
||||
"dropFilesHereOpen": "Drop files here or click the open button",
|
||||
"pdfFilesOnly": "PDF files only",
|
||||
"supportedFileTypes": "Supported file types",
|
||||
"upload": "Upload",
|
||||
"uploadFile": "Upload File",
|
||||
"uploadFiles": "Upload Files",
|
||||
"open": "Open",
|
||||
"openFile": "Open File",
|
||||
"openFiles": "Open Files",
|
||||
"noFilesInStorage": "No files available in storage. Upload some files first.",
|
||||
"noFilesInStorageOpen": "No files available in storage. Open some files first.",
|
||||
"selectFromStorage": "Select from Storage",
|
||||
"backToTools": "Back to Tools",
|
||||
"addFiles": "Add Files",
|
||||
@ -4621,6 +4630,8 @@
|
||||
"myFiles": "My Files",
|
||||
"noRecentFiles": "No recent files found",
|
||||
"googleDriveNotAvailable": "Google Drive integration not available",
|
||||
"downloadSelected": "Download Selected",
|
||||
"saveSelected": "Save Selected",
|
||||
"openFiles": "Open Files",
|
||||
"openFile": "Open File",
|
||||
"details": "File Details",
|
||||
@ -5769,12 +5780,17 @@
|
||||
"label": "Username",
|
||||
"placeholder": "Enter your username"
|
||||
},
|
||||
"email": {
|
||||
"label": "Email",
|
||||
"placeholder": "Enter your email"
|
||||
},
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"placeholder": "Enter your password"
|
||||
},
|
||||
"error": {
|
||||
"emptyUsername": "Please enter your username",
|
||||
"emptyEmail": "Please enter your email",
|
||||
"emptyPassword": "Please enter your password",
|
||||
"oauthFailed": "OAuth login failed. Please try again."
|
||||
},
|
||||
@ -6032,4 +6048,4 @@
|
||||
"tags": "text,annotation,label",
|
||||
"applySignatures": "Apply Text"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +119,28 @@ pub fn run() {
|
||||
cleanup_backend();
|
||||
// Allow the window to close
|
||||
}
|
||||
RunEvent::WindowEvent { event: WindowEvent::DragDrop(drag_drop_event), .. } => {
|
||||
use tauri::DragDropEvent;
|
||||
match drag_drop_event {
|
||||
DragDropEvent::Drop { paths, .. } => {
|
||||
add_log(format!("📂 Files dropped: {:?}", paths));
|
||||
let mut added_files = false;
|
||||
|
||||
for path in paths {
|
||||
if let Some(path_str) = path.to_str() {
|
||||
add_log(format!("📂 Processing dropped file: {}", path_str));
|
||||
add_opened_file(path_str.to_string());
|
||||
added_files = true;
|
||||
}
|
||||
}
|
||||
|
||||
if added_files {
|
||||
let _ = app_handle.emit("files-changed", ());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
RunEvent::Opened { urls } => {
|
||||
add_log(format!("📂 Tauri file opened event: {:?}", urls));
|
||||
@ -128,11 +150,9 @@ pub fn run() {
|
||||
let url_str = url.as_str();
|
||||
if url_str.starts_with("file://") {
|
||||
let file_path = url_str.strip_prefix("file://").unwrap_or(url_str);
|
||||
if file_path.ends_with(".pdf") {
|
||||
add_log(format!("📂 Processing opened PDF: {}", file_path));
|
||||
add_opened_file(file_path.to_string());
|
||||
added_files = true;
|
||||
}
|
||||
add_log(format!("📂 Processing opened file: {}", file_path));
|
||||
add_opened_file(file_path.to_string());
|
||||
added_files = true;
|
||||
}
|
||||
}
|
||||
// Emit a generic notification that files were added (frontend will re-read storage)
|
||||
|
||||
@ -6,6 +6,8 @@ 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';
|
||||
import { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
import { useFileActionIcons } from '@app/hooks/useFileActionIcons';
|
||||
|
||||
interface AddFileCardProps {
|
||||
onFileSelect: (files: File[]) => void;
|
||||
@ -23,6 +25,8 @@ const AddFileCard = ({
|
||||
const { openFilesModal } = useFilesModalContext();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const [isUploadHover, setIsUploadHover] = useState(false);
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
|
||||
const handleCardClick = () => {
|
||||
openFilesModal();
|
||||
@ -152,10 +156,10 @@ const AddFileCard = ({
|
||||
onClick={handleNativeUploadClick}
|
||||
onMouseEnter={() => setIsUploadHover(true)}
|
||||
>
|
||||
<LocalIcon icon="upload" width="1.25rem" height="1.25rem" style={{ color: 'var(--accent-interactive)' }} />
|
||||
<LocalIcon icon={icons.uploadIconName} width="1.25rem" height="1.25rem" style={{ color: 'var(--accent-interactive)' }} />
|
||||
{isUploadHover && (
|
||||
<span style={{ marginLeft: '.5rem' }}>
|
||||
{t('landing.uploadFromComputer', 'Upload from computer')}
|
||||
{terminology.uploadFromComputer}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
@ -166,7 +170,7 @@ const AddFileCard = ({
|
||||
className="text-[var(--accent-interactive)]"
|
||||
style={{ fontSize: '.8rem', textAlign: 'center', marginTop: '0.5rem' }}
|
||||
>
|
||||
{t('fileUpload.dropFilesHere', 'Drop files here or click the upload button')}
|
||||
{terminology.dropFilesHere}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3,7 +3,8 @@ import { Text, ActionIcon, CheckboxIndicator, Tooltip, Modal, Button, Group, Sta
|
||||
import { useIsMobile } from '@app/hooks/useIsMobile';
|
||||
import { alert } from '@app/components/toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined';
|
||||
import { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
import { useFileActionIcons } from '@app/hooks/useFileActionIcons';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||
import UnarchiveIcon from '@mui/icons-material/Unarchive';
|
||||
@ -57,6 +58,9 @@ const FileEditorThumbnail = ({
|
||||
isSupported = true,
|
||||
}: FileEditorThumbnailProps) => {
|
||||
const { t } = useTranslation();
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
const DownloadOutlinedIcon = icons.download;
|
||||
const {
|
||||
pinFile,
|
||||
unpinFile,
|
||||
@ -204,7 +208,7 @@ const FileEditorThumbnail = ({
|
||||
{
|
||||
id: 'download',
|
||||
icon: <DownloadOutlinedIcon style={{ fontSize: 20 }} />,
|
||||
label: t('download', 'Download'),
|
||||
label: terminology.download,
|
||||
onClick: (e) => {
|
||||
e.stopPropagation();
|
||||
onDownloadFile(file.id);
|
||||
|
||||
@ -5,12 +5,16 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useFileManagerContext } from '@app/contexts/FileManagerContext';
|
||||
import LocalIcon from '@app/components/shared/LocalIcon';
|
||||
import { BASE_PATH } from '@app/constants/app';
|
||||
import { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
import { useFileActionIcons } from '@app/hooks/useFileActionIcons';
|
||||
|
||||
const EmptyFilesState: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const { onLocalFileClick } = useFileManagerContext();
|
||||
const [isUploadHover, setIsUploadHover] = useState(false);
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
|
||||
const handleUploadClick = () => {
|
||||
onLocalFileClick();
|
||||
@ -91,10 +95,10 @@ const EmptyFilesState: React.FC = () => {
|
||||
onClick={handleUploadClick}
|
||||
onMouseEnter={() => setIsUploadHover(true)}
|
||||
>
|
||||
<LocalIcon icon="upload" width="1.25rem" height="1.25rem" style={{ color: 'var(--accent-interactive)' }} />
|
||||
<LocalIcon icon={icons.uploadIconName} width="1.25rem" height="1.25rem" style={{ color: 'var(--accent-interactive)' }} />
|
||||
{isUploadHover && (
|
||||
<span style={{ marginLeft: '.5rem' }}>
|
||||
{t('landing.uploadFromComputer', 'Upload from computer')}
|
||||
{terminology.uploadFromComputer}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
@ -105,7 +109,7 @@ const EmptyFilesState: React.FC = () => {
|
||||
className="text-[var(--accent-interactive)]"
|
||||
style={{ fontSize: '.8rem', textAlign: 'center' }}
|
||||
>
|
||||
{t('fileUpload.dropFilesHere', 'Drop files here or click the upload button')}
|
||||
{terminology.dropFilesHere}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,12 +2,16 @@ import React from "react";
|
||||
import { Group, Text, ActionIcon, Tooltip } from "@mantine/core";
|
||||
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 "@app/contexts/FileManagerContext";
|
||||
import { useFileActionTerminology } from "@app/hooks/useFileActionTerminology";
|
||||
import { useFileActionIcons } from "@app/hooks/useFileActionIcons";
|
||||
|
||||
const FileActions: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
const DownloadIcon = icons.download;
|
||||
const { recentFiles, selectedFileIds, filteredFiles, onSelectAll, onDeleteSelected, onDownloadSelected } =
|
||||
useFileManagerContext();
|
||||
|
||||
@ -95,7 +99,7 @@ const FileActions: React.FC = () => {
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label={t("fileManager.downloadSelected", "Download Selected")}>
|
||||
<Tooltip label={terminology.downloadSelected}>
|
||||
<ActionIcon
|
||||
variant="light"
|
||||
size="sm"
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import React from 'react';
|
||||
import { Stack, Text, Button, Group } from '@mantine/core';
|
||||
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 '@app/contexts/FileManagerContext';
|
||||
import { useGoogleDrivePicker } from '@app/hooks/useGoogleDrivePicker';
|
||||
import { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
import { useFileActionIcons } from '@app/hooks/useFileActionIcons';
|
||||
|
||||
interface FileSourceButtonsProps {
|
||||
horizontal?: boolean;
|
||||
@ -17,6 +18,9 @@ const FileSourceButtons: React.FC<FileSourceButtonsProps> = ({
|
||||
const { activeSource, onSourceChange, onLocalFileClick, onGoogleDriveSelect } = useFileManagerContext();
|
||||
const { t } = useTranslation();
|
||||
const { isEnabled: isGoogleDriveEnabled, openPicker: openGoogleDrivePicker } = useGoogleDrivePicker();
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
const UploadIcon = icons.upload;
|
||||
|
||||
const handleGoogleDriveClick = async () => {
|
||||
try {
|
||||
@ -76,7 +80,7 @@ const FileSourceButtons: React.FC<FileSourceButtonsProps> = ({
|
||||
}
|
||||
}}
|
||||
>
|
||||
{horizontal ? t('fileUpload.uploadFiles', 'Upload') : t('fileUpload.uploadFiles', 'Upload Files')}
|
||||
{horizontal ? terminology.upload : terminology.uploadFiles}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
|
||||
@ -2,7 +2,6 @@ import React, { useState, useCallback, useRef, useMemo, useEffect } from 'react'
|
||||
import { ActionIcon, CheckboxIndicator } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined';
|
||||
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
|
||||
import PushPinIcon from '@mui/icons-material/PushPin';
|
||||
import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined';
|
||||
@ -13,6 +12,8 @@ import styles from '@app/components/pageEditor/PageEditor.module.css';
|
||||
import { useFileContext } from '@app/contexts/FileContext';
|
||||
import { FileId } from '@app/types/file';
|
||||
import { PrivateContent } from '@app/components/shared/PrivateContent';
|
||||
import { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
import { useFileActionIcons } from '@app/hooks/useFileActionIcons';
|
||||
|
||||
interface FileItem {
|
||||
id: FileId;
|
||||
@ -51,6 +52,9 @@ const FileThumbnail = ({
|
||||
isSupported = true,
|
||||
}: FileThumbnailProps) => {
|
||||
const { t } = useTranslation();
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
const DownloadOutlinedIcon = icons.download;
|
||||
const { pinFile, unpinFile, isFilePinned, activeFiles } = useFileContext();
|
||||
|
||||
// ---- Drag state ----
|
||||
@ -86,7 +90,7 @@ const FileThumbnail = ({
|
||||
}
|
||||
|
||||
// If we can't find a way to download, surface a status message
|
||||
onSetStatus?.(typeof t === 'function' ? t('downloadUnavailable', 'Download unavailable for this item') : 'Download unavailable for this item');
|
||||
onSetStatus?.(terminology.downloadUnavailable);
|
||||
}, [file, onDownloadFile, onSetStatus, t]);
|
||||
const handleRef = useRef<HTMLSpanElement | null>(null);
|
||||
|
||||
@ -279,7 +283,7 @@ const FileThumbnail = ({
|
||||
onClick={() => { downloadSelectedFile(); setShowActions(false); }}
|
||||
>
|
||||
<DownloadOutlinedIcon fontSize="small" />
|
||||
<span>{t('download', 'Download')}</span>
|
||||
<span>{terminology.download}</span>
|
||||
</button>
|
||||
|
||||
<div className={styles.actionsDivider} />
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FileId } from '@app/types/file';
|
||||
import { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
|
||||
interface FilePickerModalProps {
|
||||
opened: boolean;
|
||||
@ -31,6 +32,7 @@ const FilePickerModal = ({
|
||||
onSelectFiles,
|
||||
}: FilePickerModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const terminology = useFileActionTerminology();
|
||||
const [selectedFileIds, setSelectedFileIds] = useState<FileId[]>([]);
|
||||
|
||||
// Reset selection when modal opens
|
||||
@ -130,7 +132,7 @@ const FilePickerModal = ({
|
||||
<Stack gap="md">
|
||||
{storedFiles.length === 0 ? (
|
||||
<Text c="dimmed" ta="center" py="xl">
|
||||
{t("fileUpload.noFilesInStorage", "No files available in storage. Upload some files first.")}
|
||||
{terminology.noFilesInStorage}
|
||||
</Text>
|
||||
) : (
|
||||
<>
|
||||
@ -253,7 +255,7 @@ const FilePickerModal = ({
|
||||
disabled={selectedFileIds.length === 0}
|
||||
>
|
||||
{selectedFileIds.length > 0
|
||||
? `${t("fileUpload.loadFromStorage", "Load")} ${selectedFileIds.length} ${t("fileUpload.uploadFiles", "Files")}`
|
||||
? `${t("fileUpload.loadFromStorage", "Load")} ${selectedFileIds.length} ${terminology.uploadFiles}`
|
||||
: t("fileUpload.loadFromStorage", "Load Files")
|
||||
}
|
||||
</Button>
|
||||
|
||||
@ -8,6 +8,8 @@ import { useFilesModalContext } from '@app/contexts/FilesModalContext';
|
||||
import { BASE_PATH } from '@app/constants/app';
|
||||
import { useLogoPath } from '@app/hooks/useLogoPath';
|
||||
import { useFileManager } from '@app/hooks/useFileManager';
|
||||
import { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
import { useFileActionIcons } from '@app/hooks/useFileActionIcons';
|
||||
|
||||
const LandingPage = () => {
|
||||
const { addFiles } = useFileHandler();
|
||||
@ -19,6 +21,8 @@ const LandingPage = () => {
|
||||
const logoPath = useLogoPath();
|
||||
const { loadRecentFiles } = useFileManager();
|
||||
const [hasRecents, setHasRecents] = React.useState<boolean>(false);
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
|
||||
const handleFileDrop = async (files: File[]) => {
|
||||
await addFiles(files);
|
||||
@ -187,10 +191,10 @@ const LandingPage = () => {
|
||||
onClick={handleNativeUploadClick}
|
||||
onMouseEnter={() => setIsUploadHover(true)}
|
||||
>
|
||||
<LocalIcon icon="upload" width="1.25rem" height="1.25rem" style={{ color: 'var(--accent-interactive)' }} />
|
||||
<LocalIcon icon={icons.uploadIconName} width="1.25rem" height="1.25rem" style={{ color: 'var(--accent-interactive)' }} />
|
||||
{isUploadHover && (
|
||||
<span style={{ marginLeft: '.5rem' }}>
|
||||
{t('landing.uploadFromComputer', 'Upload from computer')}
|
||||
{terminology.uploadFromComputer}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
@ -239,7 +243,7 @@ const LandingPage = () => {
|
||||
className="text-[var(--accent-interactive)]"
|
||||
style={{ fontSize: '.8rem' }}
|
||||
>
|
||||
{t('fileUpload.dropFilesHere', 'Drop files here or click the upload button')}
|
||||
{terminology.dropFilesHere}
|
||||
</span>
|
||||
</div>
|
||||
</Dropzone>
|
||||
|
||||
@ -6,6 +6,8 @@ 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 { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
import { useFileActionIcons } from '@app/hooks/useFileActionIcons';
|
||||
|
||||
import LanguageSelector from '@app/components/shared/LanguageSelector';
|
||||
import { useRainbowThemeContext } from '@app/components/shared/RainbowThemeProvider';
|
||||
@ -44,6 +46,8 @@ export default function RightRail() {
|
||||
const { sidebarRefs } = useSidebarContext();
|
||||
const { position: tooltipPosition, offset: tooltipOffset } = useRightRailTooltipSide(sidebarRefs);
|
||||
const { t } = useTranslation();
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
const viewerContext = React.useContext(ViewerContext);
|
||||
const { toggleTheme, themeMode } = useRainbowThemeContext();
|
||||
const { buttons, actions, allButtonsDisabled } = useRightRail();
|
||||
@ -165,9 +169,9 @@ export default function RightRail() {
|
||||
return t('rightRail.exportAll', 'Export PDF');
|
||||
}
|
||||
if (selectedCount > 0) {
|
||||
return t('rightRail.downloadSelected', 'Download Selected Files');
|
||||
return terminology.downloadSelected;
|
||||
}
|
||||
return t('rightRail.downloadAll', 'Download All');
|
||||
return terminology.downloadAll;
|
||||
}, [currentView, selectedCount, t]);
|
||||
|
||||
return (
|
||||
@ -232,7 +236,7 @@ export default function RightRail() {
|
||||
(currentView === 'viewer' ? !exportState?.canExport : totalItems === 0 || allButtonsDisabled)
|
||||
}
|
||||
>
|
||||
<LocalIcon icon="download" width="1.5rem" height="1.5rem" />
|
||||
<LocalIcon icon={icons.downloadIconName} width="1.5rem" height="1.5rem" />
|
||||
</ActionIcon>,
|
||||
downloadTooltip,
|
||||
tooltipPosition,
|
||||
|
||||
@ -14,6 +14,7 @@ import { BookmarkNode } from '@app/utils/editTableOfContents';
|
||||
import ErrorNotification from '@app/components/tools/shared/ErrorNotification';
|
||||
import ResultsPreview from '@app/components/tools/shared/ResultsPreview';
|
||||
import BookmarkEditor from '@app/components/tools/editTableOfContents/BookmarkEditor';
|
||||
import { useFileActionTerminology } from '@app/hooks/useFileActionTerminology';
|
||||
|
||||
export interface EditTableOfContentsWorkbenchViewData {
|
||||
bookmarks: BookmarkNode[];
|
||||
@ -40,6 +41,7 @@ interface EditTableOfContentsWorkbenchViewProps {
|
||||
|
||||
const EditTableOfContentsWorkbenchView = ({ data }: EditTableOfContentsWorkbenchViewProps) => {
|
||||
const { t } = useTranslation();
|
||||
const terminology = useFileActionTerminology();
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
@ -180,7 +182,7 @@ const EditTableOfContentsWorkbenchView = ({ data }: EditTableOfContentsWorkbench
|
||||
download={downloadFilename ?? undefined}
|
||||
leftSection={<LocalIcon icon='download-rounded' />}
|
||||
>
|
||||
{t('download', 'Download')}
|
||||
{terminology.download}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { Button, Stack } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import UndoIcon from "@mui/icons-material/Undo";
|
||||
import ErrorNotification from "@app/components/tools/shared/ErrorNotification";
|
||||
import ResultsPreview from "@app/components/tools/shared/ResultsPreview";
|
||||
import { SuggestedToolsSection } from "@app/components/tools/shared/SuggestedToolsSection";
|
||||
import { ToolOperationHook } from "@app/hooks/tools/shared/useToolOperation";
|
||||
import { Tooltip } from "@app/components/shared/Tooltip";
|
||||
import { useFileActionTerminology } from "@app/hooks/useFileActionTerminology";
|
||||
import { useFileActionIcons } from "@app/hooks/useFileActionIcons";
|
||||
|
||||
export interface ReviewToolStepProps<TParams = unknown> {
|
||||
isVisible: boolean;
|
||||
@ -29,6 +30,9 @@ function ReviewStepContent<TParams = unknown>({
|
||||
onUndo?: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const terminology = useFileActionTerminology();
|
||||
const icons = useFileActionIcons();
|
||||
const DownloadIcon = icons.download;
|
||||
const stepRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleUndo = async () => {
|
||||
@ -96,7 +100,7 @@ function ReviewStepContent<TParams = unknown>({
|
||||
fullWidth
|
||||
mb="md"
|
||||
>
|
||||
{t("download", "Download")}
|
||||
{terminology.download}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
|
||||
import DownloadRoundedIcon from "@mui/icons-material/DownloadRounded";
|
||||
import "@app/components/tools/showJS/ShowJSView.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useFileActionTerminology } from "@app/hooks/useFileActionTerminology";
|
||||
|
||||
import {
|
||||
tokenizeToLines,
|
||||
@ -28,6 +29,7 @@ interface ShowJSViewProps {
|
||||
|
||||
const ShowJSView: React.FC<ShowJSViewProps> = ({ data }) => {
|
||||
const { t } = useTranslation();
|
||||
const terminology = useFileActionTerminology();
|
||||
const text = useMemo(() => {
|
||||
if (typeof data === "string") return data;
|
||||
return data?.scriptText ?? "";
|
||||
@ -173,7 +175,7 @@ const ShowJSView: React.FC<ShowJSViewProps> = ({ data }) => {
|
||||
disabled={!downloadUrl}
|
||||
leftSection={<DownloadRoundedIcon fontSize="small" />}
|
||||
>
|
||||
{t("download", "Download")}
|
||||
{terminology.download}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
|
||||
15
frontend/src/core/hooks/useFileActionIcons.ts
Normal file
15
frontend/src/core/hooks/useFileActionIcons.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import UploadIcon from '@mui/icons-material/Upload';
|
||||
import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined';
|
||||
|
||||
/**
|
||||
* File action icons for web builds
|
||||
* Desktop builds override this with different icons
|
||||
*/
|
||||
export function useFileActionIcons() {
|
||||
return {
|
||||
upload: UploadIcon,
|
||||
download: DownloadOutlinedIcon,
|
||||
uploadIconName: 'upload' as const,
|
||||
downloadIconName: 'download' as const,
|
||||
};
|
||||
}
|
||||
22
frontend/src/core/hooks/useFileActionTerminology.ts
Normal file
22
frontend/src/core/hooks/useFileActionTerminology.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
/**
|
||||
* File action terminology for web builds
|
||||
* Desktop builds override this with different terminology
|
||||
*/
|
||||
export function useFileActionTerminology() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
uploadFiles: t('fileUpload.uploadFiles', 'Upload Files'),
|
||||
uploadFile: t('fileUpload.uploadFile', 'Upload File'),
|
||||
upload: t('fileUpload.upload', 'Upload'),
|
||||
dropFilesHere: t('fileUpload.dropFilesHere', 'Drop files here or click the upload button'),
|
||||
uploadFromComputer: t('landing.uploadFromComputer', 'Upload from computer'),
|
||||
download: t('download', 'Download'),
|
||||
downloadAll: t('rightRail.downloadAll', 'Download All'),
|
||||
downloadSelected: t('fileManager.downloadSelected', 'Download Selected'),
|
||||
downloadUnavailable: t('downloadUnavailable', 'Download unavailable for this item'),
|
||||
noFilesInStorage: t('fileUpload.noFilesInStorage', 'No files available in storage. Upload some files first.'),
|
||||
};
|
||||
}
|
||||
@ -25,7 +25,9 @@ export const LoginForm: React.FC<LoginFormProps> = ({ serverUrl, isSaaS = false,
|
||||
|
||||
// Validation
|
||||
if (!username.trim()) {
|
||||
setValidationError(t('setup.login.error.emptyUsername', 'Please enter your username'));
|
||||
setValidationError(isSaaS
|
||||
? t('setup.login.error.emptyEmail', 'Please enter your email')
|
||||
: t('setup.login.error.emptyUsername', 'Please enter your username'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -132,8 +134,12 @@ export const LoginForm: React.FC<LoginFormProps> = ({ serverUrl, isSaaS = false,
|
||||
)}
|
||||
|
||||
<TextInput
|
||||
label={t('setup.login.username.label', 'Username')}
|
||||
placeholder={t('setup.login.username.placeholder', 'Enter your username')}
|
||||
label={isSaaS
|
||||
? t('setup.login.email.label', 'Email')
|
||||
: t('setup.login.username.label', 'Username')}
|
||||
placeholder={isSaaS
|
||||
? t('setup.login.email.placeholder', 'Enter your email')
|
||||
: t('setup.login.username.placeholder', 'Enter your username')}
|
||||
value={username}
|
||||
onChange={(e) => {
|
||||
setUsername(e.target.value);
|
||||
|
||||
15
frontend/src/desktop/hooks/useFileActionIcons.ts
Normal file
15
frontend/src/desktop/hooks/useFileActionIcons.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import FolderOpenOutlinedIcon from '@mui/icons-material/FolderOpenOutlined';
|
||||
import SaveOutlinedIcon from '@mui/icons-material/SaveOutlined';
|
||||
|
||||
/**
|
||||
* File action icons for desktop builds
|
||||
* Overrides core implementation with desktop-appropriate icons
|
||||
*/
|
||||
export function useFileActionIcons() {
|
||||
return {
|
||||
upload: FolderOpenOutlinedIcon,
|
||||
download: SaveOutlinedIcon,
|
||||
uploadIconName: 'folder-rounded' as const,
|
||||
downloadIconName: 'save-rounded' as const,
|
||||
};
|
||||
}
|
||||
22
frontend/src/desktop/hooks/useFileActionTerminology.ts
Normal file
22
frontend/src/desktop/hooks/useFileActionTerminology.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
/**
|
||||
* File action terminology for desktop builds
|
||||
* Overrides core implementation with desktop-appropriate terminology
|
||||
*/
|
||||
export function useFileActionTerminology() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
uploadFiles: t('fileUpload.openFiles', 'Open Files'),
|
||||
uploadFile: t('fileUpload.openFile', 'Open File'),
|
||||
upload: t('fileUpload.open', 'Open'),
|
||||
dropFilesHere: t('fileUpload.dropFilesHereOpen', 'Drop files here or click the open button'),
|
||||
uploadFromComputer: t('landing.openFromComputer', 'Open from computer'),
|
||||
download: t('save', 'Save'),
|
||||
downloadAll: t('rightRail.saveAll', 'Save All'),
|
||||
downloadSelected: t('fileManager.saveSelected', 'Save Selected'),
|
||||
downloadUnavailable: t('saveUnavailable', 'Save unavailable for this item'),
|
||||
noFilesInStorage: t('fileUpload.noFilesInStorageOpen', 'No files available in storage. Open some files first.'),
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user