๐Ÿ“ฆ Stirling-Tools / Stirling-PDF

๐Ÿ“„ ADDING_TOOLS.md ยท 301 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301# Adding New React Tools to Stirling PDF

This guide covers how to add new PDF tools to the React frontend, either by migrating existing Thymeleaf templates or creating entirely new tools.

## Overview

When adding tools, follow this systematic approach using the established patterns and architecture.

## 1. Create Tool Structure

Create these files in the correct directories:
```
frontend/src/hooks/tools/[toolName]/
  โ”œโ”€โ”€ use[ToolName]Parameters.ts     # Parameter definitions and validation
  โ””โ”€โ”€ use[ToolName]Operation.ts      # Tool operation logic using useToolOperation

frontend/src/components/tools/[toolName]/
  โ””โ”€โ”€ [ToolName]Settings.tsx         # Settings UI component (if needed)

frontend/src/tools/
  โ””โ”€โ”€ [ToolName].tsx                 # Main tool component
```

## 2. Implementation Pattern

Use `useBaseTool` for simplified hook management. This is the recommended approach for all new tools:

**Parameters Hook** (`use[ToolName]Parameters.ts`):
```typescript
import { BaseParameters } from '../../../types/parameters';
import { useBaseParameters, BaseParametersHook } from '../shared/useBaseParameters';

export interface [ToolName]Parameters extends BaseParameters {
  // Define your tool-specific parameters here
  someOption: boolean;
}

export const defaultParameters: [ToolName]Parameters = {
  someOption: false,
};

export const use[ToolName]Parameters = (): BaseParametersHook<[ToolName]Parameters> => {
  return useBaseParameters({
    defaultParameters,
    endpointName: 'your-endpoint-name',
    validateFn: (params) => true, // Add validation logic
  });
};
```

**Operation Hook** (`use[ToolName]Operation.ts`):
```typescript
import { useTranslation } from 'react-i18next';
import { ToolType, useToolOperation } from '../shared/useToolOperation';
import { createStandardErrorHandler } from '../../../utils/toolErrorHandler';

export const build[ToolName]FormData = (parameters: [ToolName]Parameters, file: File): FormData => {
  const formData = new FormData();
  formData.append('fileInput', file);
  // Add parameters to formData
  return formData;
};

export const [toolName]OperationConfig = {
  toolType: ToolType.singleFile, // or ToolType.multiFile (buildFormData's file parameter will need to be updated)
  buildFormData: build[ToolName]FormData,
  operationType: '[toolName]',
  endpoint: '/api/v1/category/endpoint-name',
  filePrefix: 'processed_', // Will be overridden with translation
  defaultParameters,
} as const;

export const use[ToolName]Operation = () => {
  const { t } = useTranslation();
  return useToolOperation({
    ...[toolName]OperationConfig,
    filePrefix: t('[toolName].filenamePrefix', 'processed') + '_',
    getErrorMessage: createStandardErrorHandler(t('[toolName].error.failed', 'Operation failed'))
  });
};
```

**Main Component** (`[ToolName].tsx`):
```typescript
import { useTranslation } from "react-i18next";
import { createToolFlow } from "../components/tools/shared/createToolFlow";
import { use[ToolName]Parameters } from "../hooks/tools/[toolName]/use[ToolName]Parameters";
import { use[ToolName]Operation } from "../hooks/tools/[toolName]/use[ToolName]Operation";
import { useBaseTool } from "../hooks/tools/shared/useBaseTool";
import { BaseToolProps, ToolComponent } from "../types/tool";

const [ToolName] = (props: BaseToolProps) => {
  const { t } = useTranslation();
  const base = useBaseTool('[toolName]', use[ToolName]Parameters, use[ToolName]Operation, props);

  return createToolFlow({
    files: {
      selectedFiles: base.selectedFiles,
      isCollapsed: base.hasResults,
      placeholder: t("[toolName].files.placeholder", "Select files to get started"),
    },
    steps: [
      // Add settings steps if needed
    ],
    executeButton: {
      text: t("[toolName].submit", "Process"),
      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("[toolName].results.title", "Results"),
      onFileClick: base.handleThumbnailClick,
      onUndo: base.handleUndo,
    },
  });
};

[ToolName].tool = () => use[ToolName]Operation;
export default [ToolName] as ToolComponent;
```

**Note**: Some existing tools (like AddPassword, Compress) use a legacy pattern with manual hook management. **Always use the Modern Pattern above for new tools** - it's cleaner, more maintainable, and includes automation support.

## 3. Register Tool in System
Update these files to register your new tool:

**Tool Registry** (`frontend/src/data/useTranslatedToolRegistry.tsx`):
1. Add imports at the top:
```typescript
import [ToolName] from "../tools/[ToolName]";
import { [toolName]OperationConfig } from "../hooks/tools/[toolName]/use[ToolName]Operation";
import [ToolName]Settings from "../components/tools/[toolName]/[ToolName]Settings";
```

2. Add tool entry in the `allTools` object:
```typescript
[toolName]: {
  icon: <LocalIcon icon="your-icon-name" width="1.5rem" height="1.5rem" />,
  name: t("home.[toolName].title", "Tool Name"),
  component: [ToolName],
  description: t("home.[toolName].desc", "Tool description"),
  categoryId: ToolCategoryId.STANDARD_TOOLS, // or appropriate category
  subcategoryId: SubcategoryId.APPROPRIATE_SUBCATEGORY,
  maxFiles: -1, // or specific number
  endpoints: ["endpoint-name"],
  operationConfig: [toolName]OperationConfig,
  settingsComponent: [ToolName]Settings, // if settings exist
},
```

## 4. Add Tooltips (Optional but Recommended)
Create user-friendly tooltips to help non-technical users understand your tool. **Use simple, clear language - avoid technical jargon:**

**Tooltip Hook** (`frontend/src/components/tooltips/use[ToolName]Tips.ts`):
```typescript
import { useTranslation } from 'react-i18next';
import { TooltipContent } from '../../types/tips';

export const use[ToolName]Tips = (): TooltipContent => {
  const { t } = useTranslation();

  return {
    header: {
      title: t("[toolName].tooltip.header.title", "Tool Overview")
    },
    tips: [
      {
        title: t("[toolName].tooltip.description.title", "What does this tool do?"),
        description: t("[toolName].tooltip.description.text", "Simple explanation in everyday language that non-technical users can understand."),
        bullets: [
          t("[toolName].tooltip.description.bullet1", "Easy-to-understand benefit 1"),
          t("[toolName].tooltip.description.bullet2", "Easy-to-understand benefit 2")
        ]
      }
      // Add more tip sections as needed
    ]
  };
};
```

**Add tooltip to your main component:**
```typescript
import { use[ToolName]Tips } from "../components/tooltips/use[ToolName]Tips";

const [ToolName] = (props: BaseToolProps) => {
  const tips = use[ToolName]Tips();
  
  // In your steps array:
  steps: [
    {
      title: t("[toolName].steps.settings", "Settings"),
      tooltip: tips, // Add this line
      content: <[ToolName]Settings ... />
    }
  ]
```

## 5. Add Translations
Update translation files. **Important: Only update `en-GB` files** - other languages are handled separately.

**File to update:** `frontend/public/locales/en-GB/translation.toml`

**Required Translation Keys**:
```toml
{
  "home": {
    "[toolName]": {
      "title": "Tool Name",
      "desc": "Tool description"
    }
  },
  "[toolName]": {
    "title": "Tool Name",
    "submit": "Process",
    "filenamePrefix": "processed",
    "files": {
      "placeholder": "Select files to get started"
    },
    "steps": {
      "settings": "Settings"
    },
    "options": {
      "title": "Tool Options",
      "someOption": "Option Label",
      "someOption.desc": "Option description",
      "note": "General information about the tool."
    },
    "results": {
      "title": "Results"
    },
    "error": {
      "failed": "Operation failed"
    },
    "tooltip": {
      "header": {
        "title": "Tool Overview"
      },
      "description": {
        "title": "What does this tool do?",
        "text": "Simple explanation in everyday language",
        "bullet1": "Easy-to-understand benefit 1",
        "bullet2": "Easy-to-understand benefit 2"
      }
    }
  }
}
```

**Translation Notes:**
- **Only update `en-GB/translation.toml`** - other locale files are managed separately
- Use descriptive keys that match your component's `t()` calls
- Include tooltip translations if you created tooltip hooks
- Add `options.*` keys if your tool has settings with descriptions

**Tooltip Writing Guidelines:**
- **Use simple, everyday language** - avoid technical terms like "converts interactive elements" 
- **Focus on benefits** - explain what the user gains, not how it works internally
- **Use concrete examples** - "text boxes become regular text" vs "form fields are flattened"
- **Answer user questions** - "What does this do?", "When should I use this?", "What's this option for?"
- **Keep descriptions concise** - 1-2 sentences maximum per section
- **Use bullet points** for multiple benefits or features

## 6. Migration from Thymeleaf
When migrating existing Thymeleaf templates:

1. **Identify Form Parameters**: Look at the original `<form>` inputs to determine parameter structure
2. **Extract Translation Keys**: Find `#{key.name}` references and add them to JSON translations (For many tools these translations will already exist but some parts will be missing)
3. **Map API Endpoint**: Note the `th:action` URL for the operation hook
4. **Preserve Functionality**: Ensure all original form behaviour is replicated which is applicable to V2 react UI

## 7. Testing Your Tool
- Verify tool appears in UI with correct icon and description
- Test with various file sizes and types
- Confirm translations work
- Check error handling
- Test undo functionality
- Verify results display correctly

## Tool Development Patterns

### Three Tool Patterns:

**Pattern 1: Single-File Tools** (Individual processing)
- Backend processes one file per API call
- Set `multiFileEndpoint: false`
- Examples: Compress, Rotate

**Pattern 2: Multi-File Tools** (Batch processing)
- Backend accepts `MultipartFile[]` arrays in single API call
- Set `multiFileEndpoint: true`
- Examples: Split, Merge, Overlay

**Pattern 3: Complex Tools** (Custom processing)
- Tools with complex routing logic or non-standard processing
- Provide `customProcessor` for full control
- Examples: Convert, OCR