diff --git a/PRP.md b/PRP.md new file mode 100644 index 0000000..c7bb2db --- /dev/null +++ b/PRP.md @@ -0,0 +1,406 @@ +# Project Reconstruction Plan (PRP) for AI StyleGallery + +This document outlines the architecture, functionality, and implementation details of the "AI StyleGallery" web application. It is intended to serve as a comprehensive guide for another AI agent or LLM to understand and potentially reconstruct a similar system. + +## 1. Application Overview + +**Primary Goal:** AI StyleGallery is a web application designed to allow users to transform their uploaded images by applying various AI-generated artistic styles. Users can manage their image gallery, apply styles via external AI services, and save or discard the results. + +**Core Functionality:** +- **Image Upload:** Users can upload their own images to the gallery. +- **Style Application:** Users can select an image and choose from a predefined set of AI styles to apply. +- **AI Processing Integration:** The application interacts with external AI web services (e.g., ComfyUI, RunwareAI) to perform image transformations. +- **Gallery Management:** Users can view their original and styled images, and decide to keep (make permanent) or discard (delete) styled images. +- **Admin Panel:** An administrative interface for managing AI models, API providers, styles, users, and roles. + +## 2. Core Technologies & Stack + +* **Languages:** PHP 8.1, JavaScript +* **Frameworks & Runtimes:** Laravel 12.21.0, Vue.js 3.5.18, Inertia.js 1.3.0, Livewire 3.6.4, Vite 5.4.19 +* **Databases:** MySQL (default), with configurations for PostgreSQL, SQLite, and SQL Server. Redis is used for caching. +* **Key PHP Libraries/Dependencies:** + * Filament 3.3.34 (for the admin panel) + * Guzzle 7.9.3 (for HTTP requests) + * Laravel Sanctum 4.2.0 (for API authentication) + * Laravel Breeze 2.3.8 (authentication scaffolding) + * Laravel Pint 1.24.0 (code formatter) + * Laravel Prompts 0.3.6 (CLI prompts) + * Laravel Sail 1.44.0 (Docker development environment) + * Laravel Serializable Closure 2.0.4 + * Laravel Tinker 2.10.1 + * nesbot/carbon 3.10.2 (date/time library) + * predis/predis 3.1.0 (Redis client) + * phpunit/php-code-coverage 12.3.2 + * phpunit/php-file-iterator 6.0.0 + * phpunit/php-invoker 6.0.0 + * phpunit/php-text-template 5.0.0 + * phpunit/php-timer 8.0.0 + * phpunit/phpunit 12.3.0 + * ramsey/uuid 4.9.0 (UUID generation) + * spatie/laravel-package-tools 1.92.7 + * tightenco/ziggy 2.5.3 (Laravel routes in JS) + * symfony/clock 7.3.0 + * symfony/console 7.3.2 + * symfony/css-selector 7.3.0 + * symfony/deprecation-contracts 3.6.0 + * symfony/error-handler 7.3.2 + * symfony/event-dispatcher 7.3.0 + * symfony/event-dispatcher-contracts 3.6.0 + * symfony/finder 7.3.2 + * symfony/html-sanitizer 7.3.2 + * symfony/http-foundation 7.3.2 + * symfony/http-kernel 7.3.2 + * symfony/mailer 7.3.2 + * symfony/mime 7.3.2 + * symfony/polyfill-ctype 1.32.0 + * symfony/polyfill-intl-grapheme 1.32.0 + * symfony/polyfill-intl-idn 1.32.0 + * symfony/polyfill-intl-normalizer 1.32.0 + * symfony/polyfill-mbstring 1.32.0 + * symfony/polyfill-php80 1.32.0 + * symfony/polyfill-php83 1.32.0 + * symfony/polyfill-uuid 1.32.0 + * symfony/process 7.3.0 + * symfony/routing 7.3.2 + * symfony/service-contracts 3.6.0 + * symfony/string 7.3.2 + * symfony/translation 7.3.2 + * symfony/translation-contracts 3.6.0 + * symfony/uid 7.3.1 + * symfony/var-dumper 7.3.2 + * symfony/yaml 7.3.2 +* **Key JavaScript Libraries/Dependencies:** + * axios 1.11.0 (for frontend HTTP requests) + * tailwindcss 3.4.17 (for styling) + * @inertiajs/vue3 1.3.0 + * @vitejs/plugin-vue 5.2.4 + * @tailwindcss/forms 0.5.10 + * autoprefixer 10.4.21 + * postcss 8.5.6 + * laravel-echo 2.1.7 (WebSocket client) + * pusher-js 8.4.0 (Pusher client for WebSockets) + * @fortawesome/fontawesome-svg-core 7.0.0 + * @fortawesome/free-solid-svg-icons 7.0.0 + * @fortawesome/vue-fontawesome 3.1.1 + * vanilla-lazyload 19.1.3 (lazy loading images) +* **Package Manager(s):** Composer for PHP, npm for JavaScript. + +## 2. UI Structure (Frontend / Backend) + + +### 2.1. Frontend (Vue.js 3 with Inertia.js) + +The frontend is a Single Page Application (SPA) built with Vue.js and Inertia.js, providing a dynamic user experience. + +- **Gallery View (`resources/js/Pages/Home.vue`):** + - Displays a grid of images (both original and styled). + - Images are fetched from `/api/images`. + - Includes pagination for large galleries. + - Periodically polls `/api/images` to refresh the gallery and show newly styled images. +- **Image Context Menu (`resources/js/Components/ImageContextMenu.vue`):** + - Appears when a user taps on an image in the gallery. + - Provides options: "Drucken" (Print), "Stil ändern" (Change Style), "Schließen" (Close). + - Dynamically adjusts its position based on the tap location. + - Layout: Image preview (60% width) and options list (40% width). +- **Style Selector (`resources/js/Components/StyleSelector.vue`):** + - Displayed when "Stil ändern" is selected from the context menu. + - Fetches and lists available AI styles from `/api/styles`. + - Allows users to select a style. + - Lazy loads style preview images using `IntersectionObserver` for performance. + - Includes a "back" arrow to return to the main context menu. +- **Styled Image Display (Implicit):** + - Styled images appear in the main gallery after processing. +- **User Authentication/Profile:** + - Login/Logout functionality (handled by Laravel Breeze/Fortify and Inertia.js). + - User profile management (via Laravel's built-in features and Filament admin panel). + +### 2.2. Backend (Laravel 12 with Filament Admin Panel) + +The backend is a monolithic Laravel application, with Filament providing a powerful administrative interface. + +- **Filament Admin Panel:** + - Accessible via `/admin`. + - Provides CRUD (Create, Read, Update, Delete) operations for the following resources: + - **AI Models (`app/Filament/Resources/AiModelResource.php`):** Manage AI models, their IDs, types, associated API providers, and specific parameters (JSON). + - **API Providers (`app/Filament/Resources/ApiProviderResource.php`):** Configure connections to external AI services (API URL, authentication tokens/credentials, plugin type). + - **Styles (`app/Filament/Resources/StyleResource.php`):** Define AI styles, including their title, prompt, description, preview image, parameters (JSON), and associated AI model. + - **Images (via direct database interaction and synchronization):** While there's an `ImageResource`, image management is largely automated through synchronization. + - **Users (`app/Filament/Resources/UserResource.php`):** Manage user accounts and assign roles. + - **Roles (`app/Filament/Resources/RoleResource.php`):** Define user roles and permissions. + - **Settings (`app/Filament/Resources/SettingResource.php`):** Manage application-wide settings. + +## 3. Resources/Entities (Models) + +The application's core data structures are represented by Eloquent models: + +- **`App\Models\User`**: Represents a user account. + - `id` (PK) + - `name` + - `email` + - `password` + - `role_id` (FK to `roles` table) + - `two_factor_secret`, `two_factor_recovery_codes`, `two_factor_confirmed_at` (for 2FA) + - `settings` (JSON) +- **`App\Models\Image`**: Stores metadata for both original and styled images. + - `id` (PK) + - `path` (string, file path relative to storage disk) + - `uuid` (string, unique identifier for tracking) + - `original_image_id` (FK to `images` table, for styled images) + - `style_id` (FK to `styles` table, for styled images) + - `is_temp` (boolean, true for temporary styled images) + - `is_public` (boolean, true for publicly visible images) + - `comfyui_prompt_id` (string, for tracking ComfyUI jobs) + - `created_at`, `updated_at` +- **`App\Models\Style`**: Defines an AI style. + - `id` (PK) + - `title` (string) + - `prompt` (text, the base prompt for the AI model) + - `description` (text) + - `preview_image` (string, path to style preview image) + - `parameters` (JSON, additional parameters for the AI plugin, cast to array) + - `ai_model_id` (FK to `ai_models` table) + - `enabled` (boolean) + - `created_at`, `updated_at` +- **`App\Models\AiModel`**: Represents a specific AI model (e.g., Stable Diffusion v1.5). + - `id` (PK) + - `name` (string) + - `model_id` (string, identifier used by the AI service) + - `model_type` (string, e.g., "Stable Diffusion") + - `enabled` (boolean) + - `parameters` (JSON, additional parameters for the AI plugin, cast to array) + - `created_at`, `updated_at` +- **`App\Models\ApiProvider`**: Configures connection details for an external AI service. + - `id` (PK) + - `name` (string, e.g., "ComfyUI API") + - `api_url` (string, base URL of the AI service) + - `username` (string, nullable) + - `password` (string, nullable) + - `token` (string, nullable) + - `plugin` (string, identifies the plugin to use, e.g., "comfyui", "runwareai") + - `enabled` (boolean) + - `created_at`, `updated_at` +- **`App\Models\Role`**: Defines user roles for access control. + - `id` (PK) + - `name` (string, e.g., "admin", "user") + - `created_at`, `updated_at` +- **`App\Models\Setting`**: Stores application-wide key-value settings. + - `id` (PK) + - `key` (string) + - `value` (text) + - `created_at`, `updated_at` + +## 4. Database Structure (Schema) + +Key tables and their relevant columns: + +- **`users`** + - `id`: `bigint unsigned auto_increment primary key` + - `name`: `varchar(255)` + - `email`: `varchar(255) unique` + - `password`: `varchar(255)` + - `role_id`: `bigint unsigned null` (FK to `roles.id`) + - `two_factor_secret`: `text null` + - `two_factor_recovery_codes`: `text null` + - `two_factor_confirmed_at`: `timestamp null` + - `settings`: `json null` + - `created_at`: `timestamp null` + - `updated_at`: `timestamp null` + +- **`images`** + - `id`: `bigint unsigned auto_increment primary key` + - `path`: `varchar(255)` + - `uuid`: `char(36) unique` + - `original_image_id`: `bigint unsigned null` (FK to `images.id`) + - `style_id`: `bigint unsigned null` (FK to `styles.id`) + - `is_temp`: `tinyint(1) default 0` + - `is_public`: `tinyint(1) default 1` + - `comfyui_prompt_id`: `varchar(255) null` + - `created_at`: `timestamp null` + - `updated_at`: `timestamp null` + +- **`styles`** + - `id`: `bigint unsigned auto_increment primary key` + - `title`: `varchar(255)` + - `prompt`: `longtext` + - `description`: `longtext` + - `preview_image`: `varchar(255)` + - `parameters`: `json null` + - `ai_model_id`: `bigint unsigned` (FK to `ai_models.id`) + - `enabled`: `tinyint(1) default 1` + - `created_at`: `timestamp null` + - `updated_at`: `timestamp null` + +- **`ai_models`** + - `id`: `bigint unsigned auto_increment primary key` + - `name`: `varchar(255)` + - `model_id`: `varchar(255)` + - `model_type`: `varchar(255) null` + - `enabled`: `tinyint(1) default 1` + - `parameters`: `json null` + - `created_at`: `timestamp null` + - `updated_at`: `timestamp null` + +- **`api_providers`** + - `id`: `bigint unsigned auto_increment primary key` + - `name`: `varchar(255)` + - `api_url`: `varchar(255)` + - `username`: `varchar(255) null` + - `password`: `varchar(255) null` + - `token`: `varchar(255) null` + - `plugin`: `varchar(255)` + - `enabled`: `tinyint(1) default 1` + - `created_at`: `timestamp null` + - `updated_at`: `timestamp null` + +- **`roles`** + - `id`: `bigint unsigned auto_increment primary key` + - `name`: `varchar(255) unique` + - `created_at`: `timestamp null` + - `updated_at`: `timestamp null` + +- **`ai_model_api_provider`** (Pivot table for many-to-many relationship) + - `ai_model_id`: `bigint unsigned` (FK to `ai_models.id`) + - `api_provider_id`: `bigint unsigned` (FK to `api_providers.id`) + +- **`settings`** + - `id`: `bigint unsigned auto_increment primary key` + - `key`: `varchar(255) unique` + - `value`: `longtext` + - `created_at`: `timestamp null` + - `updated_at`: `timestamp null` + +## 5. User Interactions + +### 5.1. Frontend User Interactions + +- **Image Upload:** Users can upload images via a dedicated interface (not explicitly detailed in provided context, but implied by gallery functionality). +- **Image Selection:** Tapping on an image in the gallery opens a context menu. +- **Context Menu Actions:** + - "Drucken" (Print): Placeholder for printing functionality. + - "Stil ändern" (Change Style): Navigates to the style selection interface. + - "Schließen" (Close): Closes the context menu. +- **Style Selection:** + - Users browse a list of available styles. + - Selecting a style initiates the AI image transformation process. + - "Back" arrow: Returns to the image context menu. +- **Gallery Refresh:** The gallery automatically updates to show newly processed images (polling every 5 seconds). +- **Authentication:** Users can log in and out. + +### 5.2. Backend (Admin Panel) User Interactions + +- **Resource Management:** Full CRUD operations for AI Models, API Providers, Styles, Users, and Roles. +- **Toggle Status:** Enable/disable AI Models, API Providers, and Styles. +- **Duplication:** Duplicate existing AI Models and Styles to quickly create new ones. +- **Settings Management:** Update application-wide settings. + +## 6. External System Interactions + +### 6.1. AI Web Services + +The application integrates with external AI services for image processing. The specific plugin used is determined by the `plugin` field in the `ApiProvider` model. + +- **ComfyUI (via `App\Api\Plugins\ComfyUi.php`):** + - **Image Upload:** Sends image data (base64 encoded) to the ComfyUI server's `/upload/image` endpoint. + - **Prompt Queuing:** Sends the constructed workflow JSON to the ComfyUI server's `/prompt` endpoint. + - **Result Fetching:** Polls the ComfyUI server's `/history/{prompt_id}` endpoint to check the status and retrieve the base64 encoded styled image. +- **RunwareAI (via `App\Api\Plugins\RunwareAi.php`):** + - **Image Upload:** Sends image data (base64 encoded) to the RunwareAI API. + - **Style Change Request:** Sends a request with prompt, seed image UUID, model ID, and merged parameters to the RunwareAI API for image inference. + +### 6.2. Authentication + +- **Laravel Sanctum:** Used for API authentication, securing routes that require user login (e.g., keeping/deleting images, admin panel access). + +## 7. Call Structure and Access Rights + +### 7.1. API Routes (`routes/api.php`) + +- **Publicly Accessible Routes:** + - `GET /api/images`: Retrieves a list of images (public and temporary for unauthenticated users, all for authenticated users). Handled by `ImageController@index`. + - `GET /api/styles`: Retrieves a list of available styles. Handled by `StyleController@index`. + - `POST /api/images/style-change`: Initiates an AI style change request. Handled by `ImageController@styleChangeRequest`. + - `GET /api/comfyui-url`: Retrieves the ComfyUI API URL. Handled by `ImageController@getComfyUiUrl`. + - `GET /api/images/fetch-styled/{prompt_id}`: Fetches the result of a styled image request. Handled by `ImageController@fetchStyledImage`. +- **Authenticated Routes (requires `auth:sanctum` middleware):** + - `GET /api/user`: Retrieves authenticated user details. + - `POST /api/admin/navigation-state`: Stores admin navigation state. Handled by `NavigationStateController@store`. + - `POST /api/images/keep`: Marks a temporary styled image as permanent. Handled by `ImageController@keepImage`. + - `DELETE /api/images/{image}`: Deletes an image. Handled by `ImageController@deleteImage`. + - `GET /api/images/status`: Retrieves the status of an image (placeholder for future use). Handled by `ImageController@getStatus`. + +### 7.2. Web Routes (`routes/web.php`) + +- Standard Laravel web routes for serving the Inertia.js frontend. The main application entry point is typically handled by a route that returns an Inertia response (e.g., `Route::get('/', ...)`). + +### 7.3. Access Rights + +- **API Authentication:** Laravel Sanctum is used to protect API routes. Users must be authenticated to access certain functionalities. +- **Filament Admin Panel:** Access to the `/admin` panel is controlled by Filament's built-in authentication and authorization system, typically based on user roles. +- **Image Visibility:** + - `is_public` flag on `Image` model: Controls whether an image is visible to unauthenticated users. + - `is_temp` flag on `Image` model: Identifies temporary styled images. Unauthenticated users can see their own temporary images. +- **Resource Status:** + - `enabled` flag on `AiModel`, `ApiProvider`, and `Style` models: Controls whether these resources are active and usable within the application. + +## 8. Detailed Description of Key Logic/Flows + +### 8.1. Image Styling Process + +1. **User Action (Frontend):** A user selects an image in the gallery and chooses a style from the `StyleSelector` component. +2. **Frontend Request:** The `Home.vue` component sends an `axios.post` request to `/api/images/style-change`, including the `image_id` and `style_id`. +3. **Backend (ImageController@styleChangeRequest):** + - Retrieves the `Image` and `Style` models based on the provided IDs. + - Retrieves the associated `AiModel` and `ApiProvider` from the `Style` model. + - Loads the appropriate AI plugin (e.g., `ComfyUi`, `RunwareAi`) using `PluginLoader::getPlugin()`. + - Calls the `processImageStyleChange()` method on the loaded plugin, passing the `Image` and `Style` models. +4. **AI Plugin (`ComfyUi.php` or `RunwareAi.php`):** + - **Image Upload:** The plugin first uploads the original image to the external AI service. + - **Parameter Merging:** + - It retrieves `parameters` from both the `AiModel` and the `Style` models. + - These parameters (which are JSON objects) are deeply merged using `array_replace_recursive()`. This ensures that specific parameters from the style can override or extend parameters defined at the model level. + - **Prompt Construction:** The plugin constructs the final prompt/workflow for the AI service. + - For ComfyUI, it takes the merged parameters (workflow JSON) and performs string replacements for placeholders like `__PROMPT__` (from `Style->prompt`), `__FILENAME__` (uploaded image filename), and `__MODEL_ID__` (from `AiModel->model_id`). + - Crucially, values for placeholders are JSON-encoded and then stripped of outer quotes to ensure safe injection into the JSON workflow, preventing syntax errors. + - **API Call:** The plugin sends the prepared request (e.g., prompt to ComfyUI, inference request to RunwareAI) to the external AI service. + - **Response Handling:** The plugin waits for a response from the AI service (e.g., `prompt_id` from ComfyUI, base64 image data from RunwareAI). +5. **Backend (ImageController@styleChangeRequest - continued):** + - Receives the initial response from the plugin (e.g., `prompt_id`). + - Updates the `Image` record with the `comfyui_prompt_id` and `style_id` for tracking. + - Returns a JSON response to the frontend with the `prompt_id` and `image_uuid`. +6. **Frontend (Home.vue - Polling for Result):** + - Upon receiving the `prompt_id`, the frontend starts polling `/api/images/fetch-styled/{prompt_id}`. +7. **Backend (ImageController@fetchStyledImage):** + - Retrieves the `Image` record using the `comfyui_prompt_id`. + - Loads the relevant AI plugin. + - Calls a method on the plugin (e.g., `waitForResult` for ComfyUI) to retrieve the final base64 encoded styled image data. + - Decodes the base64 image, saves it to `public/storage/uploads` (e.g., `styled_UUID.png`). + - Creates a new `Image` record in the database for the styled image, linking it to the original image and style, and setting `is_temp` to `true`. + - Returns a JSON response with the styled image's details. +8. **Frontend (Home.vue - Gallery Refresh):** + - Upon successful fetching of the styled image, the frontend calls `fetchImages()` to refresh the gallery, which now includes the newly created styled image. + +### 8.2. Image Synchronization + +- The `ImageController@index` method performs a synchronization between the `public/storage/uploads` directory and the `images` table in the database. +- It adds new image files found on disk to the database. +- It removes database entries for images that no longer exist on disk. +- This ensures the gallery always reflects the actual files in storage. + +### 8.3. Parameter Merging Logic + +- The `parameters` fields in `Style` and `AiModel` are stored as JSON in the database and automatically cast to PHP arrays by Eloquent. +- In the AI plugins (`ComfyUi.php`, `RunwareAi.php`), when constructing the AI service request: + - `$modelParams = $style->aiModel->parameters ?? [];` + - `$styleParams = $style->parameters ?? [];` + - `$mergedParams = array_replace_recursive($modelParams, $styleParams);` + - This `array_replace_recursive` function is crucial for deep merging, allowing style-specific parameters to override or extend model-level parameters. +- Placeholders (`__PROMPT__`, `__FILENAME__`, `__MODEL_ID__`) within the merged JSON are replaced with actual values. These values are first JSON-encoded and then trimmed of their outer quotes to ensure they are safely inserted into the JSON structure without breaking it. + +### 8.4. File Storage Configuration + +- The application uses Laravel's filesystem configuration. +- The `public` disk is configured in `config/filesystems.php` to point directly to `public_path('storage')`. +- Images uploaded via Filament (e.g., style previews) are stored in `public/storage/style_previews`. +- The `public/storage` directory is a symbolic link to `storage/app/public` (though the configuration was adjusted to point directly to `public/storage` for the `public` disk). + +This detailed overview should provide a solid foundation for understanding and rebuilding the AI StyleGallery application. diff --git a/Styles Flux kontext v1.md b/Styles Flux kontext v1.md new file mode 100644 index 0000000..92a72eb --- /dev/null +++ b/Styles Flux kontext v1.md @@ -0,0 +1,46 @@ +Transform the people in the image into a pastel drawing with gentle textures and soft hues. +Transform the people in the image into an abstract painting with vibrant swirls and emotional energy. +Transform the people in the image into pixel art characters with blocky shapes and a retro video game feel. +Transform the people in the image into low-poly 3D models with geometric surfaces and flat shading. +Transform the people in the image into chrome sculptures with reflective surfaces and metallic shine. +Transform the people in the image into blueprint-style figures with line work and measurement details. +Transform the people in the image into characters from a Studio Ghibli film with soft shading and magical warmth. +Transform the people in the image into a comic book scene with halftone shading and dynamic poses. +Transform the people in the image into characters from a 3D Pixar-style movie with expressive faces and big eyes. +Transform the people in the image into characters from The Simpsons with yellow skin and over-the-top expressions. +Transform the people in the image into LEGO-style figures with plastic texture and toy-like features. +Transform the people in the image into South Park-style cutout characters with flat shapes and goofy faces. +Transform the people in the image into medieval knights and nobles in a fantasy setting with ornate costumes. make the image bright +Transform the people in the image into elves and wizards from a magical forest with glowing runes +Transform the people in the image into steampunk characters with gears, goggles, and Victorian flair. +Transform the people in the image into ancient stone statues with cracks and moss growing on them +Transform the people in the image into a group of zombies with cartoony decay and goofy expressions. make the image bright. +Transform the people in the image into carved wooden figures with natural grain patterns, warm tones, and handmade imperfections. +Transform the people in the image into graffiti-style characters on a concrete wall, with spray paint textures, vibrant strokes, and urban flair. +Transform the people in the image into 3D-printed sculptures with plastic textures, visible print lines, and geometric precision. +Transform the people in the image into painted cardboard cutouts with flat surfaces, hand-drawn outlines, and theater-prop appearance. +Transform the people in the image into figures like on an ancient Greek vase, rendered in black-figure ceramic painting with elegant poses. +Transform the people in the image into medieval illuminated manuscript figures with gold leaf accents, stylized faces, and vibrant borders. +Transform the people in the image into Aztec stone carvings with geometric patterns, mythological motifs, and ancient symbolism. +Transform the people in the image into cracked porcelain dolls with aged glaze, fine fractures, and haunting fragility. +Transform the people in the image into cardboard paper dolls with tabbed joints, illustrated features, and moveable parts. +Transform the people in the image into stylized cut-paper collages, with torn edges, colorful flat textures, and assembled human shapes. +Transform the people in the image into 80s disco icons with glitter outfits, exaggerated hair, shiny dance floors, and neon lighting. +Transform the people in the image into soap opera villains mid-dramatic twist, with wind machines, sparkles, and over-the-top reactions. +Transform the people in the image into enchanted talking teapots, cups, and kitchen objects with faces and arms, ready to sing. +Transform the image into a sunny day in front of the Eiffel Tower in Paris, with the people posing like tourists, café tables nearby and pigeons flying around. +Transform the image into a night scene in Tokyo’s Shibuya Crossing, with neon lights, animated billboards, and a bustling futuristic city backdrop. +remove the background and Transform the image into a street performance in New York’s Times Square, with giant screens, yellow taxis, and amazed onlookers. +remove the background and Transform the image into a group photo in front of the Colosseum in Rome, with sunny weather, tourists with cameras, and ancient stone textures. Keep all the face details from the original +remove the background and Transform the image into a sunset beach party in Hawaii, with palm trees, ukuleles, tiki torches, and ocean waves. +remove the background and Transform the image into a hike through a misty rainforest with lush green foliage, vines, and curious animals peeking from the trees. +remove the background and Transform the image into a field of sunflowers stretching to the horizon, with bees buzzing and the people dressed in summer clothes. +remove the background and Transform the image into a magical floating island in the sky, with waterfalls cascading into the clouds and glowing plants surrounding the group. +remove the background and Transform the image into a dreamlike moonbase picnic with Earth visible above, moon buggies nearby, and silver spacesuits. +remove the background and Transform the image into a carnival at night, with colorful tents, cotton candy, blinking lights, and joyful movement. +remove the background and Transform the image into a retro 80s arcade hall, with glowing cabinets, pixelated screens, and classic synth lighting. +remove the background and Transform the image into a Moroccan market at sunset, with patterned rugs, spices in the air, and hanging lamps lighting the scene. +remove the background and Transform the image into a steampunk city plaza with brass towers, flying machines, and the group dressed in goggles and gears. +remove the background and Transform the image into a colorful Holi festival in India, with powder paint in the air and everyone covered in bright colors. +remove the background and Transform the image into a vintage movie set from the 1920s, with sepia tones, cameras on tracks, and dramatic film star poses. +insert into styles (title,prompt,description,preview_image,created_at,ai_model_id,enabled), VALUES(*titel*, *prompt*, *beschreibung*,'uploads/ComfyUI_*laufende Nummer mit 4 führenden Nullen*_.png, NOW(), 8, 1); \ No newline at end of file diff --git a/app/Api/Plugins/ApiPluginInterface.php b/app/Api/Plugins/ApiPluginInterface.php index 2805c2c..97cb068 100644 --- a/app/Api/Plugins/ApiPluginInterface.php +++ b/app/Api/Plugins/ApiPluginInterface.php @@ -11,5 +11,5 @@ interface ApiPluginInterface public function disable(): bool; public function getStatus(string $imageUUID): array; public function getProgress(string $imageUUID): array; - public function processImageStyleChange(string $imagePath, string $prompt, string $modelId, ?string $parameters = null): array; + public function processImageStyleChange(\App\Models\Image $image, \App\Models\Style $style): array; } \ No newline at end of file diff --git a/app/Api/Plugins/ComfyUi.php b/app/Api/Plugins/ComfyUi.php index 1163b4c..f20f08d 100644 --- a/app/Api/Plugins/ComfyUi.php +++ b/app/Api/Plugins/ComfyUi.php @@ -82,16 +82,16 @@ class ComfyUi implements ApiPluginInterface return $response->json(); } - public function processImageStyleChange(string $imagePath, string $prompt, string $modelId, ?string $parameters = null): array + public function processImageStyleChange(\App\Models\Image $image, \App\Models\Style $style): array { - $this->logInfo('Starting ComfyUI style change process.', ['image_path' => $imagePath]); + $this->logInfo('Starting ComfyUI style change process.', ['image_id' => $image->id, 'style_id' => $style->id]); // 1. Upload image to ComfyUI - $uploadResponse = $this->uploadImage($imagePath); + $uploadResponse = $this->uploadImage(public_path('storage/' . $image->path)); $filename = $uploadResponse['name']; // 2. Construct the prompt - $promptData = $this->constructPrompt($prompt, $filename, $modelId, $parameters); + $promptData = $this->constructPrompt($style, $filename); // 3. Queue the prompt $queueResponse = $this->queuePrompt($promptData); @@ -119,18 +119,39 @@ class ComfyUi implements ApiPluginInterface return $response->json(); } - private function constructPrompt(string $prompt, string $filename, string $modelId, ?string $parameters): array + private function constructPrompt(\App\Models\Style $style, string $filename): array { - if (empty($parameters)) { + $modelParams = $style->aiModel->parameters ?? []; + $styleParams = $style->parameters ?? []; + + if (empty($modelParams) && empty($styleParams)) { throw new \Exception('ComfyUI workflow (parameters) is missing.'); } - $workflow = $parameters; - $workflow = str_replace('__PROMPT__', $prompt, $workflow); - $workflow = str_replace('__FILENAME__', $filename, $workflow); - $workflow = str_replace('__MODEL_ID__', $modelId, $workflow); + // Use array_replace_recursive for a deep merge + $mergedParams = array_replace_recursive($modelParams, $styleParams); + $workflow = json_encode($mergedParams); - return json_decode($workflow, true); + // Properly escape the values for JSON injection + $prompt = substr(json_encode($style->prompt, JSON_UNESCAPED_SLASHES), 1, -1); + $filename_escaped = substr(json_encode($filename, JSON_UNESCAPED_SLASHES), 1, -1); + $modelId_escaped = substr(json_encode($style->aiModel->model_id, JSON_UNESCAPED_SLASHES), 1, -1); + + $workflow = str_replace('__PROMPT__', $prompt, $workflow); + $workflow = str_replace('__FILENAME__', $filename_escaped, $workflow); + $workflow = str_replace('__MODEL_ID__', $modelId_escaped, $workflow); + + $decodedWorkflow = json_decode($workflow, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + $this->logError('Failed to decode workflow JSON after placeholder replacement.', [ + 'json_error' => json_last_error_msg(), + 'workflow_string' => $workflow + ]); + throw new \Exception('Failed to construct valid ComfyUI workflow JSON: ' . json_last_error_msg()); + } + + return $decodedWorkflow; } private function queuePrompt(array $promptData): array diff --git a/app/Api/Plugins/RunwareAi.php b/app/Api/Plugins/RunwareAi.php index 2d37e17..67822be 100644 --- a/app/Api/Plugins/RunwareAi.php +++ b/app/Api/Plugins/RunwareAi.php @@ -71,10 +71,10 @@ class RunwareAi implements ApiPluginInterface return ['progress' => 0]; } - public function processImageStyleChange(string $imagePath, string $prompt, string $modelId, ?string $parameters = null): array + public function processImageStyleChange(\App\Models\Image $image, \App\Models\Style $style): array { // Step 1: Upload the original image - $uploadResult = $this->upload($imagePath); + $uploadResult = $this->upload(public_path('storage/' . $image->path)); if (!isset($uploadResult['data'][0]['imageUUID'])) { throw new \Exception('Image upload to AI service failed or returned no UUID.'); @@ -82,7 +82,7 @@ class RunwareAi implements ApiPluginInterface $seedImageUUID = $uploadResult['data'][0]['imageUUID']; // Step 2: Request style change using the uploaded image's UUID - $result = $this->styleChangeRequest($prompt, $seedImageUUID, $modelId, $parameters); + $result = $this->styleChangeRequest($style, $seedImageUUID); if (!isset($result['base64Data'])) { throw new \Exception('AI service did not return base64 image data.'); @@ -127,9 +127,9 @@ class RunwareAi implements ApiPluginInterface } } - private function styleChangeRequest(string $prompt, string $seedImageUUID, string $modelId, ?string $parameters = null): array + private function styleChangeRequest(\App\Models\Style $style, string $seedImageUUID): array { - $this->logInfo('Attempting style change request to RunwareAI.', ['prompt' => $prompt, 'seed_image_uuid' => $seedImageUUID]); + $this->logInfo('Attempting style change request to RunwareAI.', ['style_id' => $style->id, 'seed_image_uuid' => $seedImageUUID]); if (!$this->apiProvider->api_url || !$this->apiProvider->token) { $this->logError('RunwareAI API URL or Token not configured for style change.', ['provider_name' => $this->apiProvider->name]); throw new \Exception('RunwareAI API URL or Token not configured.'); @@ -139,17 +139,20 @@ class RunwareAi implements ApiPluginInterface $token = $this->apiProvider->token; $taskUUID = (string) Str::uuid(); + $modelParams = $style->aiModel->parameters ?? []; + $styleParams = $style->parameters ?? []; + $mergedParams = array_replace_recursive($modelParams, $styleParams); + $data = [ 'taskType' => 'imageInference', 'taskUUID' => $taskUUID, - 'positivePrompt' => $prompt, + 'positivePrompt' => $style->prompt, 'seedImage' => $seedImageUUID, 'outputType' => 'base64Data', - 'model' => $modelId, + 'model' => $style->aiModel->model_id, ]; - $decodedParameters = json_decode($parameters, true) ?? []; - foreach ($decodedParameters as $key => $value) { + foreach ($mergedParams as $key => $value) { $data[$key] = $value; } diff --git a/app/Filament/Resources/AiModelResource.php b/app/Filament/Resources/AiModelResource.php index 8078d56..faf5c55 100644 --- a/app/Filament/Resources/AiModelResource.php +++ b/app/Filament/Resources/AiModelResource.php @@ -47,6 +47,13 @@ class AiModelResource extends Resource ->preload() ->searchable(false) ->label(__('filament.resource.ai_model.form.api_providers')), + Forms\Components\Textarea::make('parameters') + ->label(__('filament.resource.ai_model.form.parameters')) + ->nullable() + ->rows(15) + ->json() + ->helperText(__('filament.resource.ai_model.form.parameters_help')) + ->formatStateUsing(fn (?array $state): ?string => $state ? json_encode($state, JSON_PRETTY_PRINT) : null), ]); } diff --git a/app/Filament/Resources/StyleResource.php b/app/Filament/Resources/StyleResource.php index 87865db..80af72b 100644 --- a/app/Filament/Resources/StyleResource.php +++ b/app/Filament/Resources/StyleResource.php @@ -56,7 +56,8 @@ class StyleResource extends Resource ->nullable() ->rows(15) ->json() - ->helperText(__('filament.resource.style.form.parameters_help')), + ->helperText(__('filament.resource.style.form.parameters_help')) + ->formatStateUsing(fn (?array $state): ?string => $state ? json_encode($state, JSON_PRETTY_PRINT) : null), Select::make('ai_model_id') ->relationship('aiModel', 'name') ->label(__('filament.resource.style.form.ai_model')) diff --git a/app/Http/Controllers/Api/ImageController.php b/app/Http/Controllers/Api/ImageController.php index 4d4b48b..35999f3 100644 --- a/app/Http/Controllers/Api/ImageController.php +++ b/app/Http/Controllers/Api/ImageController.php @@ -154,12 +154,7 @@ class ImageController extends Controller } $plugin = PluginLoader::getPlugin($apiProvider->plugin, $apiProvider); - $result = $plugin->processImageStyleChange( - public_path('storage/' . $image->path), - $style->prompt, - $style->aiModel->model_id, - $style->parameters - ); + $result = $plugin->processImageStyleChange($image, $style); // Update the image model with the ComfyUI prompt_id and style_id $image->comfyui_prompt_id = $result['prompt_id']; diff --git a/app/Models/AiModel.php b/app/Models/AiModel.php index ce879bd..f4fae78 100644 --- a/app/Models/AiModel.php +++ b/app/Models/AiModel.php @@ -13,6 +13,11 @@ class AiModel extends Model 'name', 'model_id', 'model_type', + 'parameters', + ]; + + protected $casts = [ + 'parameters' => 'array', ]; public function apiProviders() diff --git a/app/Models/Style.php b/app/Models/Style.php index 31f0a35..98a83a1 100644 --- a/app/Models/Style.php +++ b/app/Models/Style.php @@ -21,6 +21,7 @@ class Style extends Model protected $casts = [ 'enabled' => 'boolean', + 'parameters' => 'array', ]; public function aiModel() diff --git a/database/migrations/2025_08_04_134349_add_parameters_to_ai_models_table.php b/database/migrations/2025_08_04_134349_add_parameters_to_ai_models_table.php new file mode 100644 index 0000000..a25354b --- /dev/null +++ b/database/migrations/2025_08_04_134349_add_parameters_to_ai_models_table.php @@ -0,0 +1,28 @@ +json('parameters')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('ai_models', function (Blueprint $table) { + $table->dropColumn('parameters'); + }); + } +}; diff --git a/package-lock.json b/package-lock.json index ca50169..ba0a940 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "@fortawesome/free-solid-svg-icons": "^7.0.0", "@fortawesome/vue-fontawesome": "^3.1.1", "laravel-echo": "^2.1.7", - "pusher-js": "^8.4.0" + "pusher-js": "^8.4.0", + "vanilla-lazyload": "^19.1.3" }, "devDependencies": { "@inertiajs/vue3": "^1.0.0", @@ -3141,6 +3142,16 @@ "dev": true, "license": "MIT" }, + "node_modules/vanilla-lazyload": { + "version": "19.1.3", + "resolved": "https://registry.npmjs.org/vanilla-lazyload/-/vanilla-lazyload-19.1.3.tgz", + "integrity": "sha512-bBMERPu2AFJc35krS+8BOhq++c6dRfL6q368lJPnkS5U92fRQagTR3FsNta69/GukfZzDwDEjD5M3U7VuSiCDw==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://ko-fi.com/verlok" + } + }, "node_modules/vite": { "version": "5.4.19", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", diff --git a/package.json b/package.json index 695f53b..7c14226 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@fortawesome/free-solid-svg-icons": "^7.0.0", "@fortawesome/vue-fontawesome": "^3.1.1", "laravel-echo": "^2.1.7", - "pusher-js": "^8.4.0" + "pusher-js": "^8.4.0", + "vanilla-lazyload": "^19.1.3" } } diff --git a/resources/js/Components/StyleSelector.vue b/resources/js/Components/StyleSelector.vue index c2f651b..7598626 100644 --- a/resources/js/Components/StyleSelector.vue +++ b/resources/js/Components/StyleSelector.vue @@ -15,7 +15,7 @@ class="style-item" @click="selectStyle(style)" > - +

{{ style.title }}

{{ style.description }}

@@ -27,7 +27,7 @@