Addition of the Show JavaScript tool (#4877)

# Description of Changes

- Added the show javascript tool.

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing)
for more details.
This commit is contained in:
EthanHealy01
2025-11-12 15:02:43 +00:00
committed by GitHub
parent a5e2b54274
commit c8615518a6
15 changed files with 1281 additions and 43 deletions

View File

@@ -0,0 +1,150 @@
import React, { useEffect, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import CodeRoundedIcon from '@mui/icons-material/CodeRounded';
import { createToolFlow } from '@app/components/tools/shared/createToolFlow';
import { useBaseTool } from '@app/hooks/tools/shared/useBaseTool';
import type { BaseToolProps, ToolComponent } from '@app/types/tool';
import { useShowJSParameters, defaultParameters } from '@app/hooks/tools/showJS/useShowJSParameters';
import { useShowJSOperation, type ShowJSOperationHook } from '@app/hooks/tools/showJS/useShowJSOperation';
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
import { useNavigationActions, useNavigationState } from '@app/contexts/NavigationContext';
import ShowJSView from '@app/components/tools/showJS/ShowJSView';
import { useFileSelection } from '@app/contexts/file/fileHooks';
const ShowJS = (props: BaseToolProps) => {
const { t } = useTranslation();
const { actions: navigationActions } = useNavigationActions();
const navigationState = useNavigationState();
const {
registerCustomWorkbenchView,
unregisterCustomWorkbenchView,
setCustomWorkbenchViewData,
clearCustomWorkbenchViewData,
} = useToolWorkflow();
const VIEW_ID = 'showJSView';
const WORKBENCH_ID = 'custom:showJS' as const;
const viewIcon = useMemo(() => <CodeRoundedIcon fontSize="small" />, []);
const base = useBaseTool('showJS', useShowJSParameters, useShowJSOperation, props, { minFiles: 1 });
const operation = base.operation as ShowJSOperationHook;
const hasResults = Boolean(operation.scriptText);
const { clearSelections } = useFileSelection();
useEffect(() => {
registerCustomWorkbenchView({
id: VIEW_ID,
workbenchId: WORKBENCH_ID,
label: t('showJS.view.title', 'JavaScript'),
icon: viewIcon,
component: ({ data }) => <ShowJSView data={data} />,
});
return () => {
clearCustomWorkbenchViewData(VIEW_ID);
unregisterCustomWorkbenchView(VIEW_ID);
};
}, [clearCustomWorkbenchViewData, registerCustomWorkbenchView, t, unregisterCustomWorkbenchView, viewIcon]);
const lastShownRef = useRef<number | null>(null);
useEffect(() => {
if (operation.scriptText) {
setCustomWorkbenchViewData(VIEW_ID, {
scriptText: operation.scriptText,
downloadUrl: operation.downloadUrl,
downloadFilename: operation.downloadFilename,
});
const marker = operation.scriptText.length;
const isNew = lastShownRef.current == null || marker !== lastShownRef.current;
if (isNew) {
lastShownRef.current = marker;
if (navigationState.selectedTool === 'showJS' && navigationState.workbench !== WORKBENCH_ID) {
navigationActions.setWorkbench(WORKBENCH_ID);
}
}
} else {
clearCustomWorkbenchViewData(VIEW_ID);
lastShownRef.current = null;
}
}, [
clearCustomWorkbenchViewData,
navigationActions,
navigationState.selectedTool,
navigationState.workbench,
operation.scriptText,
setCustomWorkbenchViewData,
]);
useEffect(() => {
if ((base.selectedFiles?.length ?? 0) === 0) {
try { base.operation.resetResults(); } catch { /* noop */ }
try { clearCustomWorkbenchViewData(VIEW_ID); } catch { /* noop */ }
if (navigationState.workbench === WORKBENCH_ID) {
try { navigationActions.setWorkbench('fileEditor'); } catch { /* noop */ }
}
lastShownRef.current = null;
}
}, [
base.selectedFiles?.length,
base.operation,
clearCustomWorkbenchViewData,
navigationActions,
navigationState.workbench,
]);
return createToolFlow({
files: {
selectedFiles: base.selectedFiles,
isCollapsed: false,
},
steps: [],
executeButton: {
text: hasResults ? t('back', 'Back') : t('showJS.submit', 'Extract JavaScript'),
loadingText: t('loading', 'Loading...'),
onClick: hasResults
? async () => {
// Clear results and deselect files so user can pick another file
try {
await base.operation.resetResults();
} catch { /* noop */ }
try {
clearSelections();
} catch { /* noop */ }
// Close the custom JS view and send user back to file manager to pick another file
try {
clearCustomWorkbenchViewData(VIEW_ID);
} catch { /* noop */ }
try {
navigationActions.setWorkbench('fileEditor');
} catch { /* noop */ }
}
: base.handleExecute,
disabled: hasResults
? false
: (
!base.hasFiles ||
(base.selectedFiles?.length ?? 0) !== 1 ||
base.operation.isLoading ||
base.endpointLoading ||
base.endpointEnabled === false
),
isVisible: true,
},
review: {
isVisible: hasResults,
operation: base.operation,
title: t('showJS.results', 'Result'),
onUndo: undefined,
},
});
};
const ShowJSTool = ShowJS as ToolComponent;
ShowJSTool.tool = () => useShowJSOperation;
ShowJSTool.getDefaultParameters = () => ({ ...defaultParameters });
export default ShowJSTool;