{"owner":"Stirling-Tools","repo":"Stirling-PDF","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to AI Agents when working with code in this repository.\n\n## Taskfile (Recommended)\n\nThis project uses [Task](https://taskfile.dev/) as a unified command runner. All build, dev, test, lint, and docker commands can be run from the repo root via `task <command>`. Run `task --list` to see all available commands.\n\nTask `desc:` fields should describe **what** the task does, not **how** it does it. Keep them generic and stable: don't reference implementation details like aliases, internal helpers, mode flags, or which other task delegates to which. The description is for users picking a command from `task --list`, not a changelog of refactors.\n\n### Quick Reference\n- `task install` — install all dependencies\n- `task dev` — start backend + frontend concurrently\n- `task dev:all` — start backend + frontend + engine concurrently\n- `task build` — build all components\n- `task test` — run all tests (backend + frontend + engine)\n- `task lint` — run all linters\n- `task format` — auto-fix formatting across all components\n- `task check` — full quality gate (lint + typecheck + test)\n- `task clean` — clean all build artifacts\n- `task docker:build` — build standard Docker image\n- `task docker:up` — start Docker compose stack\n\n## Common Development Commands\n\n### Build and Test\n- **Build project**: `task build`\n- **Run backend locally**: `task backend:dev`\n- **Run all tests**: `task test` (or individually: `task backend:test`, `task frontend:test`, `task engine:test`)\n- **Docker integration tests**: `./test.sh` (builds all Docker variants and runs comprehensive tests)\n- **Code formatting**: `task format` (or `task backend:format` for Java only)\n- **Full quality gate**: `task check` (runs lint + typecheck + test across all components)\n\nAfter modifying any files in the project, you must run the relevant `task check` command that covers that area of the code. For example, when editing frontend files run `task frontend:check`; for Python engine files run `task engine:check`; for Java backend files run `task backend:check`.\n\n### Docker Development\n- **Build standard**: `task docker:build` (or `docker build -t stirling-pdf -f docker/embedded/Dockerfile .`)\n- **Build fat version**: `task docker:build:fat`\n- **Build ultra-lite**: `task docker:build:ultra-lite`\n- **Start compose stack**: `task docker:up` (or `task docker:up:fat`, `task docker:up:ultra-lite`)\n- **Stop compose stack**: `task docker:down`\n- **View logs**: `task docker:logs`\n- **Example compose files**: Located in `exampleYmlFiles/` directory\n\n### Security Mode Development\nSet `DOCKER_ENABLE_SECURITY=true` environment variable to enable security features during development. This is required for testing the full version locally.\n\n### Python Development (AI Engine)\n\nThe engine is a Python reasoning service for Stirling: it plans and interprets work, but it does not own durable state, and it does not execute Stirling PDF operations directly. Keep the service narrow: typed contracts in, typed contracts out, with AI only where it adds reasoning value. The frontend calls the Python engine via Java as a proxy.\n\n#### Python Commands\nAll engine commands run from the repo root using Task:\n- `task engine:check` — run all checks (typecheck + lint + format-check + test)\n- `task engine:fix` — auto-fix lint + formatting\n- `task engine:install` — install Python dependencies via uv\n- `task engine:dev` — start FastAPI with hot reload (localhost:5001)\n- `task engine:test` — run pytest\n- `task engine:lint` — run ruff linting\n- `task engine:typecheck` — run pyright\n- `task engine:format` — format code with ruff\n- `task engine:tool-models` — generate `tool_models.py` from the Java OpenAPI spec\n\nThe project structure is defined in `engine/pyproject.toml`. Any new dependencies should be listed there, followed by running `task engine:install`.\n\n#### Python Code Style\n- Keep `task engine:check` passing.\n- Use modern Python when it improves clarity.\n- Prefer explicit names to cleverness.\n- Avoid nested functions and nested classes unless the language construct requires them.\n- Prefer composition to inheritance when combining concepts.\n- Avoid speculative abstractions. Add a layer only when it removes real duplication or clarifies lifecycle.\n- Add comments sparingly and only when they explain non-obvious intent.\n\n#### Python Typing and Models\n- Deserialize into Pydantic models as early as possible.\n- Serialize from Pydantic models as late as possible.\n- Do not pass raw `dict[str, Any]` or `dict[str, object]` across important boundaries when a typed model can exist instead.\n- Avoid `Any` wherever possible.\n- Avoid `cast()` wherever possible (reconsider the structure first).\n- All shared models should subclass `stirling.models.ApiModel` so the service behaves consistently.\n- Do not use string literals for any type annotations, including `cast()`.\n\n#### Python Configuration\n- Keep application-owned configuration in `stirling.config`.\n- Only add `STIRLING_*` environment variables that the engine itself truly owns.\n- Do not mirror third-party provider environment variables unless the engine is actually interpreting them.\n- Let `pydantic-ai` own provider authentication configuration when possible.\n\n#### Python Architecture\n\n**Package roles:**\n- `stirling.contracts`: request/response models and shared typed workflow contracts. If a shape crosses a module or service boundary, it probably belongs here.\n- `stirling.models`: shared model primitives and generated tool models.\n- `stirling.agents`: reasoning modules for individual capabilities.\n- `stirling.api`: HTTP layer, dependency access, and app startup wiring.\n- `stirling.services`: shared runtime and non-AI infrastructure.\n- `stirling.config`: application-owned settings.\n\n**Source of truth:**\n- `stirling.models.tool_models` is the source of truth for operation IDs and parameter models.\n- Do not duplicate operation lists if they can be derived from `tool_models.OPERATIONS`.\n- Do not hand-maintain parallel parameter schemas when the generated tool models already define them.\n- If a tool ID must match a parameter model, validate that relationship explicitly in code.\n\n**Boundaries:**\n- Keep the API layer thin. Route modules should bind requests, resolve dependencies, and call agents or services. They should not contain business logic.\n- Keep agents focused on one reasoning domain. They should not own FastAPI routing, persistence, or execution of Stirling operations.\n- Build long-lived runtime objects centrally at startup when possible rather than reconstructing heavy AI objects per request.\n- If an agent delegates to another agent, the delegated agent should remain the source of truth for its own domain output.\n\n#### Python AI Usage\n- The system must work with any AI, including self-hosted models. We require that the models support structured outputs, but should minimise model-specific code beyond that.\n- Use AI for reasoning-heavy outputs, not deterministic glue.\n- Do not ask the model to invent data that Python can derive safely.\n- Do not fabricate fallback user-facing copy in code to hide incomplete model output.\n- AI output schemas should be impossible to instantiate incorrectly.\n  - Do not require the model to keep separate structures in sync. For example, instead of generating two lists which must be the same length, generate one list of a model containing the same data.\n  - Prefer Python to derive deterministic follow-up structure from a valid AI result.\n- Use `NativeOutput(...)` for structured model outputs.\n- Use `ToolOutput(...)` when the model should select and call delegate functions.\n\n#### Python Testing\n- Test contracts directly.\n- Test agents directly where behaviour matters.\n- Test API routes as thin integration points.\n- Prefer dependency overrides or startup-state seams to monkeypatching random globals.\n\n### Frontend Development\n- **Frontend dev server**: `task frontend:dev` — requires backend on localhost:8080\n- **Tech Stack**: Vite + React + TypeScript + Mantine UI + TailwindCSS\n- **Proxy Configuration**: Vite proxies `/api/*` calls to backend (localhost:8080)\n- **Build Process**: DO NOT run build scripts manually - builds are handled by CI/CD pipelines\n- **Package Installation**: `task frontend:install`\n- **Deployment Options**:\n  - **Desktop App**: `task desktop:build`\n  - **Web Server**: `task frontend:build` then serve dist/ folder\n  - **Development**: `task desktop:dev` for desktop dev mode\n\n#### Environment Variables\n- All `VITE_*` variables must be declared in the appropriate committed env file:\n  - `frontend/editor/.env` — core and shared vars (base, loaded in every mode)\n  - `frontend/editor/.env.proprietary` — proprietary-only vars, e.g. the admin portal's SaaS/account-link keys (layered on top of `.env` in proprietary mode)\n  - `frontend/editor/.env.saas` — SaaS-only vars (layered on top of `.env` in SaaS mode)\n  - `frontend/editor/.env.desktop` — desktop (Tauri)-only vars (layered on top of `.env` in desktop mode)\n- These files are committed to Git and must not contain private keys\n- Local overrides (API keys, machine-specific settings) go in uncommitted sibling `.env.local` / `.env.saas.local` / `.env.desktop.local` files — Vite automatically layers them on top\n- Never use `|| 'hardcoded-fallback'` inline — put defaults in the committed env files\n- `task frontend:prepare` creates empty `.local` override files on first run; pass `MODE=saas` or `MODE=desktop` to also create the mode-specific `.local` file\n- Prepare runs automatically as a dependency of all `dev*`, `build*`, and `desktop*` tasks\n- See `frontend/README.md#environment-variables` for full documentation\n\n#### Import Paths - CRITICAL\n**ALWAYS use `@app/*` for imports.** Do not use `@core/*` or `@proprietary/*` unless explicitly wrapping/extending a lower layer implementation.\n\nFor a broader explanation of the frontend layering and override architecture, read @frontend/editor/DeveloperGuide.md\n\nBefore touching colours or theming (tokens, dark mode, accent colours), read @frontend/editor/src/core/theme/README.md — it explains the palette/`--c-*` token system and the rule that literal colours live only in `primitives.css`.\n\n```typescript\n// ✅ CORRECT - Use @app/* for all imports\nimport { AppLayout } from \"@app/components/AppLayout\";\nimport { useFileContext } from \"@app/contexts/FileContext\";\nimport { FileContext } from \"@app/contexts/FileContext\";\n\n// ❌ WRONG - Do not use @core/* or @proprietary/* in normal code\nimport { AppLayout } from \"@core/components/AppLayout\";\nimport { useFileContext } from \"@proprietary/contexts/FileContext\";\n```\n\n**Only use explicit aliases when:**\n- Building layer-specific override that wraps a lower layer's component\n- Example: `import { AppProviders as CoreAppProviders } from \"@core/components/AppProviders\"` when creating proprietary/AppProviders.tsx that extends the core version\n\nThe `@app/*` alias automatically resolves to the correct layer based on build target (core/proprietary/saas/desktop/cloud) and handles the fallback cascade — see \"Frontend `cloud/` Layer\" below for the full per-flavor order.\n\n#### Frontend `cloud/` Layer\n\n`@app/*` resolves through a per-flavor cascade — first existing file wins (shadow/override):\n\n- **core** → core\n- **proprietary** → proprietary → core\n- **saas** → saas → cloud → proprietary → core\n- **desktop** → desktop → cloud → proprietary → core\n- **cloud** → cloud → proprietary → core\n\nWhat goes where:\n\n- **core** — OSS base.\n- **proprietary** — licensed / offline features.\n- **cloud** — the SHARED hosted/SaaS experience used by BOTH saas + desktop: PAYG, wallet, plan, billing, usage meters, cloud config/team/onboarding.\n- **saas** — web-only: Supabase web auth, AuthCallback, avatar canvas, `window.location`.\n- **desktop** — Tauri-only: keyring authService, tauriHttpClient, native files/windows, backend routing.\n\n`cloud/` MUST NOT import `@supabase/*`, `@tauri-apps/*`, raw `fetch`, `window.location`, `localStorage`, `sessionStorage`, or `import.meta.env.VITE_*` (all enforced by the linter). It reaches platform-specific things only via `@app/*` seams: `services/apiClient`, `auth/session.getAccessToken`, `auth/supabase`, `platform/openExternal`, `services/billing`, `hooks/useSaaSMode` — each provided per-platform in `saas/` and `desktop/`.\n\nRule of thumb — **move, don't copy**: share via `cloud/`, override by shadowing the same `@app/*` path in a leaf (`saas/` or `desktop/`).\n\n**Cloud feature flags on desktop.** The local `AppConfigContext` reads `/api/v1/config/app-config` from the LOCAL bundled backend, so cloud-only flags (`aiEngineEnabled`, `premiumEnabled`, …) are never seen on desktop. To read the cloud's view, use `useSaasAppConfig()` (`desktop/hooks/useSaasAppConfig.ts`, backed by the general `saasAppConfigService` — SaaS-mode-only, public endpoint, native HTTP, 5-min cache). It returns `null` outside SaaS mode, so cloud features stay off in local/self-hosted and the server keeps the on/off switch (no desktop release needed to flip a flag). Gate a feature behind a per-platform seam — e.g. `useAiEngineEnabled()` (core reads `useAppConfig()`, desktop reads `useSaasAppConfig()`) — rather than hardcoding the flag on.\n\n#### Component Override Pattern (Stub/Shadow)\nUse this pattern for desktop-specific or proprietary-specific features WITHOUT runtime checks or conditionals.\n\n**How it works:**\n1. Core defines stub component (returns null or no-op)\n2. Desktop/proprietary overrides with same path/name\n3. Core imports via `@app/*` - higher layer \"shadows\" core in those builds\n4. No `@ts-ignore`, no `isTauri()` checks, no runtime conditionals!\n\n**Example - Desktop-specific footer:**\n\n```typescript\n// core/components/workbenchBar/WorkbenchBarFooterExtensions.tsx (stub)\ninterface WorkbenchBarFooterExtensionsProps {\n  className?: string;\n}\n\nexport function WorkbenchBarFooterExtensions(_props: WorkbenchBarFooterExtensionsProps) {\n  return null; // Stub - does nothing in web builds\n}\n```\n\n```tsx\n// desktop/components/workbenchBar/WorkbenchBarFooterExtensions.tsx (real implementation)\nimport { Box } from '@mantine/core';\nimport { BackendHealthIndicator } from '@app/components/BackendHealthIndicator';\n\ninterface WorkbenchBarFooterExtensionsProps {\n  className?: string;\n}\n\nexport function WorkbenchBarFooterExtensions({ className }: WorkbenchBarFooterExtensionsProps) {\n  return (\n    <Box className={className}>\n      <BackendHealthIndicator />\n    </Box>\n  );\n}\n```\n\n```tsx\n// core/components/shared/WorkbenchBar.tsx (usage - works in ALL builds)\nimport { WorkbenchBarFooterExtensions } from '@app/components/workbenchBar/WorkbenchBarFooterExtensions';\n\nexport function WorkbenchBar() {\n  return (\n    <div>\n      {/* In web builds: renders nothing (stub returns null) */}\n      {/* In desktop builds: renders BackendHealthIndicator */}\n      <WorkbenchBarFooterExtensions className=\"workbench-bar-footer\" />\n    </div>\n  );\n}\n```\n\n**Build resolution:**\n- **Core build**: `@app/*` → `core/*` → Gets stub (returns null)\n- **Desktop build**: `@app/*` → `desktop/*` → Gets real implementation (shadows core)\n\n**Benefits:**\n- No runtime checks or feature flags\n- Type-safe across all builds\n- Clean, readable code\n- Build-time optimization (dead code elimination)\n\n#### Multi-Tool Workflow Architecture\nFrontend designed for **stateful document processing**:\n- Users upload PDFs once, then chain tools (split → merge → compress → view)\n- File state and processing results persist across tool switches\n- No file reloading between tools - performance critical for large PDFs (up to 100GB+)\n\n#### FileContext - Central State Management\n**Location**: `frontend/editor/src/core/contexts/FileContext.tsx`\n- **Active files**: Currently loaded PDFs and their variants\n- **Tool navigation**: Current mode (viewer/pageEditor/fileEditor/toolName)\n- **Memory management**: PDF document cleanup, blob URL lifecycle, Web Worker management\n- **IndexedDB persistence**: File storage with thumbnail caching\n- **Preview system**: Tools can preview results (e.g., Split → Viewer → back to Split) without context pollution\n\n**Critical**: All file operations go through FileContext. Don't bypass with direct file handling.\n\n#### Processing Services\n- **enhancedPDFProcessingService**: Background PDF parsing and manipulation\n- **thumbnailGenerationService**: Web Worker-based with main-thread fallback\n- **fileStorage**: IndexedDB with LRU cache management\n\n#### Memory Management Strategy\n**Why manual cleanup exists**: Large PDFs (up to 100GB+) through multiple tools accumulate:\n- PDF.js documents that need explicit .destroy() calls\n- Blob URLs from tool outputs that need revocation\n- Web Workers that need termination\nWithout cleanup: browser crashes with memory leaks.\n\n#### Tool Development\n\n**Architecture**: Modular hook-based system with clear separation of concerns:\n\n- **useToolOperation** (`frontend/editor/src/core/hooks/tools/shared/useToolOperation.ts`): Main orchestrator hook\n  - Coordinates all tool operations with consistent interface\n  - Integrates with FileContext for operation tracking\n  - Handles validation, error handling, and UI state management\n\n- **Supporting Hooks**:\n  - **useToolState**: UI state management (loading, progress, error, files)\n  - **useToolApiCalls**: HTTP requests and file processing\n  - **useToolResources**: Blob URLs, thumbnails, ZIP downloads\n\n- **Utilities**:\n  - **toolErrorHandler**: Standardized error extraction and i18n support\n  - **toolResponseProcessor**: API response handling (single/zip/custom)\n  - **toolOperationTracker**: FileContext integration utilities\n\n**Three Tool Patterns**:\n\n**Pattern 1: Single-File Tools** (Individual processing)\n- Backend processes one file per API call\n- Set `multiFileEndpoint: false`\n- Examples: Compress, Rotate\n```typescript\nreturn useToolOperation({\n  operationType: 'compress',\n  endpoint: '/api/v1/misc/compress-pdf',\n  buildFormData: (params, file: File) => { /* single file */ },\n  multiFileEndpoint: false,\n});\n```\n\n**Pattern 2: Multi-File Tools** (Batch processing)\n- Backend accepts `MultipartFile[]` arrays in single API call\n- Set `multiFileEndpoint: true`\n- Examples: Split, Merge, Overlay\n```typescript\nreturn useToolOperation({\n  operationType: 'split',\n  endpoint: '/api/v1/general/split-pages',\n  buildFormData: (params, files: File[]) => { /* all files */ },\n  multiFileEndpoint: true,\n  filePrefix: 'split_',\n});\n```\n\n**Pattern 3: Complex Tools** (Custom processing)\n- Tools with complex routing logic or non-standard processing\n- Provide `customProcessor` for full control\n- Examples: Convert, OCR\n```typescript\nreturn useToolOperation({\n  operationType: 'convert',\n  customProcessor: async (params, files) => { /* custom logic */ },\n});\n```\n\n**Benefits**:\n- **No Timeouts**: Operations run until completion (supports 100GB+ files)\n- **Consistent**: All tools follow same pattern and interface\n- **Maintainable**: Single responsibility hooks, easy to test and modify\n- **i18n Ready**: Built-in internationalization support\n- **Type Safe**: Full TypeScript support with generic interfaces\n- **Memory Safe**: Automatic resource cleanup and blob URL management\n\n## Architecture Overview\n\n### Project Structure\n- **Backend**: Spring Boot application\n- **Frontend**: React-based SPA in `/frontend` directory\n  - **File Storage**: IndexedDB for client-side file persistence and thumbnails\n  - **Internationalization**: JSON-based translations (converted from backend .properties)\n- **PDF Processing**: PDFBox for core PDF operations, LibreOffice for conversions, PDF.js for client-side rendering\n- **Security**: Spring Security with optional authentication (controlled by `DOCKER_ENABLE_SECURITY`)\n- **Configuration**: YAML-based configuration with environment variable overrides\n\n### Controller Architecture\n- **API Controllers** (`src/main/java/.../controller/api/`): REST endpoints for PDF operations\n  - Organized by function: converters, security, misc, pipeline\n  - Follow pattern: `@RestController` + `@RequestMapping(\"/api/v1/...\")`\n\n### Key Components\n- **SPDFApplication.java**: Main application class with desktop UI and browser launching logic\n- **ConfigInitializer**: Handles runtime configuration and settings files\n- **Pipeline System**: Automated PDF processing workflows via `PipelineController`\n- **Security Layer**: Authentication, authorization, and user management (when enabled)\n\n### Frontend Directory Structure\nThe frontend is organized with a clear separation of concerns:\n\n- **`frontend/editor/src/core/`**: Main application code (shared, production-ready components)\n  - **`core/components/`**: React components organized by feature\n    - `core/components/tools/`: Individual PDF tool implementations\n    - `core/components/viewer/`: PDF viewer components\n    - `core/components/pageEditor/`: Page manipulation UI\n    - `core/components/tooltips/`: Help tooltips for tools\n    - `core/components/shared/`: Reusable UI components\n  - **`core/contexts/`**: React Context providers\n    - `FileContext.tsx`: Central file state management\n    - `file/`: File reducer and selectors\n    - `toolWorkflow/`: Tool workflow state\n  - **`core/hooks/`**: Custom React hooks\n    - `hooks/tools/`: Tool-specific operation hooks (one directory per tool)\n    - `hooks/tools/shared/`: Shared hook utilities (useToolOperation, etc.)\n  - **`core/constants/`**: Application constants and configuration\n  - **`core/data/`**: Static data (tool taxonomy, etc.)\n  - **`core/services/`**: Business logic services (PDF processing, storage, etc.)\n\n- **`frontend/editor/src/desktop/`**: Desktop-specific (Tauri) code\n- **`frontend/editor/src/proprietary/`**: Proprietary/licensed features\n- **`frontend/editor/src-tauri/`**: Tauri (Rust) native desktop application code\n- **`frontend/editor/public/`**: Static assets served directly\n  - `public/locales/`: Translation JSON files\n\n### Component Architecture\n- **Static Assets**: CSS, JS, and resources in `src/main/resources/static/` (legacy) + `frontend/editor/public/` (modern)\n- **Internationalization**:\n  - Backend: `messages_*.properties` files\n  - Frontend: JSON files in `frontend/editor/public/locales/` (converted from .properties)\n  - Conversion Script: `scripts/convert_properties_to_json.py`\n\n### Configuration Modes\n- **Ultra-lite**: Basic PDF operations only\n- **Standard**: Full feature set\n- **Fat**: Pre-downloaded dependencies for air-gapped environments\n- **Security Mode**: Adds authentication, user management, and enterprise features\n\n### Testing Strategy\n- **Integration Tests**: Cucumber tests in `testing/cucumber/`\n- **Docker Testing**: `test.sh` validates all Docker variants\n- **Manual Testing**: No unit tests currently - relies on UI and API testing\n\n## Development Workflow\n\n1. **Local Development** (using Taskfile):\n   - Backend + frontend: `task dev`\n   - All services (including AI engine): `task dev:all`\n   - Or individually: `task backend:dev` (localhost:8080), `task frontend:dev` (localhost:5173), `task engine:dev` (localhost:5001)\n2. **Quality Gate**: Run `task check` before submitting PRs\n3. **Docker Testing**: Use `./test.sh` for full Docker integration tests\n4. **Code Style**: Spotless enforces Google Java Format automatically (`task backend:format`)\n5. **Translations**:\n   - Backend: Use helper scripts in `/scripts` for multi-language updates\n   - Frontend: Update JSON files in `frontend/editor/public/locales/` or use conversion script\n6. **Documentation**: API docs auto-generated and available at `/swagger-ui/index.html`\n\n## Frontend Architecture Status\n\n- **Core Status**: React SPA architecture complete with multi-tool workflow support\n- **State Management**: FileContext handles all file operations and tool navigation\n- **File Processing**: Production-ready with memory management for large PDF workflows (up to 100GB+)\n- **Tool Integration**: Modular hook architecture with `useToolOperation` orchestrator\n  - Individual hooks: `useToolState`, `useToolApiCalls`, `useToolResources`\n  - Utilities: `toolErrorHandler`, `toolResponseProcessor`, `toolOperationTracker`\n  - Pattern: Each tool creates focused operation hook, UI consumes state/actions\n- **Preview System**: Tool results can be previewed without polluting file context (Split tool example)\n- **Performance**: Web Worker thumbnails, IndexedDB persistence, background processing\n\n## Translation Rules\n\n- **CRITICAL**: Always update translations in `en-US` only - all other languages (including `en-GB`) are handled separately\n- Translation files are located in `frontend/editor/public/locales/`\n- After changing any translation file, run `task pre-commit:fix`\n\n## Important Notes\n\n- **Java Version**: Requires JDK 25.\n- **Lombok**: Used extensively - ensure IDE plugin is installed\n- **File Persistence**:\n  - **Backend**: Designed to be stateless - files are processed in memory/temp locations only\n  - **Frontend**: Uses IndexedDB for client-side file storage and caching (with thumbnails)\n- **Security**: When `DOCKER_ENABLE_SECURITY=false`, security-related classes are excluded from compilation\n- **Import Paths**: ALWAYS use `@app/*` for imports - never use `@core/*` or `@proprietary/*` unless explicitly wrapping/extending a lower layer\n- **FileContext**: All file operations MUST go through FileContext - never bypass with direct File handling\n- **Memory Management**: Manual cleanup required for PDF.js documents and blob URLs - don't remove cleanup code\n- **Tool Development**: New tools should follow `useToolOperation` hook pattern (see `useCompressOperation.ts`)\n- **Performance Target**: Must handle PDFs up to 100GB+ without browser crashes\n- **Preview System**: Tools can preview results without polluting main file context (see Split tool implementation)\n- **Adding Tools**: See `ADDING_TOOLS.md` for complete guide to creating new PDF tools\n\n## Communication Style\n- Be direct and to the point\n- No apologies or conversational filler\n- Answer questions directly without preamble\n- Explain reasoning concisely when asked\n- Avoid unnecessary elaboration\n\n## Decision Making\n- Ask clarifying questions before making assumptions\n- Stop and ask when uncertain about project-specific details\n- Confirm approach before making structural changes\n- Request guidance on preferences (cross-platform vs specific tools, etc.)\n- Verify understanding of requirements before proceeding\n\n\n## Stack reality check (don't trust LLM training data) <!-- bleeding-edge-stack-note -->\n\nThis codebase is on bleeding-edge versions of its core JVM stack: **Spring Boot 4.0.6**,\n**Jackson 3 (`tools.jackson`)**, **JDK 21/25 source/target with JDK 25 toolchain**.\nAll three are *post*-2024 releases and your training corpus is overwhelmingly Spring Boot 2/3 and\nJackson 2 patterns — those patterns will compile, run differently, or hallucinate APIs that no\nlonger exist.\n\nBefore writing or editing Spring / Jackson / JDK code:\n\n1. Open an existing module in `app/core/` or `app/common/` and grep for the actual imports being\n   used — `import tools.jackson...` not `import com.fasterxml.jackson...`, and the new\n   `org.springframework.boot` 4.x package layout.\n2. If you're not sure whether an API exists in this stack version, **check the source on disk\n   first** (the dependency JARs are downloaded under `~/.gradle/caches/modules-2/`).\n3. Do not silently downgrade a Spring Boot 4 pattern to a Spring Boot 3 equivalent. If something\n   doesn't work, surface it to the human — don't guess.\n\nSame goes for Jackson 3's API surface (renamed `ObjectMapper` builder methods, new\n`tools.jackson.databind` namespace) and JDK 25 preview features. Ground your code in this repo's\nactual imports, not what worked three years ago.\n"},"files":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to AI Agents when working with code in this repository.\n\n## Taskfile (Recommended)\n\nThis project uses [Task](https://taskfile.dev/) as a unified command runner. All build, dev, test, lint, and docker commands can be run from the repo root via `task <command>`. Run `task --list` to see all available commands.\n\nTask `desc:` fields should describe **what** the task does, not **how** it does it. Keep them generic and stable: don't reference implementation details like aliases, internal helpers, mode flags, or which other task delegates to which. The description is for users picking a command from `task --list`, not a changelog of refactors.\n\n### Quick Reference\n- `task install` — install all dependencies\n- `task dev` — start backend + frontend concurrently\n- `task dev:all` — start backend + frontend + engine concurrently\n- `task build` — build all components\n- `task test` — run all tests (backend + frontend + engine)\n- `task lint` — run all linters\n- `task format` — auto-fix formatting across all components\n- `task check` — full quality gate (lint + typecheck + test)\n- `task clean` — clean all build artifacts\n- `task docker:build` — build standard Docker image\n- `task docker:up` — start Docker compose stack\n\n## Common Development Commands\n\n### Build and Test\n- **Build project**: `task build`\n- **Run backend locally**: `task backend:dev`\n- **Run all tests**: `task test` (or individually: `task backend:test`, `task frontend:test`, `task engine:test`)\n- **Docker integration tests**: `./test.sh` (builds all Docker variants and runs comprehensive tests)\n- **Code formatting**: `task format` (or `task backend:format` for Java only)\n- **Full quality gate**: `task check` (runs lint + typecheck + test across all components)\n\nAfter modifying any files in the project, you must run the relevant `task check` command that covers that area of the code. For example, when editing frontend files run `task frontend:check`; for Python engine files run `task engine:check`; for Java backend files run `task backend:check`.\n\n### Docker Development\n- **Build standard**: `task docker:build` (or `docker build -t stirling-pdf -f docker/embedded/Dockerfile .`)\n- **Build fat version**: `task docker:build:fat`\n- **Build ultra-lite**: `task docker:build:ultra-lite`\n- **Start compose stack**: `task docker:up` (or `task docker:up:fat`, `task docker:up:ultra-lite`)\n- **Stop compose stack**: `task docker:down`\n- **View logs**: `task docker:logs`\n- **Example compose files**: Located in `exampleYmlFiles/` directory\n\n### Security Mode Development\nSet `DOCKER_ENABLE_SECURITY=true` environment variable to enable security features during development. This is required for testing the full version locally.\n\n### Python Development (AI Engine)\n\nThe engine is a Python reasoning service for Stirling: it plans and interprets work, but it does not own durable state, and it does not execute Stirling PDF operations directly. Keep the service narrow: typed contracts in, typed contracts out, with AI only where it adds reasoning value. The frontend calls the Python engine via Java as a proxy.\n\n#### Python Commands\nAll engine commands run from the repo root using Task:\n- `task engine:check` — run all checks (typecheck + lint + format-check + test)\n- `task engine:fix` — auto-fix lint + formatting\n- `task engine:install` — install Python dependencies via uv\n- `task engine:dev` — start FastAPI with hot reload (localhost:5001)\n- `task engine:test` — run pytest\n- `task engine:lint` — run ruff linting\n- `task engine:typecheck` — run pyright\n- `task engine:format` — format code with ruff\n- `task engine:tool-models` — generate `tool_models.py` from the Java OpenAPI spec\n\nThe project structure is defined in `engine/pyproject.toml`. Any new dependencies should be listed there, followed by running `task engine:install`.\n\n#### Python Code Style\n- Keep `task engine:check` passing.\n- Use modern Python when it improves clarity.\n- Prefer explicit names to cleverness.\n- Avoid nested functions and nested classes unless the language construct requires them.\n- Prefer composition to inheritance when combining concepts.\n- Avoid speculative abstractions. Add a layer only when it removes real duplication or clarifies lifecycle.\n- Add comments sparingly and only when they explain non-obvious intent.\n\n#### Python Typing and Models\n- Deserialize into Pydantic models as early as possible.\n- Serialize from Pydantic models as late as possible.\n- Do not pass raw `dict[str, Any]` or `dict[str, object]` across important boundaries when a typed model can exist instead.\n- Avoid `Any` wherever possible.\n- Avoid `cast()` wherever possible (reconsider the structure first).\n- All shared models should subclass `stirling.models.ApiModel` so the service behaves consistently.\n- Do not use string literals for any type annotations, including `cast()`.\n\n#### Python Configuration\n- Keep application-owned configuration in `stirling.config`.\n- Only add `STIRLING_*` environment variables that the engine itself truly owns.\n- Do not mirror third-party provider environment variables unless the engine is actually interpreting them.\n- Let `pydantic-ai` own provider authentication configuration when possible.\n\n#### Python Architecture\n\n**Package roles:**\n- `stirling.contracts`: request/response models and shared typed workflow contracts. If a shape crosses a module or service boundary, it probably belongs here.\n- `stirling.models`: shared model primitives and generated tool models.\n- `stirling.agents`: reasoning modules for individual capabilities.\n- `stirling.api`: HTTP layer, dependency access, and app startup wiring.\n- `stirling.services`: shared runtime and non-AI infrastructure.\n- `stirling.config`: application-owned settings.\n\n**Source of truth:**\n- `stirling.models.tool_models` is the source of truth for operation IDs and parameter models.\n- Do not duplicate operation lists if they can be derived from `tool_models.OPERATIONS`.\n- Do not hand-maintain parallel parameter schemas when the generated tool models already define them.\n- If a tool ID must match a parameter model, validate that relationship explicitly in code.\n\n**Boundaries:**\n- Keep the API layer thin. Route modules should bind requests, resolve dependencies, and call agents or services. They should not contain business logic.\n- Keep agents focused on one reasoning domain. They should not own FastAPI routing, persistence, or execution of Stirling operations.\n- Build long-lived runtime objects centrally at startup when possible rather than reconstructing heavy AI objects per request.\n- If an agent delegates to another agent, the delegated agent should remain the source of truth for its own domain output.\n\n#### Python AI Usage\n- The system must work with any AI, including self-hosted models. We require that the models support structured outputs, but should minimise model-specific code beyond that.\n- Use AI for reasoning-heavy outputs, not deterministic glue.\n- Do not ask the model to invent data that Python can derive safely.\n- Do not fabricate fallback user-facing copy in code to hide incomplete model output.\n- AI output schemas should be impossible to instantiate incorrectly.\n  - Do not require the model to keep separate structures in sync. For example, instead of generating two lists which must be the same length, generate one list of a model containing the same data.\n  - Prefer Python to derive deterministic follow-up structure from a valid AI result.\n- Use `NativeOutput(...)` for structured model outputs.\n- Use `ToolOutput(...)` when the model should select and call delegate functions.\n\n#### Python Testing\n- Test contracts directly.\n- Test agents directly where behaviour matters.\n- Test API routes as thin integration points.\n- Prefer dependency overrides or startup-state seams to monkeypatching random globals.\n\n### Frontend Development\n- **Frontend dev server**: `task frontend:dev` — requires backend on localhost:8080\n- **Tech Stack**: Vite + React + TypeScript + Mantine UI + TailwindCSS\n- **Proxy Configuration**: Vite proxies `/api/*` calls to backend (localhost:8080)\n- **Build Process**: DO NOT run build scripts manually - builds are handled by CI/CD pipelines\n- **Package Installation**: `task frontend:install`\n- **Deployment Options**:\n  - **Desktop App**: `task desktop:build`\n  - **Web Server**: `task frontend:build` then serve dist/ folder\n  - **Development**: `task desktop:dev` for desktop dev mode\n\n#### Environment Variables\n- All `VITE_*` variables must be declared in the appropriate committed env file:\n  - `frontend/editor/.env` — core and shared vars (base, loaded in every mode)\n  - `frontend/editor/.env.proprietary` — proprietary-only vars, e.g. the admin portal's SaaS/account-link keys (layered on top of `.env` in proprietary mode)\n  - `frontend/editor/.env.saas` — SaaS-only vars (layered on top of `.env` in SaaS mode)\n  - `frontend/editor/.env.desktop` — desktop (Tauri)-only vars (layered on top of `.env` in desktop mode)\n- These files are committed to Git and must not contain private keys\n- Local overrides (API keys, machine-specific settings) go in uncommitted sibling `.env.local` / `.env.saas.local` / `.env.desktop.local` files — Vite automatically layers them on top\n- Never use `|| 'hardcoded-fallback'` inline — put defaults in the committed env files\n- `task frontend:prepare` creates empty `.local` override files on first run; pass `MODE=saas` or `MODE=desktop` to also create the mode-specific `.local` file\n- Prepare runs automatically as a dependency of all `dev*`, `build*`, and `desktop*` tasks\n- See `frontend/README.md#environment-variables` for full documentation\n\n#### Import Paths - CRITICAL\n**ALWAYS use `@app/*` for imports.** Do not use `@core/*` or `@proprietary/*` unless explicitly wrapping/extending a lower layer implementation.\n\nFor a broader explanation of the frontend layering and override architecture, read @frontend/editor/DeveloperGuide.md\n\nBefore touching colours or theming (tokens, dark mode, accent colours), read @frontend/editor/src/core/theme/README.md — it explains the palette/`--c-*` token system and the rule that literal colours live only in `primitives.css`.\n\n```typescript\n// ✅ CORRECT - Use @app/* for all imports\nimport { AppLayout } from \"@app/components/AppLayout\";\nimport { useFileContext } from \"@app/contexts/FileContext\";\nimport { FileContext } from \"@app/contexts/FileContext\";\n\n// ❌ WRONG - Do not use @core/* or @proprietary/* in normal code\nimport { AppLayout } from \"@core/components/AppLayout\";\nimport { useFileContext } from \"@proprietary/contexts/FileContext\";\n```\n\n**Only use explicit aliases when:**\n- Building layer-specific override that wraps a lower layer's component\n- Example: `import { AppProviders as CoreAppProviders } from \"@core/components/AppProviders\"` when creating proprietary/AppProviders.tsx that extends the core version\n\nThe `@app/*` alias automatically resolves to the correct layer based on build target (core/proprietary/saas/desktop/cloud) and handles the fallback cascade — see \"Frontend `cloud/` Layer\" below for the full per-flavor order.\n\n#### Frontend `cloud/` Layer\n\n`@app/*` resolves through a per-flavor cascade — first existing file wins (shadow/override):\n\n- **core** → core\n- **proprietary** → proprietary → core\n- **saas** → saas → cloud → proprietary → core\n- **desktop** → desktop → cloud → proprietary → core\n- **cloud** → cloud → proprietary → core\n\nWhat goes where:\n\n- **core** — OSS base.\n- **proprietary** — licensed / offline features.\n- **cloud** — the SHARED hosted/SaaS experience used by BOTH saas + desktop: PAYG, wallet, plan, billing, usage meters, cloud config/team/onboarding.\n- **saas** — web-only: Supabase web auth, AuthCallback, avatar canvas, `window.location`.\n- **desktop** — Tauri-only: keyring authService, tauriHttpClient, native files/windows, backend routing.\n\n`cloud/` MUST NOT import `@supabase/*`, `@tauri-apps/*`, raw `fetch`, `window.location`, `localStorage`, `sessionStorage`, or `import.meta.env.VITE_*` (all enforced by the linter). It reaches platform-specific things only via `@app/*` seams: `services/apiClient`, `auth/session.getAccessToken`, `auth/supabase`, `platform/openExternal`, `services/billing`, `hooks/useSaaSMode` — each provided per-platform in `saas/` and `desktop/`.\n\nRule of thumb — **move, don't copy**: share via `cloud/`, override by shadowing the same `@app/*` path in a leaf (`saas/` or `desktop/`).\n\n**Cloud feature flags on desktop.** The local `AppConfigContext` reads `/api/v1/config/app-config` from the LOCAL bundled backend, so cloud-only flags (`aiEngineEnabled`, `premiumEnabled`, …) are never seen on desktop. To read the cloud's view, use `useSaasAppConfig()` (`desktop/hooks/useSaasAppConfig.ts`, backed by the general `saasAppConfigService` — SaaS-mode-only, public endpoint, native HTTP, 5-min cache). It returns `null` outside SaaS mode, so cloud features stay off in local/self-hosted and the server keeps the on/off switch (no desktop release needed to flip a flag). Gate a feature behind a per-platform seam — e.g. `useAiEngineEnabled()` (core reads `useAppConfig()`, desktop reads `useSaasAppConfig()`) — rather than hardcoding the flag on.\n\n#### Component Override Pattern (Stub/Shadow)\nUse this pattern for desktop-specific or proprietary-specific features WITHOUT runtime checks or conditionals.\n\n**How it works:**\n1. Core defines stub component (returns null or no-op)\n2. Desktop/proprietary overrides with same path/name\n3. Core imports via `@app/*` - higher layer \"shadows\" core in those builds\n4. No `@ts-ignore`, no `isTauri()` checks, no runtime conditionals!\n\n**Example - Desktop-specific footer:**\n\n```typescript\n// core/components/workbenchBar/WorkbenchBarFooterExtensions.tsx (stub)\ninterface WorkbenchBarFooterExtensionsProps {\n  className?: string;\n}\n\nexport function WorkbenchBarFooterExtensions(_props: WorkbenchBarFooterExtensionsProps) {\n  return null; // Stub - does nothing in web builds\n}\n```\n\n```tsx\n// desktop/components/workbenchBar/WorkbenchBarFooterExtensions.tsx (real implementation)\nimport { Box } from '@mantine/core';\nimport { BackendHealthIndicator } from '@app/components/BackendHealthIndicator';\n\ninterface WorkbenchBarFooterExtensionsProps {\n  className?: string;\n}\n\nexport function WorkbenchBarFooterExtensions({ className }: WorkbenchBarFooterExtensionsProps) {\n  return (\n    <Box className={className}>\n      <BackendHealthIndicator />\n    </Box>\n  );\n}\n```\n\n```tsx\n// core/components/shared/WorkbenchBar.tsx (usage - works in ALL builds)\nimport { WorkbenchBarFooterExtensions } from '@app/components/workbenchBar/WorkbenchBarFooterExtensions';\n\nexport function WorkbenchBar() {\n  return (\n    <div>\n      {/* In web builds: renders nothing (stub returns null) */}\n      {/* In desktop builds: renders BackendHealthIndicator */}\n      <WorkbenchBarFooterExtensions className=\"workbench-bar-footer\" />\n    </div>\n  );\n}\n```\n\n**Build resolution:**\n- **Core build**: `@app/*` → `core/*` → Gets stub (returns null)\n- **Desktop build**: `@app/*` → `desktop/*` → Gets real implementation (shadows core)\n\n**Benefits:**\n- No runtime checks or feature flags\n- Type-safe across all builds\n- Clean, readable code\n- Build-time optimization (dead code elimination)\n\n#### Multi-Tool Workflow Architecture\nFrontend designed for **stateful document processing**:\n- Users upload PDFs once, then chain tools (split → merge → compress → view)\n- File state and processing results persist across tool switches\n- No file reloading between tools - performance critical for large PDFs (up to 100GB+)\n\n#### FileContext - Central State Management\n**Location**: `frontend/editor/src/core/contexts/FileContext.tsx`\n- **Active files**: Currently loaded PDFs and their variants\n- **Tool navigation**: Current mode (viewer/pageEditor/fileEditor/toolName)\n- **Memory management**: PDF document cleanup, blob URL lifecycle, Web Worker management\n- **IndexedDB persistence**: File storage with thumbnail caching\n- **Preview system**: Tools can preview results (e.g., Split → Viewer → back to Split) without context pollution\n\n**Critical**: All file operations go through FileContext. Don't bypass with direct file handling.\n\n#### Processing Services\n- **enhancedPDFProcessingService**: Background PDF parsing and manipulation\n- **thumbnailGenerationService**: Web Worker-based with main-thread fallback\n- **fileStorage**: IndexedDB with LRU cache management\n\n#### Memory Management Strategy\n**Why manual cleanup exists**: Large PDFs (up to 100GB+) through multiple tools accumulate:\n- PDF.js documents that need explicit .destroy() calls\n- Blob URLs from tool outputs that need revocation\n- Web Workers that need termination\nWithout cleanup: browser crashes with memory leaks.\n\n#### Tool Development\n\n**Architecture**: Modular hook-based system with clear separation of concerns:\n\n- **useToolOperation** (`frontend/editor/src/core/hooks/tools/shared/useToolOperation.ts`): Main orchestrator hook\n  - Coordinates all tool operations with consistent interface\n  - Integrates with FileContext for operation tracking\n  - Handles validation, error handling, and UI state management\n\n- **Supporting Hooks**:\n  - **useToolState**: UI state management (loading, progress, error, files)\n  - **useToolApiCalls**: HTTP requests and file processing\n  - **useToolResources**: Blob URLs, thumbnails, ZIP downloads\n\n- **Utilities**:\n  - **toolErrorHandler**: Standardized error extraction and i18n support\n  - **toolResponseProcessor**: API response handling (single/zip/custom)\n  - **toolOperationTracker**: FileContext integration utilities\n\n**Three Tool Patterns**:\n\n**Pattern 1: Single-File Tools** (Individual processing)\n- Backend processes one file per API call\n- Set `multiFileEndpoint: false`\n- Examples: Compress, Rotate\n```typescript\nreturn useToolOperation({\n  operationType: 'compress',\n  endpoint: '/api/v1/misc/compress-pdf',\n  buildFormData: (params, file: File) => { /* single file */ },\n  multiFileEndpoint: false,\n});\n```\n\n**Pattern 2: Multi-File Tools** (Batch processing)\n- Backend accepts `MultipartFile[]` arrays in single API call\n- Set `multiFileEndpoint: true`\n- Examples: Split, Merge, Overlay\n```typescript\nreturn useToolOperation({\n  operationType: 'split',\n  endpoint: '/api/v1/general/split-pages',\n  buildFormData: (params, files: File[]) => { /* all files */ },\n  multiFileEndpoint: true,\n  filePrefix: 'split_',\n});\n```\n\n**Pattern 3: Complex Tools** (Custom processing)\n- Tools with complex routing logic or non-standard processing\n- Provide `customProcessor` for full control\n- Examples: Convert, OCR\n```typescript\nreturn useToolOperation({\n  operationType: 'convert',\n  customProcessor: async (params, files) => { /* custom logic */ },\n});\n```\n\n**Benefits**:\n- **No Timeouts**: Operations run until completion (supports 100GB+ files)\n- **Consistent**: All tools follow same pattern and interface\n- **Maintainable**: Single responsibility hooks, easy to test and modify\n- **i18n Ready**: Built-in internationalization support\n- **Type Safe**: Full TypeScript support with generic interfaces\n- **Memory Safe**: Automatic resource cleanup and blob URL management\n\n## Architecture Overview\n\n### Project Structure\n- **Backend**: Spring Boot application\n- **Frontend**: React-based SPA in `/frontend` directory\n  - **File Storage**: IndexedDB for client-side file persistence and thumbnails\n  - **Internationalization**: JSON-based translations (converted from backend .properties)\n- **PDF Processing**: PDFBox for core PDF operations, LibreOffice for conversions, PDF.js for client-side rendering\n- **Security**: Spring Security with optional authentication (controlled by `DOCKER_ENABLE_SECURITY`)\n- **Configuration**: YAML-based configuration with environment variable overrides\n\n### Controller Architecture\n- **API Controllers** (`src/main/java/.../controller/api/`): REST endpoints for PDF operations\n  - Organized by function: converters, security, misc, pipeline\n  - Follow pattern: `@RestController` + `@RequestMapping(\"/api/v1/...\")`\n\n### Key Components\n- **SPDFApplication.java**: Main application class with desktop UI and browser launching logic\n- **ConfigInitializer**: Handles runtime configuration and settings files\n- **Pipeline System**: Automated PDF processing workflows via `PipelineController`\n- **Security Layer**: Authentication, authorization, and user management (when enabled)\n\n### Frontend Directory Structure\nThe frontend is organized with a clear separation of concerns:\n\n- **`frontend/editor/src/core/`**: Main application code (shared, production-ready components)\n  - **`core/components/`**: React components organized by feature\n    - `core/components/tools/`: Individual PDF tool implementations\n    - `core/components/viewer/`: PDF viewer components\n    - `core/components/pageEditor/`: Page manipulation UI\n    - `core/components/tooltips/`: Help tooltips for tools\n    - `core/components/shared/`: Reusable UI components\n  - **`core/contexts/`**: React Context providers\n    - `FileContext.tsx`: Central file state management\n    - `file/`: File reducer and selectors\n    - `toolWorkflow/`: Tool workflow state\n  - **`core/hooks/`**: Custom React hooks\n    - `hooks/tools/`: Tool-specific operation hooks (one directory per tool)\n    - `hooks/tools/shared/`: Shared hook utilities (useToolOperation, etc.)\n  - **`core/constants/`**: Application constants and configuration\n  - **`core/data/`**: Static data (tool taxonomy, etc.)\n  - **`core/services/`**: Business logic services (PDF processing, storage, etc.)\n\n- **`frontend/editor/src/desktop/`**: Desktop-specific (Tauri) code\n- **`frontend/editor/src/proprietary/`**: Proprietary/licensed features\n- **`frontend/editor/src-tauri/`**: Tauri (Rust) native desktop application code\n- **`frontend/editor/public/`**: Static assets served directly\n  - `public/locales/`: Translation JSON files\n\n### Component Architecture\n- **Static Assets**: CSS, JS, and resources in `src/main/resources/static/` (legacy) + `frontend/editor/public/` (modern)\n- **Internationalization**:\n  - Backend: `messages_*.properties` files\n  - Frontend: JSON files in `frontend/editor/public/locales/` (converted from .properties)\n  - Conversion Script: `scripts/convert_properties_to_json.py`\n\n### Configuration Modes\n- **Ultra-lite**: Basic PDF operations only\n- **Standard**: Full feature set\n- **Fat**: Pre-downloaded dependencies for air-gapped environments\n- **Security Mode**: Adds authentication, user management, and enterprise features\n\n### Testing Strategy\n- **Integration Tests**: Cucumber tests in `testing/cucumber/`\n- **Docker Testing**: `test.sh` validates all Docker variants\n- **Manual Testing**: No unit tests currently - relies on UI and API testing\n\n## Development Workflow\n\n1. **Local Development** (using Taskfile):\n   - Backend + frontend: `task dev`\n   - All services (including AI engine): `task dev:all`\n   - Or individually: `task backend:dev` (localhost:8080), `task frontend:dev` (localhost:5173), `task engine:dev` (localhost:5001)\n2. **Quality Gate**: Run `task check` before submitting PRs\n3. **Docker Testing**: Use `./test.sh` for full Docker integration tests\n4. **Code Style**: Spotless enforces Google Java Format automatically (`task backend:format`)\n5. **Translations**:\n   - Backend: Use helper scripts in `/scripts` for multi-language updates\n   - Frontend: Update JSON files in `frontend/editor/public/locales/` or use conversion script\n6. **Documentation**: API docs auto-generated and available at `/swagger-ui/index.html`\n\n## Frontend Architecture Status\n\n- **Core Status**: React SPA architecture complete with multi-tool workflow support\n- **State Management**: FileContext handles all file operations and tool navigation\n- **File Processing**: Production-ready with memory management for large PDF workflows (up to 100GB+)\n- **Tool Integration**: Modular hook architecture with `useToolOperation` orchestrator\n  - Individual hooks: `useToolState`, `useToolApiCalls`, `useToolResources`\n  - Utilities: `toolErrorHandler`, `toolResponseProcessor`, `toolOperationTracker`\n  - Pattern: Each tool creates focused operation hook, UI consumes state/actions\n- **Preview System**: Tool results can be previewed without polluting file context (Split tool example)\n- **Performance**: Web Worker thumbnails, IndexedDB persistence, background processing\n\n## Translation Rules\n\n- **CRITICAL**: Always update translations in `en-US` only - all other languages (including `en-GB`) are handled separately\n- Translation files are located in `frontend/editor/public/locales/`\n- After changing any translation file, run `task pre-commit:fix`\n\n## Important Notes\n\n- **Java Version**: Requires JDK 25.\n- **Lombok**: Used extensively - ensure IDE plugin is installed\n- **File Persistence**:\n  - **Backend**: Designed to be stateless - files are processed in memory/temp locations only\n  - **Frontend**: Uses IndexedDB for client-side file storage and caching (with thumbnails)\n- **Security**: When `DOCKER_ENABLE_SECURITY=false`, security-related classes are excluded from compilation\n- **Import Paths**: ALWAYS use `@app/*` for imports - never use `@core/*` or `@proprietary/*` unless explicitly wrapping/extending a lower layer\n- **FileContext**: All file operations MUST go through FileContext - never bypass with direct File handling\n- **Memory Management**: Manual cleanup required for PDF.js documents and blob URLs - don't remove cleanup code\n- **Tool Development**: New tools should follow `useToolOperation` hook pattern (see `useCompressOperation.ts`)\n- **Performance Target**: Must handle PDFs up to 100GB+ without browser crashes\n- **Preview System**: Tools can preview results without polluting main file context (see Split tool implementation)\n- **Adding Tools**: See `ADDING_TOOLS.md` for complete guide to creating new PDF tools\n\n## Communication Style\n- Be direct and to the point\n- No apologies or conversational filler\n- Answer questions directly without preamble\n- Explain reasoning concisely when asked\n- Avoid unnecessary elaboration\n\n## Decision Making\n- Ask clarifying questions before making assumptions\n- Stop and ask when uncertain about project-specific details\n- Confirm approach before making structural changes\n- Request guidance on preferences (cross-platform vs specific tools, etc.)\n- Verify understanding of requirements before proceeding\n\n\n## Stack reality check (don't trust LLM training data) <!-- bleeding-edge-stack-note -->\n\nThis codebase is on bleeding-edge versions of its core JVM stack: **Spring Boot 4.0.6**,\n**Jackson 3 (`tools.jackson`)**, **JDK 21/25 source/target with JDK 25 toolchain**.\nAll three are *post*-2024 releases and your training corpus is overwhelmingly Spring Boot 2/3 and\nJackson 2 patterns — those patterns will compile, run differently, or hallucinate APIs that no\nlonger exist.\n\nBefore writing or editing Spring / Jackson / JDK code:\n\n1. Open an existing module in `app/core/` or `app/common/` and grep for the actual imports being\n   used — `import tools.jackson...` not `import com.fasterxml.jackson...`, and the new\n   `org.springframework.boot` 4.x package layout.\n2. If you're not sure whether an API exists in this stack version, **check the source on disk\n   first** (the dependency JARs are downloaded under `~/.gradle/caches/modules-2/`).\n3. Do not silently downgrade a Spring Boot 4 pattern to a Spring Boot 3 equivalent. If something\n   doesn't work, surface it to the human — don't guess.\n\nSame goes for Jackson 3's API surface (renamed `ObjectMapper` builder methods, new\n`tools.jackson.databind` namespace) and JDK 25 preview features. Ground your code in this repo's\nactual imports, not what worked three years ago.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# AGENTS.md\n\nThis file provides guidance to AI Agents when working with code in this repository.\n\n## Taskfile (Recommended)\n\nThis project uses [Task](https://taskfile.dev/) as a unified command runner. All build, dev, test, lint, and docker commands can be run from the repo root via `task <command>`. Run `task --list` to see all available commands.\n\nTask `desc:` fields should describe **what** the task does, not **how** it does it. Keep them generic and stable: don't reference implementation details like aliases, internal helpers, mode flags, or which other task delegates to which. The description is for users picking a command from `task --list`, not a changelog of refactors.\n\n### Quick Reference\n- `task install` — install all dependencies\n- `task dev` — start backend + frontend concurrently\n- `task dev:all` — start backend + frontend + engine concurrently\n- `task build` — build all components\n- `task test` — run all tests (backend + frontend + engine)\n- `task lint` — run all linters\n- `task format` — auto-fix formatting across all components\n- `task check` — full quality gate (lint + typecheck + test)\n- `task clean` — clean all build artifacts\n- `task docker:build` — build standard Docker image\n- `task docker:up` — start Docker compose stack\n\n## Common Development Commands\n\n### Build and Test\n- **Build project**: `task build`\n- **Run backend locally**: `task backend:dev`\n- **Run all tests**: `task test` (or individually: `task backend:test`, `task frontend:test`, `task engine:test`)\n- **Docker integration tests**: `./test.sh` (builds all Docker variants and runs comprehensive tests)\n- **Code formatting**: `task format` (or `task backend:format` for Java only)\n- **Full quality gate**: `task check` (runs lint + typecheck + test across all components)\n\nAfter modifying any files in the project, you must run the relevant `task check` command that covers that area of the code. For example, when editing frontend files run `task frontend:check`; for Python engine files run `task engine:check`; for Java backend files run `task backend:check`.\n\n### Docker Development\n- **Build standard**: `task docker:build` (or `docker build -t stirling-pdf -f docker/embedded/Dockerfile .`)\n- **Build fat version**: `task docker:build:fat`\n- **Build ultra-lite**: `task docker:build:ultra-lite`\n- **Start compose stack**: `task docker:up` (or `task docker:up:fat`, `task docker:up:ultra-lite`)\n- **Stop compose stack**: `task docker:down`\n- **View logs**: `task docker:logs`\n- **Example compose files**: Located in `exampleYmlFiles/` directory\n\n### Security Mode Development\nSet `DOCKER_ENABLE_SECURITY=true` environment variable to enable security features during development. This is required for testing the full version locally.\n\n### Python Development (AI Engine)\n\nThe engine is a Python reasoning service for Stirling: it plans and interprets work, but it does not own durable state, and it does not execute Stirling PDF operations directly. Keep the service narrow: typed contracts in, typed contracts out, with AI only where it adds reasoning value. The frontend calls the Python engine via Java as a proxy.\n\n#### Python Commands\nAll engine commands run from the repo root using Task:\n- `task engine:check` — run all checks (typecheck + lint + format-check + test)\n- `task engine:fix` — auto-fix lint + formatting\n- `task engine:install` — install Python dependencies via uv\n- `task engine:dev` — start FastAPI with hot reload (localhost:5001)\n- `task engine:test` — run pytest\n- `task engine:lint` — run ruff linting\n- `task engine:typecheck` — run pyright\n- `task engine:format` — format code with ruff\n- `task engine:tool-models` — generate `tool_models.py` from the Java OpenAPI spec\n\nThe project structure is defined in `engine/pyproject.toml`. Any new dependencies should be listed there, followed by running `task engine:install`.\n\n#### Python Code Style\n- Keep `task engine:check` passing.\n- Use modern Python when it improves clarity.\n- Prefer explicit names to cleverness.\n- Avoid nested functions and nested classes unless the language construct requires them.\n- Prefer composition to inheritance when combining concepts.\n- Avoid speculative abstractions. Add a layer only when it removes real duplication or clarifies lifecycle.\n- Add comments sparingly and only when they explain non-obvious intent.\n\n#### Python Typing and Models\n- Deserialize into Pydantic models as early as possible.\n- Serialize from Pydantic models as late as possible.\n- Do not pass raw `dict[str, Any]` or `dict[str, object]` across important boundaries when a typed model can exist instead.\n- Avoid `Any` wherever possible.\n- Avoid `cast()` wherever possible (reconsider the structure first).\n- All shared models should subclass `stirling.models.ApiModel` so the service behaves consistently.\n- Do not use string literals for any type annotations, including `cast()`.\n\n#### Python Configuration\n- Keep application-owned configuration in `stirling.config`.\n- Only add `STIRLING_*` environment variables that the engine itself truly owns.\n- Do not mirror third-party provider environment variables unless the engine is actually interpreting them.\n- Let `pydantic-ai` own provider authentication configuration when possible.\n\n#### Python Architecture\n\n**Package roles:**\n- `stirling.contracts`: request/response models and shared typed workflow contracts. If a shape crosses a module or service boundary, it probably belongs here.\n- `stirling.models`: shared model primitives and generated tool models.\n- `stirling.agents`: reasoning modules for individual capabilities.\n- `stirling.api`: HTTP layer, dependency access, and app startup wiring.\n- `stirling.services`: shared runtime and non-AI infrastructure.\n- `stirling.config`: application-owned settings.\n\n**Source of truth:**\n- `stirling.models.tool_models` is the source of truth for operation IDs and parameter models.\n- Do not duplicate operation lists if they can be derived from `tool_models.OPERATIONS`.\n- Do not hand-maintain parallel parameter schemas when the generated tool models already define them.\n- If a tool ID must match a parameter model, validate that relationship explicitly in code.\n\n**Boundaries:**\n- Keep the API layer thin. Route modules should bind requests, resolve dependencies, and call agents or services. They should not contain business logic.\n- Keep agents focused on one reasoning domain. They should not own FastAPI routing, persistence, or execution of Stirling operations.\n- Build long-lived runtime objects centrally at startup when possible rather than reconstructing heavy AI objects per request.\n- If an agent delegates to another agent, the delegated agent should remain the source of truth for its own domain output.\n\n#### Python AI Usage\n- The system must work with any AI, including self-hosted models. We require that the models support structured outputs, but should minimise model-specific code beyond that.\n- Use AI for reasoning-heavy outputs, not deterministic glue.\n- Do not ask the model to invent data that Python can derive safely.\n- Do not fabricate fallback user-facing copy in code to hide incomplete model output.\n- AI output schemas should be impossible to instantiate incorrectly.\n  - Do not require the model to keep separate structures in sync. For example, instead of generating two lists which must be the same length, generate one list of a model containing the same data.\n  - Prefer Python to derive deterministic follow-up structure from a valid AI result.\n- Use `NativeOutput(...)` for structured model outputs.\n- Use `ToolOutput(...)` when the model should select and call delegate functions.\n\n#### Python Testing\n- Test contracts directly.\n- Test agents directly where behaviour matters.\n- Test API routes as thin integration points.\n- Prefer dependency overrides or startup-state seams to monkeypatching random globals.\n\n### Frontend Development\n- **Frontend dev server**: `task frontend:dev` — requires backend on localhost:8080\n- **Tech Stack**: Vite + React + TypeScript + Mantine UI + TailwindCSS\n- **Proxy Configuration**: Vite proxies `/api/*` calls to backend (localhost:8080)\n- **Build Process**: DO NOT run build scripts manually - builds are handled by CI/CD pipelines\n- **Package Installation**: `task frontend:install`\n- **Deployment Options**:\n  - **Desktop App**: `task desktop:build`\n  - **Web Server**: `task frontend:build` then serve dist/ folder\n  - **Development**: `task desktop:dev` for desktop dev mode\n\n#### Environment Variables\n- All `VITE_*` variables must be declared in the appropriate committed env file:\n  - `frontend/editor/.env` — core and shared vars (base, loaded in every mode)\n  - `frontend/editor/.env.proprietary` — proprietary-only vars, e.g. the admin portal's SaaS/account-link keys (layered on top of `.env` in proprietary mode)\n  - `frontend/editor/.env.saas` — SaaS-only vars (layered on top of `.env` in SaaS mode)\n  - `frontend/editor/.env.desktop` — desktop (Tauri)-only vars (layered on top of `.env` in desktop mode)\n- These files are committed to Git and must not contain private keys\n- Local overrides (API keys, machine-specific settings) go in uncommitted sibling `.env.local` / `.env.saas.local` / `.env.desktop.local` files — Vite automatically layers them on top\n- Never use `|| 'hardcoded-fallback'` inline — put defaults in the committed env files\n- `task frontend:prepare` creates empty `.local` override files on first run; pass `MODE=saas` or `MODE=desktop` to also create the mode-specific `.local` file\n- Prepare runs automatically as a dependency of all `dev*`, `build*`, and `desktop*` tasks\n- See `frontend/README.md#environment-variables` for full documentation\n\n#### Import Paths - CRITICAL\n**ALWAYS use `@app/*` for imports.** Do not use `@core/*` or `@proprietary/*` unless explicitly wrapping/extending a lower layer implementation.\n\nFor a broader explanation of the frontend layering and override architecture, read @frontend/editor/DeveloperGuide.md\n\nBefore touching colours or theming (tokens, dark mode, accent colours), read @frontend/editor/src/core/theme/README.md — it explains the palette/`--c-*` token system and the rule that literal colours live only in `primitives.css`.\n\n```typescript\n// ✅ CORRECT - Use @app/* for all imports\nimport { AppLayout } from \"@app/components/AppLayout\";\nimport { useFileContext } from \"@app/contexts/FileContext\";\nimport { FileContext } from \"@app/contexts/FileContext\";\n\n// ❌ WRONG - Do not use @core/* or @proprietary/* in normal code\nimport { AppLayout } from \"@core/components/AppLayout\";\nimport { useFileContext } from \"@proprietary/contexts/FileContext\";\n```\n\n**Only use explicit aliases when:**\n- Building layer-specific override that wraps a lower layer's component\n- Example: `import { AppProviders as CoreAppProviders } from \"@core/components/AppProviders\"` when creating proprietary/AppProviders.tsx that extends the core version\n\nThe `@app/*` alias automatically resolves to the correct layer based on build target (core/proprietary/saas/desktop/cloud) and handles the fallback cascade — see \"Frontend `cloud/` Layer\" below for the full per-flavor order.\n\n#### Frontend `cloud/` Layer\n\n`@app/*` resolves through a per-flavor cascade — first existing file wins (shadow/override):\n\n- **core** → core\n- **proprietary** → proprietary → core\n- **saas** → saas → cloud → proprietary → core\n- **desktop** → desktop → cloud → proprietary → core\n- **cloud** → cloud → proprietary → core\n\nWhat goes where:\n\n- **core** — OSS base.\n- **proprietary** — licensed / offline features.\n- **cloud** — the SHARED hosted/SaaS experience used by BOTH saas + desktop: PAYG, wallet, plan, billing, usage meters, cloud config/team/onboarding.\n- **saas** — web-only: Supabase web auth, AuthCallback, avatar canvas, `window.location`.\n- **desktop** — Tauri-only: keyring authService, tauriHttpClient, native files/windows, backend routing.\n\n`cloud/` MUST NOT import `@supabase/*`, `@tauri-apps/*`, raw `fetch`, `window.location`, `localStorage`, `sessionStorage`, or `import.meta.env.VITE_*` (all enforced by the linter). It reaches platform-specific things only via `@app/*` seams: `services/apiClient`, `auth/session.getAccessToken`, `auth/supabase`, `platform/openExternal`, `services/billing`, `hooks/useSaaSMode` — each provided per-platform in `saas/` and `desktop/`.\n\nRule of thumb — **move, don't copy**: share via `cloud/`, override by shadowing the same `@app/*` path in a leaf (`saas/` or `desktop/`).\n\n**Cloud feature flags on desktop.** The local `AppConfigContext` reads `/api/v1/config/app-config` from the LOCAL bundled backend, so cloud-only flags (`aiEngineEnabled`, `premiumEnabled`, …) are never seen on desktop. To read the cloud's view, use `useSaasAppConfig()` (`desktop/hooks/useSaasAppConfig.ts`, backed by the general `saasAppConfigService` — SaaS-mode-only, public endpoint, native HTTP, 5-min cache). It returns `null` outside SaaS mode, so cloud features stay off in local/self-hosted and the server keeps the on/off switch (no desktop release needed to flip a flag). Gate a feature behind a per-platform seam — e.g. `useAiEngineEnabled()` (core reads `useAppConfig()`, desktop reads `useSaasAppConfig()`) — rather than hardcoding the flag on.\n\n#### Component Override Pattern (Stub/Shadow)\nUse this pattern for desktop-specific or proprietary-specific features WITHOUT runtime checks or conditionals.\n\n**How it works:**\n1. Core defines stub component (returns null or no-op)\n2. Desktop/proprietary overrides with same path/name\n3. Core imports via `@app/*` - higher layer \"shadows\" core in those builds\n4. No `@ts-ignore`, no `isTauri()` checks, no runtime conditionals!\n\n**Example - Desktop-specific footer:**\n\n```typescript\n// core/components/workbenchBar/WorkbenchBarFooterExtensions.tsx (stub)\ninterface WorkbenchBarFooterExtensionsProps {\n  className?: string;\n}\n\nexport function WorkbenchBarFooterExtensions(_props: WorkbenchBarFooterExtensionsProps) {\n  return null; // Stub - does nothing in web builds\n}\n```\n\n```tsx\n// desktop/components/workbenchBar/WorkbenchBarFooterExtensions.tsx (real implementation)\nimport { Box } from '@mantine/core';\nimport { BackendHealthIndicator } from '@app/components/BackendHealthIndicator';\n\ninterface WorkbenchBarFooterExtensionsProps {\n  className?: string;\n}\n\nexport function WorkbenchBarFooterExtensions({ className }: WorkbenchBarFooterExtensionsProps) {\n  return (\n    <Box className={className}>\n      <BackendHealthIndicator />\n    </Box>\n  );\n}\n```\n\n```tsx\n// core/components/shared/WorkbenchBar.tsx (usage - works in ALL builds)\nimport { WorkbenchBarFooterExtensions } from '@app/components/workbenchBar/WorkbenchBarFooterExtensions';\n\nexport function WorkbenchBar() {\n  return (\n    <div>\n      {/* In web builds: renders nothing (stub returns null) */}\n      {/* In desktop builds: renders BackendHealthIndicator */}\n      <WorkbenchBarFooterExtensions className=\"workbench-bar-footer\" />\n    </div>\n  );\n}\n```\n\n**Build resolution:**\n- **Core build**: `@app/*` → `core/*` → Gets stub (returns null)\n- **Desktop build**: `@app/*` → `desktop/*` → Gets real implementation (shadows core)\n\n**Benefits:**\n- No runtime checks or feature flags\n- Type-safe across all builds\n- Clean, readable code\n- Build-time optimization (dead code elimination)\n\n#### Multi-Tool Workflow Architecture\nFrontend designed for **stateful document processing**:\n- Users upload PDFs once, then chain tools (split → merge → compress → view)\n- File state and processing results persist across tool switches\n- No file reloading between tools - performance critical for large PDFs (up to 100GB+)\n\n#### FileContext - Central State Management\n**Location**: `frontend/editor/src/core/contexts/FileContext.tsx`\n- **Active files**: Currently loaded PDFs and their variants\n- **Tool navigation**: Current mode (viewer/pageEditor/fileEditor/toolName)\n- **Memory management**: PDF document cleanup, blob URL lifecycle, Web Worker management\n- **IndexedDB persistence**: File storage with thumbnail caching\n- **Preview system**: Tools can preview results (e.g., Split → Viewer → back to Split) without context pollution\n\n**Critical**: All file operations go through FileContext. Don't bypass with direct file handling.\n\n#### Processing Services\n- **enhancedPDFProcessingService**: Background PDF parsing and manipulation\n- **thumbnailGenerationService**: Web Worker-based with main-thread fallback\n- **fileStorage**: IndexedDB with LRU cache management\n\n#### Memory Management Strategy\n**Why manual cleanup exists**: Large PDFs (up to 100GB+) through multiple tools accumulate:\n- PDF.js documents that need explicit .destroy() calls\n- Blob URLs from tool outputs that need revocation\n- Web Workers that need termination\nWithout cleanup: browser crashes with memory leaks.\n\n#### Tool Development\n\n**Architecture**: Modular hook-based system with clear separation of concerns:\n\n- **useToolOperation** (`frontend/editor/src/core/hooks/tools/shared/useToolOperation.ts`): Main orchestrator hook\n  - Coordinates all tool operations with consistent interface\n  - Integrates with FileContext for operation tracking\n  - Handles validation, error handling, and UI state management\n\n- **Supporting Hooks**:\n  - **useToolState**: UI state management (loading, progress, error, files)\n  - **useToolApiCalls**: HTTP requests and file processing\n  - **useToolResources**: Blob URLs, thumbnails, ZIP downloads\n\n- **Utilities**:\n  - **toolErrorHandler**: Standardized error extraction and i18n support\n  - **toolResponseProcessor**: API response handling (single/zip/custom)\n  - **toolOperationTracker**: FileContext integration utilities\n\n**Three Tool Patterns**:\n\n**Pattern 1: Single-File Tools** (Individual processing)\n- Backend processes one file per API call\n- Set `multiFileEndpoint: false`\n- Examples: Compress, Rotate\n```typescript\nreturn useToolOperation({\n  operationType: 'compress',\n  endpoint: '/api/v1/misc/compress-pdf',\n  buildFormData: (params, file: File) => { /* single file */ },\n  multiFileEndpoint: false,\n});\n```\n\n**Pattern 2: Multi-File Tools** (Batch processing)\n- Backend accepts `MultipartFile[]` arrays in single API call\n- Set `multiFileEndpoint: true`\n- Examples: Split, Merge, Overlay\n```typescript\nreturn useToolOperation({\n  operationType: 'split',\n  endpoint: '/api/v1/general/split-pages',\n  buildFormData: (params, files: File[]) => { /* all files */ },\n  multiFileEndpoint: true,\n  filePrefix: 'split_',\n});\n```\n\n**Pattern 3: Complex Tools** (Custom processing)\n- Tools with complex routing logic or non-standard processing\n- Provide `customProcessor` for full control\n- Examples: Convert, OCR\n```typescript\nreturn useToolOperation({\n  operationType: 'convert',\n  customProcessor: async (params, files) => { /* custom logic */ },\n});\n```\n\n**Benefits**:\n- **No Timeouts**: Operations run until completion (supports 100GB+ files)\n- **Consistent**: All tools follow same pattern and interface\n- **Maintainable**: Single responsibility hooks, easy to test and modify\n- **i18n Ready**: Built-in internationalization support\n- **Type Safe**: Full TypeScript support with generic interfaces\n- **Memory Safe**: Automatic resource cleanup and blob URL management\n\n## Architecture Overview\n\n### Project Structure\n- **Backend**: Spring Boot application\n- **Frontend**: React-based SPA in `/frontend` directory\n  - **File Storage**: IndexedDB for client-side file persistence and thumbnails\n  - **Internationalization**: JSON-based translations (converted from backend .properties)\n- **PDF Processing**: PDFBox for core PDF operations, LibreOffice for conversions, PDF.js for client-side rendering\n- **Security**: Spring Security with optional authentication (controlled by `DOCKER_ENABLE_SECURITY`)\n- **Configuration**: YAML-based configuration with environment variable overrides\n\n### Controller Architecture\n- **API Controllers** (`src/main/java/.../controller/api/`): REST endpoints for PDF operations\n  - Organized by function: converters, security, misc, pipeline\n  - Follow pattern: `@RestController` + `@RequestMapping(\"/api/v1/...\")`\n\n### Key Components\n- **SPDFApplication.java**: Main application class with desktop UI and browser launching logic\n- **ConfigInitializer**: Handles runtime configuration and settings files\n- **Pipeline System**: Automated PDF processing workflows via `PipelineController`\n- **Security Layer**: Authentication, authorization, and user management (when enabled)\n\n### Frontend Directory Structure\nThe frontend is organized with a clear separation of concerns:\n\n- **`frontend/editor/src/core/`**: Main application code (shared, production-ready components)\n  - **`core/components/`**: React components organized by feature\n    - `core/components/tools/`: Individual PDF tool implementations\n    - `core/components/viewer/`: PDF viewer components\n    - `core/components/pageEditor/`: Page manipulation UI\n    - `core/components/tooltips/`: Help tooltips for tools\n    - `core/components/shared/`: Reusable UI components\n  - **`core/contexts/`**: React Context providers\n    - `FileContext.tsx`: Central file state management\n    - `file/`: File reducer and selectors\n    - `toolWorkflow/`: Tool workflow state\n  - **`core/hooks/`**: Custom React hooks\n    - `hooks/tools/`: Tool-specific operation hooks (one directory per tool)\n    - `hooks/tools/shared/`: Shared hook utilities (useToolOperation, etc.)\n  - **`core/constants/`**: Application constants and configuration\n  - **`core/data/`**: Static data (tool taxonomy, etc.)\n  - **`core/services/`**: Business logic services (PDF processing, storage, etc.)\n\n- **`frontend/editor/src/desktop/`**: Desktop-specific (Tauri) code\n- **`frontend/editor/src/proprietary/`**: Proprietary/licensed features\n- **`frontend/editor/src-tauri/`**: Tauri (Rust) native desktop application code\n- **`frontend/editor/public/`**: Static assets served directly\n  - `public/locales/`: Translation JSON files\n\n### Component Architecture\n- **Static Assets**: CSS, JS, and resources in `src/main/resources/static/` (legacy) + `frontend/editor/public/` (modern)\n- **Internationalization**:\n  - Backend: `messages_*.properties` files\n  - Frontend: JSON files in `frontend/editor/public/locales/` (converted from .properties)\n  - Conversion Script: `scripts/convert_properties_to_json.py`\n\n### Configuration Modes\n- **Ultra-lite**: Basic PDF operations only\n- **Standard**: Full feature set\n- **Fat**: Pre-downloaded dependencies for air-gapped environments\n- **Security Mode**: Adds authentication, user management, and enterprise features\n\n### Testing Strategy\n- **Integration Tests**: Cucumber tests in `testing/cucumber/`\n- **Docker Testing**: `test.sh` validates all Docker variants\n- **Manual Testing**: No unit tests currently - relies on UI and API testing\n\n## Development Workflow\n\n1. **Local Development** (using Taskfile):\n   - Backend + frontend: `task dev`\n   - All services (including AI engine): `task dev:all`\n   - Or individually: `task backend:dev` (localhost:8080), `task frontend:dev` (localhost:5173), `task engine:dev` (localhost:5001)\n2. **Quality Gate**: Run `task check` before submitting PRs\n3. **Docker Testing**: Use `./test.sh` for full Docker integration tests\n4. **Code Style**: Spotless enforces Google Java Format automatically (`task backend:format`)\n5. **Translations**:\n   - Backend: Use helper scripts in `/scripts` for multi-language updates\n   - Frontend: Update JSON files in `frontend/editor/public/locales/` or use conversion script\n6. **Documentation**: API docs auto-generated and available at `/swagger-ui/index.html`\n\n## Frontend Architecture Status\n\n- **Core Status**: React SPA architecture complete with multi-tool workflow support\n- **State Management**: FileContext handles all file operations and tool navigation\n- **File Processing**: Production-ready with memory management for large PDF workflows (up to 100GB+)\n- **Tool Integration**: Modular hook architecture with `useToolOperation` orchestrator\n  - Individual hooks: `useToolState`, `useToolApiCalls`, `useToolResources`\n  - Utilities: `toolErrorHandler`, `toolResponseProcessor`, `toolOperationTracker`\n  - Pattern: Each tool creates focused operation hook, UI consumes state/actions\n- **Preview System**: Tool results can be previewed without polluting file context (Split tool example)\n- **Performance**: Web Worker thumbnails, IndexedDB persistence, background processing\n\n## Translation Rules\n\n- **CRITICAL**: Always update translations in `en-US` only - all other languages (including `en-GB`) are handled separately\n- Translation files are located in `frontend/editor/public/locales/`\n- After changing any translation file, run `task pre-commit:fix`\n\n## Important Notes\n\n- **Java Version**: Requires JDK 25.\n- **Lombok**: Used extensively - ensure IDE plugin is installed\n- **File Persistence**:\n  - **Backend**: Designed to be stateless - files are processed in memory/temp locations only\n  - **Frontend**: Uses IndexedDB for client-side file storage and caching (with thumbnails)\n- **Security**: When `DOCKER_ENABLE_SECURITY=false`, security-related classes are excluded from compilation\n- **Import Paths**: ALWAYS use `@app/*` for imports - never use `@core/*` or `@proprietary/*` unless explicitly wrapping/extending a lower layer\n- **FileContext**: All file operations MUST go through FileContext - never bypass with direct File handling\n- **Memory Management**: Manual cleanup required for PDF.js documents and blob URLs - don't remove cleanup code\n- **Tool Development**: New tools should follow `useToolOperation` hook pattern (see `useCompressOperation.ts`)\n- **Performance Target**: Must handle PDFs up to 100GB+ without browser crashes\n- **Preview System**: Tools can preview results without polluting main file context (see Split tool implementation)\n- **Adding Tools**: See `ADDING_TOOLS.md` for complete guide to creating new PDF tools\n\n## Communication Style\n- Be direct and to the point\n- No apologies or conversational filler\n- Answer questions directly without preamble\n- Explain reasoning concisely when asked\n- Avoid unnecessary elaboration\n\n## Decision Making\n- Ask clarifying questions before making assumptions\n- Stop and ask when uncertain about project-specific details\n- Confirm approach before making structural changes\n- Request guidance on preferences (cross-platform vs specific tools, etc.)\n- Verify understanding of requirements before proceeding\n\n\n## Stack reality check (don't trust LLM training data) <!-- bleeding-edge-stack-note -->\n\nThis codebase is on bleeding-edge versions of its core JVM stack: **Spring Boot 4.0.6**,\n**Jackson 3 (`tools.jackson`)**, **JDK 21/25 source/target with JDK 25 toolchain**.\nAll three are *post*-2024 releases and your training corpus is overwhelmingly Spring Boot 2/3 and\nJackson 2 patterns — those patterns will compile, run differently, or hallucinate APIs that no\nlonger exist.\n\nBefore writing or editing Spring / Jackson / JDK code:\n\n1. Open an existing module in `app/core/` or `app/common/` and grep for the actual imports being\n   used — `import tools.jackson...` not `import com.fasterxml.jackson...`, and the new\n   `org.springframework.boot` 4.x package layout.\n2. If you're not sure whether an API exists in this stack version, **check the source on disk\n   first** (the dependency JARs are downloaded under `~/.gradle/caches/modules-2/`).\n3. Do not silently downgrade a Spring Boot 4 pattern to a Spring Boot 3 equivalent. If something\n   doesn't work, surface it to the human — don't guess.\n\nSame goes for Jackson 3's API surface (renamed `ObjectMapper` builder methods, new\n`tools.jackson.databind` namespace) and JDK 25 preview features. Ground your code in this repo's\nactual imports, not what worked three years ago.\n","category":"root","tokens":6918}]}