diff --git a/README.md b/README.md index 4ab3b3697b6..21fc5de6f32 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,21 @@ Through [contributions, donations, and sponsorship](https://github.com/sponsors/ Contributions welcome! Check out our [Contribution Guide](docs/Contribute.md) and [documentation](https://github.com/LycheeOrg/Lychee/tree/master/docs) for setup, coding standards, and PR guidelines. +**AI-assisted contributions** are permitted — see our [AI/Claude Guidelines](docs/Contribute.md#using-aiclaude-for-contributions) and [AGENTS.md](AGENTS.md) for the Specification-Driven Development workflow. + ## Installation +### Quick Try (Docker) + +Want to quickly test Lychee? Use the minimal docker-compose template: + +```bash +curl -O https://raw.githubusercontent.com/LycheeOrg/Lychee/master/docker-compose.minimal.yaml +docker compose -f docker-compose.minimal.yaml up -d +``` + +Then open http://localhost:8000 in your browser. This setup includes a separate worker container for background jobs. + ### Docker (Recommended) The easiest way to deploy Lychee with all dependencies configured: diff --git a/docs/Contribute.md b/docs/Contribute.md index c6665a36960..088717bb12e 100644 --- a/docs/Contribute.md +++ b/docs/Contribute.md @@ -177,6 +177,68 @@ git push origin feature/your-feature-name - References to any related issues - Screenshots (if applicable for UI changes) +## Using AI/Claude for Contributions + +AI-assisted development is permitted and welcomed. However, contributions using AI tools must follow our **Specification-Driven Development (SDD)** workflow: + +### Guidelines + +1. **Read [AGENTS.md](../AGENTS.md) first** — This file contains the instructions that guide AI agents working on this codebase. It defines the workflow, guardrails, and expectations for AI-assisted development. + +2. **Follow Spec-Driven Development** — AI-generated code must be anchored in explicit specifications: + - Start by creating or updating the feature specification at `docs/specs/4-architecture/features/-/spec.md` + - Generate a feature plan (`plan.md`) and tasks checklist (`tasks.md`) + - Write tests before implementation (test-first cadence) + - Use the templates in `docs/specs/templates/` for consistency + +3. **Understand before generating** — AI tools should explore and understand the existing codebase before proposing changes. Use the documentation structure in `docs/specs/` to build context. + +4. **Quality gates still apply** — All AI-generated code must pass the same quality checks as human-written code: + - PHPStan static analysis + - Full test suite + - Code formatting (php-cs-fixer, Prettier) + - TypeScript type checking + +5. **Review and understand all output** — Contributors are responsible for understanding and validating any AI-generated code before submitting. Do not submit code you don't understand. + +6. **Document open questions** — When AI encounters ambiguity, log questions in `docs/specs/4-architecture/open-questions.md` and wait for clarification before proceeding. + +### Recommended AI Models + +We recommend using **Claude Sonnet or Claude Opus** for AI-assisted contributions. Avoid free-tier models from GitHub Copilot as they tend to hallucinate heavily and struggle to follow structured task files. + +When using **Claude Code**, reference `@AGENTS.md` as the first step in your conversation to guide the agent through the SDD workflow. + +### Two-PR Workflow (Recommended) + +For SDD contributions, we recommend splitting your work into **two pull requests**: + +1. **Specification PR** — Submit the spec, plan, and tasks files first. This allows maintainers to review the proposed approach before implementation begins. +2. **Implementation PR** — Once the specification is approved, submit the implementation that builds from the approved spec. + +This workflow ensures that: +- The specification can be reviewed and refined before any code is written +- Time and resources are not wasted on implementations that don't align with project goals +- The spec serves as a clear contract for what the implementation should deliver + +### Small Fixes Exception + +The full SDD workflow is not required for trivial changes such as: +- Typo fixes +- Single-line bug fixes with obvious solutions +- Minor documentation corrections +- Simple configuration changes + +For these cases, a direct PR without specifications is acceptable. Use your judgment — if the change requires design decisions or affects multiple files, use SDD. + +### Why SDD with AI? + +Specification-Driven Development ensures that AI-assisted contributions: +- Are traceable back to explicit requirements +- Follow established architectural patterns +- Maintain test coverage organically +- Don't introduce undocumented behavior or "magic" code + ## Pull Request Guidelines - Keep your changes focused and atomic @@ -208,4 +270,4 @@ Thank you for contributing to Lychee! 🌸 --- -*Last updated: December 22, 2025* \ No newline at end of file +*Last updated: January 21, 2026* \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index cd11169cc05..ed888bb2197 100644 --- a/docs/README.md +++ b/docs/README.md @@ -66,6 +66,8 @@ Documentation is organized following the [Diátaxis framework](https://diataxis. ### Contributing - [Contribution Guide](Contribute.md) - How to contribute to Lychee - [Coding Conventions](specs/3-reference/coding-conventions.md) - PHP and Vue3 coding standards +- [AI/Claude Guidelines](Contribute.md#using-aiclaude-for-contributions) - Guidelines for AI-assisted development +- [AGENTS.md](../AGENTS.md) - Instructions for AI agents working on this codebase ## Additional Resources @@ -77,4 +79,4 @@ For more information about Lychee: --- -*Last updated: December 22, 2025* \ No newline at end of file +*Last updated: January 21, 2026* \ No newline at end of file diff --git a/docs/specs/1-concepts/README.md b/docs/specs/1-concepts/README.md index c35f637309a..3a10d83aba3 100644 --- a/docs/specs/1-concepts/README.md +++ b/docs/specs/1-concepts/README.md @@ -72,7 +72,8 @@ Album ├─ has children → Albums (self-referencing, nested tree) ├─ owned by → User (many-to-one) ├─ has → AccessPermissions (one-to-many) - ├─ has → Statistics (one-to-one) + ├─ has → AlbumSizeStatistics (one-to-one) - size/count aggregates + ├─ has → Statistics (one-to-one) - visit/download tracking └─ can be → Purchasable (one-to-one) Photo @@ -132,4 +133,4 @@ Now that you understand Lychee's core concepts: --- -*Last updated: December 22, 2025* +*Last updated: January 21, 2026* diff --git a/docs/specs/2-how-to/configure-pagination.md b/docs/specs/2-how-to/configure-pagination.md index 5e73c23dfaa..555e5975071 100644 --- a/docs/specs/2-how-to/configure-pagination.md +++ b/docs/specs/2-how-to/configure-pagination.md @@ -1,7 +1,7 @@ # How-To: Configure Album and Photo Pagination **Author:** Lychee Team -**Last Updated:** 2026-01-10 +**Last Updated:** 2026-01-21 **Feature:** 007-pagination **Related:** [Feature 007 Spec](../4-architecture/features/007-pagination/spec.md) @@ -20,7 +20,7 @@ Pagination improves performance and user experience for large galleries: ## Configuration Settings -Pagination is configured through four settings in the admin panel under **Settings > Gallery**. +Pagination is configured through six settings in the admin panel under **Settings > Gallery**. ### Page Size Settings @@ -98,6 +98,32 @@ Controls how users load additional album pages. Same options as photos. | Default | infinite_scroll | | Location | Settings > Gallery | +### Infinite Scroll Threshold Settings + +These settings control when the next page loads during infinite scroll mode. + +#### photos_infinite_scroll_threshold + +Controls how early to trigger loading the next page of photos. + +| Property | Value | +|----------|-------| +| Type | Integer | +| Default | 2 | +| Location | Settings > Gallery | + +The value represents the number of viewport heights from the bottom of the page at which to trigger loading. A higher value means earlier loading (preloading more content), but may load unnecessary data if the user doesn't scroll that far. + +#### albums_infinite_scroll_threshold + +Controls how early to trigger loading the next page of albums. Same behavior as photos threshold. + +| Property | Value | +|----------|-------| +| Type | Integer | +| Default | 2 | +| Location | Settings > Gallery | + ## Configuring via Admin Panel 1. Log in as an administrator diff --git a/docs/specs/2-how-to/sql-timeout-logging.md b/docs/specs/2-how-to/sql-timeout-logging.md index 4cceec091f0..a4216820133 100644 --- a/docs/specs/2-how-to/sql-timeout-logging.md +++ b/docs/specs/2-how-to/sql-timeout-logging.md @@ -41,7 +41,7 @@ The max execution time is configured in `config/octane.php`: ### 1. Query Execution Logging -**Location**: [app/Providers/AppServiceProvider.php:238-300](app/Providers/AppServiceProvider.php#L238-L300) +**Location**: [app/Providers/AppServiceProvider.php:261-350](app/Providers/AppServiceProvider.php#L261-L350) The `logSQL()` method logs queries after they complete with severity based on execution time: - **Debug**: Normal slow queries (>100ms) @@ -52,7 +52,7 @@ The `logSQL()` method logs queries after they complete with severity based on ex **Location**: [app/Listeners/LogQueryTimeout.php](app/Listeners/LogQueryTimeout.php) -Registered in [app/Providers/EventServiceProvider.php:97-99](app/Providers/EventServiceProvider.php#L97-L99) +Registered in [app/Providers/EventServiceProvider.php:99](app/Providers/EventServiceProvider.php#L99) This listener provides detailed logging for queries that exceed warning/critical thresholds: - **70% threshold**: WARNING level log @@ -60,7 +60,7 @@ This listener provides detailed logging for queries that exceed warning/critical ### 3. PHP Timeout Handler -**Location**: [app/Providers/AppServiceProvider.php:200-216](app/Providers/AppServiceProvider.php#L200-L216) +**Location**: [app/Providers/AppServiceProvider.php:204-220](app/Providers/AppServiceProvider.php#L204-L220) A shutdown function that catches when PHP times out entirely, logging: - Error message @@ -125,7 +125,7 @@ PDO::MYSQL_ATTR_INIT_COMMAND => 'SET SESSION wait_timeout=28800', // 8 hours ### Octane Database Ping -The AppServiceProvider pings database connections every 30 seconds to prevent timeouts: [app/Providers/AppServiceProvider.php:340-341](app/Providers/AppServiceProvider.php#L340-L341) +The AppServiceProvider pings database connections every 30 seconds to prevent timeouts: [app/Providers/AppServiceProvider.php:409-424](app/Providers/AppServiceProvider.php#L409-L424) ## Viewing Logs diff --git a/docs/specs/2-how-to/translating-lychee.md b/docs/specs/2-how-to/translating-lychee.md index 5b7470057ce..8df27961e4e 100644 --- a/docs/specs/2-how-to/translating-lychee.md +++ b/docs/specs/2-how-to/translating-lychee.md @@ -37,7 +37,7 @@ return [ Ensure consistency across all languages: ```bash -php artisan test --filter TranslationTest +php artisan test --filter LangTest ``` ### Best Practices for Developers @@ -217,4 +217,4 @@ This localization system ensures Lychee remains accessible to users worldwide wh --- -*Last updated: December 22, 2025* +*Last updated: January 21, 2026* diff --git a/docs/specs/2-how-to/using-renamer.md b/docs/specs/2-how-to/using-renamer.md index 78e80f0d8f3..2adce545506 100644 --- a/docs/specs/2-how-to/using-renamer.md +++ b/docs/specs/2-how-to/using-renamer.md @@ -18,11 +18,12 @@ The Renamer module allows you to automatically transform filenames during import To create a basic replacement rule: 1. Access the Renamer API or use the admin interface -2. Define the pattern to find (needle) -3. Specify the replacement text -4. Choose the replacement mode (FIRST, ALL, or REGEX) +2. Define the pattern to find (needle) - not needed for case/trim modes +3. Specify the replacement text - not needed for case/trim modes +4. Choose the replacement mode (FIRST, ALL, REGEX, TRIM, LOWER, UPPER, UCWORDS, or UCFIRST) 5. Set the processing order 6. Enable the rule +7. Optionally set `is_photo_rule` and/or `is_album_rule` to control where the rule applies ### Example: Replace Camera Prefix @@ -31,13 +32,15 @@ Replace `IMG_` with `Photo_`: ```php $rule = new RenamerRule(); $rule->owner_id = Auth::id(); -$rule->name = 'Replace IMG_'; +$rule->rule = 'Replace IMG_'; $rule->description = 'Replaces IMG_ with Photo_'; $rule->needle = 'IMG_'; $rule->replacement = 'Photo_'; $rule->mode = RenamerModeType::FIRST; // Only replace first occurrence $rule->order = 1; $rule->is_enabled = true; +$rule->is_photo_rule = true; // Apply to photo filenames +$rule->is_album_rule = false; // Don't apply to album titles $rule->save(); ``` @@ -50,7 +53,7 @@ Replace all underscores with spaces: ```php $rule = new RenamerRule(); $rule->owner_id = Auth::id(); -$rule->name = 'Underscores to Spaces'; +$rule->rule = 'Underscores to Spaces'; $rule->description = 'Replace all underscores with spaces'; $rule->needle = '_'; $rule->replacement = ' '; @@ -69,7 +72,7 @@ Use regex to add a date prefix: ```php $rule = new RenamerRule(); $rule->owner_id = Auth::id(); -$rule->name = 'Add Date Prefix'; +$rule->rule = 'Add Date Prefix'; $rule->description = 'Extract date from filename and move to beginning'; $rule->needle = '/^(.+)_(\d{4}-\d{2}-\d{2})(.+)$/'; $rule->replacement = '$2_$1$3'; @@ -81,6 +84,39 @@ $rule->save(); **Result**: `vacation_2024-06-15_beach.jpg` becomes `2024-06-15_vacation_beach.jpg` +### Example: Transform Case + +Use case transformation modes (needle/replacement are ignored for these): + +```php +// Convert to lowercase +$rule = new RenamerRule(); +$rule->owner_id = Auth::id(); +$rule->rule = 'Lowercase'; +$rule->description = 'Convert filename to lowercase'; +$rule->mode = RenamerModeType::LOWER; +$rule->order = 4; +$rule->is_enabled = true; +$rule->save(); +``` + +**Result**: `VACATION_Photo.jpg` becomes `vacation_photo.jpg` + +### Example: Trim Whitespace + +```php +$rule = new RenamerRule(); +$rule->owner_id = Auth::id(); +$rule->rule = 'Trim spaces'; +$rule->description = 'Remove leading/trailing whitespace'; +$rule->mode = RenamerModeType::TRIM; +$rule->order = 5; +$rule->is_enabled = true; +$rule->save(); +``` + +**Result**: ` photo name.jpg ` becomes `photo name.jpg` + ## Applying Patterns ### Single Filename @@ -259,4 +295,4 @@ Ensure: --- -*Last updated: December 22, 2025* +*Last updated: January 21, 2026* diff --git a/docs/specs/3-reference/api-design.md b/docs/specs/3-reference/api-design.md index 46ec74e6cc5..df4d7371b65 100644 --- a/docs/specs/3-reference/api-design.md +++ b/docs/specs/3-reference/api-design.md @@ -297,6 +297,8 @@ Page sizes and UI modes are configurable via the admin settings panel or directl | photos_per_page | integer (1-1000) | 100 | Number of photos per page | | albums_pagination_ui_mode | enum | infinite_scroll | UI mode for album pagination | | photos_pagination_ui_mode | enum | infinite_scroll | UI mode for photo pagination | +| albums_infinite_scroll_threshold | integer | 2 | Viewport heights from bottom to trigger album loading | +| photos_infinite_scroll_threshold | integer | 2 | Viewport heights from bottom to trigger photo loading | **UI Mode Options:** - `infinite_scroll` - Auto-load next page on scroll (default) @@ -324,4 +326,4 @@ Page sizes and UI modes are configurable via the admin settings panel or directl --- -*Last updated: January 10, 2026* +*Last updated: January 21, 2026* diff --git a/docs/specs/3-reference/database-schema.md b/docs/specs/3-reference/database-schema.md index 0a627a6c94f..12cd881507d 100644 --- a/docs/specs/3-reference/database-schema.md +++ b/docs/specs/3-reference/database-schema.md @@ -83,7 +83,6 @@ Individual photos with metadata, EXIF data, and file information. - `id`: Primary key - `title`: Photo title - `description`: Optional description -- `album_id`: Foreign key to Album (nullable) - `owner_id`: Foreign key to User - `type`: MIME type - `original_checksum`: SHA-256 checksum @@ -93,13 +92,13 @@ Individual photos with metadata, EXIF data, and file information. - `latitude`, `longitude`: GPS coordinates **Relationships:** -- Belongs to `Album` +- Belongs to many `Album` through `photo_album` pivot table (many-to-many) - Belongs to `User` (owner) - Has many `SizeVariant` - Has one `Palette` -- Has many `Tag` through `photo_tag` pivot table +- Has many `Tag` through `photos_tags` pivot table - Has many `PhotoRating` -- Has one `PhotoStatistics` (virtual relationship for aggregated metrics) +- Has one `Statistics` (visit/download tracking) #### SizeVariant Different size versions of photos (original, medium, small, thumb). @@ -228,10 +227,11 @@ This dual approach allows Lychee to provide: - **Users**: Own albums and photos, belong to user groups (SE edition) ### Many-to-Many -- **Tags**: Many-to-many with photos through `photo_tag` pivot table +- **Photos-Albums**: Many-to-many through `photo_album` pivot table (photos can belong to multiple albums) +- **Tags**: Many-to-many with photos through `photos_tags` pivot table ### One-to-Many -- **Photos**: Belong to one album, owned by one user +- **Photos**: Owned by one user (but can belong to multiple albums) - **Size Variants**: Multiple variants per photo - **Access Permissions**: Multiple permissions per album @@ -258,4 +258,4 @@ Eager loading enforced with `Model::shouldBeStrict()`, which throws an exception --- -*Last updated: December 22, 2025* +*Last updated: January 21, 2026* diff --git a/docs/specs/3-reference/localization.md b/docs/specs/3-reference/localization.md index 6b8028ad846..049a27d79e0 100644 --- a/docs/specs/3-reference/localization.md +++ b/docs/specs/3-reference/localization.md @@ -39,25 +39,30 @@ lang/ Each language directory contains the same set of PHP files that return associative arrays of translation keys: +- **`all_settings.php`** - Comprehensive settings labels and descriptions +- **`aspect_ratio.php`** - Aspect ratio and layout options +- **`changelogs.php`** - Version history and update information +- **`diagnostics.php`** - System diagnostics and health checks +- **`dialogs.php`** - Modal dialogs, confirmations, and user interactions +- **`duplicate-finder.php`** - Duplicate photo detection +- **`fix-tree.php`** - Album tree maintenance utilities +- **`flow.php`** - Photo flow/timeline interface - **`gallery.php`** - Main gallery interface, albums, photos, and navigation +- **`import_from_server.php`** - Server-side import functionality +- **`jobs.php`** - Background job status and management +- **`landing.php`** - Landing page content +- **`left-menu.php`** - Left sidebar navigation menu +- **`maintenance.php`** - Maintenance mode and system operations +- **`profile.php`** - User profile management +- **`renamer.php`** - Filename renaming rules interface - **`settings.php`** - Application settings and configuration options -- **`dialogs.php`** - Modal dialogs, confirmations, and user interactions -- **`toasts.php`** - Notification messages and alerts - **`sharing.php`** - Album and photo sharing functionality -- **`profile.php`** - User profile management -- **`users.php`** - User management (admin features) -- **`user-groups.php`** - User group management - **`statistics.php`** - Statistics and analytics displays -- **`maintenance.php`** - Maintenance mode and system operations -- **`jobs.php`** - Background job status and management -- **`diagnostics.php`** - System diagnostics and health checks -- **`changelogs.php`** - Version history and update information -- **`left-menu.php`** - Left sidebar navigation menu -- **`landing.php`** - Landing page content -- **`flow.php`** - Photo flow/timeline interface -- **`fix-tree.php`** - Album tree maintenance utilities -- **`duplicate-finder.php`** - Duplicate photo detection -- **`aspect_ratio.php`** - Aspect ratio and layout options +- **`tags.php`** - Tag management interface +- **`toasts.php`** - Notification messages and alerts +- **`user-groups.php`** - User group management +- **`users.php`** - User management (admin features) +- **`webshop.php`** - E-commerce and webshop functionality ## Translation Key Conventions @@ -157,7 +162,7 @@ Lychee's test suite validates translation consistency: php artisan test # Run specific translation tests -php artisan test --filter TranslationTest +php artisan test --filter LangTest ``` #### Common Test Failures @@ -198,4 +203,4 @@ Weblate provides a web-based interface for translators to contribute translation --- -*Last updated: December 22, 2025* +*Last updated: January 21, 2026* diff --git a/docs/specs/3-reference/renamer-system.md b/docs/specs/3-reference/renamer-system.md index e344f98a3b1..df580d2bdd8 100644 --- a/docs/specs/3-reference/renamer-system.md +++ b/docs/specs/3-reference/renamer-system.md @@ -14,7 +14,7 @@ The Renamer module provides functionality to create, manage, and apply rules for 1. **Renamer Class**: Located in `App\Metadata\Renamer`, this is the main class that handles the application of renamer rules to strings. 2. **RenamerRule Model**: Represents a single renaming rule in the database. -3. **RenamerModeType Enum**: Defines the available replacement modes (FIRST, ALL, REGEX). +3. **RenamerModeType Enum**: Defines the available modes (FIRST, ALL, REGEX, TRIM, LOWER, UPPER, UCWORDS, UCFIRST). 4. **RenamerController**: Handles API requests for managing renaming rules. ### Database Schema @@ -25,24 +25,32 @@ The Renamer module uses a `renamer_rules` table with the following structure: |--------|------|-------------| | id | bigint | Primary key | | owner_id | bigint | Foreign key to the user who owns this rule | -| name | string | Name of the rule | -| description | string | Optional description of what the rule does | -| needle | string | The string to find (pattern to match) | -| replacement | string | The replacement text | -| mode | enum | Mode of operation (FIRST, ALL, REGEX) | | order | int | Processing order (lower numbers processed first) | +| rule | string | Name/identifier of the rule | +| description | string | Optional description of what the rule does | +| needle | string | The string to find (pattern to match) - ignored for case/trim modes | +| replacement | string | The replacement text - ignored for case/trim modes | +| mode | enum | Mode of operation (FIRST, ALL, REGEX, TRIM, LOWER, UPPER, UCWORDS, UCFIRST) | | is_enabled | boolean | Whether the rule is active | -| created_at | timestamp | Creation timestamp | -| updated_at | timestamp | Last update timestamp | +| is_photo_rule | boolean | Whether to apply rule to photo filenames | +| is_album_rule | boolean | Whether to apply rule to album titles | ## Mode Types -The Renamer module supports three modes of operation (defined in the `RenamerModeType` enum): +The Renamer module supports eight modes of operation (defined in the `RenamerModeType` enum): +**Replacement Modes** (use needle/replacement fields): 1. **First occurrence** (`FIRST`): Replaces only the first occurrence of the pattern. 2. **All occurrences** (`ALL`): Replaces all occurrences of the pattern. 3. **Regular expression** (`REGEX`): Uses regular expressions for pattern matching and replacement. +**Transformation Modes** (ignore needle/replacement fields): +4. **Trim** (`TRIM`): Removes leading and trailing whitespace. +5. **Lowercase** (`LOWER`): Converts entire string to lowercase. +6. **Uppercase** (`UPPER`): Converts entire string to uppercase. +7. **Uppercase words** (`UCWORDS`): Capitalizes the first letter of each word. +8. **Uppercase first** (`UCFIRST`): Capitalizes only the first letter of the string. + ## Processing Order Rules are processed in order from lowest to highest `order` value, meaning rules with lower order numbers have higher priority. Only enabled rules are applied. @@ -85,13 +93,15 @@ $newFilenames = $renamer->handleMany(['IMG_1234.jpg', 'DSC_5678.jpg']); ```php $rule = new RenamerRule(); $rule->owner_id = Auth::id(); // Current user's ID -$rule->name = 'Replace IMG_'; +$rule->rule = 'Replace IMG_'; $rule->description = 'Replaces IMG_ with Photo_'; $rule->needle = 'IMG_'; $rule->replacement = 'Photo_'; $rule->mode = RenamerModeType::FIRST; $rule->order = 1; $rule->is_enabled = true; +$rule->is_photo_rule = true; // Apply to photo filenames +$rule->is_album_rule = false; // Don't apply to album titles $rule->save(); ``` @@ -103,14 +113,16 @@ $rule->save(); class RenamerRule extends Model { public int $id; - public int $owner_id; // User who owns this rule - public string $name; // Rule name - public ?string $description; // Optional description - public string $needle; // Pattern to match - public string $replacement; // Replacement text - public RenamerModeType $mode; // FIRST, ALL, or REGEX + public int $owner_id; // User who owns this rule public int $order; // Processing priority + public string $rule; // Rule name/identifier + public ?string $description; // Optional description + public string $needle; // Pattern to match (ignored for transformation modes) + public string $replacement; // Replacement text (ignored for transformation modes) + public RenamerModeType $mode; // FIRST, ALL, REGEX, TRIM, LOWER, UPPER, UCWORDS, or UCFIRST public bool $is_enabled; // Active status + public bool $is_photo_rule; // Apply to photo filenames + public bool $is_album_rule; // Apply to album titles } ``` @@ -121,4 +133,4 @@ class RenamerRule extends Model --- -*Last updated: December 22, 2025* +*Last updated: January 21, 2026*