mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
Preserve local paths for desktop saves (#5543)
# Summary - Adds desktop file tracking: local paths are preserved and save buttons now work as expcted (doing Save/Save As as appropriate) - Adds logic to track whether files are 'dirty' (they've been modified by some tool, and not saved to disk yet). - Improves file state UX (dirty vs saved) and close warnings - Web behaviour should be unaffected by these changes ## Indicators Files now have indicators in desktop mode to tell you their state. ### File up-to-date with disk <img width="318" height="393" alt="image" src="https://github.com/user-attachments/assets/06325f9a-afd7-4c2f-8a5b-6d11e3093115" /> ### File modified by a tool but not saved to disk yet <img width="357" height="385" alt="image" src="https://github.com/user-attachments/assets/1a7716d9-c6f7-4d13-be0d-c1de6493954b" /> ### File not tracked on disk <img width="312" height="379" alt="image" src="https://github.com/user-attachments/assets/9cffe300-bd9a-4e19-97c7-9b98bebefacc" /> # Limitations - It's a bit weird that we still have files stored in indexeddb in the app, which are still loadable. We might want to change this behaviour in the future - Viewer's Save doesn't persist to disk. I've left that out here because it'd need a lot of testing to make sure the logic's right with making sure you can leave the Viewer with applying the changes to the PDF _without_ saving to disk - There's no current way to do Save As on a file that has already been persisted to disk - it's only ever Save. Similarly, there's no way to duplicate a file. --------- Co-authored-by: James Brunton <[email protected]> Co-authored-by: James Brunton <[email protected]>
This commit is contained in:
co-authored by
James Brunton
James Brunton
parent
946196de43
commit
b8ce4e47c1
@@ -2,6 +2,7 @@ import { ReactNode, useEffect, useState } from "react";
|
||||
import { AppProviders as ProprietaryAppProviders } from "@proprietary/components/AppProviders";
|
||||
import { DesktopConfigSync } from '@app/components/DesktopConfigSync';
|
||||
import { DesktopBannerInitializer } from '@app/components/DesktopBannerInitializer';
|
||||
import { SaveShortcutListener } from '@app/components/SaveShortcutListener';
|
||||
import { SetupWizard } from '@app/components/SetupWizard';
|
||||
import { useFirstLaunchCheck } from '@app/hooks/useFirstLaunchCheck';
|
||||
import { useBackendInitializer } from '@app/hooks/useBackendInitializer';
|
||||
@@ -149,6 +150,7 @@ export function AppProviders({ children }: { children: ReactNode }) {
|
||||
>
|
||||
<DesktopConfigSync />
|
||||
<DesktopBannerInitializer />
|
||||
<SaveShortcutListener />
|
||||
{children}
|
||||
</ProprietaryAppProviders>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useSaveShortcut } from '@app/hooks/useSaveShortcut';
|
||||
import { useExitWarning } from '@app/hooks/useExitWarning';
|
||||
|
||||
/**
|
||||
* Desktop-only component that sets up keyboard shortcuts and exit warnings
|
||||
* - Ctrl/Cmd+S to save selected files
|
||||
* - Warning on app exit if unsaved files
|
||||
* Renders nothing, just sets up the listeners
|
||||
*/
|
||||
export function SaveShortcutListener() {
|
||||
useSaveShortcut();
|
||||
useExitWarning();
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import { Tooltip } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { StirlingFileStub } from '@app/types/fileContext';
|
||||
import { PrivateContent } from '@app/components/shared/PrivateContent';
|
||||
|
||||
interface FileEditorFileNameProps {
|
||||
file: StirlingFileStub;
|
||||
}
|
||||
|
||||
const FileEditorFileName = ({ file }: FileEditorFileNameProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<PrivateContent>{file.name}</PrivateContent>
|
||||
{!file.localFilePath && (
|
||||
<Tooltip label={t('fileNotSavedToDisk', 'Not saved to disk')}>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '6px',
|
||||
height: '6px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'var(--mantine-color-red-6)',
|
||||
flexShrink: 0
|
||||
}}
|
||||
aria-label={t('fileNotSavedToDisk', 'Not saved to disk')}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{file.localFilePath && file.isDirty && (
|
||||
<Tooltip label={t('unsavedChanges', 'Unsaved changes')}>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '6px',
|
||||
height: '6px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'var(--mantine-color-yellow-6)',
|
||||
flexShrink: 0
|
||||
}}
|
||||
aria-label={t('unsavedChanges', 'Unsaved changes')}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{file.localFilePath && !file.isDirty && (
|
||||
<Tooltip label={t('fileSavedToDisk', 'File saved to disk')}>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '6px',
|
||||
height: '6px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'var(--mantine-color-green-6)',
|
||||
flexShrink: 0
|
||||
}}
|
||||
aria-label={t('fileSavedToDisk', 'File saved to disk')}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileEditorFileName;
|
||||
Reference in New Issue
Block a user