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,137 @@
import { useCallback, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import apiClient from '@app/services/apiClient';
import type { ToolOperationHook } from '@app/hooks/tools/shared/useToolOperation';
import type { StirlingFile } from '@app/types/fileContext';
import { extractErrorMessage } from '@app/utils/toolErrorHandler';
import type { ShowJSParameters } from '@app/hooks/tools/showJS/useShowJSParameters';
import type { ResponseType } from 'axios';
export interface ShowJSOperationHook extends ToolOperationHook<ShowJSParameters> {
scriptText: string | null;
}
export const useShowJSOperation = (): ShowJSOperationHook => {
const { t } = useTranslation();
const [isLoading, setIsLoading] = useState(false);
const [status, setStatus] = useState('');
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [files, setFiles] = useState<File[]>([]);
const [downloadUrl, setDownloadUrl] = useState<string | null>(null);
const [downloadFilename, setDownloadFilename] = useState('');
const [scriptText, setScriptText] = useState<string | null>(null);
const cancelRequested = useRef(false);
const previousUrl = useRef<string | null>(null);
const cleanupDownloadUrl = useCallback(() => {
if (previousUrl.current) {
URL.revokeObjectURL(previousUrl.current);
previousUrl.current = null;
}
}, []);
const resetResults = useCallback(() => {
cancelRequested.current = false;
setScriptText(null);
setFiles([]);
cleanupDownloadUrl();
setDownloadUrl(null);
setDownloadFilename('');
setStatus('');
setErrorMessage(null);
}, [cleanupDownloadUrl]);
const clearError = useCallback(() => {
setErrorMessage(null);
}, []);
const executeOperation = useCallback(
async (_params: ShowJSParameters, selectedFiles: StirlingFile[]) => {
if (selectedFiles.length === 0) {
setErrorMessage(t('noFileSelected', 'No files selected'));
return;
}
cancelRequested.current = false;
setIsLoading(true);
setStatus(t('showJS.processing', 'Extracting JavaScript...'));
setErrorMessage(null);
setScriptText(null);
setFiles([]);
cleanupDownloadUrl();
setDownloadUrl(null);
setDownloadFilename('');
try {
const file = selectedFiles[0];
const formData = new FormData();
formData.append('fileInput', file);
const response = await apiClient.post('/api/v1/misc/show-javascript', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
responseType: 'text' as ResponseType,
transformResponse: [(data) => data],
});
const text: string = typeof response.data === 'string' ? response.data : '';
setScriptText(text);
// Optional: prepare a downloadable file
const outFile = new File([text], (file.name?.replace(/\.[^.]+$/, '') || 'extracted') + '.js', {
type: 'application/javascript',
});
setFiles([outFile]);
const blobUrl = URL.createObjectURL(outFile);
previousUrl.current = blobUrl;
setDownloadUrl(blobUrl);
setDownloadFilename(outFile.name);
setStatus(t('showJS.done', 'JavaScript extracted'));
} catch (error: unknown) {
setErrorMessage(extractErrorMessage(error));
setStatus('');
} finally {
setIsLoading(false);
}
},
[t, cleanupDownloadUrl]
);
const cancelOperation = useCallback(() => {
cancelRequested.current = true;
setIsLoading(false);
setStatus(t('operationCancelled', 'Operation cancelled'));
}, [t]);
const undoOperation = useCallback(async () => {
// No-op for this tool
setStatus(t('nothingToUndo', 'Nothing to undo'));
}, [t]);
return {
// State (align with ToolOperationHook)
files,
thumbnails: [],
isGeneratingThumbnails: false,
downloadUrl,
downloadFilename,
isLoading,
status,
errorMessage,
progress: null,
// Custom state
scriptText,
// Actions
executeOperation,
resetResults,
clearError,
cancelOperation,
undoOperation,
};
};

View File

@@ -0,0 +1,21 @@
import { useBaseParameters, type BaseParametersHook } from '@app/hooks/tools/shared/useBaseParameters';
import { BaseParameters } from '@app/types/parameters';
export interface ShowJSParameters extends BaseParameters {
// Extends BaseParameters - ready for future parameter additions if needed
}
export const defaultParameters: ShowJSParameters = {
// No parameters needed
};
export type ShowJSParametersHook = BaseParametersHook<ShowJSParameters>;
export const useShowJSParameters = (): ShowJSParametersHook => {
return useBaseParameters({
defaultParameters,
endpointName: 'show-javascript',
});
};