diff --git a/frontend/public/locales/en-US/translation.json b/frontend/public/locales/en-US/translation.json index 4f79f9d4d..a72c04221 100644 --- a/frontend/public/locales/en-US/translation.json +++ b/frontend/public/locales/en-US/translation.json @@ -1766,11 +1766,10 @@ "rotation": "Rotation (degrees)", "opacity": "Opacity (%)", "spacing": { - "width": "Width Spacing", - "height": "Height Spacing" + "horizontal": "Horizontal Spacing", + "vertical": "Vertical Spacing" }, - "convertToImage": "Convert result to image-based PDF", - "convertToImageDesc": "Creates a PDF with images instead of text (more secure but larger file size)" + "convertToImage": "Flatten PDF pages to images" }, "alphabet": { "roman": "Roman/Latin", @@ -1782,10 +1781,10 @@ }, "steps": { "type": "Watermark Type", - "textContent": "Text Content", - "imageContent": "Image Content", - "style": "Style & Position", - "advanced": "Advanced Options" + "wording": "Wording", + "textStyle": "Style", + "file": "Watermark File", + "formatting": "Formatting" }, "results": { "title": "Watermark Results" @@ -1854,6 +1853,91 @@ "bullet3": "Higher values create more spread out patterns" } }, + "wording": { + "header": { + "title": "Text Content" + }, + "text": { + "title": "Watermark Text", + "text": "Enter the text that will appear as your watermark across the document.", + "bullet1": "Keep it concise for better readability", + "bullet2": "Common examples: 'CONFIDENTIAL', 'DRAFT', company name", + "bullet3": "Supports all Unicode characters" + }, + "fontSize": { + "title": "Font Size", + "text": "Control the size of your watermark text (8-72 points).", + "bullet1": "Smaller sizes (8-14pt) for subtle watermarks", + "bullet2": "Medium sizes (16-24pt) for standard visibility", + "bullet3": "Large sizes (30-72pt) for prominent watermarks" + } + }, + "textStyle": { + "header": { + "title": "Text Style" + }, + "language": { + "title": "Language Support", + "text": "Choose the appropriate language setting to ensure proper font rendering.", + "bullet1": "Roman/Latin for Western languages", + "bullet2": "Arabic for Arabic script", + "bullet3": "Japanese, Korean, Chinese for Asian languages", + "bullet4": "Thai for Thai script" + }, + "color": { + "title": "Color Selection", + "text": "Choose a color that provides good contrast with your document content.", + "bullet1": "Light gray (#d3d3d3) for subtle watermarks", + "bullet2": "Black or dark colors for high contrast", + "bullet3": "Custom colors for branding purposes" + } + }, + "file": { + "header": { + "title": "Image Upload" + }, + "upload": { + "title": "Image Selection", + "text": "Upload an image file to use as your watermark.", + "bullet1": "Supports common formats: PNG, JPG, GIF, BMP", + "bullet2": "PNG with transparency works best", + "bullet3": "Higher resolution images maintain quality better" + }, + "recommendations": { + "title": "Best Practices", + "text": "Tips for optimal image watermark results.", + "bullet1": "Use logos or stamps with transparent backgrounds", + "bullet2": "Simple designs work better than complex images", + "bullet3": "Consider the final document size when choosing resolution" + } + }, + "formatting": { + "header": { + "title": "Formatting & Layout" + }, + "appearance": { + "title": "Appearance Settings", + "text": "Control how your watermark looks and blends with the document.", + "bullet1": "Rotation: -360° to 360° for angled watermarks", + "bullet2": "Opacity: 0-100% for transparency control", + "bullet3": "Lower opacity creates subtle watermarks" + }, + "spacing": { + "title": "Spacing Control", + "text": "Adjust the spacing between repeated watermarks across the page.", + "bullet1": "Horizontal spacing: Distance between watermarks left to right", + "bullet2": "Vertical spacing: Distance between watermarks top to bottom", + "bullet3": "Higher values create more spread out patterns" + }, + "security": { + "title": "Security Option", + "text": "Flatten PDF pages to images for enhanced security.", + "bullet1": "Prevents text selection and copying", + "bullet2": "Makes watermarks harder to remove", + "bullet3": "Results in larger file sizes", + "bullet4": "Best for sensitive or copyrighted content" + } + }, "advanced": { "header": { "title": "Advanced Options" diff --git a/frontend/src/components/tools/addWatermark/WatermarkContentSettings.tsx b/frontend/src/components/tools/addWatermark/WatermarkContentSettings.tsx deleted file mode 100644 index 5bab1ab83..000000000 --- a/frontend/src/components/tools/addWatermark/WatermarkContentSettings.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useRef } from "react"; -import { Stack, Text, TextInput, FileButton, Button, NumberInput, Select, ColorInput } from "@mantine/core"; -import { useTranslation } from "react-i18next"; -import { AddWatermarkParameters } from "./types"; - -interface WatermarkContentSettingsProps { - parameters: AddWatermarkParameters; - onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; - disabled?: boolean; -} - -const WatermarkContentSettings = ({ parameters, onParameterChange, disabled = false }: WatermarkContentSettingsProps) => { - const { t } = useTranslation(); - const resetRef = useRef<() => void>(null); - - const alphabetOptions = [ - { value: 'roman', label: t('watermark.alphabet.roman', 'Roman/Latin') }, - { value: 'arabic', label: t('watermark.alphabet.arabic', 'Arabic') }, - { value: 'japanese', label: t('watermark.alphabet.japanese', 'Japanese') }, - { value: 'korean', label: t('watermark.alphabet.korean', 'Korean') }, - { value: 'chinese', label: t('watermark.alphabet.chinese', 'Chinese') }, - { value: 'thai', label: t('watermark.alphabet.thai', 'Thai') } - ]; - - return ( - - {/* Text Watermark Settings */} - {parameters.watermarkType === 'text' && ( - - {t('watermark.settings.text.label', 'Watermark Text')} - onParameterChange('watermarkText', e.target.value)} - disabled={disabled} - /> - - {t('watermark.settings.fontSize', 'Font Size')} - onParameterChange('fontSize', value || 12)} - min={8} - max={72} - disabled={disabled} - /> - - {t('watermark.settings.alphabet', 'Font/Language')} - value && onParameterChange('alphabet', value)} - data={alphabetOptions} - disabled={disabled} - /> - - {t('watermark.settings.color', 'Watermark Color')} - onParameterChange('customColor', value)} - disabled={disabled} - format="hex" - swatches={['#d3d3d3', '#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff']} - /> - - )} - - {/* Image Watermark Settings */} - {parameters.watermarkType === 'image' && ( - - {t('watermark.settings.image.label', 'Watermark Image')} - onParameterChange('watermarkImage', file)} - accept="image/*" - disabled={disabled} - > - {(props) => ( - - {parameters.watermarkImage ? parameters.watermarkImage.name : t('watermark.settings.image.choose', 'Choose Image')} - - )} - - {parameters.watermarkImage && ( - - {t('watermark.settings.image.selected', 'Selected: {{filename}}', { filename: parameters.watermarkImage.name })} - - )} - - )} - - ); -}; - -export default WatermarkContentSettings; \ No newline at end of file diff --git a/frontend/src/components/tools/addWatermark/WatermarkFile.tsx b/frontend/src/components/tools/addWatermark/WatermarkFile.tsx new file mode 100644 index 000000000..8aa27f28b --- /dev/null +++ b/frontend/src/components/tools/addWatermark/WatermarkFile.tsx @@ -0,0 +1,34 @@ +import React, { useRef } from "react"; +import { Stack, Text, FileButton, Button } from "@mantine/core"; +import { useTranslation } from "react-i18next"; +import { AddWatermarkParameters } from "./types"; + +interface WatermarkFileProps { + parameters: AddWatermarkParameters; + onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + disabled?: boolean; +} + +const WatermarkFile = ({ parameters, onParameterChange, disabled = false }: WatermarkFileProps) => { + const { t } = useTranslation(); + const resetRef = useRef<() => void>(null); + + return ( + + onParameterChange('watermarkImage', file)} + accept="image/*" + disabled={disabled} + > + {(props) => ( + + {parameters.watermarkImage ? parameters.watermarkImage.name : t('watermark.settings.image.choose', 'Choose Image')} + + )} + + + ); +}; + +export default WatermarkFile; diff --git a/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx b/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx new file mode 100644 index 000000000..42e382559 --- /dev/null +++ b/frontend/src/components/tools/addWatermark/WatermarkFormatting.tsx @@ -0,0 +1,73 @@ +import React from "react"; +import { Stack, Checkbox, Group } from "@mantine/core"; +import { useTranslation } from "react-i18next"; +import { AddWatermarkParameters } from "./types"; +import NumberInputWithUnit from "../shared/NumberInputWithUnit"; + +interface WatermarkFormattingProps { + parameters: AddWatermarkParameters; + onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + disabled?: boolean; +} + +const WatermarkFormatting = ({ parameters, onParameterChange, disabled = false }: WatermarkFormattingProps) => { + const { t } = useTranslation(); + + return ( + + {/* Position & Appearance - 2 per row */} + + onParameterChange('rotation', typeof value === 'number' ? value : 0)} + unit="°" + min={-360} + max={360} + disabled={disabled} + /> + onParameterChange('opacity', typeof value === 'number' ? value : 50)} + unit="%" + min={0} + max={100} + disabled={disabled} + /> + + + {/* Spacing - 2 per row */} + + onParameterChange('widthSpacer', typeof value === 'number' ? value : 50)} + unit="px" + min={0} + max={200} + disabled={disabled} + /> + onParameterChange('heightSpacer', typeof value === 'number' ? value : 50)} + unit="px" + min={0} + max={200} + disabled={disabled} + /> + + + {/* Advanced Options */} + onParameterChange('convertPDFToImage', event.currentTarget.checked)} + disabled={disabled} + /> + + ); +}; + +export default WatermarkFormatting; \ No newline at end of file diff --git a/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx b/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx new file mode 100644 index 000000000..519ce1581 --- /dev/null +++ b/frontend/src/components/tools/addWatermark/WatermarkTextStyle.tsx @@ -0,0 +1,65 @@ +import React from "react"; +import { Stack, Text, Select, ColorInput, NumberInput, Group } from "@mantine/core"; +import { useTranslation } from "react-i18next"; +import { AddWatermarkParameters } from "./types"; + +interface WatermarkTextStyleProps { + parameters: AddWatermarkParameters; + onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void; + disabled?: boolean; +} + +const WatermarkTextStyle = ({ parameters, onParameterChange, disabled = false }: WatermarkTextStyleProps) => { + const { t } = useTranslation(); + + const alphabetOptions = [ + { value: "roman", label: "Roman" }, + { value: "arabic", label: "العربية" }, + { value: "japanese", label: "日本語" }, + { value: "korean", label: "한국어" }, + { value: "chinese", label: "简体中文" }, + { value: "thai", label: "ไทย" }, + ]; + + return ( + + + + + {t("watermark.settings.color", "Colour")} + + onParameterChange("customColor", value)} + disabled={disabled} + format="hex" + /> + + + + + {t("watermark.settings.fontSize", "Size")} + + onParameterChange("fontSize", value || 12)} + min={8} + max={72} + disabled={disabled} + /> + + + + {t("watermark.settings.alphabet", "Alphabet")} + + value && onParameterChange("alphabet", value)} + data={alphabetOptions} + disabled={disabled} + /> + + ); +}; + +export default WatermarkTextStyle; diff --git a/frontend/src/components/tools/addWatermark/WatermarkTypeSettings.tsx b/frontend/src/components/tools/addWatermark/WatermarkTypeSettings.tsx index 438d781a6..f97454c5c 100644 --- a/frontend/src/components/tools/addWatermark/WatermarkTypeSettings.tsx +++ b/frontend/src/components/tools/addWatermark/WatermarkTypeSettings.tsx @@ -13,7 +13,6 @@ const WatermarkTypeSettings = ({ watermarkType, onWatermarkTypeChange, disabled return ( - {t('watermark.settings.type', 'Watermark Type')} void; + disabled?: boolean; +} + +const WatermarkWording = ({ parameters, onParameterChange, disabled = false }: WatermarkWordingProps) => { + const { t } = useTranslation(); + + return ( + + onParameterChange('watermarkText', e.target.value)} + disabled={disabled} + /> + + ); +}; + +export default WatermarkWording; diff --git a/frontend/src/components/tools/shared/NumberInputWithUnit.tsx b/frontend/src/components/tools/shared/NumberInputWithUnit.tsx new file mode 100644 index 000000000..207a19af3 --- /dev/null +++ b/frontend/src/components/tools/shared/NumberInputWithUnit.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { Stack, Text, NumberInput } from "@mantine/core"; + +interface NumberInputWithUnitProps { + label: string; + value: number; + onChange: (value: number | string) => void; + unit: string; + min?: number; + max?: number; + disabled?: boolean; +} + +const NumberInputWithUnit = ({ + label, + value, + onChange, + unit, + min, + max, + disabled = false +}: NumberInputWithUnitProps) => { + return ( + + + {label} + + + {unit} + + } + rightSectionWidth={unit.length * 8 + 20} // Dynamic width based on unit length + styles={{ + rightSection: { + pointerEvents: 'none', + } + }} + /> + + ); +}; + +export default NumberInputWithUnit; \ No newline at end of file diff --git a/frontend/src/components/tooltips/useWatermarkTips.ts b/frontend/src/components/tooltips/useWatermarkTips.ts index 408d5de2f..675d56d52 100644 --- a/frontend/src/components/tooltips/useWatermarkTips.ts +++ b/frontend/src/components/tooltips/useWatermarkTips.ts @@ -97,27 +97,132 @@ export const useWatermarkStyleTips = (): TooltipContent => { }; }; -export const useWatermarkAdvancedTips = (): TooltipContent => { +export const useWatermarkWordingTips = (): TooltipContent => { const { t } = useTranslation(); return { header: { - title: t("watermark.tooltip.advanced.header.title", "Advanced Options") + title: t("watermark.tooltip.wording.header.title", "Text Content") }, tips: [ { - title: t("watermark.tooltip.advanced.conversion.title", "PDF to Image Conversion"), - description: t("watermark.tooltip.advanced.conversion.text", "Convert the final PDF to an image-based format for enhanced security."), + 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.advanced.conversion.bullet1", "Prevents text selection and copying"), - t("watermark.tooltip.advanced.conversion.bullet2", "Makes watermarks harder to remove"), - t("watermark.tooltip.advanced.conversion.bullet3", "Results in larger file sizes"), - t("watermark.tooltip.advanced.conversion.bullet4", "Best for sensitive or copyrighted content") + 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", "Supports all Unicode characters") ] }, { - title: t("watermark.tooltip.advanced.security.title", "Security Considerations"), - description: t("watermark.tooltip.advanced.security.text", "Image-based PDFs provide additional protection against unauthorized editing and content extraction.") + title: t("watermark.tooltip.wording.fontSize.title", "Font Size"), + description: t("watermark.tooltip.wording.fontSize.text", "Control the size of your watermark text (8-72 points)."), + bullets: [ + t("watermark.tooltip.wording.fontSize.bullet1", "Smaller sizes (8-14pt) for subtle watermarks"), + t("watermark.tooltip.wording.fontSize.bullet2", "Medium sizes (16-24pt) for standard visibility"), + t("watermark.tooltip.wording.fontSize.bullet3", "Large sizes (30-72pt) for prominent watermarks") + ] + } + ] + }; +}; + +export const useWatermarkTextStyleTips = (): TooltipContent => { + const { t } = useTranslation(); + + return { + header: { + title: t("watermark.tooltip.textStyle.header.title", "Text Style") + }, + tips: [ + { + title: t("watermark.tooltip.textStyle.language.title", "Language Support"), + description: t("watermark.tooltip.textStyle.language.text", "Choose the appropriate language setting to ensure proper font rendering."), + bullets: [ + t("watermark.tooltip.textStyle.language.bullet1", "Roman/Latin for Western languages"), + t("watermark.tooltip.textStyle.language.bullet2", "Arabic for Arabic script"), + t("watermark.tooltip.textStyle.language.bullet3", "Japanese, Korean, Chinese for Asian languages"), + t("watermark.tooltip.textStyle.language.bullet4", "Thai for Thai script") + ] + }, + { + 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") + ] + } + ] + }; +}; + +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(); + + return { + header: { + title: t("watermark.tooltip.formatting.header.title", "Formatting & Layout") + }, + tips: [ + { + title: t("watermark.tooltip.formatting.appearance.title", "Appearance Settings"), + description: t("watermark.tooltip.formatting.appearance.text", "Control how your watermark looks and blends with the document."), + bullets: [ + t("watermark.tooltip.formatting.appearance.bullet1", "Rotation: -360° to 360° for angled watermarks"), + t("watermark.tooltip.formatting.appearance.bullet2", "Opacity: 0-100% for transparency control"), + t("watermark.tooltip.formatting.appearance.bullet3", "Lower opacity creates subtle watermarks") + ] + }, + { + title: t("watermark.tooltip.formatting.spacing.title", "Spacing Control"), + description: t("watermark.tooltip.formatting.spacing.text", "Adjust the spacing between repeated watermarks across the page."), + bullets: [ + t("watermark.tooltip.formatting.spacing.bullet1", "Width spacing: Horizontal distance between watermarks"), + t("watermark.tooltip.formatting.spacing.bullet2", "Height spacing: Vertical distance between watermarks"), + t("watermark.tooltip.formatting.spacing.bullet3", "Higher values create more spread out patterns") + ] + }, + { + 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") + ] } ] }; diff --git a/frontend/src/tools/AddWatermark.tsx b/frontend/src/tools/AddWatermark.tsx index 1e30f948e..57cff6f59 100644 --- a/frontend/src/tools/AddWatermark.tsx +++ b/frontend/src/tools/AddWatermark.tsx @@ -7,13 +7,14 @@ import { useToolFileSelection } from "../contexts/FileSelectionContext"; import { createToolFlow } from "../components/tools/shared/createToolFlow"; import WatermarkTypeSettings from "../components/tools/addWatermark/WatermarkTypeSettings"; -import WatermarkContentSettings from "../components/tools/addWatermark/WatermarkContentSettings"; -import WatermarkStyleSettings from "../components/tools/addWatermark/WatermarkStyleSettings"; -import WatermarkAdvancedSettings from "../components/tools/addWatermark/WatermarkAdvancedSettings"; +import WatermarkWording from "../components/tools/addWatermark/WatermarkWording"; +import WatermarkTextStyle from "../components/tools/addWatermark/WatermarkTextStyle"; +import WatermarkFile from "../components/tools/addWatermark/WatermarkFile"; +import WatermarkFormatting from "../components/tools/addWatermark/WatermarkFormatting"; import { useAddWatermarkParameters } from "../hooks/tools/addWatermark/useAddWatermarkParameters"; import { useAddWatermarkOperation } from "../hooks/tools/addWatermark/useAddWatermarkOperation"; -import { useWatermarkTypeTips, useWatermarkContentTips, useWatermarkStyleTips, useWatermarkAdvancedTips } from "../components/tooltips/useWatermarkTips"; +import { useWatermarkTypeTips, useWatermarkWordingTips, useWatermarkTextStyleTips, useWatermarkFileTips, useWatermarkFormattingTips } from "../components/tooltips/useWatermarkTips"; import { BaseToolProps } from "../types/tool"; const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => { @@ -28,9 +29,10 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => const watermarkParams = useAddWatermarkParameters(); const watermarkOperation = useAddWatermarkOperation(); const watermarkTypeTips = useWatermarkTypeTips(); - const watermarkContentTips = useWatermarkContentTips(); - const watermarkStyleTips = useWatermarkStyleTips(); - const watermarkAdvancedTips = useWatermarkAdvancedTips(); + const watermarkWordingTips = useWatermarkWordingTips(); + const watermarkTextStyleTips = useWatermarkTextStyleTips(); + const watermarkFileTips = useWatermarkFileTips(); + const watermarkFormattingTips = useWatermarkFormattingTips(); // Endpoint validation const { enabled: endpointEnabled, loading: endpointLoading } = useEndpointEnabled("add-watermark"); @@ -78,24 +80,22 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => // Step completion logic const typeStepCompleted = hasFiles && !!watermarkParams.parameters.watermarkType; - const contentStepCompleted = typeStepCompleted && ( - (watermarkParams.parameters.watermarkType === 'text' && watermarkParams.parameters.watermarkText.trim().length > 0) || - (watermarkParams.parameters.watermarkType === 'image' && watermarkParams.parameters.watermarkImage !== undefined) - ); + const textWordingCompleted = typeStepCompleted && + watermarkParams.parameters.watermarkType === 'text' && + watermarkParams.parameters.watermarkText.trim().length > 0; + const imageFileCompleted = typeStepCompleted && + watermarkParams.parameters.watermarkType === 'image' && + watermarkParams.parameters.watermarkImage !== undefined; - // Step visibility logic - all steps always visible once files are selected - const styleCollapsed = collapsedStyle || hasResults; - const advancedCollapsed = collapsedAdvanced || hasResults; + // Dynamic step structure based on watermark type + const getSteps = () => { + const steps = []; - return createToolFlow({ - files: { - selectedFiles, - isCollapsed: hasFiles || hasResults, - }, - steps: [ - { + // Step 1: Watermark Type (always visible after files) + if (hasFiles) { + steps.push({ title: t("watermark.steps.type", "Watermark Type"), - isCollapsed: settingsCollapsed? true : collapsedType, + isCollapsed: hasResults ? true : collapsedType, onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedType(!collapsedType), tooltip: watermarkTypeTips, content: ( @@ -105,48 +105,97 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => disabled={endpointLoading} /> ), - }, - { - title: watermarkParams.parameters.watermarkType === 'text' - ? t("watermark.steps.textContent", "Text Content") - : t("watermark.steps.imageContent", "Image Content"), - isCollapsed: settingsCollapsed? true : contentStepCompleted, - tooltip: watermarkContentTips, + }); + } + + // Text watermark path + if (watermarkParams.parameters.watermarkType === 'text') { + // Step 2: Wording + steps.push({ + title: t("watermark.steps.wording", "Wording"), + isCollapsed: hasResults, + tooltip: watermarkWordingTips, content: ( - ), - }, - { - title: t("watermark.steps.style", "Style & Position"), - isCollapsed: settingsCollapsed? true : styleCollapsed, + }); + + // Step 3: Style + steps.push({ + title: t("watermark.steps.textStyle", "Style"), + isCollapsed: hasResults ? true : collapsedStyle, onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedStyle(!collapsedStyle), - tooltip: watermarkStyleTips, + tooltip: watermarkTextStyleTips, content: ( - ), - }, - { - title: t("watermark.steps.advanced", "Advanced Options"), - isCollapsed: settingsCollapsed? true : advancedCollapsed, + }); + + // Step 4: Formatting + steps.push({ + title: t("watermark.steps.formatting", "Formatting"), + isCollapsed: hasResults ? true : collapsedAdvanced, onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedAdvanced(!collapsedAdvanced), - tooltip: watermarkAdvancedTips, + tooltip: watermarkFormattingTips, content: ( - ), - }, - ], + }); + } + + // Image watermark path + if (watermarkParams.parameters.watermarkType === 'image') { + // Step 2: Watermark File + steps.push({ + title: t("watermark.steps.file", "Watermark File"), + isCollapsed: false, + tooltip: watermarkFileTips, + content: ( + + ), + }); + + // Step 3: Formatting + steps.push({ + title: t("watermark.steps.formatting", "Formatting"), + isCollapsed: hasResults ? true : collapsedAdvanced, + onCollapsedClick: hasResults ? handleSettingsReset : () => setCollapsedAdvanced(!collapsedAdvanced), + tooltip: watermarkFormattingTips, + content: ( + + ), + }); + } + + return steps; + }; + + return createToolFlow({ + files: { + selectedFiles, + isCollapsed: hasFiles || hasResults, + }, + steps: getSteps(), executeButton: { text: t("watermark.submit", "Add Watermark"), isVisible: !hasResults,