mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-26 17:52:59 +02:00
Update dependencies and enhance ESLint config
This commit is contained in:
parent
fd52dc0226
commit
4a8e2e477c
1
.gitignore
vendored
1
.gitignore
vendored
@ -210,3 +210,4 @@ node_modules/
|
|||||||
test_batch.json
|
test_batch.json
|
||||||
*.backup.*.json
|
*.backup.*.json
|
||||||
frontend/public/locales/*/translation.backup*.json
|
frontend/public/locales/*/translation.backup*.json
|
||||||
|
/frontend
|
||||||
|
@ -1,19 +1,39 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
import eslint from '@eslint/js';
|
import eslint from '@eslint/js';
|
||||||
|
import globals from 'globals';
|
||||||
import { defineConfig } from 'eslint/config';
|
import { defineConfig } from 'eslint/config';
|
||||||
|
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
||||||
|
import reactPlugin from 'eslint-plugin-react';
|
||||||
import tseslint from 'typescript-eslint';
|
import tseslint from 'typescript-eslint';
|
||||||
|
|
||||||
|
const tsconfigRootDir = fileURLToPath(new URL('./', import.meta.url));
|
||||||
|
|
||||||
|
const srcGlobs = ['{src,frontend/src}/**/*.{ts,tsx,js,jsx}'];
|
||||||
|
const srcTsGlobs = ['{src,frontend/src}/**/*.{ts,tsx}'];
|
||||||
|
const nodeGlobs = [
|
||||||
|
'scripts/**/*.{js,ts}',
|
||||||
|
'vite.config.ts',
|
||||||
|
'vitest.config.ts',
|
||||||
|
'vitest.minimal.config.ts',
|
||||||
|
'playwright.config.ts',
|
||||||
|
'tailwind.config.js',
|
||||||
|
'postcss.config.js',
|
||||||
|
'eslint.config.mjs',
|
||||||
|
];
|
||||||
|
|
||||||
export default defineConfig(
|
export default defineConfig(
|
||||||
eslint.configs.recommended,
|
|
||||||
tseslint.configs.recommended,
|
|
||||||
{
|
{
|
||||||
ignores: [
|
ignores: [
|
||||||
"dist", // Contains 3rd party code
|
"dist", // Contains 3rd party code
|
||||||
"public", // Contains 3rd party code
|
"public", // Contains 3rd party code
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
eslint.configs.recommended,
|
||||||
{
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
extends: [...tseslint.configs.recommended],
|
||||||
rules: {
|
rules: {
|
||||||
"no-undef": "off", // Temporarily disabled until codebase conformant
|
"no-undef": "off", // Temporarily disabled until codebase conformant
|
||||||
"@typescript-eslint/no-empty-object-type": [
|
"@typescript-eslint/no-empty-object-type": [
|
||||||
@ -26,17 +46,112 @@ export default defineConfig(
|
|||||||
"@typescript-eslint/no-explicit-any": "off", // Temporarily disabled until codebase conformant
|
"@typescript-eslint/no-explicit-any": "off", // Temporarily disabled until codebase conformant
|
||||||
"@typescript-eslint/no-require-imports": "off", // Temporarily disabled until codebase conformant
|
"@typescript-eslint/no-require-imports": "off", // Temporarily disabled until codebase conformant
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
"error",
|
"warn",
|
||||||
{
|
{
|
||||||
"args": "all", // All function args must be used (or explicitly ignored)
|
args: 'all', // All function args must be used (or explicitly ignored)
|
||||||
"argsIgnorePattern": "^_", // Allow unused variables beginning with an underscore
|
argsIgnorePattern: '^_', // Allow unused variables beginning with an underscore
|
||||||
"caughtErrors": "all", // Caught errors must be used (or explicitly ignored)
|
caughtErrors: 'all', // Caught errors must be used (or explicitly ignored)
|
||||||
"caughtErrorsIgnorePattern": "^_", // Allow unused variables beginning with an underscore
|
caughtErrorsIgnorePattern: '^_', // Allow unused variables beginning with an underscore
|
||||||
"destructuredArrayIgnorePattern": "^_", // Allow unused variables beginning with an underscore
|
destructuredArrayIgnorePattern: '^_', // Allow unused variables beginning with an underscore
|
||||||
"varsIgnorePattern": "^_", // Allow unused variables beginning with an underscore
|
varsIgnorePattern: '^_', // Allow unused variables beginning with an underscore
|
||||||
"ignoreRestSiblings": true, // Allow unused variables when removing attributes from objects (otherwise this requires explicit renaming like `({ x: _x, ...y }) => y`, which is clunky)
|
ignoreRestSiblings: true, // Allow unused variables when removing attributes from objects (otherwise this requires explicit renaming like `({ x: _x, ...y }) => y`, which is clunky)
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: srcTsGlobs,
|
||||||
|
extends: [
|
||||||
|
...tseslint.configs.recommendedTypeChecked,
|
||||||
|
...tseslint.configs.stylisticTypeChecked,
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: srcGlobs,
|
||||||
|
extends: [reactPlugin.configs.flat.recommended],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
react: reactPlugin,
|
||||||
|
'react-hooks': reactHooksPlugin,
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'react/react-in-jsx-scope': 'off', // Not needed with React 17+
|
||||||
|
|
||||||
|
'react-hooks/exhaustive-deps': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'react-hooks/rules-of-hooks': 'warn',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
|
||||||
|
'@typescript-eslint/no-unsafe-member-access': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
"@typescript-eslint/no-inferrable-types": "off", // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/prefer-nullish-coalescing': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-unsafe-assignment': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-unsafe-return': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-unsafe-call': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-unsafe-arguments': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-unsafe-argument': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/require-await': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/only-throw-error': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-floating-promises': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/prefer-promise-reject-errors': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/prefer-optional-chain': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-unnecessary-type-assertions': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/unbound-method': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-base-to-string': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-misused-promises': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-unnecessary-type-assertion': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/restrict-template-expressions': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/dot-notation': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/prefer-regexp-exec': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/prefer-includes': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/consistent-indexed-object-style': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/non-nullable-type-assertion-style': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/consistent-generic-constructors': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/class-literal-property-style': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/consistent-type-definitions': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'@typescript-eslint/no-redundant-type-constituents': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
|
||||||
|
'react/no-children-prop': 'warn', // Children should be passed as actual children, not via the children prop
|
||||||
|
'react/prop-types': 'off', // We use TypeScript's types for props instead
|
||||||
|
'react/display-name': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
'react/no-unescaped-entities': 'off', // Temporarily disabled until codebase conformant
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'src/**/*.{test,spec}.{ts,tsx}',
|
||||||
|
'src/tests/**/*.{ts,tsx}',
|
||||||
|
],
|
||||||
|
extends: [tseslint.configs.disableTypeChecked],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.vitest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: nodeGlobs,
|
||||||
|
extends: [tseslint.configs.disableTypeChecked],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
2075
frontend/package-lock.json
generated
2075
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,32 +6,36 @@
|
|||||||
"proxy": "http://localhost:8080",
|
"proxy": "http://localhost:8080",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
|
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
|
||||||
"@embedpdf/core": "^1.2.1",
|
"@embedpdf/core": "^1.3.1",
|
||||||
"@embedpdf/engines": "^1.2.1",
|
"@embedpdf/engines": "^1.3.1",
|
||||||
"@embedpdf/plugin-interaction-manager": "^1.2.1",
|
"@embedpdf/plugin-interaction-manager": "^1.3.1",
|
||||||
"@embedpdf/plugin-loader": "^1.2.1",
|
"@embedpdf/plugin-loader": "^1.3.1",
|
||||||
"@embedpdf/plugin-pan": "^1.2.1",
|
"@embedpdf/plugin-pan": "^1.3.1",
|
||||||
"@embedpdf/plugin-render": "^1.2.1",
|
"@embedpdf/plugin-render": "^1.3.1",
|
||||||
"@embedpdf/plugin-rotate": "^1.2.1",
|
"@embedpdf/plugin-rotate": "^1.3.1",
|
||||||
"@embedpdf/plugin-scroll": "^1.2.1",
|
"@embedpdf/plugin-scroll": "^1.3.1",
|
||||||
"@embedpdf/plugin-search": "^1.2.1",
|
"@embedpdf/plugin-search": "^1.3.1",
|
||||||
"@embedpdf/plugin-selection": "^1.2.1",
|
"@embedpdf/plugin-selection": "^1.3.1",
|
||||||
"@embedpdf/plugin-spread": "^1.2.1",
|
"@embedpdf/plugin-spread": "^1.3.1",
|
||||||
"@embedpdf/plugin-thumbnail": "^1.2.1",
|
"@embedpdf/plugin-thumbnail": "^1.3.1",
|
||||||
"@embedpdf/plugin-tiling": "^1.2.1",
|
"@embedpdf/plugin-tiling": "^1.3.1",
|
||||||
"@embedpdf/plugin-viewport": "^1.2.1",
|
"@embedpdf/plugin-viewport": "^1.3.1",
|
||||||
"@embedpdf/plugin-zoom": "^1.2.1",
|
"@embedpdf/plugin-zoom": "^1.3.1",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@iconify/react": "^6.0.2",
|
"@iconify/react": "^6.0.2",
|
||||||
"@mantine/core": "^8.3.1",
|
"@mantine/core": "^8.3.2",
|
||||||
"@mantine/dates": "^8.3.1",
|
"@mantine/dates": "^8.3.2",
|
||||||
"@mantine/dropzone": "^8.3.1",
|
"@mantine/dropzone": "^8.3.2",
|
||||||
"@mantine/hooks": "^8.3.1",
|
"@mantine/hooks": "^8.3.2",
|
||||||
"@mui/icons-material": "^7.3.2",
|
"@mui/icons-material": "^7.3.2",
|
||||||
"@mui/material": "^7.3.2",
|
"@mui/material": "^7.3.2",
|
||||||
"@tailwindcss/postcss": "^4.1.13",
|
"@tailwindcss/postcss": "^4.1.13",
|
||||||
"@tanstack/react-virtual": "^3.13.12",
|
"@tanstack/react-virtual": "^3.13.12",
|
||||||
|
"@testing-library/dom": "^10.4.1",
|
||||||
|
"@testing-library/jest-dom": "^6.8.0",
|
||||||
|
"@testing-library/react": "^16.3.0",
|
||||||
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.12.2",
|
||||||
"i18next": "^25.5.2",
|
"i18next": "^25.5.2",
|
||||||
@ -41,11 +45,11 @@
|
|||||||
"license-report": "^6.8.0",
|
"license-report": "^6.8.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"pdfjs-dist": "^5.4.149",
|
"pdfjs-dist": "^5.4.149",
|
||||||
"posthog-js": "^1.268.0",
|
"posthog-js": "^1.268.5",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-i18next": "^15.7.3",
|
"react-i18next": "^16.0.0",
|
||||||
"react-router-dom": "^7.9.1",
|
"react-router-dom": "^7.9.2",
|
||||||
"tailwindcss": "^4.1.13",
|
"tailwindcss": "^4.1.13",
|
||||||
"web-vitals": "^5.1.0"
|
"web-vitals": "^5.1.0"
|
||||||
},
|
},
|
||||||
@ -54,6 +58,7 @@
|
|||||||
"dev": "npm run typecheck && vite",
|
"dev": "npm run typecheck && vite",
|
||||||
"prebuild": "npm run generate-icons",
|
"prebuild": "npm run generate-icons",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
|
"lint:fix": "eslint --fix",
|
||||||
"build": "npm run typecheck && vite build",
|
"build": "npm run typecheck && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
@ -94,9 +99,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.36.0",
|
"@eslint/js": "^9.36.0",
|
||||||
"@iconify-json/material-symbols": "^1.2.37",
|
"@iconify-json/material-symbols": "^1.2.39",
|
||||||
"@iconify/utils": "^3.0.2",
|
"@iconify/utils": "^3.0.2",
|
||||||
"@playwright/test": "^1.55.0",
|
"@playwright/test": "^1.55.1",
|
||||||
"@testing-library/dom": "^10.4.1",
|
"@testing-library/dom": "^10.4.1",
|
||||||
"@testing-library/jest-dom": "^6.8.0",
|
"@testing-library/jest-dom": "^6.8.0",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
@ -109,7 +114,9 @@
|
|||||||
"@vitejs/plugin-react-swc": "^4.1.0",
|
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||||
"@vitest/coverage-v8": "^3.2.4",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
"eslint": "^9.36.0",
|
"eslint": "^9.36.0",
|
||||||
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
|
"globals": "^16.4.0",
|
||||||
"jsdom": "^27.0.0",
|
"jsdom": "^27.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"madge": "^8.0.0",
|
"madge": "^8.0.0",
|
||||||
|
@ -14,7 +14,7 @@ interface DragDropGridProps<T extends DragDropItem> {
|
|||||||
selectionMode: boolean;
|
selectionMode: boolean;
|
||||||
isAnimating: boolean;
|
isAnimating: boolean;
|
||||||
onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPageIds?: string[]) => void;
|
onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPageIds?: string[]) => void;
|
||||||
renderItem: (item: T, index: number, refs: React.MutableRefObject<Map<string, HTMLDivElement>>) => React.ReactNode;
|
renderItem: (item: T, index: number, refs: React.RefObject<Map<string, HTMLDivElement>>) => React.ReactNode;
|
||||||
renderSplitMarker?: (item: T, index: number) => React.ReactNode;
|
renderSplitMarker?: (item: T, index: number) => React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ interface PageThumbnailProps {
|
|||||||
selectionMode: boolean;
|
selectionMode: boolean;
|
||||||
movingPage: number | null;
|
movingPage: number | null;
|
||||||
isAnimating: boolean;
|
isAnimating: boolean;
|
||||||
pageRefs: React.MutableRefObject<Map<string, HTMLDivElement>>;
|
pageRefs: React.RefObject<Map<string, HTMLDivElement>>;
|
||||||
onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPageIds?: string[]) => void;
|
onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPageIds?: string[]) => void;
|
||||||
onTogglePage: (pageId: string) => void;
|
onTogglePage: (pageId: string) => void;
|
||||||
onAnimateReorder: () => void;
|
onAnimateReorder: () => void;
|
||||||
|
@ -8,7 +8,7 @@ import { StirlingFileStub } from "../../types/fileContext";
|
|||||||
import { FileId } from "../../types/file";
|
import { FileId } from "../../types/file";
|
||||||
|
|
||||||
interface FileGridProps {
|
interface FileGridProps {
|
||||||
files: Array<{ file: File; record?: StirlingFileStub }>;
|
files: { file: File; record?: StirlingFileStub }[];
|
||||||
onRemove?: (index: number) => void;
|
onRemove?: (index: number) => void;
|
||||||
onDoubleClick?: (item: { file: File; record?: StirlingFileStub }) => void;
|
onDoubleClick?: (item: { file: File; record?: StirlingFileStub }) => void;
|
||||||
onView?: (item: { file: File; record?: StirlingFileStub }) => void;
|
onView?: (item: { file: File; record?: StirlingFileStub }) => void;
|
||||||
|
@ -8,7 +8,7 @@ export { useToast, ToastProvider, ToastRenderer };
|
|||||||
let _api: ReturnType<typeof createImperativeApi> | null = null;
|
let _api: ReturnType<typeof createImperativeApi> | null = null;
|
||||||
|
|
||||||
function createImperativeApi() {
|
function createImperativeApi() {
|
||||||
const subscribers: Array<(fn: any) => void> = [];
|
const subscribers: ((fn: any) => void)[] = [];
|
||||||
let api: any = null;
|
let api: any = null;
|
||||||
return {
|
return {
|
||||||
provide(instance: any) {
|
provide(instance: any) {
|
||||||
|
@ -9,7 +9,7 @@ import NoToolsFound from './shared/NoToolsFound';
|
|||||||
import "./toolPicker/ToolPicker.css";
|
import "./toolPicker/ToolPicker.css";
|
||||||
|
|
||||||
interface SearchResultsProps {
|
interface SearchResultsProps {
|
||||||
filteredTools: Array<{ item: [string, ToolRegistryEntry]; matchedText?: string }>;
|
filteredTools: { item: [string, ToolRegistryEntry]; matchedText?: string }[];
|
||||||
onSelect: (id: string) => void;
|
onSelect: (id: string) => void;
|
||||||
searchQuery?: string;
|
searchQuery?: string;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import { renderToolButtons } from "./shared/renderToolButtons";
|
|||||||
interface ToolPickerProps {
|
interface ToolPickerProps {
|
||||||
selectedToolKey: string | null;
|
selectedToolKey: string | null;
|
||||||
onSelect: (id: string) => void;
|
onSelect: (id: string) => void;
|
||||||
filteredTools: Array<{ item: [string, ToolRegistryEntry]; matchedText?: string }>;
|
filteredTools: { item: [string, ToolRegistryEntry]; matchedText?: string }[];
|
||||||
isSearching?: boolean;
|
isSearching?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ describe('ChangePermissionsSettings', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Should render checkboxes for all permission types
|
// Should render checkboxes for all permission types
|
||||||
const permissionKeys = Object.keys(defaultParameters) as Array<keyof ChangePermissionsParameters>;
|
const permissionKeys = Object.keys(defaultParameters) as (keyof ChangePermissionsParameters)[];
|
||||||
const checkboxes = screen.getAllByRole('checkbox');
|
const checkboxes = screen.getAllByRole('checkbox');
|
||||||
expect(checkboxes).toHaveLength(permissionKeys.length);
|
expect(checkboxes).toHaveLength(permissionKeys.length);
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ describe('ChangePermissionsSettings', () => {
|
|||||||
</TestWrapper>
|
</TestWrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
const permissionKeys = Object.keys(defaultParameters) as Array<keyof ChangePermissionsParameters>;
|
const permissionKeys = Object.keys(defaultParameters) as (keyof ChangePermissionsParameters)[];
|
||||||
|
|
||||||
permissionKeys.forEach(permission => {
|
permissionKeys.forEach(permission => {
|
||||||
expect(screen.getByText(`mock-changePermissions.permissions.${permission}.label`)).toBeInTheDocument();
|
expect(screen.getByText(`mock-changePermissions.permissions.${permission}.label`)).toBeInTheDocument();
|
||||||
@ -183,13 +183,13 @@ describe('ChangePermissionsSettings', () => {
|
|||||||
</TestWrapper>
|
</TestWrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
const permissionKeys = Object.keys(defaultParameters) as Array<keyof ChangePermissionsParameters>;
|
const permissionKeys = Object.keys(defaultParameters) as (keyof ChangePermissionsParameters)[];
|
||||||
permissionKeys.forEach(permission => {
|
permissionKeys.forEach(permission => {
|
||||||
expect(mockT).toHaveBeenCalledWith(`changePermissions.permissions.${permission}.label`, permission);
|
expect(mockT).toHaveBeenCalledWith(`changePermissions.permissions.${permission}.label`, permission);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each(Object.keys(defaultParameters) as Array<keyof ChangePermissionsParameters>)('should handle %s permission type individually', (permission) => {
|
test.each(Object.keys(defaultParameters) as (keyof ChangePermissionsParameters)[])('should handle %s permission type individually', (permission) => {
|
||||||
const testParameters: ChangePermissionsParameters = {
|
const testParameters: ChangePermissionsParameters = {
|
||||||
...defaultParameters,
|
...defaultParameters,
|
||||||
[permission]: true
|
[permission]: true
|
||||||
|
@ -14,7 +14,7 @@ const ChangePermissionsSettings = ({ parameters, onParameterChange, disabled = f
|
|||||||
return (
|
return (
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
{(Object.keys(parameters) as Array<keyof ChangePermissionsParameters>).map((key) => (
|
{(Object.keys(parameters) as (keyof ChangePermissionsParameters)[]).map((key) => (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
key={key}
|
key={key}
|
||||||
label={t(`changePermissions.permissions.${key}.label`, key)}
|
label={t(`changePermissions.permissions.${key}.label`, key)}
|
||||||
|
@ -27,7 +27,7 @@ import { StirlingFile } from "../../../types/fileContext";
|
|||||||
interface ConvertSettingsProps {
|
interface ConvertSettingsProps {
|
||||||
parameters: ConvertParameters;
|
parameters: ConvertParameters;
|
||||||
onParameterChange: <K extends keyof ConvertParameters>(key: K, value: ConvertParameters[K]) => void;
|
onParameterChange: <K extends keyof ConvertParameters>(key: K, value: ConvertParameters[K]) => void;
|
||||||
getAvailableToExtensions: (fromExtension: string) => Array<{value: string, label: string, group: string}>;
|
getAvailableToExtensions: (fromExtension: string) => {value: string, label: string, group: string}[];
|
||||||
selectedFiles: StirlingFile[];
|
selectedFiles: StirlingFile[];
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ interface SanitizeSettingsProps {
|
|||||||
const SanitizeSettings = ({ parameters, onParameterChange, disabled = false }: SanitizeSettingsProps) => {
|
const SanitizeSettings = ({ parameters, onParameterChange, disabled = false }: SanitizeSettingsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const options = (Object.keys(defaultParameters) as Array<keyof SanitizeParameters>).map((key) => ({
|
const options = (Object.keys(defaultParameters) as (keyof SanitizeParameters)[]).map((key) => ({
|
||||||
key: key,
|
key: key,
|
||||||
label: t(`sanitize.options.${key}`, key),
|
label: t(`sanitize.options.${key}`, key),
|
||||||
description: t(`sanitize.options.${key}.desc`, `${key} from the PDF`),
|
description: t(`sanitize.options.${key}.desc`, `${key} from the PDF`),
|
||||||
|
@ -14,7 +14,7 @@ export const renderToolButtons = (
|
|||||||
onSelect: (id: string) => void,
|
onSelect: (id: string) => void,
|
||||||
showSubcategoryHeader: boolean = true,
|
showSubcategoryHeader: boolean = true,
|
||||||
disableNavigation: boolean = false,
|
disableNavigation: boolean = false,
|
||||||
searchResults?: Array<{ item: [string, any]; matchedText?: string }>
|
searchResults?: { item: [string, any]; matchedText?: string }[]
|
||||||
) => {
|
) => {
|
||||||
// Create a map of matched text for quick lookup
|
// Create a map of matched text for quick lookup
|
||||||
const matchedTextMap = new Map<string, string>();
|
const matchedTextMap = new Map<string, string>();
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { TooltipContent } from '../../types/tips';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reusable tooltip for page selection functionality.
|
|
||||||
* Can be used by any tool that uses the GeneralUtils.parsePageList syntax.
|
|
||||||
*/
|
|
||||||
export const usePageSelectionTips = (): TooltipContent => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return {
|
|
||||||
header: {
|
|
||||||
title: t("pageSelection.tooltip.header.title", "Page Selection Guide")
|
|
||||||
},
|
|
||||||
tips: [
|
|
||||||
{
|
|
||||||
description: t("pageSelection.tooltip.description", "Choose which pages to use for the operation. Supports single pages, ranges, formulas, and the all keyword.")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("pageSelection.tooltip.individual.title", "Individual Pages"),
|
|
||||||
description: t("pageSelection.tooltip.individual.description", "Enter numbers separated by commas."),
|
|
||||||
bullets: [
|
|
||||||
t("pageSelection.tooltip.individual.bullet1", "<strong>1,3,5</strong> → selects pages 1, 3, 5"),
|
|
||||||
t("pageSelection.tooltip.individual.bullet2", "<strong>2,7,12</strong> → selects pages 2, 7, 12")
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("pageSelection.tooltip.ranges.title", "Page Ranges"),
|
|
||||||
description: t("pageSelection.tooltip.ranges.description", "Use - for consecutive pages."),
|
|
||||||
bullets: [
|
|
||||||
t("pageSelection.tooltip.ranges.bullet1", "<strong>3-6</strong> → selects pages 3–6"),
|
|
||||||
t("pageSelection.tooltip.ranges.bullet2", "<strong>10-15</strong> → selects pages 10–15"),
|
|
||||||
t("pageSelection.tooltip.ranges.bullet3", "<strong>5-</strong> → selects pages 5 to end")
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("pageSelection.tooltip.mathematical.title", "Mathematical Functions"),
|
|
||||||
description: t("pageSelection.tooltip.mathematical.description", "Use n in formulas for patterns."),
|
|
||||||
bullets: [
|
|
||||||
t("pageSelection.tooltip.mathematical.bullet2", "<strong>2n-1</strong> → all odd pages (1, 3, 5…)"),
|
|
||||||
t("pageSelection.tooltip.mathematical.bullet1", "<strong>2n</strong> → all even pages (2, 4, 6…)"),
|
|
||||||
t("pageSelection.tooltip.mathematical.bullet3", "<strong>3n</strong> → every 3rd page (3, 6, 9…)"),
|
|
||||||
t("pageSelection.tooltip.mathematical.bullet4", "<strong>4n-1</strong> → pages 3, 7, 11, 15…")
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("pageSelection.tooltip.special.title", "Special Keywords"),
|
|
||||||
bullets: [
|
|
||||||
t("pageSelection.tooltip.special.bullet1", "<strong>all</strong> → selects all pages"),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("pageSelection.tooltip.complex.title", "Complex Combinations"),
|
|
||||||
description: t("pageSelection.tooltip.complex.description", "Mix different types."),
|
|
||||||
bullets: [
|
|
||||||
t("pageSelection.tooltip.complex.bullet1", "<strong>1,3-5,8,2n</strong> → pages 1, 3–5, 8, plus evens"),
|
|
||||||
t("pageSelection.tooltip.complex.bullet2", "<strong>10-,2n-1</strong> → from page 10 to end + odd pages")
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
};
|
|
@ -14,13 +14,13 @@ interface SearchLayerProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SearchResultState {
|
interface SearchResultState {
|
||||||
results: Array<{
|
results: {
|
||||||
pageIndex: number;
|
pageIndex: number;
|
||||||
rects: Array<{
|
rects: {
|
||||||
origin: { x: number; y: number };
|
origin: { x: number; y: number };
|
||||||
size: { width: number; height: number };
|
size: { width: number; height: number };
|
||||||
}>;
|
}[];
|
||||||
}>;
|
}[];
|
||||||
activeResultIndex?: number;
|
activeResultIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,10 @@ import { useViewer } from '../../contexts/ViewerContext';
|
|||||||
|
|
||||||
interface SearchResult {
|
interface SearchResult {
|
||||||
pageIndex: number;
|
pageIndex: number;
|
||||||
rects: Array<{
|
rects: {
|
||||||
origin: { x: number; y: number };
|
origin: { x: number; y: number };
|
||||||
size: { width: number; height: number };
|
size: { width: number; height: number };
|
||||||
}>;
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,7 +101,7 @@ interface ToolWorkflowContextValue extends ToolWorkflowState {
|
|||||||
handleReaderToggle: () => void;
|
handleReaderToggle: () => void;
|
||||||
|
|
||||||
// Computed values
|
// Computed values
|
||||||
filteredTools: Array<{ item: [string, ToolRegistryEntry]; matchedText?: string }>; // Filtered by search
|
filteredTools: { item: [string, ToolRegistryEntry]; matchedText?: string }[]; // Filtered by search
|
||||||
isPanelVisible: boolean;
|
isPanelVisible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,10 +82,10 @@ interface RotationState {
|
|||||||
|
|
||||||
interface SearchResult {
|
interface SearchResult {
|
||||||
pageIndex: number;
|
pageIndex: number;
|
||||||
rects: Array<{
|
rects: {
|
||||||
origin: { x: number; y: number };
|
origin: { x: number; y: number };
|
||||||
size: { width: number; height: number };
|
size: { width: number; height: number };
|
||||||
}>;
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SearchState {
|
interface SearchState {
|
||||||
|
@ -25,7 +25,7 @@ const DEBUG = process.env.NODE_ENV === 'development';
|
|||||||
*/
|
*/
|
||||||
class SimpleMutex {
|
class SimpleMutex {
|
||||||
private locked = false;
|
private locked = false;
|
||||||
private queue: Array<() => void> = [];
|
private queue: (() => void)[] = [];
|
||||||
|
|
||||||
async lock(): Promise<void> {
|
async lock(): Promise<void> {
|
||||||
if (!this.locked) {
|
if (!this.locked) {
|
||||||
@ -151,7 +151,7 @@ interface AddFileOptions {
|
|||||||
files?: File[];
|
files?: File[];
|
||||||
|
|
||||||
// For 'processed' files
|
// For 'processed' files
|
||||||
filesWithThumbnails?: Array<{ file: File; thumbnail?: string; pageCount?: number }>;
|
filesWithThumbnails?: { file: File; thumbnail?: string; pageCount?: number }[];
|
||||||
|
|
||||||
// Insertion position
|
// Insertion position
|
||||||
insertAfterPageId?: string;
|
insertAfterPageId?: string;
|
||||||
@ -165,8 +165,8 @@ interface AddFileOptions {
|
|||||||
*/
|
*/
|
||||||
export async function addFiles(
|
export async function addFiles(
|
||||||
options: AddFileOptions,
|
options: AddFileOptions,
|
||||||
stateRef: React.MutableRefObject<FileContextState>,
|
stateRef: React.RefObject<FileContextState>,
|
||||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
filesRef: React.RefObject<Map<FileId, File>>,
|
||||||
dispatch: React.Dispatch<FileContextAction>,
|
dispatch: React.Dispatch<FileContextAction>,
|
||||||
lifecycleManager: FileLifecycleManager,
|
lifecycleManager: FileLifecycleManager,
|
||||||
enablePersistence: boolean = false
|
enablePersistence: boolean = false
|
||||||
@ -278,7 +278,7 @@ export async function consumeFiles(
|
|||||||
inputFileIds: FileId[],
|
inputFileIds: FileId[],
|
||||||
outputStirlingFiles: StirlingFile[],
|
outputStirlingFiles: StirlingFile[],
|
||||||
outputStirlingFileStubs: StirlingFileStub[],
|
outputStirlingFileStubs: StirlingFileStub[],
|
||||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
filesRef: React.RefObject<Map<FileId, File>>,
|
||||||
dispatch: React.Dispatch<FileContextAction>
|
dispatch: React.Dispatch<FileContextAction>
|
||||||
): Promise<FileId[]> {
|
): Promise<FileId[]> {
|
||||||
if (DEBUG) console.log(`📄 consumeFiles: Processing ${inputFileIds.length} input files, ${outputStirlingFiles.length} output files with pre-created stubs`);
|
if (DEBUG) console.log(`📄 consumeFiles: Processing ${inputFileIds.length} input files, ${outputStirlingFiles.length} output files with pre-created stubs`);
|
||||||
@ -355,9 +355,9 @@ export async function consumeFiles(
|
|||||||
* Helper function to restore files to filesRef and manage IndexedDB cleanup
|
* Helper function to restore files to filesRef and manage IndexedDB cleanup
|
||||||
*/
|
*/
|
||||||
async function restoreFilesAndCleanup(
|
async function restoreFilesAndCleanup(
|
||||||
filesToRestore: Array<{ file: File; record: StirlingFileStub }>,
|
filesToRestore: { file: File; record: StirlingFileStub }[],
|
||||||
fileIdsToRemove: FileId[],
|
fileIdsToRemove: FileId[],
|
||||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
filesRef: React.RefObject<Map<FileId, File>>,
|
||||||
indexedDB?: { deleteFile: (fileId: FileId) => Promise<void> } | null
|
indexedDB?: { deleteFile: (fileId: FileId) => Promise<void> } | null
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Remove files from filesRef
|
// Remove files from filesRef
|
||||||
@ -406,7 +406,7 @@ export async function undoConsumeFiles(
|
|||||||
inputFiles: File[],
|
inputFiles: File[],
|
||||||
inputStirlingFileStubs: StirlingFileStub[],
|
inputStirlingFileStubs: StirlingFileStub[],
|
||||||
outputFileIds: FileId[],
|
outputFileIds: FileId[],
|
||||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
filesRef: React.RefObject<Map<FileId, File>>,
|
||||||
dispatch: React.Dispatch<FileContextAction>,
|
dispatch: React.Dispatch<FileContextAction>,
|
||||||
indexedDB?: { saveFile: (file: File, fileId: FileId, existingThumbnail?: string) => Promise<any>; deleteFile: (fileId: FileId) => Promise<void> } | null
|
indexedDB?: { saveFile: (file: File, fileId: FileId, existingThumbnail?: string) => Promise<any>; deleteFile: (fileId: FileId) => Promise<void> } | null
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -468,8 +468,8 @@ export async function undoConsumeFiles(
|
|||||||
export async function addStirlingFileStubs(
|
export async function addStirlingFileStubs(
|
||||||
stirlingFileStubs: StirlingFileStub[],
|
stirlingFileStubs: StirlingFileStub[],
|
||||||
options: { insertAfterPageId?: string; selectFiles?: boolean } = {},
|
options: { insertAfterPageId?: string; selectFiles?: boolean } = {},
|
||||||
stateRef: React.MutableRefObject<FileContextState>,
|
stateRef: React.RefObject<FileContextState>,
|
||||||
filesRef: React.MutableRefObject<Map<FileId, File>>,
|
filesRef: React.RefObject<Map<FileId, File>>,
|
||||||
dispatch: React.Dispatch<FileContextAction>,
|
dispatch: React.Dispatch<FileContextAction>,
|
||||||
_lifecycleManager: FileLifecycleManager
|
_lifecycleManager: FileLifecycleManager
|
||||||
): Promise<StirlingFile[]> {
|
): Promise<StirlingFile[]> {
|
||||||
|
@ -15,8 +15,8 @@ import {
|
|||||||
* Create stable selectors using stateRef and filesRef
|
* Create stable selectors using stateRef and filesRef
|
||||||
*/
|
*/
|
||||||
export function createFileSelectors(
|
export function createFileSelectors(
|
||||||
stateRef: React.MutableRefObject<FileContextState>,
|
stateRef: React.RefObject<FileContextState>,
|
||||||
filesRef: React.MutableRefObject<Map<FileId, File>>
|
filesRef: React.RefObject<Map<FileId, File>>
|
||||||
): FileContextSelectors {
|
): FileContextSelectors {
|
||||||
return {
|
return {
|
||||||
getFile: (id: FileId) => {
|
getFile: (id: FileId) => {
|
||||||
@ -111,7 +111,7 @@ export function buildQuickKeySet(stirlingFileStubs: Record<FileId, StirlingFileS
|
|||||||
/**
|
/**
|
||||||
* Helper for building quickKey sets from IndexedDB metadata
|
* Helper for building quickKey sets from IndexedDB metadata
|
||||||
*/
|
*/
|
||||||
export function buildQuickKeySetFromMetadata(metadata: Array<{ name: string; size: number; lastModified: number }>): Set<string> {
|
export function buildQuickKeySetFromMetadata(metadata: { name: string; size: number; lastModified: number }[]): Set<string> {
|
||||||
const quickKeys = new Set<string>();
|
const quickKeys = new Set<string>();
|
||||||
metadata.forEach(meta => {
|
metadata.forEach(meta => {
|
||||||
// Format: name|size|lastModified (same as createQuickKey)
|
// Format: name|size|lastModified (same as createQuickKey)
|
||||||
@ -125,8 +125,8 @@ export function buildQuickKeySetFromMetadata(metadata: Array<{ name: string; siz
|
|||||||
* Get primary file (first in list) - commonly used pattern
|
* Get primary file (first in list) - commonly used pattern
|
||||||
*/
|
*/
|
||||||
export function getPrimaryFile(
|
export function getPrimaryFile(
|
||||||
stateRef: React.MutableRefObject<FileContextState>,
|
stateRef: React.RefObject<FileContextState>,
|
||||||
filesRef: React.MutableRefObject<Map<FileId, File>>
|
filesRef: React.RefObject<Map<FileId, File>>
|
||||||
): { file?: File; record?: StirlingFileStub } {
|
): { file?: File; record?: StirlingFileStub } {
|
||||||
const primaryFileId = stateRef.current.files.ids[0];
|
const primaryFileId = stateRef.current.files.ids[0];
|
||||||
if (!primaryFileId) return {};
|
if (!primaryFileId) return {};
|
||||||
|
@ -16,7 +16,7 @@ export class FileLifecycleManager {
|
|||||||
private fileGenerations = new Map<string, number>(); // Generation tokens to prevent stale cleanup
|
private fileGenerations = new Map<string, number>(); // Generation tokens to prevent stale cleanup
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private filesRef: React.MutableRefObject<Map<FileId, File>>,
|
private filesRef: React.RefObject<Map<FileId, File>>,
|
||||||
private dispatch: React.Dispatch<FileContextAction>
|
private dispatch: React.Dispatch<FileContextAction>
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ export class FileLifecycleManager {
|
|||||||
/**
|
/**
|
||||||
* Clean up resources for a specific file (with stateRef access for complete cleanup)
|
* Clean up resources for a specific file (with stateRef access for complete cleanup)
|
||||||
*/
|
*/
|
||||||
cleanupFile = (fileId: FileId, stateRef?: React.MutableRefObject<any>): void => {
|
cleanupFile = (fileId: FileId, stateRef?: React.RefObject<any>): void => {
|
||||||
// Use comprehensive cleanup (same as removeFiles)
|
// Use comprehensive cleanup (same as removeFiles)
|
||||||
this.cleanupAllResourcesForFile(fileId, stateRef);
|
this.cleanupAllResourcesForFile(fileId, stateRef);
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ export class FileLifecycleManager {
|
|||||||
/**
|
/**
|
||||||
* Schedule delayed cleanup for a file with generation token to prevent stale cleanup
|
* Schedule delayed cleanup for a file with generation token to prevent stale cleanup
|
||||||
*/
|
*/
|
||||||
scheduleCleanup = (fileId: FileId, delay: number = 30000, stateRef?: React.MutableRefObject<any>): void => {
|
scheduleCleanup = (fileId: FileId, delay: number = 30000, stateRef?: React.RefObject<any>): void => {
|
||||||
// Cancel existing timer
|
// Cancel existing timer
|
||||||
const existingTimer = this.cleanupTimers.get(fileId);
|
const existingTimer = this.cleanupTimers.get(fileId);
|
||||||
if (existingTimer) {
|
if (existingTimer) {
|
||||||
@ -101,7 +101,7 @@ export class FileLifecycleManager {
|
|||||||
/**
|
/**
|
||||||
* Remove a file immediately with complete resource cleanup
|
* Remove a file immediately with complete resource cleanup
|
||||||
*/
|
*/
|
||||||
removeFiles = (fileIds: FileId[], stateRef?: React.MutableRefObject<any>): void => {
|
removeFiles = (fileIds: FileId[], stateRef?: React.RefObject<any>): void => {
|
||||||
fileIds.forEach(fileId => {
|
fileIds.forEach(fileId => {
|
||||||
// Clean up all resources for this file
|
// Clean up all resources for this file
|
||||||
this.cleanupAllResourcesForFile(fileId, stateRef);
|
this.cleanupAllResourcesForFile(fileId, stateRef);
|
||||||
@ -114,7 +114,7 @@ export class FileLifecycleManager {
|
|||||||
/**
|
/**
|
||||||
* Complete resource cleanup for a single file
|
* Complete resource cleanup for a single file
|
||||||
*/
|
*/
|
||||||
private cleanupAllResourcesForFile = (fileId: FileId, stateRef?: React.MutableRefObject<any>): void => {
|
private cleanupAllResourcesForFile = (fileId: FileId, stateRef?: React.RefObject<any>): void => {
|
||||||
// Remove from files ref
|
// Remove from files ref
|
||||||
this.filesRef.current.delete(fileId);
|
this.filesRef.current.delete(fileId);
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ export class FileLifecycleManager {
|
|||||||
/**
|
/**
|
||||||
* Update file record with race condition guards
|
* Update file record with race condition guards
|
||||||
*/
|
*/
|
||||||
updateStirlingFileStub = (fileId: FileId, updates: Partial<StirlingFileStub>, stateRef?: React.MutableRefObject<any>): void => {
|
updateStirlingFileStub = (fileId: FileId, updates: Partial<StirlingFileStub>, stateRef?: React.RefObject<any>): void => {
|
||||||
// Guard against updating removed files (race condition protection)
|
// Guard against updating removed files (race condition protection)
|
||||||
if (!this.filesRef.current.has(fileId)) {
|
if (!this.filesRef.current.has(fileId)) {
|
||||||
if (DEBUG) console.warn(`🗂️ Attempted to update removed file (filesRef): ${fileId}`);
|
if (DEBUG) console.warn(`🗂️ Attempted to update removed file (filesRef): ${fileId}`);
|
||||||
|
@ -125,7 +125,7 @@ describe('useAddPasswordParameters', () => {
|
|||||||
expect(result.current.validateParameters()).toBe(true);
|
expect(result.current.validateParameters()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each(Object.keys(defaultChangePermissionsParameters) as Array<keyof ChangePermissionsParameters>)('should handle boolean restriction parameter %s', (param) => {
|
test.each(Object.keys(defaultChangePermissionsParameters) as (keyof ChangePermissionsParameters)[])('should handle boolean restriction parameter %s', (param) => {
|
||||||
const { result } = renderHook(() => useAddPasswordParameters());
|
const { result } = renderHook(() => useAddPasswordParameters());
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
|
@ -96,7 +96,7 @@ describe('useChangePermissionsOperation', () => {
|
|||||||
// Verify the form data contains the file
|
// Verify the form data contains the file
|
||||||
expect(formData.get('fileInput')).toBe(testFile);
|
expect(formData.get('fileInput')).toBe(testFile);
|
||||||
|
|
||||||
(Object.keys(testParameters) as Array<keyof ChangePermissionsParameters>).forEach(key => {
|
(Object.keys(testParameters) as (keyof ChangePermissionsParameters)[]).forEach(key => {
|
||||||
expect(formData.get(key), `Parameter ${key} should be set correctly`).toBe(testParameters[key].toString());
|
expect(formData.get(key), `Parameter ${key} should be set correctly`).toBe(testParameters[key].toString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,7 @@ describe('useChangePermissionsParameters', () => {
|
|||||||
test('should update all permission parameters', () => {
|
test('should update all permission parameters', () => {
|
||||||
const { result } = renderHook(() => useChangePermissionsParameters());
|
const { result } = renderHook(() => useChangePermissionsParameters());
|
||||||
|
|
||||||
const permissionKeys = Object.keys(defaultParameters) as Array<keyof ChangePermissionsParameters>;
|
const permissionKeys = Object.keys(defaultParameters) as (keyof ChangePermissionsParameters)[];
|
||||||
|
|
||||||
// Set all to true
|
// Set all to true
|
||||||
act(() => {
|
act(() => {
|
||||||
@ -99,7 +99,7 @@ describe('useChangePermissionsParameters', () => {
|
|||||||
|
|
||||||
// Set all restrictions - should still be valid
|
// Set all restrictions - should still be valid
|
||||||
act(() => {
|
act(() => {
|
||||||
const permissionKeys = Object.keys(defaultParameters) as Array<keyof ChangePermissionsParameters>;
|
const permissionKeys = Object.keys(defaultParameters) as (keyof ChangePermissionsParameters)[];
|
||||||
permissionKeys.forEach(key => {
|
permissionKeys.forEach(key => {
|
||||||
result.current.updateParameter(key, true);
|
result.current.updateParameter(key, true);
|
||||||
});
|
});
|
||||||
|
@ -42,8 +42,8 @@ export interface ConvertParameters extends BaseParameters {
|
|||||||
|
|
||||||
export interface ConvertParametersHook extends BaseParametersHook<ConvertParameters> {
|
export interface ConvertParametersHook extends BaseParametersHook<ConvertParameters> {
|
||||||
getEndpoint: () => string;
|
getEndpoint: () => string;
|
||||||
getAvailableToExtensions: (fromExtension: string) => Array<{value: string, label: string, group: string}>;
|
getAvailableToExtensions: (fromExtension: string) => {value: string, label: string, group: string}[];
|
||||||
analyzeFileTypes: (files: Array<{name: string}>) => void;
|
analyzeFileTypes: (files: {name: string}[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultParameters: ConvertParameters = {
|
export const defaultParameters: ConvertParameters = {
|
||||||
@ -157,7 +157,7 @@ export const useConvertParameters = (): ConvertParametersHook => {
|
|||||||
const getAvailableToExtensions = getAvailableToExtensionsUtil;
|
const getAvailableToExtensions = getAvailableToExtensionsUtil;
|
||||||
|
|
||||||
|
|
||||||
const analyzeFileTypes = useCallback((files: Array<{name: string}>) => {
|
const analyzeFileTypes = useCallback((files: {name: string}[]) => {
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
// No files - only reset smart detection, keep user's format choices
|
// No files - only reset smart detection, keep user's format choices
|
||||||
baseHook.setParameters(prev => {
|
baseHook.setParameters(prev => {
|
||||||
|
@ -345,7 +345,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
|||||||
test('should handle malformed file objects', () => {
|
test('should handle malformed file objects', () => {
|
||||||
const { result } = renderHook(() => useConvertParameters());
|
const { result } = renderHook(() => useConvertParameters());
|
||||||
|
|
||||||
const malformedFiles: Array<{name: string}> = [
|
const malformedFiles: {name: string}[] = [
|
||||||
{ name: 'valid.pdf' },
|
{ name: 'valid.pdf' },
|
||||||
// @ts-expect-error - Testing runtime resilience
|
// @ts-expect-error - Testing runtime resilience
|
||||||
{ name: null },
|
{ name: null },
|
||||||
|
@ -4,7 +4,7 @@ import { SUBCATEGORY_ORDER, SubcategoryId, ToolCategoryId, ToolRegistryEntry } f
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
type SubcategoryIdMap = {
|
type SubcategoryIdMap = {
|
||||||
[subcategoryId in SubcategoryId]: Array<{ id: string /* FIX ME: Should be ToolId */; tool: ToolRegistryEntry }>;
|
[subcategoryId in SubcategoryId]: { id: string /* FIX ME: Should be ToolId */; tool: ToolRegistryEntry }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type GroupedTools = {
|
type GroupedTools = {
|
||||||
@ -28,7 +28,7 @@ export interface ToolSection {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function useToolSections(
|
export function useToolSections(
|
||||||
filteredTools: Array<{ item: [string /* FIX ME: Should be ToolId */, ToolRegistryEntry]; matchedText?: string }>,
|
filteredTools: { item: [string /* FIX ME: Should be ToolId */, ToolRegistryEntry]; matchedText?: string }[],
|
||||||
searchQuery?: string
|
searchQuery?: string
|
||||||
) {
|
) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -6,10 +6,10 @@ export interface AutomationConfig {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
operations: Array<{
|
operations: {
|
||||||
operation: string;
|
operation: string;
|
||||||
parameters: any;
|
parameters: any;
|
||||||
}>;
|
}[];
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,12 @@ import { FileId } from '../types/file';
|
|||||||
|
|
||||||
export interface ProcessedFileMetadata {
|
export interface ProcessedFileMetadata {
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
pages: Array<{
|
pages: {
|
||||||
pageNumber: number;
|
pageNumber: number;
|
||||||
thumbnail?: string;
|
thumbnail?: string;
|
||||||
rotation: number;
|
rotation: number;
|
||||||
splitBefore: boolean;
|
splitBefore: boolean;
|
||||||
}>;
|
}[];
|
||||||
thumbnailUrl?: string; // Page 1 thumbnail for FileEditor
|
thumbnailUrl?: string; // Page 1 thumbnail for FileEditor
|
||||||
lastProcessed: number;
|
lastProcessed: number;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
type ConversionEndpoint
|
type ConversionEndpoint
|
||||||
} from '../helpers/conversionEndpointDiscovery';
|
} from '../helpers/conversionEndpointDiscovery';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
// Test configuration
|
// Test configuration
|
||||||
const BASE_URL = process.env.BASE_URL || 'http://localhost:5173';
|
const BASE_URL = process.env.BASE_URL || 'http://localhost:5173';
|
||||||
@ -238,7 +238,7 @@ async function testConversion(page: Page, conversion: ConversionEndpoint) {
|
|||||||
// Save and verify file is not empty
|
// Save and verify file is not empty
|
||||||
const path = await download.path();
|
const path = await download.path();
|
||||||
if (path) {
|
if (path) {
|
||||||
const fs = require('fs');
|
// fs is already imported at the top of the file
|
||||||
const stats = fs.statSync(path);
|
const stats = fs.statSync(path);
|
||||||
expect(stats.size).toBeGreaterThan(0);
|
expect(stats.size).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ export function createTestStirlingFile(
|
|||||||
* Create multiple StirlingFile objects for testing
|
* Create multiple StirlingFile objects for testing
|
||||||
*/
|
*/
|
||||||
export function createTestFilesWithId(
|
export function createTestFilesWithId(
|
||||||
files: Array<{ name: string; content?: string; type?: string }>
|
files: { name: string; content?: string; type?: string }[]
|
||||||
): StirlingFile[] {
|
): StirlingFile[] {
|
||||||
return files.map(({ name, content = 'test content', type = 'application/pdf' }) =>
|
return files.map(({ name, content = 'test content', type = 'application/pdf' }) =>
|
||||||
createTestStirlingFile(name, content, type)
|
createTestStirlingFile(name, content, type)
|
||||||
|
@ -64,7 +64,7 @@ export const isWebFormat = (extension: string): boolean => {
|
|||||||
* Gets available target extensions for a given source extension
|
* Gets available target extensions for a given source extension
|
||||||
* Extracted from useConvertParameters to be reusable in automation settings
|
* Extracted from useConvertParameters to be reusable in automation settings
|
||||||
*/
|
*/
|
||||||
export const getAvailableToExtensions = (fromExtension: string): Array<{value: string, label: string, group: string}> => {
|
export const getAvailableToExtensions = (fromExtension: string): {value: string, label: string, group: string}[] => {
|
||||||
if (!fromExtension) return [];
|
if (!fromExtension) return [];
|
||||||
|
|
||||||
// Handle dynamic format identifiers (file-<extension>)
|
// Handle dynamic format identifiers (file-<extension>)
|
||||||
|
@ -82,8 +82,8 @@ export function isFuzzyMatch(query: string, target: string, minScore?: number):
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convenience: rank a list of items by best score across provided getters
|
// Convenience: rank a list of items by best score across provided getters
|
||||||
export function rankByFuzzy<T>(items: T[], query: string, getters: Array<(item: T) => string>, minScore?: number): Array<{ item: T; score: number; matchedText?: string }>{
|
export function rankByFuzzy<T>(items: T[], query: string, getters: ((item: T) => string)[], minScore?: number): { item: T; score: number; matchedText?: string }[]{
|
||||||
const results: Array<{ item: T; score: number; matchedText?: string }> = [];
|
const results: { item: T; score: number; matchedText?: string }[] = [];
|
||||||
const threshold = typeof minScore === 'number' ? minScore : minScoreForQuery(query);
|
const threshold = typeof minScore === 'number' ? minScore : minScoreForQuery(query);
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
let best = 0;
|
let best = 0;
|
||||||
|
@ -18,10 +18,10 @@ export function filterToolRegistryByQuery(
|
|||||||
const nq = normalizeForSearch(query);
|
const nq = normalizeForSearch(query);
|
||||||
const threshold = minScoreForQuery(query);
|
const threshold = minScoreForQuery(query);
|
||||||
|
|
||||||
const exactName: Array<{ id: string; tool: ToolRegistryEntry; pos: number }> = [];
|
const exactName: { id: string; tool: ToolRegistryEntry; pos: number }[] = [];
|
||||||
const exactSyn: Array<{ id: string; tool: ToolRegistryEntry; text: string; pos: number }> = [];
|
const exactSyn: { id: string; tool: ToolRegistryEntry; text: string; pos: number }[] = [];
|
||||||
const fuzzyName: Array<{ id: string; tool: ToolRegistryEntry; score: number; text: string }> = [];
|
const fuzzyName: { id: string; tool: ToolRegistryEntry; score: number; text: string }[] = [];
|
||||||
const fuzzySyn: Array<{ id: string; tool: ToolRegistryEntry; score: number; text: string }> = [];
|
const fuzzySyn: { id: string; tool: ToolRegistryEntry; score: number; text: string }[] = [];
|
||||||
|
|
||||||
for (const [id, tool] of entries) {
|
for (const [id, tool] of entries) {
|
||||||
const nameNorm = normalizeForSearch(tool.name || '');
|
const nameNorm = normalizeForSearch(tool.name || '');
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
/* Modules */
|
/* Modules */
|
||||||
"module": "esnext", /* Specify what module code is generated. */
|
"module": "esnext", /* Specify what module code is generated. */
|
||||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
@ -113,6 +112,7 @@
|
|||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src",
|
"src",
|
||||||
"src/global.d.ts"
|
"src/global.d.ts",
|
||||||
, "vite.config.ts" ]
|
"vite.config.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user