mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-24 12:36:13 +00:00
Compare commits
No commits in common. "b9b8e6e4e12ea75f5f50c02ac0690e203923dfdd" and "a16ee308e558e25255a736a0acda96bff9955468" have entirely different histories.
b9b8e6e4e1
...
a16ee308e5
@ -26,8 +26,8 @@ const AddWatermarkSingleStepSettings = ({ parameters, onParameterChange, disable
|
||||
<Stack gap="lg">
|
||||
{/* Watermark Type Selection */}
|
||||
<WatermarkTypeSettings
|
||||
parameters={parameters}
|
||||
onParameterChange={onParameterChange}
|
||||
watermarkType={parameters.watermarkType}
|
||||
onWatermarkTypeChange={(type) => onParameterChange("watermarkType", type)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
|
@ -3,21 +3,21 @@ import { Button, Stack, Text } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface WatermarkTypeSettingsProps {
|
||||
parameters: { watermarkType?: 'text' | 'image' };
|
||||
onParameterChange: (key: 'watermarkType', value: 'text' | 'image') => void;
|
||||
watermarkType?: 'text' | 'image';
|
||||
onWatermarkTypeChange: (type: 'text' | 'image') => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const WatermarkTypeSettings = ({ parameters, onParameterChange, disabled = false }: WatermarkTypeSettingsProps) => {
|
||||
const WatermarkTypeSettings = ({ watermarkType, onWatermarkTypeChange, disabled = false }: WatermarkTypeSettingsProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack gap="sm">
|
||||
<div style={{ display: 'flex', gap: '4px' }}>
|
||||
<Button
|
||||
variant={parameters.watermarkType === 'text' ? 'filled' : 'outline'}
|
||||
color={parameters.watermarkType === 'text' ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('watermarkType', 'text')}
|
||||
variant={watermarkType === 'text' ? 'filled' : 'outline'}
|
||||
color={watermarkType === 'text' ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onWatermarkTypeChange('text')}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
@ -26,9 +26,9 @@ const WatermarkTypeSettings = ({ parameters, onParameterChange, disabled = false
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant={parameters.watermarkType === 'image' ? 'filled' : 'outline'}
|
||||
color={parameters.watermarkType === 'image' ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onParameterChange('watermarkType', 'image')}
|
||||
variant={watermarkType === 'image' ? 'filled' : 'outline'}
|
||||
color={watermarkType === 'image' ? 'blue' : 'var(--text-muted)'}
|
||||
onClick={() => onWatermarkTypeChange('image')}
|
||||
disabled={disabled}
|
||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
||||
>
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { GenericToolProps } from './toolDefinition';
|
||||
import { useBaseTool } from '../../../hooks/tools/shared/useBaseTool';
|
||||
@ -20,31 +19,8 @@ function GenericTool<TParams>(props: GenericToolProps<TParams>) {
|
||||
props
|
||||
);
|
||||
|
||||
// Get steps (either static array or dynamic function result)
|
||||
const stepDefinitions = typeof definition.steps === 'function'
|
||||
? definition.steps(base.params.parameters, base.hasFiles, base.hasResults)
|
||||
: definition.steps;
|
||||
|
||||
// State for individual step collapse - each step manages its own collapse state
|
||||
const [stepCollapseStates, setStepCollapseStates] = useState<Record<string, boolean>>(() => {
|
||||
// Initialize collapse states for all steps
|
||||
const initialStates: Record<string, boolean> = {};
|
||||
stepDefinitions.forEach((stepDef, index) => {
|
||||
// First step starts expanded, others start collapsed
|
||||
initialStates[stepDef.key] = index > 0;
|
||||
});
|
||||
return initialStates;
|
||||
});
|
||||
|
||||
const toggleStepCollapse = useCallback((stepKey: string) => {
|
||||
setStepCollapseStates(prev => ({
|
||||
...prev,
|
||||
[stepKey]: !prev[stepKey]
|
||||
}));
|
||||
}, []);
|
||||
|
||||
// Build steps from definition - filter and map in separate operations for better typing
|
||||
const visibleSteps = stepDefinitions.filter((stepDef) => {
|
||||
const visibleSteps = definition.steps.filter((stepDef) => {
|
||||
const isVisible = typeof stepDef.isVisible === 'function'
|
||||
? stepDef.isVisible(base.params.parameters, base.hasFiles, base.hasResults)
|
||||
: stepDef.isVisible ?? true;
|
||||
@ -53,8 +29,8 @@ function GenericTool<TParams>(props: GenericToolProps<TParams>) {
|
||||
|
||||
const steps: MiddleStepConfig[] = visibleSteps.map((stepDef) => ({
|
||||
title: stepDef.title(t),
|
||||
isCollapsed: base.hasResults ? true : (stepCollapseStates[stepDef.key] ?? false),
|
||||
onCollapsedClick: base.hasResults ? base.handleSettingsReset : () => toggleStepCollapse(stepDef.key),
|
||||
isCollapsed: base.settingsCollapsed,
|
||||
onCollapsedClick: base.hasResults ? base.handleSettingsReset : undefined,
|
||||
tooltip: stepDef.tooltip?.(t),
|
||||
content: (
|
||||
<stepDef.component
|
||||
|
@ -74,8 +74,8 @@ export interface ToolDefinition<TParams> {
|
||||
/** Hook that provides operation execution */
|
||||
useOperation: () => ToolOperationHook<TParams>;
|
||||
|
||||
/** Configuration steps for the tool - can be static array or dynamic function */
|
||||
steps: ToolStepDefinition<TParams>[] | ((params: TParams, hasFiles: boolean, hasResults: boolean) => ToolStepDefinition<TParams>[]);
|
||||
/** Configuration steps for the tool */
|
||||
steps: ToolStepDefinition<TParams>[];
|
||||
|
||||
/** Execute button configuration */
|
||||
executeButton: ToolExecuteButtonDefinition;
|
||||
|
21
frontend/src/components/tooltips/useChangePermissionsTips.ts
Normal file
21
frontend/src/components/tooltips/useChangePermissionsTips.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TooltipContent } from '../../types/tips';
|
||||
|
||||
export const useChangePermissionsTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("changePermissions.tooltip.header.title", "Change Permissions")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
description: t("changePermissions.tooltip.description.text", "Changes document permissions, allowing/disallowing access to different features in PDF readers.")
|
||||
},
|
||||
{
|
||||
title: t("warning.tooltipTitle", "Warning"),
|
||||
description: t("changePermissions.tooltip.warning.text", "To make these permissions unchangeable, use the Add Password tool to set an owner password.")
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
20
frontend/src/components/tooltips/useRemovePasswordTips.ts
Normal file
20
frontend/src/components/tooltips/useRemovePasswordTips.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TooltipContent } from '../../types/tips';
|
||||
|
||||
export const useRemovePasswordTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("removePassword.title", "Remove Password")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
description: t(
|
||||
"removePassword.tooltip.description",
|
||||
"Removing password protection requires the current password that was used to encrypt the PDF. This will decrypt the document, making it accessible without a password."
|
||||
)
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
176
frontend/src/components/tooltips/useWatermarkTips.ts
Normal file
176
frontend/src/components/tooltips/useWatermarkTips.ts
Normal file
@ -0,0 +1,176 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TooltipContent, TooltipTip } from '../../types/tips';
|
||||
|
||||
// Shared tooltip content to reduce duplication
|
||||
const useSharedWatermarkContent = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const languageSupportTip: TooltipTip = {
|
||||
title: t("watermark.tooltip.language.title", "Language Support"),
|
||||
description: t("watermark.tooltip.language.text", "Choose the appropriate language setting to ensure proper font rendering for your text.")
|
||||
};
|
||||
|
||||
const appearanceTip: TooltipTip = {
|
||||
title: t("watermark.tooltip.appearance.title", "Appearance Settings"),
|
||||
description: t("watermark.tooltip.appearance.text", "Control how your watermark looks and blends with the document."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.appearance.bullet1", "Rotation: -360° to 360° for angled watermarks"),
|
||||
t("watermark.tooltip.appearance.bullet2", "Opacity: 0-100% for transparency control"),
|
||||
t("watermark.tooltip.appearance.bullet3", "Lower opacity creates subtle watermarks")
|
||||
]
|
||||
};
|
||||
|
||||
const spacingTip: TooltipTip = {
|
||||
title: t("watermark.tooltip.spacing.title", "Spacing Control"),
|
||||
description: t("watermark.tooltip.spacing.text", "Adjust the spacing between repeated watermarks across the page."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.spacing.bullet1", "Width spacing: Horizontal distance between watermarks"),
|
||||
t("watermark.tooltip.spacing.bullet2", "Height spacing: Vertical distance between watermarks"),
|
||||
t("watermark.tooltip.spacing.bullet3", "Higher values create more spread out patterns")
|
||||
]
|
||||
};
|
||||
|
||||
return { languageSupportTip, appearanceTip, spacingTip };
|
||||
};
|
||||
|
||||
export const useWatermarkTypeTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("watermark.tooltip.type.header.title", "Watermark Type Selection")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.type.description.title", "Choose Your Watermark"),
|
||||
description: t("watermark.tooltip.type.description.text", "Select between text or image watermarks based on your needs.")
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.type.text.title", "Text Watermarks"),
|
||||
description: t("watermark.tooltip.type.text.text", "Perfect for adding copyright notices, company names, or confidentiality labels. Supports multiple languages and custom colors."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.type.text.bullet1", "Customizable fonts and languages"),
|
||||
t("watermark.tooltip.type.text.bullet2", "Adjustable colors and transparency"),
|
||||
t("watermark.tooltip.type.text.bullet3", "Ideal for legal or branding text")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.type.image.title", "Image Watermarks"),
|
||||
description: t("watermark.tooltip.type.image.text", "Use logos, stamps, or any image as a watermark. Great for branding and visual identification."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.type.image.bullet1", "Upload any image format"),
|
||||
t("watermark.tooltip.type.image.bullet2", "Maintains image quality"),
|
||||
t("watermark.tooltip.type.image.bullet3", "Perfect for logos and stamps")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const useWatermarkWordingTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("watermark.tooltip.wording.header.title", "Text Content")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.wording.text.title", "Watermark Text"),
|
||||
description: t("watermark.tooltip.wording.text.text", "Enter the text that will appear as your watermark across the document."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.wording.text.bullet1", "Keep it concise for better readability"),
|
||||
t("watermark.tooltip.wording.text.bullet2", "Common examples: 'CONFIDENTIAL', 'DRAFT', company name"),
|
||||
t("watermark.tooltip.wording.text.bullet3", "Emoji characters are not supported and will be filtered out")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
export const useWatermarkTextStyleTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
const { languageSupportTip } = useSharedWatermarkContent();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("watermark.tooltip.textStyle.header.title", "Text Style")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.textStyle.color.title", "Color Selection"),
|
||||
description: t("watermark.tooltip.textStyle.color.text", "Choose a color that provides good contrast with your document content."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.textStyle.color.bullet1", "Light gray (#d3d3d3) for subtle watermarks"),
|
||||
t("watermark.tooltip.textStyle.color.bullet2", "Black or dark colors for high contrast"),
|
||||
t("watermark.tooltip.textStyle.color.bullet3", "Custom colors for branding purposes")
|
||||
]
|
||||
},
|
||||
languageSupportTip
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
export const useWatermarkFileTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("watermark.tooltip.file.header.title", "Image Upload")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.file.upload.title", "Image Selection"),
|
||||
description: t("watermark.tooltip.file.upload.text", "Upload an image file to use as your watermark."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.file.upload.bullet1", "Supports common formats: PNG, JPG, GIF, BMP"),
|
||||
t("watermark.tooltip.file.upload.bullet2", "PNG with transparency works best"),
|
||||
t("watermark.tooltip.file.upload.bullet3", "Higher resolution images maintain quality better")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.file.recommendations.title", "Best Practices"),
|
||||
description: t("watermark.tooltip.file.recommendations.text", "Tips for optimal image watermark results."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.file.recommendations.bullet1", "Use logos or stamps with transparent backgrounds"),
|
||||
t("watermark.tooltip.file.recommendations.bullet2", "Simple designs work better than complex images"),
|
||||
t("watermark.tooltip.file.recommendations.bullet3", "Consider the final document size when choosing resolution")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
export const useWatermarkFormattingTips = (): TooltipContent => {
|
||||
const { t } = useTranslation();
|
||||
const { appearanceTip, spacingTip } = useSharedWatermarkContent();
|
||||
|
||||
return {
|
||||
header: {
|
||||
title: t("watermark.tooltip.formatting.header.title", "Formatting & Layout")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.formatting.size.title", "Size Control"),
|
||||
description: t("watermark.tooltip.formatting.size.text", "Adjust the size of your watermark (text or image)."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.formatting.size.bullet1", "Larger sizes create more prominent watermarks")
|
||||
]
|
||||
},
|
||||
appearanceTip,
|
||||
spacingTip,
|
||||
{
|
||||
title: t("watermark.tooltip.formatting.security.title", "Security Option"),
|
||||
description: t("watermark.tooltip.formatting.security.text", "Convert the final PDF to an image-based format for enhanced security."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.formatting.security.bullet1", "Prevents text selection and copying"),
|
||||
t("watermark.tooltip.formatting.security.bullet2", "Makes watermarks harder to remove"),
|
||||
t("watermark.tooltip.formatting.security.bullet3", "Results in larger file sizes"),
|
||||
t("watermark.tooltip.formatting.security.bullet4", "Best for sensitive or copyrighted content")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
@ -1,11 +1,220 @@
|
||||
import { BaseToolProps, ToolComponent } from "../types/tool";
|
||||
import GenericTool from "../components/tools/shared/GenericTool";
|
||||
import { addWatermarkDefinition } from "./definitions/addWatermarkDefinition";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
||||
import { useFileSelection } from "../contexts/FileContext";
|
||||
import { useNavigationActions } from "../contexts/NavigationContext";
|
||||
|
||||
const AddWatermark = (props: BaseToolProps) => {
|
||||
return <GenericTool definition={addWatermarkDefinition} {...props} />;
|
||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||
|
||||
import WatermarkTypeSettings from "../components/tools/addWatermark/WatermarkTypeSettings";
|
||||
import WatermarkWording from "../components/tools/addWatermark/WatermarkWording";
|
||||
import WatermarkTextStyle from "../components/tools/addWatermark/WatermarkTextStyle";
|
||||
import WatermarkImageFile from "../components/tools/addWatermark/WatermarkImageFile";
|
||||
import WatermarkFormatting from "../components/tools/addWatermark/WatermarkFormatting";
|
||||
|
||||
import { useAddWatermarkParameters } from "../hooks/tools/addWatermark/useAddWatermarkParameters";
|
||||
import { useAddWatermarkOperation } from "../hooks/tools/addWatermark/useAddWatermarkOperation";
|
||||
import {
|
||||
useWatermarkTypeTips,
|
||||
useWatermarkWordingTips,
|
||||
useWatermarkTextStyleTips,
|
||||
useWatermarkFileTips,
|
||||
useWatermarkFormattingTips,
|
||||
} from "../components/tooltips/useWatermarkTips";
|
||||
import { BaseToolProps, ToolComponent } from "../types/tool";
|
||||
|
||||
const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { actions } = useNavigationActions();
|
||||
const { selectedFiles } = useFileSelection();
|
||||
|
||||
const [collapsedType, setCollapsedType] = useState(false);
|
||||
const [collapsedStyle, setCollapsedStyle] = useState(true);
|
||||
const [collapsedFormatting, setCollapsedFormatting] = useState(true);
|
||||
|
||||
const watermarkParams = useAddWatermarkParameters();
|
||||
const watermarkOperation = useAddWatermarkOperation();
|
||||
const watermarkTypeTips = useWatermarkTypeTips();
|
||||
const watermarkWordingTips = useWatermarkWordingTips();
|
||||
const watermarkTextStyleTips = useWatermarkTextStyleTips();
|
||||
const watermarkFileTips = useWatermarkFileTips();
|
||||
const watermarkFormattingTips = useWatermarkFormattingTips();
|
||||
|
||||
// Endpoint validation
|
||||
const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled("add-watermark");
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
watermarkOperation.resetResults();
|
||||
onPreviewFile?.(null);
|
||||
}, [watermarkParams.parameters]);
|
||||
|
||||
// Auto-collapse type step after selection
|
||||
useEffect(() => {
|
||||
if (watermarkParams.parameters.watermarkType && !collapsedType) {
|
||||
setCollapsedType(true);
|
||||
}
|
||||
}, [watermarkParams.parameters.watermarkType]);
|
||||
|
||||
const handleAddWatermark = async () => {
|
||||
try {
|
||||
await watermarkOperation.executeOperation(watermarkParams.parameters, selectedFiles);
|
||||
if (watermarkOperation.files && onComplete) {
|
||||
onComplete(watermarkOperation.files);
|
||||
}
|
||||
} catch (error) {
|
||||
if (onError) {
|
||||
onError(error instanceof Error ? error.message : t("watermark.error.failed", "Add watermark operation failed"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleThumbnailClick = (file: File) => {
|
||||
onPreviewFile?.(file);
|
||||
sessionStorage.setItem("previousMode", "watermark");
|
||||
};
|
||||
|
||||
const handleSettingsReset = () => {
|
||||
watermarkOperation.resetResults();
|
||||
onPreviewFile?.(null);
|
||||
};
|
||||
|
||||
const handleUndo = async () => {
|
||||
await watermarkOperation.undoOperation();
|
||||
onPreviewFile?.(null);
|
||||
};
|
||||
|
||||
const hasFiles = selectedFiles.length > 0;
|
||||
const hasResults = watermarkOperation.files.length > 0 || watermarkOperation.downloadUrl !== null;
|
||||
|
||||
// Dynamic step structure based on watermark type
|
||||
const getSteps = () => {
|
||||
const steps = [];
|
||||
|
||||
steps.push({
|
||||
title: t("watermark.steps.type", "Watermark Type"),
|
||||
isCollapsed: hasResults ? true : collapsedType,
|
||||
isVisible: hasFiles || hasResults,
|
||||
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedType(!collapsedType),
|
||||
tooltip: watermarkTypeTips,
|
||||
content: (
|
||||
<WatermarkTypeSettings
|
||||
watermarkType={watermarkParams.parameters.watermarkType}
|
||||
onWatermarkTypeChange={(type) => watermarkParams.updateParameter("watermarkType", type)}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
});
|
||||
|
||||
if (hasFiles || hasResults) {
|
||||
// Text watermark path
|
||||
if (watermarkParams.parameters.watermarkType === "text") {
|
||||
// Step 2: Wording
|
||||
steps.push({
|
||||
title: t("watermark.steps.wording", "Wording"),
|
||||
isCollapsed: hasResults,
|
||||
tooltip: watermarkWordingTips,
|
||||
content: (
|
||||
<WatermarkWording
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
});
|
||||
|
||||
// Step 3: Style
|
||||
steps.push({
|
||||
title: t("watermark.steps.textStyle", "Style"),
|
||||
isCollapsed: hasResults ? true : collapsedStyle,
|
||||
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedStyle(!collapsedStyle),
|
||||
tooltip: watermarkTextStyleTips,
|
||||
content: (
|
||||
<WatermarkTextStyle
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
});
|
||||
|
||||
// Step 4: Formatting
|
||||
steps.push({
|
||||
title: t("watermark.steps.formatting", "Formatting"),
|
||||
isCollapsed: hasResults ? true : collapsedFormatting,
|
||||
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedFormatting(!collapsedFormatting),
|
||||
tooltip: watermarkFormattingTips,
|
||||
content: (
|
||||
<WatermarkFormatting
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
// Image watermark path
|
||||
if (watermarkParams.parameters.watermarkType === "image") {
|
||||
// Step 2: Watermark File
|
||||
steps.push({
|
||||
title: t("watermark.steps.file", "Watermark File"),
|
||||
isCollapsed: hasResults,
|
||||
tooltip: watermarkFileTips,
|
||||
content: (
|
||||
<WatermarkImageFile
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
});
|
||||
|
||||
// Step 3: Formatting
|
||||
steps.push({
|
||||
title: t("watermark.steps.formatting", "Formatting"),
|
||||
isCollapsed: hasResults ? true : collapsedFormatting,
|
||||
onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedFormatting(!collapsedFormatting),
|
||||
tooltip: watermarkFormattingTips,
|
||||
content: (
|
||||
<WatermarkFormatting
|
||||
parameters={watermarkParams.parameters}
|
||||
onParameterChange={watermarkParams.updateParameter}
|
||||
disabled={endpointLoading}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return steps;
|
||||
};
|
||||
|
||||
return createToolFlow({
|
||||
files: {
|
||||
selectedFiles,
|
||||
isCollapsed: hasResults,
|
||||
},
|
||||
steps: getSteps(),
|
||||
executeButton: {
|
||||
text: t("watermark.submit", "Add Watermark"),
|
||||
isVisible: !hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: handleAddWatermark,
|
||||
disabled: !watermarkParams.validateParameters() || !hasFiles || !endpointEnabled,
|
||||
},
|
||||
review: {
|
||||
isVisible: hasResults,
|
||||
operation: watermarkOperation,
|
||||
title: t("watermark.results.title", "Watermark Results"),
|
||||
onFileClick: handleThumbnailClick,
|
||||
onUndo: handleUndo,
|
||||
},
|
||||
forceStepNumbers: true,
|
||||
});
|
||||
};
|
||||
|
||||
AddWatermark.tool = () => addWatermarkDefinition.useOperation;
|
||||
// Static method to get the operation hook for automation
|
||||
AddWatermark.tool = () => useAddWatermarkOperation;
|
||||
|
||||
export default AddWatermark as ToolComponent;
|
||||
|
@ -1,12 +1,61 @@
|
||||
import GenericTool from '../components/tools/shared/GenericTool';
|
||||
import { changePermissionsDefinition } from './definitions/changePermissionsDefinition';
|
||||
import { BaseToolProps, ToolComponent } from '../types/tool';
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||
import ChangePermissionsSettings from "../components/tools/changePermissions/ChangePermissionsSettings";
|
||||
import { useChangePermissionsParameters } from "../hooks/tools/changePermissions/useChangePermissionsParameters";
|
||||
import { useChangePermissionsOperation } from "../hooks/tools/changePermissions/useChangePermissionsOperation";
|
||||
import { useChangePermissionsTips } from "../components/tooltips/useChangePermissionsTips";
|
||||
import { useBaseTool } from "../hooks/tools/shared/useBaseTool";
|
||||
import { BaseToolProps, ToolComponent } from "../types/tool";
|
||||
|
||||
const ChangePermissions = (props: BaseToolProps) => {
|
||||
return <GenericTool definition={changePermissionsDefinition} {...props} />;
|
||||
const { t } = useTranslation();
|
||||
const changePermissionsTips = useChangePermissionsTips();
|
||||
|
||||
const base = useBaseTool(
|
||||
'changePermissions',
|
||||
useChangePermissionsParameters,
|
||||
useChangePermissionsOperation,
|
||||
props
|
||||
);
|
||||
|
||||
return createToolFlow({
|
||||
files: {
|
||||
selectedFiles: base.selectedFiles,
|
||||
isCollapsed: base.hasResults,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
title: t("changePermissions.title", "Document Permissions"),
|
||||
isCollapsed: base.settingsCollapsed,
|
||||
onCollapsedClick: base.settingsCollapsed ? base.handleSettingsReset : undefined,
|
||||
tooltip: changePermissionsTips,
|
||||
content: (
|
||||
<ChangePermissionsSettings
|
||||
parameters={base.params.parameters}
|
||||
onParameterChange={base.params.updateParameter}
|
||||
disabled={base.endpointLoading}
|
||||
/>
|
||||
),
|
||||
},
|
||||
],
|
||||
executeButton: {
|
||||
text: t("changePermissions.submit", "Change Permissions"),
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
operation: base.operation,
|
||||
title: t("changePermissions.results.title", "Modified PDFs"),
|
||||
onFileClick: base.handleThumbnailClick,
|
||||
onUndo: base.handleUndo,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Static method to get the operation hook for automation
|
||||
ChangePermissions.tool = () => changePermissionsDefinition.useOperation;
|
||||
ChangePermissions.tool = () => useChangePermissionsOperation;
|
||||
|
||||
export default ChangePermissions as ToolComponent;
|
||||
|
@ -1,12 +1,45 @@
|
||||
import GenericTool from '../components/tools/shared/GenericTool';
|
||||
import { removeCertificateSignDefinition } from './definitions/removeCertificateSignDefinition';
|
||||
import { BaseToolProps, ToolComponent } from '../types/tool';
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||
import { useRemoveCertificateSignParameters } from "../hooks/tools/removeCertificateSign/useRemoveCertificateSignParameters";
|
||||
import { useRemoveCertificateSignOperation } from "../hooks/tools/removeCertificateSign/useRemoveCertificateSignOperation";
|
||||
import { useBaseTool } from "../hooks/tools/shared/useBaseTool";
|
||||
import { BaseToolProps, ToolComponent } from "../types/tool";
|
||||
|
||||
const RemoveCertificateSign = (props: BaseToolProps) => {
|
||||
return <GenericTool definition={removeCertificateSignDefinition} {...props} />;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const base = useBaseTool(
|
||||
'removeCertificateSign',
|
||||
useRemoveCertificateSignParameters,
|
||||
useRemoveCertificateSignOperation,
|
||||
props
|
||||
);
|
||||
|
||||
return createToolFlow({
|
||||
files: {
|
||||
selectedFiles: base.selectedFiles,
|
||||
isCollapsed: base.hasResults,
|
||||
placeholder: t("removeCertSign.files.placeholder", "Select a PDF file in the main view to get started"),
|
||||
},
|
||||
steps: [],
|
||||
executeButton: {
|
||||
text: t("removeCertSign.submit", "Remove Signature"),
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
operation: base.operation,
|
||||
title: t("removeCertSign.results.title", "Certificate Removal Results"),
|
||||
onFileClick: base.handleThumbnailClick,
|
||||
onUndo: base.handleUndo,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Static method to get the operation hook for automation
|
||||
RemoveCertificateSign.tool = () => removeCertificateSignDefinition.useOperation;
|
||||
RemoveCertificateSign.tool = () => useRemoveCertificateSignOperation;
|
||||
|
||||
export default RemoveCertificateSign as ToolComponent;
|
||||
|
@ -1,12 +1,61 @@
|
||||
import GenericTool from '../components/tools/shared/GenericTool';
|
||||
import { removePasswordDefinition } from './definitions/removePasswordDefinition';
|
||||
import { BaseToolProps, ToolComponent } from '../types/tool';
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||
import RemovePasswordSettings from "../components/tools/removePassword/RemovePasswordSettings";
|
||||
import { useRemovePasswordParameters } from "../hooks/tools/removePassword/useRemovePasswordParameters";
|
||||
import { useRemovePasswordOperation } from "../hooks/tools/removePassword/useRemovePasswordOperation";
|
||||
import { useRemovePasswordTips } from "../components/tooltips/useRemovePasswordTips";
|
||||
import { useBaseTool } from "../hooks/tools/shared/useBaseTool";
|
||||
import { BaseToolProps, ToolComponent } from "../types/tool";
|
||||
|
||||
const RemovePassword = (props: BaseToolProps) => {
|
||||
return <GenericTool definition={removePasswordDefinition} {...props} />;
|
||||
const { t } = useTranslation();
|
||||
const removePasswordTips = useRemovePasswordTips();
|
||||
|
||||
const base = useBaseTool(
|
||||
'removePassword',
|
||||
useRemovePasswordParameters,
|
||||
useRemovePasswordOperation,
|
||||
props
|
||||
);
|
||||
|
||||
return createToolFlow({
|
||||
files: {
|
||||
selectedFiles: base.selectedFiles,
|
||||
isCollapsed: base.hasResults,
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
title: t("removePassword.password.stepTitle", "Remove Password"),
|
||||
isCollapsed: base.settingsCollapsed,
|
||||
onCollapsedClick: base.hasResults ? base.handleSettingsReset : undefined,
|
||||
tooltip: removePasswordTips,
|
||||
content: (
|
||||
<RemovePasswordSettings
|
||||
parameters={base.params.parameters}
|
||||
onParameterChange={base.params.updateParameter}
|
||||
disabled={base.endpointLoading}
|
||||
/>
|
||||
),
|
||||
},
|
||||
],
|
||||
executeButton: {
|
||||
text: t("removePassword.submit", "Remove Password"),
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
operation: base.operation,
|
||||
title: t("removePassword.results.title", "Decrypted PDFs"),
|
||||
onFileClick: base.handleThumbnailClick,
|
||||
onUndo: base.handleUndo,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Static method to get the operation hook for automation
|
||||
RemovePassword.tool = () => removePasswordDefinition.useOperation;
|
||||
RemovePassword.tool = () => useRemovePasswordOperation;
|
||||
|
||||
export default RemovePassword as ToolComponent;
|
||||
|
@ -1,12 +1,59 @@
|
||||
import GenericTool from '../components/tools/shared/GenericTool';
|
||||
import { sanitizeDefinition } from './definitions/sanitizeDefinition';
|
||||
import { BaseToolProps, ToolComponent } from '../types/tool';
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||
import SanitizeSettings from "../components/tools/sanitize/SanitizeSettings";
|
||||
import { useSanitizeParameters } from "../hooks/tools/sanitize/useSanitizeParameters";
|
||||
import { useSanitizeOperation } from "../hooks/tools/sanitize/useSanitizeOperation";
|
||||
import { useBaseTool } from "../hooks/tools/shared/useBaseTool";
|
||||
import { BaseToolProps, ToolComponent } from "../types/tool";
|
||||
|
||||
const Sanitize = (props: BaseToolProps) => {
|
||||
return <GenericTool definition={sanitizeDefinition} {...props} />;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const base = useBaseTool(
|
||||
'sanitize',
|
||||
useSanitizeParameters,
|
||||
useSanitizeOperation,
|
||||
props
|
||||
);
|
||||
|
||||
return createToolFlow({
|
||||
files: {
|
||||
selectedFiles: base.selectedFiles,
|
||||
isCollapsed: base.hasResults,
|
||||
placeholder: t("sanitize.files.placeholder", "Select a PDF file in the main view to get started"),
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
title: t("sanitize.steps.settings", "Settings"),
|
||||
isCollapsed: base.settingsCollapsed,
|
||||
onCollapsedClick: base.settingsCollapsed ? base.handleSettingsReset : undefined,
|
||||
content: (
|
||||
<SanitizeSettings
|
||||
parameters={base.params.parameters}
|
||||
onParameterChange={base.params.updateParameter}
|
||||
disabled={base.endpointLoading}
|
||||
/>
|
||||
),
|
||||
},
|
||||
],
|
||||
executeButton: {
|
||||
text: t("sanitize.submit", "Sanitize PDF"),
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
operation: base.operation,
|
||||
title: t("sanitize.sanitizationResults", "Sanitization Results"),
|
||||
onFileClick: base.handleThumbnailClick,
|
||||
onUndo: base.handleUndo,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Static method to get the operation hook for automation
|
||||
Sanitize.tool = () => sanitizeDefinition.useOperation;
|
||||
Sanitize.tool = () => useSanitizeOperation;
|
||||
|
||||
export default Sanitize as ToolComponent;
|
||||
|
@ -1,12 +1,45 @@
|
||||
import GenericTool from '../components/tools/shared/GenericTool';
|
||||
import { singleLargePageDefinition } from './definitions/singleLargePageDefinition';
|
||||
import { BaseToolProps, ToolComponent } from '../types/tool';
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||
import { useSingleLargePageParameters } from "../hooks/tools/singleLargePage/useSingleLargePageParameters";
|
||||
import { useSingleLargePageOperation } from "../hooks/tools/singleLargePage/useSingleLargePageOperation";
|
||||
import { useBaseTool } from "../hooks/tools/shared/useBaseTool";
|
||||
import { BaseToolProps, ToolComponent } from "../types/tool";
|
||||
|
||||
const SingleLargePage = (props: BaseToolProps) => {
|
||||
return <GenericTool definition={singleLargePageDefinition} {...props} />;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const base = useBaseTool(
|
||||
'singleLargePage',
|
||||
useSingleLargePageParameters,
|
||||
useSingleLargePageOperation,
|
||||
props
|
||||
);
|
||||
|
||||
return createToolFlow({
|
||||
files: {
|
||||
selectedFiles: base.selectedFiles,
|
||||
isCollapsed: base.hasResults,
|
||||
placeholder: t("pdfToSinglePage.files.placeholder", "Select a PDF file in the main view to get started"),
|
||||
},
|
||||
steps: [],
|
||||
executeButton: {
|
||||
text: t("pdfToSinglePage.submit", "Convert To Single Page"),
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
operation: base.operation,
|
||||
title: t("pdfToSinglePage.results.title", "Single Page Results"),
|
||||
onFileClick: base.handleThumbnailClick,
|
||||
onUndo: base.handleUndo,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Static method to get the operation hook for automation
|
||||
SingleLargePage.tool = () => singleLargePageDefinition.useOperation;
|
||||
SingleLargePage.tool = () => useSingleLargePageOperation;
|
||||
|
||||
export default SingleLargePage as ToolComponent;
|
||||
|
@ -1,12 +1,45 @@
|
||||
import GenericTool from '../components/tools/shared/GenericTool';
|
||||
import { unlockPdfFormsDefinition } from './definitions/unlockPdfFormsDefinition';
|
||||
import { BaseToolProps, ToolComponent } from '../types/tool';
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToolFlow } from "../components/tools/shared/createToolFlow";
|
||||
import { useUnlockPdfFormsParameters } from "../hooks/tools/unlockPdfForms/useUnlockPdfFormsParameters";
|
||||
import { useUnlockPdfFormsOperation } from "../hooks/tools/unlockPdfForms/useUnlockPdfFormsOperation";
|
||||
import { useBaseTool } from "../hooks/tools/shared/useBaseTool";
|
||||
import { BaseToolProps, ToolComponent } from "../types/tool";
|
||||
|
||||
const UnlockPdfForms = (props: BaseToolProps) => {
|
||||
return <GenericTool definition={unlockPdfFormsDefinition} {...props} />;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const base = useBaseTool(
|
||||
'unlockPdfForms',
|
||||
useUnlockPdfFormsParameters,
|
||||
useUnlockPdfFormsOperation,
|
||||
props
|
||||
);
|
||||
|
||||
return createToolFlow({
|
||||
files: {
|
||||
selectedFiles: base.selectedFiles,
|
||||
isCollapsed: base.hasFiles || base.hasResults,
|
||||
placeholder: t("unlockPDFForms.files.placeholder", "Select a PDF file in the main view to get started"),
|
||||
},
|
||||
steps: [],
|
||||
executeButton: {
|
||||
text: t("unlockPDFForms.submit", "Unlock Forms"),
|
||||
isVisible: !base.hasResults,
|
||||
loadingText: t("loading"),
|
||||
onClick: base.handleExecute,
|
||||
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
|
||||
},
|
||||
review: {
|
||||
isVisible: base.hasResults,
|
||||
operation: base.operation,
|
||||
title: t("unlockPDFForms.results.title", "Unlocked Forms Results"),
|
||||
onFileClick: base.handleThumbnailClick,
|
||||
onUndo: base.handleUndo,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Static method to get the operation hook for automation
|
||||
UnlockPdfForms.tool = () => unlockPdfFormsDefinition.useOperation;
|
||||
UnlockPdfForms.tool = () => useUnlockPdfFormsOperation;
|
||||
|
||||
export default UnlockPdfForms as ToolComponent;
|
||||
|
@ -1,238 +0,0 @@
|
||||
import { ToolDefinition, ToolStepDefinition } from '../../components/tools/shared/toolDefinition';
|
||||
import { AddWatermarkParameters, useAddWatermarkParameters } from '../../hooks/tools/addWatermark/useAddWatermarkParameters';
|
||||
import { useAddWatermarkOperation } from '../../hooks/tools/addWatermark/useAddWatermarkOperation';
|
||||
import WatermarkTypeSettings from '../../components/tools/addWatermark/WatermarkTypeSettings';
|
||||
import WatermarkWording from '../../components/tools/addWatermark/WatermarkWording';
|
||||
import WatermarkTextStyle from '../../components/tools/addWatermark/WatermarkTextStyle';
|
||||
import WatermarkImageFile from '../../components/tools/addWatermark/WatermarkImageFile';
|
||||
import WatermarkFormatting from '../../components/tools/addWatermark/WatermarkFormatting';
|
||||
import { TooltipTip } from '../../types/tips';
|
||||
import { TFunction } from 'i18next';
|
||||
|
||||
const languageSupportTip: (t: TFunction) => TooltipTip = (t) => ({
|
||||
title: t("watermark.tooltip.language.title", "Language Support"),
|
||||
description: t("watermark.tooltip.language.text", "Choose the appropriate language setting to ensure proper font rendering for your text.")
|
||||
});
|
||||
|
||||
const appearanceTip: (t: TFunction) => TooltipTip = (t) => ({
|
||||
title: t("watermark.tooltip.appearance.title", "Appearance Settings"),
|
||||
description: t("watermark.tooltip.appearance.text", "Control how your watermark looks and blends with the document."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.appearance.bullet1", "Rotation: -360° to 360° for angled watermarks"),
|
||||
t("watermark.tooltip.appearance.bullet2", "Opacity: 0-100% for transparency control"),
|
||||
t("watermark.tooltip.appearance.bullet3", "Lower opacity creates subtle watermarks")
|
||||
]
|
||||
});
|
||||
|
||||
const spacingTip: (t: TFunction) => TooltipTip = (t) => ({
|
||||
title: t("watermark.tooltip.spacing.title", "Spacing Control"),
|
||||
description: t("watermark.tooltip.spacing.text", "Adjust the spacing between repeated watermarks across the page."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.spacing.bullet1", "Width spacing: Horizontal distance between watermarks"),
|
||||
t("watermark.tooltip.spacing.bullet2", "Height spacing: Vertical distance between watermarks"),
|
||||
t("watermark.tooltip.spacing.bullet3", "Higher values create more spread out patterns")
|
||||
]
|
||||
});
|
||||
|
||||
export const addWatermarkDefinition: ToolDefinition<AddWatermarkParameters> = {
|
||||
id: 'addWatermark',
|
||||
|
||||
useParameters: useAddWatermarkParameters,
|
||||
useOperation: useAddWatermarkOperation,
|
||||
|
||||
steps: (params, hasFiles, hasResults) => {
|
||||
const steps: ToolStepDefinition<AddWatermarkParameters>[] = [];
|
||||
|
||||
// Step 1: Watermark Type (always present)
|
||||
steps.push({
|
||||
key: 'type',
|
||||
title: (t) => t("watermark.steps.type", "Watermark Type"),
|
||||
component: WatermarkTypeSettings,
|
||||
tooltip: (t) => ({
|
||||
header: {
|
||||
title: t("watermark.tooltip.type.header.title", "Watermark Type Selection")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.type.description.title", "Choose Your Watermark"),
|
||||
description: t("watermark.tooltip.type.description.text", "Select between text or image watermarks based on your needs.")
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.type.text.title", "Text Watermarks"),
|
||||
description: t("watermark.tooltip.type.text.text", "Perfect for adding copyright notices, company names, or confidentiality labels. Supports multiple languages and custom colors."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.type.text.bullet1", "Customizable fonts and languages"),
|
||||
t("watermark.tooltip.type.text.bullet2", "Adjustable colors and transparency"),
|
||||
t("watermark.tooltip.type.text.bullet3", "Ideal for legal or branding text")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.type.image.title", "Image Watermarks"),
|
||||
description: t("watermark.tooltip.type.image.text", "Use logos, stamps, or any image as a watermark. Great for branding and visual identification."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.type.image.bullet1", "Upload any image format"),
|
||||
t("watermark.tooltip.type.image.bullet2", "Maintains image quality"),
|
||||
t("watermark.tooltip.type.image.bullet3", "Perfect for logos and stamps")
|
||||
]
|
||||
}
|
||||
]
|
||||
}),
|
||||
});
|
||||
|
||||
// Only show remaining steps if we have files and watermark type is selected
|
||||
if ((hasFiles || hasResults) && params.watermarkType) {
|
||||
// Text watermark path
|
||||
if (params.watermarkType === "text") {
|
||||
// Step 2: Wording
|
||||
steps.push({
|
||||
key: 'wording',
|
||||
title: (t) => t("watermark.steps.wording", "Wording"),
|
||||
component: WatermarkWording,
|
||||
tooltip: (t) => ({
|
||||
header: {
|
||||
title: t("watermark.tooltip.wording.header.title", "Text Content")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.wording.text.title", "Watermark Text"),
|
||||
description: t("watermark.tooltip.wording.text.text", "Enter the text that will appear as your watermark across the document."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.wording.text.bullet1", "Keep it concise for better readability"),
|
||||
t("watermark.tooltip.wording.text.bullet2", "Common examples: 'CONFIDENTIAL', 'DRAFT', company name"),
|
||||
t("watermark.tooltip.wording.text.bullet3", "Emoji characters are not supported and will be filtered out")
|
||||
]
|
||||
}
|
||||
]
|
||||
}),
|
||||
});
|
||||
|
||||
// Step 3: Text Style
|
||||
steps.push({
|
||||
key: 'textStyle',
|
||||
title: (t) => t("watermark.steps.textStyle", "Style"),
|
||||
component: WatermarkTextStyle,
|
||||
tooltip: (t) => ({
|
||||
header: {
|
||||
title: t("watermark.tooltip.textStyle.header.title", "Text Style")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.textStyle.color.title", "Color Selection"),
|
||||
description: t("watermark.tooltip.textStyle.color.text", "Choose a color that provides good contrast with your document content."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.textStyle.color.bullet1", "Light gray (#d3d3d3) for subtle watermarks"),
|
||||
t("watermark.tooltip.textStyle.color.bullet2", "Black or dark colors for high contrast"),
|
||||
t("watermark.tooltip.textStyle.color.bullet3", "Custom colors for branding purposes")
|
||||
]
|
||||
},
|
||||
languageSupportTip(t),
|
||||
]
|
||||
}),
|
||||
});
|
||||
|
||||
// Step 4: Formatting
|
||||
steps.push({
|
||||
key: 'formatting',
|
||||
title: (t) => t("watermark.steps.formatting", "Formatting"),
|
||||
component: WatermarkFormatting,
|
||||
tooltip: (t) => ({
|
||||
header: {
|
||||
title: t("watermark.tooltip.textStyle.header.title", "Text Style")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.textStyle.color.title", "Color Selection"),
|
||||
description: t("watermark.tooltip.textStyle.color.text", "Choose a color that provides good contrast with your document content."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.textStyle.color.bullet1", "Light gray (#d3d3d3) for subtle watermarks"),
|
||||
t("watermark.tooltip.textStyle.color.bullet2", "Black or dark colors for high contrast"),
|
||||
t("watermark.tooltip.textStyle.color.bullet3", "Custom colors for branding purposes")
|
||||
]
|
||||
},
|
||||
languageSupportTip(t),
|
||||
]
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// Image watermark path
|
||||
if (params.watermarkType === "image") {
|
||||
// Step 2: Watermark File
|
||||
steps.push({
|
||||
key: 'imageFile',
|
||||
title: (t) => t("watermark.steps.file", "Watermark File"),
|
||||
component: WatermarkImageFile,
|
||||
tooltip: (t) => ({
|
||||
header: {
|
||||
title: t("watermark.tooltip.file.header.title", "Image Upload")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.file.upload.title", "Image Selection"),
|
||||
description: t("watermark.tooltip.file.upload.text", "Upload an image file to use as your watermark."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.file.upload.bullet1", "Supports common formats: PNG, JPG, GIF, BMP"),
|
||||
t("watermark.tooltip.file.upload.bullet2", "PNG with transparency works best"),
|
||||
t("watermark.tooltip.file.upload.bullet3", "Higher resolution images maintain quality better")
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t("watermark.tooltip.file.recommendations.title", "Best Practices"),
|
||||
description: t("watermark.tooltip.file.recommendations.text", "Tips for optimal image watermark results."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.file.recommendations.bullet1", "Use logos or stamps with transparent backgrounds"),
|
||||
t("watermark.tooltip.file.recommendations.bullet2", "Simple designs work better than complex images"),
|
||||
t("watermark.tooltip.file.recommendations.bullet3", "Consider the final document size when choosing resolution")
|
||||
]
|
||||
}
|
||||
]
|
||||
}),
|
||||
});
|
||||
|
||||
// Step 3: Formatting
|
||||
steps.push({
|
||||
key: 'formatting',
|
||||
title: (t) => t("watermark.steps.formatting", "Formatting"),
|
||||
component: WatermarkFormatting,
|
||||
tooltip: (t) => ({
|
||||
header: {
|
||||
title: t("watermark.tooltip.formatting.header.title", "Formatting & Layout"),
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
title: t("watermark.tooltip.formatting.size.title", "Size Control"),
|
||||
description: t("watermark.tooltip.formatting.size.text", "Adjust the size of your watermark (text or image)."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.formatting.size.bullet1", "Larger sizes create more prominent watermarks"),
|
||||
],
|
||||
},
|
||||
appearanceTip(t),
|
||||
spacingTip(t),
|
||||
{
|
||||
title: t("watermark.tooltip.formatting.security.title", "Security Option"),
|
||||
description: t("watermark.tooltip.formatting.security.text", "Convert the final PDF to an image-based format for enhanced security."),
|
||||
bullets: [
|
||||
t("watermark.tooltip.formatting.security.bullet1", "Prevents text selection and copying"),
|
||||
t("watermark.tooltip.formatting.security.bullet2", "Makes watermarks harder to remove"),
|
||||
t("watermark.tooltip.formatting.security.bullet3", "Results in larger file sizes"),
|
||||
t("watermark.tooltip.formatting.security.bullet4", "Best for sensitive or copyrighted content"),
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return steps;
|
||||
},
|
||||
|
||||
executeButton: {
|
||||
text: (t) => t("watermark.submit", "Add Watermark"),
|
||||
loadingText: (t) => t("loading"),
|
||||
},
|
||||
|
||||
review: {
|
||||
title: (t) => t("watermark.results.title", "Watermark Results"),
|
||||
},
|
||||
};
|
@ -1,42 +0,0 @@
|
||||
import { ToolDefinition } from '../../components/tools/shared/toolDefinition';
|
||||
import { ChangePermissionsParameters, useChangePermissionsParameters } from '../../hooks/tools/changePermissions/useChangePermissionsParameters';
|
||||
import { useChangePermissionsOperation } from '../../hooks/tools/changePermissions/useChangePermissionsOperation';
|
||||
import ChangePermissionsSettings from '../../components/tools/changePermissions/ChangePermissionsSettings';
|
||||
|
||||
export const changePermissionsDefinition: ToolDefinition<ChangePermissionsParameters> = {
|
||||
id: 'changePermissions',
|
||||
|
||||
useParameters: useChangePermissionsParameters,
|
||||
useOperation: useChangePermissionsOperation,
|
||||
|
||||
steps: [
|
||||
{
|
||||
key: 'settings',
|
||||
title: (t) => t("changePermissions.title", "Document Permissions"),
|
||||
component: ChangePermissionsSettings,
|
||||
tooltip: (t) => ({
|
||||
header: {
|
||||
title: t("changePermissions.tooltip.header.title", "Change Permissions")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
description: t("changePermissions.tooltip.description.text", "Changes document permissions, allowing/disallowing access to different features in PDF readers.")
|
||||
},
|
||||
{
|
||||
title: t("warning.tooltipTitle", "Warning"),
|
||||
description: t("changePermissions.tooltip.warning.text", "To make these permissions unchangeable, use the Add Password tool to set an owner password.")
|
||||
}
|
||||
]
|
||||
}),
|
||||
},
|
||||
],
|
||||
|
||||
executeButton: {
|
||||
text: (t) => t("changePermissions.submit", "Change Permissions"),
|
||||
loadingText: (t) => t("loading"),
|
||||
},
|
||||
|
||||
review: {
|
||||
title: (t) => t("changePermissions.results.title", "Modified PDFs"),
|
||||
},
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
import { ToolDefinition } from '../../components/tools/shared/toolDefinition';
|
||||
import { RemoveCertificateSignParameters, useRemoveCertificateSignParameters } from '../../hooks/tools/removeCertificateSign/useRemoveCertificateSignParameters';
|
||||
import { useRemoveCertificateSignOperation } from '../../hooks/tools/removeCertificateSign/useRemoveCertificateSignOperation';
|
||||
|
||||
export const removeCertificateSignDefinition: ToolDefinition<RemoveCertificateSignParameters> = {
|
||||
id: 'removeCertificateSign',
|
||||
|
||||
useParameters: useRemoveCertificateSignParameters,
|
||||
useOperation: useRemoveCertificateSignOperation,
|
||||
|
||||
steps: [],
|
||||
|
||||
executeButton: {
|
||||
text: (t) => t("removeCertSign.submit", "Remove Signature"),
|
||||
loadingText: (t) => t("loading"),
|
||||
},
|
||||
|
||||
review: {
|
||||
title: (t) => t("removeCertSign.results.title", "Certificate Removal Results"),
|
||||
},
|
||||
};
|
@ -1,41 +0,0 @@
|
||||
import { ToolDefinition } from '../../components/tools/shared/toolDefinition';
|
||||
import { RemovePasswordParameters, useRemovePasswordParameters } from '../../hooks/tools/removePassword/useRemovePasswordParameters';
|
||||
import { useRemovePasswordOperation } from '../../hooks/tools/removePassword/useRemovePasswordOperation';
|
||||
import RemovePasswordSettings from '../../components/tools/removePassword/RemovePasswordSettings';
|
||||
|
||||
export const removePasswordDefinition: ToolDefinition<RemovePasswordParameters> = {
|
||||
id: 'removePassword',
|
||||
|
||||
useParameters: useRemovePasswordParameters,
|
||||
useOperation: useRemovePasswordOperation,
|
||||
|
||||
steps: [
|
||||
{
|
||||
key: 'settings',
|
||||
title: (t) => t("removePassword.password.stepTitle", "Remove Password"),
|
||||
component: RemovePasswordSettings,
|
||||
tooltip: (t) => ({
|
||||
header: {
|
||||
title: t("removePassword.title", "Remove Password")
|
||||
},
|
||||
tips: [
|
||||
{
|
||||
description: t(
|
||||
"removePassword.tooltip.description",
|
||||
"Removing password protection requires the current password that was used to encrypt the PDF. This will decrypt the document, making it accessible without a password."
|
||||
)
|
||||
}
|
||||
]
|
||||
}),
|
||||
},
|
||||
],
|
||||
|
||||
executeButton: {
|
||||
text: (t) => t("removePassword.submit", "Remove Password"),
|
||||
loadingText: (t) => t("loading"),
|
||||
},
|
||||
|
||||
review: {
|
||||
title: (t) => t("removePassword.results.title", "Decrypted PDFs"),
|
||||
},
|
||||
};
|
@ -1,28 +0,0 @@
|
||||
import { ToolDefinition } from '../../components/tools/shared/toolDefinition';
|
||||
import { SanitizeParameters, useSanitizeParameters } from '../../hooks/tools/sanitize/useSanitizeParameters';
|
||||
import { useSanitizeOperation } from '../../hooks/tools/sanitize/useSanitizeOperation';
|
||||
import SanitizeSettings from '../../components/tools/sanitize/SanitizeSettings';
|
||||
|
||||
export const sanitizeDefinition: ToolDefinition<SanitizeParameters> = {
|
||||
id: 'sanitize',
|
||||
|
||||
useParameters: useSanitizeParameters,
|
||||
useOperation: useSanitizeOperation,
|
||||
|
||||
steps: [
|
||||
{
|
||||
key: 'settings',
|
||||
title: (t) => t("sanitize.steps.settings", "Settings"),
|
||||
component: SanitizeSettings,
|
||||
},
|
||||
],
|
||||
|
||||
executeButton: {
|
||||
text: (t) => t("sanitize.submit", "Sanitize PDF"),
|
||||
loadingText: (t) => t("loading"),
|
||||
},
|
||||
|
||||
review: {
|
||||
title: (t) => t("sanitize.sanitizationResults", "Sanitization Results"),
|
||||
},
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
import { ToolDefinition } from '../../components/tools/shared/toolDefinition';
|
||||
import { SingleLargePageParameters, useSingleLargePageParameters } from '../../hooks/tools/singleLargePage/useSingleLargePageParameters';
|
||||
import { useSingleLargePageOperation } from '../../hooks/tools/singleLargePage/useSingleLargePageOperation';
|
||||
|
||||
export const singleLargePageDefinition: ToolDefinition<SingleLargePageParameters> = {
|
||||
id: 'singleLargePage',
|
||||
|
||||
useParameters: useSingleLargePageParameters,
|
||||
useOperation: useSingleLargePageOperation,
|
||||
|
||||
steps: [],
|
||||
|
||||
executeButton: {
|
||||
text: (t) => t("pdfToSinglePage.submit", "Convert To Single Page"),
|
||||
loadingText: (t) => t("loading"),
|
||||
},
|
||||
|
||||
review: {
|
||||
title: (t) => t("pdfToSinglePage.results.title", "Single Page Results"),
|
||||
},
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
import { ToolDefinition } from '../../components/tools/shared/toolDefinition';
|
||||
import { UnlockPdfFormsParameters, useUnlockPdfFormsParameters } from '../../hooks/tools/unlockPdfForms/useUnlockPdfFormsParameters';
|
||||
import { useUnlockPdfFormsOperation } from '../../hooks/tools/unlockPdfForms/useUnlockPdfFormsOperation';
|
||||
|
||||
export const unlockPdfFormsDefinition: ToolDefinition<UnlockPdfFormsParameters> = {
|
||||
id: 'unlockPdfForms',
|
||||
|
||||
useParameters: useUnlockPdfFormsParameters,
|
||||
useOperation: useUnlockPdfFormsOperation,
|
||||
|
||||
steps: [],
|
||||
|
||||
executeButton: {
|
||||
text: (t) => t("unlockPDFForms.submit", "Unlock Forms"),
|
||||
loadingText: (t) => t("loading"),
|
||||
},
|
||||
|
||||
review: {
|
||||
title: (t) => t("unlockPDFForms.results.title", "Unlocked Forms Results"),
|
||||
},
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user