mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
tabs
This commit is contained in:
parent
458cb7fab4
commit
bd383fb1e7
@ -4533,6 +4533,32 @@
|
|||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"confirm": "Reset and Change Mode"
|
"confirm": "Reset and Change Mode"
|
||||||
},
|
},
|
||||||
|
"welcomeBanner": {
|
||||||
|
"title": "Welcome to PDF Text Editor (Early Access)",
|
||||||
|
"experimental": "This is an experimental feature in active development. Expect some instability and issues during use.",
|
||||||
|
"howItWorks": "This tool converts your PDF to an editable format where you can modify text content and reposition images. Changes are saved back as a new PDF.",
|
||||||
|
"bestFor": "Works Best With:",
|
||||||
|
"bestFor1": "Simple PDFs containing primarily text and images",
|
||||||
|
"bestFor2": "Documents with standard paragraph formatting",
|
||||||
|
"bestFor3": "Letters, essays, reports, and basic documents",
|
||||||
|
"notIdealFor": "Not Ideal For:",
|
||||||
|
"notIdealFor1": "PDFs with special formatting like bullet points, tables, or multi-column layouts",
|
||||||
|
"notIdealFor2": "Magazines, brochures, or heavily designed documents",
|
||||||
|
"notIdealFor3": "Instruction manuals with complex layouts",
|
||||||
|
"limitations": "Current Limitations:",
|
||||||
|
"limitation1": "Font rendering may differ slightly from the original PDF",
|
||||||
|
"limitation2": "Complex graphics, form fields, and annotations are preserved but not editable",
|
||||||
|
"limitation3": "Large files may take time to convert and process",
|
||||||
|
"knownIssues": "Known Issues (Being Fixed):",
|
||||||
|
"issue1": "Text colour is not currently preserved (will be added soon)",
|
||||||
|
"issue2": "Paragraph mode has more alignment and spacing issues - Single Line mode recommended",
|
||||||
|
"issue3": "The preview display differs from the exported PDF - exported PDFs are closer to the original",
|
||||||
|
"issue4": "Rotated text alignment may need manual adjustment",
|
||||||
|
"issue5": "Transparency and layering effects may vary from original",
|
||||||
|
"feedback": "This is an early access feature. Please report any issues you encounter to help us improve!",
|
||||||
|
"gotIt": "Got it",
|
||||||
|
"dontShowAgain": "Don't show again"
|
||||||
|
},
|
||||||
"disclaimer": {
|
"disclaimer": {
|
||||||
"heading": "Preview limitations",
|
"heading": "Preview limitations",
|
||||||
"textFocus": "This workspace focuses on editing text and repositioning embedded images. Complex page artwork, form widgets, and layered graphics are preserved for export but are not fully editable here.",
|
"textFocus": "This workspace focuses on editing text and repositioning embedded images. Complex page artwork, form widgets, and layered graphics are preserved for export but are not fully editable here.",
|
||||||
@ -4579,6 +4605,21 @@
|
|||||||
"standard14": "Standard PDF Font",
|
"standard14": "Standard PDF Font",
|
||||||
"warnings": "Warnings",
|
"warnings": "Warnings",
|
||||||
"suggestions": "Notes"
|
"suggestions": "Notes"
|
||||||
|
},
|
||||||
|
"manual": {
|
||||||
|
"mergeTooltip": "Merge selected boxes into a single paragraph",
|
||||||
|
"merge": "Merge selection",
|
||||||
|
"ungroupTooltip": "Split paragraph back into separate lines",
|
||||||
|
"ungroup": "Ungroup selection",
|
||||||
|
"widthMenu": "Width options",
|
||||||
|
"expandWidth": "Expand to page edge",
|
||||||
|
"resetWidth": "Reset width",
|
||||||
|
"resizeHandle": "Adjust text width"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"manualGrouping": {
|
||||||
|
"descriptionInline": "Tip: Hold Ctrl (Cmd) or Shift to multi-select text boxes. A floating toolbar will appear above the selection so you can merge, ungroup, or adjust widths."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace": {
|
"workspace": {
|
||||||
|
|||||||
@ -121,10 +121,11 @@ export const NavigationProvider: React.FC<{
|
|||||||
hasUnsavedChanges
|
hasUnsavedChanges
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we're leaving pageEditor or viewer workbench and have unsaved changes, request navigation
|
// If we're leaving pageEditor, viewer, or custom workbench and have unsaved changes, request navigation
|
||||||
const leavingWorkbenchWithChanges =
|
const leavingWorkbenchWithChanges =
|
||||||
(state.workbench === 'pageEditor' && workbench !== 'pageEditor' && hasUnsavedChanges) ||
|
(state.workbench === 'pageEditor' && workbench !== 'pageEditor' && hasUnsavedChanges) ||
|
||||||
(state.workbench === 'viewer' && workbench !== 'viewer' && hasUnsavedChanges);
|
(state.workbench === 'viewer' && workbench !== 'viewer' && hasUnsavedChanges) ||
|
||||||
|
(state.workbench.startsWith('custom:') && workbench !== state.workbench && hasUnsavedChanges);
|
||||||
|
|
||||||
if (leavingWorkbenchWithChanges) {
|
if (leavingWorkbenchWithChanges) {
|
||||||
// Update state to reflect unsaved changes so modal knows
|
// Update state to reflect unsaved changes so modal knows
|
||||||
@ -132,7 +133,19 @@ export const NavigationProvider: React.FC<{
|
|||||||
dispatch({ type: 'SET_UNSAVED_CHANGES', payload: { hasChanges: true } });
|
dispatch({ type: 'SET_UNSAVED_CHANGES', payload: { hasChanges: true } });
|
||||||
}
|
}
|
||||||
const performWorkbenchChange = () => {
|
const performWorkbenchChange = () => {
|
||||||
dispatch({ type: 'SET_WORKBENCH', payload: { workbench } });
|
// When leaving a custom workbench, clear the selected tool
|
||||||
|
console.log('[NavigationContext] performWorkbenchChange executing', {
|
||||||
|
from: state.workbench,
|
||||||
|
to: workbench,
|
||||||
|
isCustom: state.workbench.startsWith('custom:')
|
||||||
|
});
|
||||||
|
if (state.workbench.startsWith('custom:')) {
|
||||||
|
console.log('[NavigationContext] Clearing tool and changing workbench to:', workbench);
|
||||||
|
dispatch({ type: 'SET_TOOL_AND_WORKBENCH', payload: { toolId: null, workbench } });
|
||||||
|
} else {
|
||||||
|
console.log('[NavigationContext] Just changing workbench to:', workbench);
|
||||||
|
dispatch({ type: 'SET_WORKBENCH', payload: { workbench } });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: performWorkbenchChange } });
|
dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: performWorkbenchChange } });
|
||||||
dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: true } });
|
dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: true } });
|
||||||
@ -149,10 +162,11 @@ export const NavigationProvider: React.FC<{
|
|||||||
// Check for unsaved changes using registered checker or state
|
// Check for unsaved changes using registered checker or state
|
||||||
const hasUnsavedChanges = unsavedChangesCheckerRef.current?.() || state.hasUnsavedChanges;
|
const hasUnsavedChanges = unsavedChangesCheckerRef.current?.() || state.hasUnsavedChanges;
|
||||||
|
|
||||||
// If we're leaving pageEditor or viewer workbench and have unsaved changes, request navigation
|
// If we're leaving pageEditor, viewer, or custom workbench and have unsaved changes, request navigation
|
||||||
const leavingWorkbenchWithChanges =
|
const leavingWorkbenchWithChanges =
|
||||||
(state.workbench === 'pageEditor' && workbench !== 'pageEditor' && hasUnsavedChanges) ||
|
(state.workbench === 'pageEditor' && workbench !== 'pageEditor' && hasUnsavedChanges) ||
|
||||||
(state.workbench === 'viewer' && workbench !== 'viewer' && hasUnsavedChanges);
|
(state.workbench === 'viewer' && workbench !== 'viewer' && hasUnsavedChanges) ||
|
||||||
|
(state.workbench.startsWith('custom:') && workbench !== state.workbench && hasUnsavedChanges);
|
||||||
|
|
||||||
if (leavingWorkbenchWithChanges) {
|
if (leavingWorkbenchWithChanges) {
|
||||||
const performWorkbenchChange = () => {
|
const performWorkbenchChange = () => {
|
||||||
@ -192,13 +206,19 @@ export const NavigationProvider: React.FC<{
|
|||||||
}, [state.hasUnsavedChanges]),
|
}, [state.hasUnsavedChanges]),
|
||||||
|
|
||||||
confirmNavigation: useCallback(() => {
|
confirmNavigation: useCallback(() => {
|
||||||
|
console.log('[NavigationContext] confirmNavigation called', {
|
||||||
|
hasPendingNav: !!state.pendingNavigation,
|
||||||
|
currentWorkbench: state.workbench,
|
||||||
|
currentTool: state.selectedTool
|
||||||
|
});
|
||||||
if (state.pendingNavigation) {
|
if (state.pendingNavigation) {
|
||||||
state.pendingNavigation();
|
state.pendingNavigation();
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } });
|
dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } });
|
||||||
dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: false } });
|
dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: false } });
|
||||||
}, [state.pendingNavigation]),
|
console.log('[NavigationContext] confirmNavigation completed');
|
||||||
|
}, [state.pendingNavigation, state.workbench, state.selectedTool]),
|
||||||
|
|
||||||
cancelNavigation: useCallback(() => {
|
cancelNavigation: useCallback(() => {
|
||||||
dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } });
|
dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } });
|
||||||
|
|||||||
@ -218,15 +218,25 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
|||||||
}, [customViewRegistry, customViewData]);
|
}, [customViewRegistry, customViewData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isBaseWorkbench(navigationState.workbench)) {
|
const { workbench } = navigationState;
|
||||||
|
if (isBaseWorkbench(workbench)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentCustomView = customWorkbenchViews.find(view => view.workbenchId === navigationState.workbench);
|
const currentCustomView = customWorkbenchViews.find(view => view.workbenchId === workbench);
|
||||||
|
const expectedWorkbench = selectedTool?.workbench;
|
||||||
|
const workbenchOwnedBySelectedTool = expectedWorkbench === workbench;
|
||||||
|
|
||||||
if (!currentCustomView || currentCustomView.data == null) {
|
if (!currentCustomView || currentCustomView.data == null) {
|
||||||
|
// If the currently selected tool expects this custom workbench, allow it
|
||||||
|
// some time to register/populate the view instead of immediately bouncing
|
||||||
|
// the user back to Active Files.
|
||||||
|
if (workbenchOwnedBySelectedTool) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
actions.setWorkbench(getDefaultWorkbench());
|
actions.setWorkbench(getDefaultWorkbench());
|
||||||
}
|
}
|
||||||
}, [actions, customWorkbenchViews, navigationState.workbench]);
|
}, [actions, customWorkbenchViews, navigationState.workbench, selectedTool]);
|
||||||
|
|
||||||
// Persisted via PreferencesContext; no direct localStorage writes needed here
|
// Persisted via PreferencesContext; no direct localStorage writes needed here
|
||||||
|
|
||||||
@ -421,4 +431,4 @@ export function useToolWorkflow(): ToolWorkflowContextValue {
|
|||||||
throw new Error('useToolWorkflow must be used within a ToolWorkflowProvider');
|
throw new Error('useToolWorkflow must be used within a ToolWorkflowProvider');
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import MergeTypeIcon from '@mui/icons-material/MergeType';
|
|||||||
import CallSplitIcon from '@mui/icons-material/CallSplit';
|
import CallSplitIcon from '@mui/icons-material/CallSplit';
|
||||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||||
import { Rnd } from 'react-rnd';
|
import { Rnd } from 'react-rnd';
|
||||||
|
import NavigationWarningModal from '@core/components/shared/NavigationWarningModal';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PdfTextEditorViewData,
|
PdfTextEditorViewData,
|
||||||
@ -347,6 +348,30 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => {
|
|||||||
maxWidth: number;
|
maxWidth: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
|
// First-time banner state
|
||||||
|
const [showWelcomeBanner, setShowWelcomeBanner] = useState(() => {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem('pdfTextEditor.welcomeBannerDismissed') !== 'true';
|
||||||
|
} catch {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleDismissWelcomeBanner = useCallback(() => {
|
||||||
|
// Just dismiss for this session, don't save to localStorage
|
||||||
|
setShowWelcomeBanner(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDontShowAgain = useCallback(() => {
|
||||||
|
// Save to localStorage to never show again
|
||||||
|
try {
|
||||||
|
localStorage.setItem('pdfTextEditor.welcomeBannerDismissed', 'true');
|
||||||
|
} catch {
|
||||||
|
// Ignore localStorage errors
|
||||||
|
}
|
||||||
|
setShowWelcomeBanner(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
document: pdfDocument,
|
document: pdfDocument,
|
||||||
groupsByPage,
|
groupsByPage,
|
||||||
@ -373,6 +398,7 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => {
|
|||||||
onReset,
|
onReset,
|
||||||
onDownloadJson,
|
onDownloadJson,
|
||||||
onGeneratePdf,
|
onGeneratePdf,
|
||||||
|
onGeneratePdfForNavigation,
|
||||||
onForceSingleTextElementChange,
|
onForceSingleTextElementChange,
|
||||||
onGroupingModeChange,
|
onGroupingModeChange,
|
||||||
onMergeGroups,
|
onMergeGroups,
|
||||||
@ -1705,6 +1731,82 @@ const selectionToolbarPosition = useMemo(() => {
|
|||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
opened={showWelcomeBanner}
|
||||||
|
onClose={handleDismissWelcomeBanner}
|
||||||
|
title={
|
||||||
|
<Group gap="xs">
|
||||||
|
<InfoOutlinedIcon fontSize="small" />
|
||||||
|
<Text fw={600}>{t('pdfTextEditor.welcomeBanner.title', 'Welcome to PDF Text Editor (Early Access)')}</Text>
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
|
centered
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
<ScrollArea style={{ maxHeight: '70vh' }} offsetScrollbars>
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Alert color="orange" variant="light" radius="md">
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{t('pdfTextEditor.welcomeBanner.experimental', 'This is an experimental feature in active development. Expect some instability and issues during use.')}
|
||||||
|
</Text>
|
||||||
|
</Alert>
|
||||||
|
<Text size="sm">
|
||||||
|
{t('pdfTextEditor.welcomeBanner.howItWorks', 'This tool converts your PDF to an editable format where you can modify text content and reposition images. Changes are saved back as a new PDF.')}
|
||||||
|
</Text>
|
||||||
|
<Divider />
|
||||||
|
<Text size="sm" fw={500} c="green.7">
|
||||||
|
{t('pdfTextEditor.welcomeBanner.bestFor', 'Works Best With:')}
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" component="ul" style={{ marginLeft: '1rem', marginTop: '0.25rem' }}>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.bestFor1', 'Simple PDFs containing primarily text and images')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.bestFor2', 'Documents with standard paragraph formatting')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.bestFor3', 'Letters, essays, reports, and basic documents')}</li>
|
||||||
|
</Text>
|
||||||
|
<Divider />
|
||||||
|
<Text size="sm" fw={500} c="orange.7">
|
||||||
|
{t('pdfTextEditor.welcomeBanner.notIdealFor', 'Not Ideal For:')}
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" component="ul" style={{ marginLeft: '1rem', marginTop: '0.25rem' }}>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.notIdealFor1', 'PDFs with special formatting like bullet points, tables, or multi-column layouts')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.notIdealFor2', 'Magazines, brochures, or heavily designed documents')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.notIdealFor3', 'Instruction manuals with complex layouts')}</li>
|
||||||
|
</Text>
|
||||||
|
<Divider />
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{t('pdfTextEditor.welcomeBanner.limitations', 'Current Limitations:')}
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" component="ul" style={{ marginLeft: '1rem', marginTop: '0.25rem' }}>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.limitation1', 'Font rendering may differ slightly from the original PDF')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.limitation2', 'Complex graphics, form fields, and annotations are preserved but not editable')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.limitation3', 'Large files may take time to convert and process')}</li>
|
||||||
|
</Text>
|
||||||
|
<Divider />
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{t('pdfTextEditor.welcomeBanner.knownIssues', 'Known Issues (Being Fixed):')}
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" component="ul" style={{ marginLeft: '1rem', marginTop: '0.25rem' }}>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.issue1', 'Text colour is not currently preserved (will be added soon)')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.issue2', 'Paragraph mode has more alignment and spacing issues - Single Line mode recommended')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.issue3', 'The preview display differs from the exported PDF - exported PDFs are closer to the original')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.issue4', 'Rotated text alignment may need manual adjustment')}</li>
|
||||||
|
<li>{t('pdfTextEditor.welcomeBanner.issue5', 'Transparency and layering effects may vary from original')}</li>
|
||||||
|
</Text>
|
||||||
|
<Divider />
|
||||||
|
<Text size="xs" c="dimmed">
|
||||||
|
{t('pdfTextEditor.welcomeBanner.feedback', 'This is an early access feature. Please report any issues you encounter to help us improve!')}
|
||||||
|
</Text>
|
||||||
|
<Group justify="flex-end" gap="sm" mt="md">
|
||||||
|
<Button variant="default" onClick={handleDismissWelcomeBanner}>
|
||||||
|
{t('pdfTextEditor.welcomeBanner.gotIt', 'Got it')}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleDontShowAgain}>
|
||||||
|
{t('pdfTextEditor.welcomeBanner.dontShowAgain', "Don't show again")}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</ScrollArea>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
withBorder
|
withBorder
|
||||||
padding="md"
|
padding="md"
|
||||||
@ -2169,7 +2271,7 @@ const selectionToolbarPosition = useMemo(() => {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
minHeight: '100%',
|
minHeight: '100%',
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
padding: 0,
|
padding: '2px',
|
||||||
backgroundColor: 'rgba(255,255,255,0.95)',
|
backgroundColor: 'rgba(255,255,255,0.95)',
|
||||||
color: textColor,
|
color: textColor,
|
||||||
fontSize: `${fontSizePx}px`,
|
fontSize: `${fontSizePx}px`,
|
||||||
@ -2212,7 +2314,7 @@ const selectionToolbarPosition = useMemo(() => {
|
|||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
minHeight: '100%',
|
minHeight: '100%',
|
||||||
padding: 0,
|
padding: '2px',
|
||||||
whiteSpace,
|
whiteSpace,
|
||||||
wordBreak,
|
wordBreak,
|
||||||
overflowWrap,
|
overflowWrap,
|
||||||
@ -2342,6 +2444,11 @@ const selectionToolbarPosition = useMemo(() => {
|
|||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{/* Navigation Warning Modal */}
|
||||||
|
<NavigationWarningModal
|
||||||
|
onApplyAndContinue={onGeneratePdfForNavigation}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -207,6 +207,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
} = useToolWorkflow();
|
} = useToolWorkflow();
|
||||||
const { actions: navigationActions } = useNavigationActions();
|
const { actions: navigationActions } = useNavigationActions();
|
||||||
const navigationState = useNavigationState();
|
const navigationState = useNavigationState();
|
||||||
|
const { registerUnsavedChangesChecker, unregisterUnsavedChangesChecker } = navigationActions;
|
||||||
|
|
||||||
const [loadedDocument, setLoadedDocument] = useState<PdfJsonDocument | null>(null);
|
const [loadedDocument, setLoadedDocument] = useState<PdfJsonDocument | null>(null);
|
||||||
const [groupsByPage, setGroupsByPage] = useState<TextGroup[][]>([]);
|
const [groupsByPage, setGroupsByPage] = useState<TextGroup[][]>([]);
|
||||||
@ -959,7 +960,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
}
|
}
|
||||||
}, [buildPayload, onComplete]);
|
}, [buildPayload, onComplete]);
|
||||||
|
|
||||||
const handleGeneratePdf = useCallback(async () => {
|
const handleGeneratePdf = useCallback(async (skipComplete = false) => {
|
||||||
try {
|
try {
|
||||||
setIsGeneratingPdf(true);
|
setIsGeneratingPdf(true);
|
||||||
|
|
||||||
@ -1053,7 +1054,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
|
|
||||||
downloadBlob(response.data, downloadName);
|
downloadBlob(response.data, downloadName);
|
||||||
|
|
||||||
if (onComplete) {
|
if (onComplete && !skipComplete) {
|
||||||
const pdfFile = new File([response.data], downloadName, { type: 'application/pdf' });
|
const pdfFile = new File([response.data], downloadName, { type: 'application/pdf' });
|
||||||
onComplete([pdfFile]);
|
onComplete([pdfFile]);
|
||||||
}
|
}
|
||||||
@ -1094,7 +1095,7 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
|
|
||||||
downloadBlob(response.data, downloadName);
|
downloadBlob(response.data, downloadName);
|
||||||
|
|
||||||
if (onComplete) {
|
if (onComplete && !skipComplete) {
|
||||||
const pdfFile = new File([response.data], downloadName, { type: 'application/pdf' });
|
const pdfFile = new File([response.data], downloadName, { type: 'application/pdf' });
|
||||||
onComplete([pdfFile]);
|
onComplete([pdfFile]);
|
||||||
}
|
}
|
||||||
@ -1273,6 +1274,10 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
onReset: handleResetEdits,
|
onReset: handleResetEdits,
|
||||||
onDownloadJson: handleDownloadJson,
|
onDownloadJson: handleDownloadJson,
|
||||||
onGeneratePdf: handleGeneratePdf,
|
onGeneratePdf: handleGeneratePdf,
|
||||||
|
onGeneratePdfForNavigation: async () => {
|
||||||
|
// Generate PDF without triggering tool completion
|
||||||
|
await handleGeneratePdf(true);
|
||||||
|
},
|
||||||
onForceSingleTextElementChange: setForceSingleTextElement,
|
onForceSingleTextElementChange: setForceSingleTextElement,
|
||||||
onGroupingModeChange: setGroupingMode,
|
onGroupingModeChange: setGroupingMode,
|
||||||
onMergeGroups: handleMergeGroups,
|
onMergeGroups: handleMergeGroups,
|
||||||
@ -1370,14 +1375,30 @@ const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
unregisterCustomWorkbenchView,
|
unregisterCustomWorkbenchView,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Note: Compare tool doesn't auto-force workbench, and neither should we
|
||||||
|
// The workbench should be set when the tool is selected via proper channels
|
||||||
|
// (tool registry, tool picker, etc.) - not forced here
|
||||||
|
|
||||||
|
// Keep hasChanges in a ref for the checker to access
|
||||||
|
const hasChangesRef = useRef(hasChanges);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
hasChangesRef.current = hasChanges;
|
||||||
navigationState.selectedTool === 'pdfTextEditor' &&
|
console.log('[PdfTextEditor] hasChanges updated to:', hasChanges);
|
||||||
navigationState.workbench !== WORKBENCH_ID
|
}, [hasChanges]);
|
||||||
) {
|
|
||||||
navigationActions.setWorkbench(WORKBENCH_ID);
|
// Register unsaved changes checker for navigation guard
|
||||||
}
|
useEffect(() => {
|
||||||
}, [navigationActions, navigationState.selectedTool, navigationState.workbench]);
|
const checker = () => {
|
||||||
|
console.log('[PdfTextEditor] Checking unsaved changes:', hasChangesRef.current);
|
||||||
|
return hasChangesRef.current;
|
||||||
|
};
|
||||||
|
registerUnsavedChangesChecker(checker);
|
||||||
|
console.log('[PdfTextEditor] Registered unsaved changes checker');
|
||||||
|
return () => {
|
||||||
|
console.log('[PdfTextEditor] Unregistered unsaved changes checker');
|
||||||
|
unregisterUnsavedChangesChecker();
|
||||||
|
};
|
||||||
|
}, [registerUnsavedChangesChecker, unregisterUnsavedChangesChecker]);
|
||||||
|
|
||||||
const lastSentViewDataRef = useRef<PdfTextEditorViewData | null>(null);
|
const lastSentViewDataRef = useRef<PdfTextEditorViewData | null>(null);
|
||||||
|
|
||||||
|
|||||||
@ -218,6 +218,7 @@ export interface PdfTextEditorViewData {
|
|||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
onDownloadJson: () => void;
|
onDownloadJson: () => void;
|
||||||
onGeneratePdf: () => void;
|
onGeneratePdf: () => void;
|
||||||
|
onGeneratePdfForNavigation: () => Promise<void>;
|
||||||
onForceSingleTextElementChange: (value: boolean) => void;
|
onForceSingleTextElementChange: (value: boolean) => void;
|
||||||
onGroupingModeChange: (value: 'auto' | 'paragraph' | 'singleLine') => void;
|
onGroupingModeChange: (value: 'auto' | 'paragraph' | 'singleLine') => void;
|
||||||
onMergeGroups: (pageIndex: number, groupIds: string[]) => boolean;
|
onMergeGroups: (pageIndex: number, groupIds: string[]) => boolean;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user