This commit introduces advanced environment variable handling capabilities including:
- Nested environment variable resolution with circular dependency detection
- Extraction of hardcoded environment variables from docker-compose.yml
- New ShowHardcoded Livewire component for displaying detected variables
- Enhanced UI for better environment variable management
The changes improve the user experience by automatically detecting and displaying
environment variables that are hardcoded in docker-compose files, allowing users
to override them if needed. The nested variable resolution ensures complex variable
dependencies are properly handled.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Use x-callout component in developer view for env var note
- Simplify label text from "Comment (Optional)" to "Comment"
- Minor code formatting improvements via Pint
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Moved the Update button to appear inline with the comment field for better UX when editing comments on locked environment variables. The button now appears on the same row as the comment input on larger screens.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed the duplicate delete button that was appearing at the bottom of locked environment variables. The delete button at the top (next to the lock icon) is sufficient.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed instantSave from comment field and added a proper Update button with Delete modal for locked environment variables. This ensures users can explicitly save their comment changes on locked variables.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Modified the locked environment variable view to keep the comment field editable even when the variable value is locked. Users with update permission can now edit comments on locked variables, while users without permission can still view the comment for reference.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When an environment variable is locked (is_shown_once=true), the comment field is now displayed as disabled/read-only for future reference. This allows users to see the documentation/note about what the variable is for, even when the value is hidden for security.
The comment field appears after the key field and before the configuration checkboxes in the locked view.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comment field support to the "New Shared Variable" modal, ensuring it's saved properly for both normal and shared environment variables at all levels (Team, Project, Environment).
Changes:
- Add comment property, validation, and dispatch to Add component (Livewire & view)
- Update saveKey methods in Team, Project, and Environment to accept comment
- Replace SharedEnvironmentVariable model's $guarded with explicit $fillable array
- Include comment field in creation flow for all shared variable types
The comment field (max 256 chars, optional) is now available when creating shared variables and is consistently saved across all variable types.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comment field to EnvironmentVariable model and database
- Update parseEnvFormatToArray to extract inline comments from env files
- Update Livewire components to handle comment field
- Add UI for displaying and editing comments
- Add tests for comment parsing functionality
Changed from '2025-Dec-18 11:39:13.685710' to '2025-Dec-18 11:39:13'.
Microseconds are still used in wire:key for uniqueness.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Shows a subtle loading indicator over the logs container during refresh
or when enabling streaming. This masks any brief visual glitches from
Livewire's DOM morphing when log content changes significantly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added morph.added hook to apply color highlighting when logs are first
loaded. The morph.updated hook only fires on subsequent updates, not
on initial DOM insertion.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Applies the same fix from deployment logs to runtime logs (GetLogs component).
Prevents HTML entities like " from appearing when search highlighting
is applied to logs containing special characters.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The hook was preventing new logs from appearing when text was selected.
The hasActiveLogSelection() check in highlightText() is sufficient.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Remove per-line x-effect directives that re-evaluated for every log line during polling
- Replace with efficient applySearch() function that updates logs once after Livewire morph
- Remove unnecessary caching mechanisms (renderTrigger, decodeCache, matchCountCache)
- Remove double HTML encoding of log lines (e() + Blade escaping)
- Add decodeHtml() helper to properly decode HTML entities from data attributes
- Use morph.updated hook instead of commit hook for efficient DOM updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Use atomic update pattern in backend: collect logs into temp variable
before replacing outputs (prevents empty state flash)
- Remove per-line x-effect directives that caused 4000+ reactive
evaluations on every update
- Replace inline Alpine.js class bindings with CSS utility classes
- Use single $watch and morph.updated hook instead of renderTrigger
- Remove HTML entity decode cache (no longer needed)
- Fix search highlight padding that caused text shifting
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use dataset.logContent instead of textContent for counting search
matches in getMatchCount(). This aligns the counting logic with
matchesSearch() which uses the same data attribute for filtering
visibility, ensuring the displayed match count accurately reflects
the number of visible/hidden lines.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use CSS content-visibility: auto for lazy rendering of off-screen log lines
- Replace setInterval auto-scroll with requestAnimationFrame for smoother scrolling
- Cache HTML entity decoding to avoid repeated DOMParser calls (up to 5000 entries)
- Cache match count calculations to prevent repeated DOM queries
- Debounce search input (300ms) in deployment logs view
- Debounce Livewire render updates (100ms) to batch rapid changes
- Add log-line utility class with content-visibility optimization
- Add log-highlight utility class for search result highlighting
These changes address browser freezing when viewing deployment logs with
3500+ lines (GitHub issue #7668). The content-visibility CSS property lets
the browser skip rendering of off-screen content, significantly reducing
initial render time and memory usage.
Fixes#7668
Add copy-to-clipboard functionality for both deployment logs and runtime container logs with success notification. Fixed event dispatch to use Livewire.dispatch() for proper toast notification handling. Reorganized copy and download buttons to appear consecutively in runtime logs toolbar.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Logs views should not use Livewire navigation, similar to terminal views.
This ensures full page loads when navigating to logs instead of SPA-like transitions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Remove wireNavigate() from:
- External links (docs, GitHub) - can't be handled by Livewire SPA
- url()->previous() in all error pages - dynamic URL can't prefetch
- Links with target="_blank" - conflicts with wire:navigate semantics
Keep wireNavigate() on internal Dashboard links in error pages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add wire:navigate.hover to application, database, and service cards on the environment resource index page for SPA navigation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Implement instance-wide SPA navigation toggle that enables smooth page transitions with prefetching on hover. Excludes terminal links which require full page lifecycle for WebSocket connections. Adds defensive checks to global-search component for SPA navigation compatibility.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Updated dropdown toggle logic to use a single toggle function for better readability.
- Replaced span elements with buttons for dropdown toggles to enhance accessibility.
- Added click outside functionality to close dropdowns when clicking outside.
- Adjusted styling for dropdowns and hover states to use neutral colors for better UI consistency.
- Ensured proper alignment and positioning of dropdowns to prevent overflow issues.
- Removed unnecessary SVG icons from the environment edit view for cleaner UI.
- Deleted the environment select component as it was no longer needed.
- Enhanced the project resource index view with a dropdown for environments and resources, improving navigation.
- Implemented dynamic dropdowns for environments and their associated resources, allowing for better user interaction.
- Added transitions and hover effects for a more responsive design.
- Updated the layout to ensure a consistent user experience across different project resources.
Add a copy button to individual container logs that strips sensitive
data before copying to clipboard. Includes sanitization for emails,
database URLs with passwords, JWT tokens, API keys, private key blocks,
and git access tokens.
Reorder toolbar buttons so that Refresh and Stream Logs (polling)
are adjacent, making the related actions easier to find.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use Livewire's morph.updating hook to skip DOM morphing of the logs
container when user has text selected. This prevents the selection from
being lost when polling or manual refresh occurs.
The previous fix only prevented the JavaScript-based re-render, but
Livewire's morphing was still replacing the DOM elements entirely.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Apply the same selection preservation fix to the runtime logs component
(get-logs.blade.php) that was applied to deployment logs:
- Add hasActiveLogSelection() helper to detect active text selection
- Skip re-render when user has text selected (preserves copy ability)
- Add renderTrigger mechanism to ensure filtering works after refresh
- Use x-effect for hidden state to properly react to Livewire updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Ensure initial render happens even when selection is active by checking
if element already has content before skipping re-render.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Prevent text selection from being cleared when logs are re-rendered during polling
- Preserve fullscreen state when toggling debug logs or other Livewire updates
- Fix log filtering to properly apply when debug mode is toggled
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Add Import Backup section to ServiceDatabase view for supported
database types (PostgreSQL, MySQL, MariaDB, MongoDB).
This enables users to import backups directly from the ServiceDatabase
configuration page, utilizing the existing Import Livewire component.
Update tooltip text to correctly show that the maximum execution time is 36000 seconds (10 hours) instead of 3600 seconds. The validation already allowed up to 36000 seconds, but the UI was displaying incorrect information.
🤖 Generated with Claude Code
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Add isServiceResource() and shouldBeReadOnlyInUI() to LocalFileVolume
- Update path matching to handle leading slashes in volume comparisons
- Update FileStorage and Show components to use shouldBeReadOnlyInUI()
- Show consolidated warning message for service/compose resources in all.blade.php
- Remove redundant per-volume warnings for service resources
- Clean up configuration.blade.php formatting
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fixed isReadOnlyVolume() to detect both short-form (:ro) and long-form (read_only: true) Docker Compose volume syntax
- Fixed path matching to use mount_path only (fs_path is transformed during parsing from ./file to absolute path)
- Added "Load from server" button for read-only volumes to allow users to refresh content
- Changed loadStorageOnServer() authorization from 'update' to 'view' since loading is a read operation
- Added helper text to Content field warning users that content may be outdated
- Applied fixes to both LocalFileVolume and LocalPersistentVolume models
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The option is now always visible for git-based applications but only editable when Preview Deployments are enabled. This allows users to understand and configure the public PR deployment setting regardless of preview deployment status.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Reduce deployment logs max-height from 40rem to 30rem to prevent overlap with deployments indicator
- Add conditional opacity (60%) to deployments indicator on deployment pages only (100% on other pages)
- Indicator becomes fully visible on hover or when expanded
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed auto-disable behaviors that caused follow logs to stop unexpectedly:
- Removed scroll detection that disabled following when user scrolled >50px from bottom
- Removed fullscreen exit handler that disabled following
- Removed ServiceChecked event listener that caused unnecessary flickers
Follow logs now only stops when:
- User explicitly clicks the Follow Logs button
- Deployment finishes (auto-scrolls to end first, then disables after 500ms delay)
Also improved get-logs component with memory optimizations:
- Limited display to last 2000 lines to prevent memory exhaustion
- Added debounced search (300ms) and scroll handling (150ms)
- Optimized DOM rendering
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Change empty state message from "Refresh to get the logs..." to "No logs yet." with proper styling
- Auto-fetch logs when expanding containers by passing refresh=true to getLogs()
- Ensure fullscreen mode covers entire viewport with solid background by overriding x-collapse styles
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The {{port}} template variable was undocumented and caused a double port bug
when used in preview URL templates. Since ports are always appended to the final
URL anyway, we remove {{port}} substitution entirely and ensure consistent port
handling across ApplicationPreview, PreviewsCompose, and the applicationParser helper.
Also fix PreviewsCompose.php which wasn't preserving ports at all, and improve
the Blade template formatting in previews-compose.blade.php.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Initialize logsLoaded as false to ensure init() triggers log loading
- Set logsLoaded=true after calling getLogs() in init()
- Allow services/PRs to load logs automatically when expandByDefault=true (single container)
- Previously, services would skip initial load unless refresh=true, now single containers work
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a new server-level setting that allows administrators to disable
per-application image retention globally for all applications on a server.
When enabled, Docker cleanup will only keep the currently running image
regardless of individual application retention settings.
Changes:
- Add migration for disable_application_image_retention boolean field
- Update ServerSetting model with cast
- Add checkbox in DockerCleanup page (Advanced section)
- Modify CleanupDocker action to check server-level setting
- Update Rollback page to show warning and disable inputs when server
retention is disabled
- Add helper text noting server-level override capability
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Existing Docker Compose deployments may have 'latest' or custom tags
that aren't valid git commit SHAs. When rollback is triggered with these
tags, the deployment fails because the system tries to use the tag as a
git commit reference.
This change:
- Detects if image tag is a valid commit SHA or PR tag
- Disables rollback button for non-commit tags with helpful tooltip
- Displays appropriate label (SHA/PR/Tag) based on tag type
- Guides users to re-deploy to create rollback-enabled images
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement a per-application setting (`docker_images_to_keep`) in `application_settings` table to control how many Docker images are preserved during cleanup. The cleanup process now:
- Respects per-application retention settings (default: 2 images)
- Preserves the N most recent images per application for easy rollback
- Always deletes PR images and keeps the currently running image
- Dynamically excludes application images from general Docker image prune
- Cleans up non-Coolify unused images to prevent disk bloat
Fixes issues where cleanup would delete all images needed for rollback.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add color toggle button (paint brush icon) to runtime logs toolbar
- Color log lines by level: error (red), warning (yellow), debug (purple), info (blue)
- Store color preference in localStorage ('coolify-color-logs')
- Widen Lines input from w-24 to w-32 for better usability
- Remove color features from deployment logs (keeping it cleaner)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added a new `collapsible` property to GetLogs component that allows disabling the expandable header, useful for log viewers in dedicated pages and slide-overs. Applied this to Sentinel logs, Proxy logs, and Coolify Proxy log pages. Also improved the toolbar by moving the lines counter to the left side with an inline prefix label and repositioning the match counter next to it for better organization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace text "Documentation" links with SVG icons positioned in top-right corner, matching the established design pattern used in the service list. Update all four PostgreSQL type options: PostgreSQL 17, Supabase, PostGIS, and PGVector.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Updated the timestamp rendering condition to check both $timestamp and $showTimeStamps, ensuring the "Toggle Timestamps" button properly controls timestamp visibility in the log viewer.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added maximum iteration limit (maxIterations = 3) to the decodeHtml function to prevent potential DoS attacks from deeply nested HTML entities. This matches the implementation in deployment/show.blade.php and ensures the function cannot be exploited for excessive CPU usage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Moved .log-highlight styles from Livewire component views to resources/css/app.css for better separation of concerns and reusability. This follows Laravel and Livewire best practices by keeping styles in the appropriate location rather than inline in component views.
Changes:
- Added .log-highlight styles to resources/css/app.css
- Removed inline <style> tags from deployment/show.blade.php
- Removed inline <style> tags from get-logs.blade.php
- Added XSS security test for log viewer
- Applied code formatting with Laravel Pint
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Features:
- Add client-side search filtering for runtime and deployment logs
- Add log download functionality (respects search filters)
- Make runtime log sections collapsible by default
- Auto-expand single container and lazy load logs on first expand
- Match deployment and runtime log view heights (40rem)
- Add debug toggle for deployment logs
- Improve scroll behavior with follow logs feature
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Apply the same frontend path normalization pattern from commit f6398f7cf
to the General Settings page for consistency across all forms.
Changes:
- Add Alpine.js path normalization to Docker Compose section (base directory + compose location)
- Add Alpine.js path normalization to non-Docker Compose section (base directory + dockerfile location)
- Change wire:model to wire:model.defer to prevent backend requests during tab navigation
- Add @blur event handlers for immediate path normalization feedback
- Backend normalization remains as defensive fallback
This ensures consistent validation behavior and fixes potential tab focus
issues on the General Settings page.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change wire:model.blur to wire:model.defer to prevent backend requests
during form navigation. Add Alpine.js path normalization functions that
run on blur, fixing tab focus issues while keeping path validation
purely on the frontend.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Users can now switch between the enhanced color-coded log view and the original simple raw text view using a new toggle checkbox. The preference is saved to localStorage and persists across page reloads and different resources.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace substring matching with exact base image name comparison in isDatabaseImage() to prevent false positives (postgres no longer matches postgrest)
- Add 'timescaledb' and 'timescaledb-ha' to DATABASE_DOCKER_IMAGES constants for proper namespace handling
- Add empty state messaging when no applications are defined in Docker Compose configuration
- Maintain backward compatibility with all existing database patterns
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add instantSaveSettings() method to save gzip, stripprefix, and
exclude_from_status checkboxes without triggering port validation modal.
These settings don't require domain/port validation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Updated color classes in NotifyDemo.php to use warning colors.
- Added new warning color variables in app.css.
- Changed warning icon colors in callout.blade.php.
- Updated loading spinner and hover states in global-search.blade.php.
- Refactored warning messages and styles in project application views.
- Adjusted log display colors in get-logs.blade.php.
- Updated private key status indicators in index.blade.php.
- Changed hover and text colors for documentation links in cloudflare-tunnel.blade.php.
- Refactored server creation messages in by-hetzner.blade.php.
- Updated proxy warning button colors in proxy.blade.php.
- Changed loading spinner colors in show.blade.php.
- Updated deployment status colors in deployments.blade.php and show.blade.php.
- Updated dashboard, destination, project, and server views to replace 'box' class with 'coolbox' for improved visual consistency.
- Modified links and buttons in shared variables and scheduled tasks views to utilize 'coolbox' class.
- Ensured all relevant components reflect the new styling approach, enhancing user experience and interface coherence.
Wrap ActivityMonitor components in wire:ignore to prevent parent component
re-renders from destroying the Livewire component and causing "Snapshot missing"
errors in production mode.
The issue occurred when toggling the "Backup includes all databases" checkbox
during database restore operations. The checkbox uses wire:model.live which
triggers immediate parent re-renders, destroying the nested ActivityMonitor
component in the slide-over.
Changes:
- Wrap ActivityMonitor in wire:ignore div in import.blade.php
- Apply same fix preventatively to heading.blade.php
wire:ignore prevents Livewire from re-rendering the DOM inside the wrapper,
while still allowing event listeners and Alpine.js functionality to work
correctly. The existing reset logic (slideOverClosed event) continues to
function properly.
Fixes#7335🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add two new application settings to control Docker build cache invalidation:
- inject_build_args_to_dockerfile (default: true) - Skip Dockerfile ARG injection
- include_source_commit_in_build (default: false) - Exclude SOURCE_COMMIT from build context
These toggles let users preserve Docker cache when SOURCE_COMMIT or custom ARGs change frequently. Development-only logging shows which ARGs are being injected for debugging.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes a critical bug in the environment variable autocomplete component
where arrow key navigation could cause divide-by-zero errors when the
suggestions array is empty.
Changes:
- Add early guard in handleKeydown to check for empty suggestions array
before performing modulo operations
- Remove unreachable "No suggestions" template that could never display
- Add validation to hide dropdown when user types third brace ({{{)
- Refactor add.blade.php to use @if directives instead of x-show for
better performance and cleaner code
The fix ensures arrow keys do nothing when suggestions are empty,
preventing JavaScript errors while maintaining all existing functionality
including the scoped empty state messages with helpful links.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add path attribute mutator to S3Storage model ensuring paths start with /
- Add updatedS3Path hook to normalize path and reset validation state on blur
- Add updatedS3StorageId hook to reset validation state when storage changes
- Add Enter key support to trigger file check from path input
- Use wire:model.live for S3 storage select, wire:model.blur for path input
- Improve shell escaping in restore job cleanup commands
- Fix isSafeTmpPath helper logic for directory validation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>