# Resource Transfer (Non-Character) — Implementation Plan ## Scope ### In scope - Add **Transfer** support and UI to these resource types: - **Looks** - **Outfits** - **Actions** - **Styles** - **Scenes** - **Detailers** ### Out of scope / explicitly excluded - **Characters** (existing flow may remain separate) - **Checkpoints** - **Presets** ## User experience requirements ### Entry points - A **Transfer** button should appear on **all supported resource detail pages**. - Clicking **Transfer** opens a transfer form for the currently viewed resource. ### Transfer form fields The form must allow the user to: - Select **target category** (destination resource type) - Edit **new name** - Edit **new id/slug** - Optionally choose **Use LLM** to regenerate JSON (recommended default: enabled) All of these fields should be **pre-populated using the existing resource’s information**. ### Post-submit behavior - After transfer completes, redirect to the **target category index page** (e.g. `/looks`). - **Highlight** the newly created item on the index page. - Do **not** auto-generate previews/covers. ## Current state (reference implementation) - Character-only transfer route exists: [`transfer_character()`](../app.py:2216) - Character detail page shows Transfer in header: [`templates/detail.html`](../templates/detail.html:79) - Resource detail templates do not currently show Transfer (examples): - Looks: [`templates/looks/detail.html`](../templates/looks/detail.html:123) - Outfits: [`templates/outfits/detail.html`](../templates/outfits/detail.html:118) - Actions: [`templates/actions/detail.html`](../templates/actions/detail.html:129) ## Proposed backend design ### 1) Add a generic resource transfer route Implement a category-based route: - `GET|POST /resource///transfer` Responsibilities: - Validate `category` is in allowlist: `looks,outfits,actions,styles,scenes,detailers` - Load the appropriate SQLAlchemy model instance by `slug` - Render a resource-agnostic transfer form on GET - On POST: - Validate new name/id constraints - Generate a safe/unique slug for the destination category - Create destination JSON (LLM path or non-LLM fallback) - Write JSON file to correct `data//` directory - Create DB row for the new resource - Redirect to destination index with highlight ### 2) Shared mapping table Create a mapping table/dict: - `category → model_class` - `category → target_dir` - `category → id_field` (e.g. `look_id`, `outfit_id`, ...) - `category → index_route` (e.g. `looks_index`) This mirrors the `target_type` mapping approach in [`transfer_character()`](../app.py:2251). ## JSON generation strategy ### LLM path (primary) - Use an explicit transfer prompt (existing code loads `transfer_system.txt` in [`transfer_character()`](../app.py:2289)). - Provide the source resource JSON as input context. - Instruct the LLM: - to output **JSON only** (no markdown) - to conform to the **target resource schema** - After parsing: - enforce required fields: `_id` and `_name` (pattern used in [`transfer_character()`](../app.py:2316)) ### Non-LLM fallback - Create a minimal template JSON for the target type. - Copy safe common fields where present (e.g. `tags`, `lora`). - Add a provenance description like “Transferred from ”. ## Frontend changes ### 1) Add Transfer button to supported resource detail pages Add a Transfer button near existing header actions, pointing to `/resource///transfer`. Targets: - [`templates/looks/detail.html`](../templates/looks/detail.html:123) - [`templates/outfits/detail.html`](../templates/outfits/detail.html:118) - [`templates/actions/detail.html`](../templates/actions/detail.html:129) - [`templates/styles/detail.html`](../templates/styles/detail.html:1) - [`templates/scenes/detail.html`](../templates/scenes/detail.html:1) - [`templates/detailers/detail.html`](../templates/detailers/detail.html:1) ### 2) Highlight the new item on the destination index Redirect scheme: - `/looks?highlight=` (similar for each category) Index templates to update: - [`templates/looks/index.html`](../templates/looks/index.html:1) - [`templates/outfits/index.html`](../templates/outfits/index.html:1) - [`templates/actions/index.html`](../templates/actions/index.html:1) - [`templates/styles/index.html`](../templates/styles/index.html:1) - [`templates/scenes/index.html`](../templates/scenes/index.html:1) - [`templates/detailers/index.html`](../templates/detailers/index.html:1) Implementation idea: - Add `data-slug="..."` to each item card/container and a small script that: - reads `highlight` query param - finds the element - scrolls into view - applies a temporary CSS class (e.g. `highlight-pulse`) ## Validation & error handling - Reject invalid/unsupported categories. - Enforce name length limits (existing character transfer uses `<= 100` in [`transfer_character()`](../app.py:2228)). - Sanitize id/slug to safe filename characters. - Ensure destination JSON filename is unique; if exists, auto-suffix `_2`, `_3`, ... - LLM error handling: - JSON parse failures should show a user-facing flash message - preserve form inputs on redirect if possible ## Testing Add tests covering: - Happy path transfer for one representative resource (e.g. look → outfit) - Invalid category rejected - Duplicate slug resolution works - LLM returns invalid JSON → proper failure handling ## Deliverables checklist - New transfer route and supporting helper mapping in app backend - Resource-agnostic transfer template - Transfer button on all supported detail pages - Index page highlight behavior - Prompt file verified/added for transfer conversions - Tests + minimal docs update