Fix ORA editor layer visibility and mask preview

- Layer visibility toggles now show/hide individual layer images on canvas
- Added tint red checkbox per layer for manual mask verification
- Mask preview modal uses CSS mask-image to show red overlay only on selected areas (white pixels become semi-transparent red, black pixels remain transparent)
- Added spinner icons to Open and Extract Mask buttons during long operations
This commit is contained in:
2026-03-27 15:26:25 -07:00
parent 0c1fb8ccca
commit efe72ff065
2 changed files with 227 additions and 96 deletions

View File

@@ -99,7 +99,8 @@ All file paths are relative to the project root: `/home/noti/dev/ai-game-2`
| Operation | Description |
|-----------|-------------|
| **Toggle Visibility** | Show/hide layer on canvas |
| **Toggle Visibility** | Show/hide specific layer image on canvas |
| **Tint Red Preview** | Apply red tint to layer for mask verification (client-only) |
| **Rename** | Change layer/entity name |
| **Delete** | Remove layer from ORA |
| **Reorder** | Move layer up/down in stack (changes z-index) |
@@ -109,18 +110,25 @@ All file paths are relative to the project root: `/home/noti/dev/ai-game-2`
## Canvas Display
### Image Rendering
- Layers rendered as stacked `<img>` elements with CSS positioning
- Base layer at bottom, entity layers stacked above
- Visibility controlled by `opacity: 0` or `opacity: 1`
### Image Rendering
- Individual layers rendered as stacked `<img>` elements with CSS positioning
- Layers stacked in order from list (visual z-index matches layer order)
- Visibility togglecheckbox hides/shows each layer's image on canvas
- Tint checkbox applies semi-transparent red overlay for mask verification (client-only, visual aid)
- No server-side compositing for preview
### Layer DOM Structure
```html
<div class="relative w-full h-full">
<img src="/api/image/base" class="absolute inset-0">
<img src="/api/image/door_0" class="absolute inset-0" style="opacity: 1">
<img src="/api/image/chest_0" class="absolute inset-0" style="opacity: 0">
<!-- Individual layers rendered in order (x-show controls visibility) -->
<template x-for="layer in layers">
<img
x-show="layer.visible"
:src="'/api/image/layer/' + layer.name"
class="absolute inset-0"
:style="layer.tintRed ? 'mix-blend-multiply; opacity: 0.6;' : ''"
>
</template>
<!-- Polygon overlay when active -->
<canvas id="polygon-canvas" class="absolute inset-0 pointer-events-auto"></canvas>
</div>
@@ -193,9 +201,12 @@ canvas.addEventListener('click', (e) => {
### Mask Preview Modal
When mask is ready:
1. Full-screen modal appears
2. Shows base image with mask applied as colored tint overlay
2. Shows base image with semi-transparent red overlay where the mask is white (selected area)
- Uses CSS `mask-image` property to use grayscale values as alpha channel
- White pixels = fully opaque red tint, black/dark pixels = transparent (no tint)
- Dark/gray areas of the mask remain invisible so you can clearly see the mask boundary
3. Three buttons:
- **Re-roll**: Re-run extraction with same params
- **Re-roll**: Re-run extraction with same params
- **Use This Mask**: Add masked layer to ORA, close modal
- **Cancel**: Discard mask, close modal
@@ -355,6 +366,14 @@ Serve a layer PNG. Returns image data.
#### `GET /api/image/base`
Serve base/merged image.
#### `GET /api/image/layer/<layer_name>`
Serve a specific layer as image.
**Query params:**
- `ora_path`: Path to ORA file
**Response:** PNG image data or 404 if layer not found.
#### `GET /api/image/polygon`
Serve polygon overlay image (for drawing mode).
@@ -506,12 +525,13 @@ Check if temp file was modified.
```
┌──────────────────────────────────────────────────────────────────────┐
│ [Open: ________________________] [Open File] [Settings ⚙] │
│ [Open: ________________________] [🌀 Open] [Settings ⚙]
├───────────────────┬──────────────────────────────────────────────────┤
│ LAYERS │ │
│ ☑ base │ │
│ ☑ door_0 │ [Image Canvas with │
│ ☐ chest_0 │ stacked layers]
│ ☑ base │ │
│ ☑ door_0 │ [Image Canvas with │
│ ☐ chest_0 │ stacked layers - visibility toggles
│ │ show/hide individual layer images] │
│ │ │
│ [Rename] [Delete] │ │
│ [▲ Up] [▼ Down] │ │
@@ -528,7 +548,7 @@ Check if temp file was modified.
│ MASK EXTRACTION │ │
│ Subject: [______]│ │
│ ☑ Use polygon │ │
│ [Extract Mask]
│ [🌀 Extract Mask]│ (spinner shown when extracting)
│ │ │
│ ─────────────────│ │
│ [Open in Krita] │ │
@@ -538,6 +558,8 @@ Check if temp file was modified.
└──────────────────────────────────────────────────────────────────────┘
```
Legend: ☑ = visible checkbox, ☒ = tint red checkbox (red when checked)
---
## Mask Preview Modal
@@ -547,7 +569,8 @@ Check if temp file was modified.
│ EXTRACTED MASK │
│ ───────────────────────────────────────────────────── │
│ │
│ [Base image with mask applied as tinted overlay]
│ [Base image with RED mask overlay]
│ (mask shown in semi-transparent red) │
│ │
│ [Re-roll] [Use This Mask] [Cancel] │
│ │
@@ -557,6 +580,7 @@ Check if temp file was modified.
- **Re-roll**: Re-runs extraction with same params
- **Use This Mask**: Calls `POST /api/layer/add`, closes modal
- **Cancel**: Closes modal, mask discarded
- **Red tint**: Uses CSS filter to render grayscale mask as semi-transparent red
---