feat: fix demo page to correspond refs

This commit is contained in:
2026-08-29 07:23:31 +05:00
parent 9ff732f2cf
commit d29ec73322
23 changed files with 1296 additions and 606 deletions
+897 -273
View File
File diff suppressed because it is too large Load Diff
+156 -65
View File
@@ -1,95 +1,186 @@
# DOM audit (phase A) # DOM audit (phase A)
_2026-08-29T01:35:12.853Z_ _2026-08-29T02:19:33.867Z_
## Сводка ## Сводка
| route | ref | added | removed | tag | text | | route | ref | added | removed | tag | text |
|---|---|---|---|---|---| |---|---|---|---|---|---|
| `/preview/demo` | demo.html | 4 | 8 | 2 | 1 | | `/preview/demo` | demo.html | 0 | 0 | 0 | 0 |
| `/preview/list-tools` | list-tools.html | 5 | 8 | 1 | 1 | | `/preview/list-tools` | list-tools.html | 78 | 22 | 5 | 1 |
| `/preview/tools/linear-gradient-png` | gradient.html | 4 | 5 | 1 | 1 | | `/preview/tools/linear-gradient-png` | gradient.html | 6 | 7 | 2 | 2 |
| `/preview/tools/remove-background-png` | background-remover.html | 4 | 5 | 1 | 1 | | `/preview/tools/remove-background-png` | background-remover.html | 8 | 7 | 2 | 2 |
## /preview/demo (vs demo.html) ## /preview/demo (vs demo.html)
- added: **4** (≈227 узл.), removed: **8** (≈210 узл.), tag: **2**, text: **1** - added: **0** (≈0 узл.), removed: **0** (≈0 узл.), tag: **0**, text: **0**
### Расхождения (топ 15) ### Расхождения (топ 0)
- `body > div:1` **tag**: ours `div` → ref `main` _расхождений нет_
- `body > div:1 > div:1 > header:1` **added** (`header` · 20 узл.)
- `body > div:1 > div:1 > div:2` **tag**: ours `div` → ref `section`
- `body > div:1 > div:1 > div:2 > div:1` **added** (`div` · 1 узл.)
- `body > div:1 > div:1 > div:2 > div:2` **text**: ours `` → ref `PNG PROCESSING WORKSPACE`
- `body > div:1 > div:1 > div:2 > div:2 > div:1` **added** (`div` · 203 узл.)
- `body > div:1 > div:1 > div:2 > div:2 > span:1` **removed** (`span` "/" · 1 узл.)
- `body > div:1 > div:1 > div:2 > div:2` **removed** (`div` · 8 узл.)
- `body > div:1 > div:1 > div:2 > div:3` **removed** (`div` · 7 узл.)
- `body > div:1 > div:1 > div:2 > div:4` **removed** (`div` · 97 узл.)
- `body > div:1 > div:1 > div:2 > div:5` **removed** (`div` · 5 узл.)
- `body > div:1 > div:1 > footer:3` **added** (`footer` · 3 узл.)
- `body > div:1 > div:1 > section:2` **removed** (`section` · 65 узл.)
- `body > div:1 > header:1` **removed** (`header` · 21 узл.)
- `body > div:1 > footer:3` **removed** (`footer` · 6 узл.)
## /preview/list-tools (vs list-tools.html) ## /preview/list-tools (vs list-tools.html)
- added: **5** (≈986 узл.), removed: **8** (≈297 узл.), tag: **1**, text: **1** - added: **78** (≈963 узл.), removed: **22** (≈274 узл.), tag: **5**, text: **1**
### Расхождения (топ 15) ### Расхождения (топ 106)
- `body > div:1` **tag**: ours `div` → ref `main` - `body > main:1 > header:1 > a:1 > span:3` **added** (`span` "/ LIST-TOOLS" · 1 узл.)
- `body > div:1 > div:1 > header:1` **added** (`header` · 20 узл.) - `body > main:1 > header:1 > a:1 > span:3` **removed** (`span` "/ CATALOG" · 1 узл.)
- `body > div:1 > div:1 > div:2 > div:1` **added** (`div` · 1 узл.) - `body > main:1 > header:1 > div:3 > span:1` **text**: ours `AUTO PIPELINE` → ref `LOCAL MODE / READY`
- `body > div:1 > div:1 > div:2 > div:2 > section:1` **added** (`section` · 963 узл.) - `body > main:1 > section:2` **tag**: ours `section` → ref `div`
- `body > div:1 > div:1 > div:2 > div:2 > div:1` **removed** (`div` "EASY-PNG-TOOLS / CATALOG" · 1 узл.) - `body > main:1 > section:2 > header:1` **added** (`header` · 4 узл.)
- `body > div:1 > div:1 > div:2 > div:2 > h1:2` **removed** (`h1` "Tool catalog" · 1 узл.) - `body > main:1 > section:2 > section:2` **tag**: ours `section` → ref `div`
- `body > div:1 > div:1 > div:2 > div:2 > p:3` **removed** (`p` "Focused utilities for working with PNG. Inspect, transform, and export — locally in your browser." · 1 узл.) - `body > main:1 > section:2 > section:2 > header:1` **added** (`header` · 4 узл.)
- `body > div:1 > div:1 > div:2 > div:2` **removed** (`div` · 4 узл.) - `body > main:1 > section:2 > section:2 > div:2 > a:1` **added** (`a` · 7 узл.)
- `body > div:1 > div:1 > footer:3` **text**: ours `` → ref `ALL OPERATIONS RUN LOCALLY YOUR FILES NEVER LEAVE THIS DEVICE` - `body > main:1 > section:2 > section:2 > div:2 > a:2` **added** (`a` · 7 узл.)
- `body > div:1 > div:1 > footer:3 > span:1` **added** (`span` "v0.1.0" · 1 узл.) - `body > main:1 > section:2 > section:2 > div:2 > a:3` **added** (`a` · 7 узл.)
- `body > div:1 > div:1 > footer:3 > span:2` **added** (`span` "© 2026" · 1 узл.) - `body > main:1 > section:2 > section:2 > div:2 > a:4` **added** (`a` · 7 узл.)
- `body > div:1 > div:1 > footer:3 > span:1` **removed** (`span` "•" · 1 узл.) - `body > main:1 > section:2 > section:2 > div:2 > a:5` **added** (`a` · 7 узл.)
- `body > div:1 > div:1 > div:2` **removed** (`div` · 13 узл.) - `body > main:1 > section:2 > section:2 > div:2 > a:6` **added** (`a` · 7 узл.)
- `body > div:1 > div:1 > div:3` **removed** (`div` · 255 узл.) - `body > main:1 > section:2 > section:2 > div:2 > a:7` **added** (`a` · 7 узл.)
- `body > div:1 > header:1` **removed** (`header` · 21 узл.) - `body > main:1 > section:2 > section:2 > div:2 > a:8` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:9` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:10` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:11` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:12` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:13` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:14` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:15` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:16` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:17` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:18` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:19` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:20` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > a:21` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > div:1` **removed** (`div` "EASY-PNG-TOOLS / CATALOG" · 1 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > h1:2` **removed** (`h1` "Tool catalog" · 1 узл.)
- `body > main:1 > section:2 > section:2 > div:2 > p:3` **removed** (`p` "Focused utilities for working with PNG. Inspect, transform, and export — locally in your browser." · 1 узл.)
- `body > main:1 > section:2 > section:2 > div:2` **removed** (`div` · 4 узл.)
- `body > main:1 > section:2 > section:3` **tag**: ours `section` → ref `div`
- `body > main:1 > section:2 > section:3 > header:1` **added** (`header` · 4 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:1` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:2` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:3` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:4` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:5` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:6` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:7` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:8` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:9` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:10` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:11` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:12` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:13` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:14` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:15` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:16` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:17` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:18` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:19` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:20` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > a:21` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > svg:1` **removed** (`svg` · 1 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > button:2` **removed** (`button` "ALL" · 1 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > button:3` **removed** (`button` "CONVERT" · 1 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > button:4` **removed** (`button` "TRANSPARENCY" · 1 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > button:5` **removed** (`button` "COLOR" · 1 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > button:6` **removed** (`button` "GEOMETRY" · 1 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > button:7` **removed** (`button` "FILTERS" · 1 узл.)
- `body > main:1 > section:2 > section:3 > div:2 > button:8` **removed** (`button` "ANALYZE" · 1 узл.)
- `body > main:1 > section:2 > section:3 > label:1` **removed** (`label` · 3 узл.)
- `body > main:1 > section:2 > section:4` **tag**: ours `section` → ref `div`
- `body > main:1 > section:2 > section:4 > header:1` **added** (`header` · 4 узл.)
- `body > main:1 > section:2 > section:4 > div:2` **tag**: ours `div` → ref `section`
- `body > main:1 > section:2 > section:4 > div:2 > a:1` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:2` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:3` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:4` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:5` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:6` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:7` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:8` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:9` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:10` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:11` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:12` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:13` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:14` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:15` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:16` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:17` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:18` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:19` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:20` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:21` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:22` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:23` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:24` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > a:25` **added** (`a` · 7 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > div:1` **removed** (`div` · 3 узл.)
- `body > main:1 > section:2 > section:4 > div:2 > div:2` **removed** (`div` · 41 узл.)
- `body > main:1 > section:2 > section:4 > section:2` **removed** (`section` · 45 узл.)
- `body > main:1 > section:2 > section:4 > section:3` **removed** (`section` · 45 узл.)
- `body > main:1 > section:2 > section:4 > section:4` **removed** (`section` · 45 узл.)
- `body > main:1 > section:2 > section:4 > section:5` **removed** (`section` · 37 узл.)
- `body > main:1 > section:2 > section:4 > section:6` **removed** (`section` · 37 узл.)
- `body > main:1 > section:2 > section:5` **added** (`section` · 132 узл.)
- `body > main:1 > section:2 > section:6` **added** (`section` · 62 узл.)
- `body > main:1 > section:2 > section:7` **added** (`section` · 27 узл.)
- `body > main:1 > section:2 > section:8` **added** (`section` · 97 узл.)
- `body > main:1 > section:2 > section:9` **added** (`section` · 153 узл.)
- `body > main:1 > section:2 > footer:4` **removed** (`footer` "ALL OPERATIONS RUN LOCALLY YOUR FILES NEVER LEAVE THIS DEVICE" · 2 узл.)
- `body > main:1 > footer:3` **added** (`footer` · 6 узл.)
## /preview/tools/linear-gradient-png (vs gradient.html) ## /preview/tools/linear-gradient-png (vs gradient.html)
- added: **4** (≈186 узл.), removed: **5** (≈99 узл.), tag: **1**, text: **1** - added: **6** (≈155 узл.), removed: **7** (≈73 узл.), tag: **2**, text: **2**
### Расхождения (топ 11) ### Расхождения (топ 17)
- `body > div:1` **tag**: ours `div` → ref `main` - `body > main:1 > header:1 > a:1 > span:3` **added** (`span` "/ TOOLS / LINEAR-GRADIENT-PNG" · 1 узл.)
- `body > div:1 > div:1 > header:1` **added** (`header` · 20 узл.) - `body > main:1 > header:1 > a:1 > span:3` **removed** (`span` "/ GRADIENT" · 1 узл.)
- `body > div:1 > div:1 > div:2` **text**: ours `` → ref `PNG PROCESSING SINGLE TOOL` - `body > main:1 > header:1 > div:3 > span:1` **text**: ours `AUTO PIPELINE` → ref `LIVE PREVIEW`
- `body > div:1 > div:1 > div:2 > div:1` **added** (`div` · 1 узл.) - `body > main:1 > div:2 > section:1` **tag**: ours `section` → ref `div`
- `body > div:1 > div:1 > div:2 > div:2` **added** (`div` · 162 узл.) - `body > main:1 > div:2 > section:1` **text**: ours `` → ref `PNG PROCESSING SINGLE TOOL`
- `body > div:1 > div:1 > div:2 > span:1` **removed** (`span` "/" · 1 узл.) - `body > main:1 > div:2 > section:1 > section:1` **added** (`section` · 145 узл.)
- `body > div:1 > div:1 > footer:3` **added** (`footer` · 3 узл.) - `body > main:1 > div:2 > section:1 > span:1` **removed** (`span` "/" · 1 узл.)
- `body > div:1 > div:1 > div:2` **removed** (`div` · 6 узл.) - `body > main:1 > div:2 > section:2` **tag**: ours `section` → ref `div`
- `body > div:1 > div:1 > div:3` **removed** (`div` · 66 узл.) - `body > main:1 > div:2 > section:2 > header:1` **added** (`header` · 5 узл.)
- `body > div:1 > header:1` **removed** (`header` · 21 узл.) - `body > main:1 > div:2 > section:2 > div:2 > div:1` **added** (`div` · 1 узл.)
- `body > div:1 > footer:3` **removed** (`footer` · 5 узл.) - `body > main:1 > div:2 > section:2 > div:2 > div:2` **added** (`div` · 1 узл.)
- `body > main:1 > div:2 > section:2 > div:2 > h1:1` **removed** (`h1` "Gradient background." · 1 узл.)
- `body > main:1 > div:2 > section:2 > div:2 > p:2` **removed** (`p` "Create a clean, export-ready gradient with precise control over color, direction and transparency." · 1 узл.)
- `body > main:1 > div:2 > section:2 > span:2` **removed** (`span` "LIVE PREVIEW" · 2 узл.)
- `body > main:1 > div:2 > div:3` **removed** (`div` · 66 узл.)
- `body > main:1 > footer:3 > span:2` **added** (`span` "pipeline is local-only" · 2 узл.)
- `body > main:1 > footer:3 > span:2` **removed** (`span` "gradient tool · local-only" · 1 узл.)
## /preview/tools/remove-background-png (vs background-remover.html) ## /preview/tools/remove-background-png (vs background-remover.html)
- added: **4** (≈186 узл.), removed: **5** (≈104 узл.), tag: **1**, text: **1** - added: **8** (≈153 узл.), removed: **7** (≈78 узл.), tag: **2**, text: **2**
### Расхождения (топ 11) ### Расхождения (топ 19)
- `body > div:1` **tag**: ours `div` → ref `main` - `body > main:1 > header:1 > a:1 > span:3` **added** (`span` "/ TOOLS / REMOVE-BACKGROUND-PNG" · 1 узл.)
- `body > div:1 > div:1 > header:1` **added** (`header` · 20 узл.) - `body > main:1 > header:1 > a:1 > span:3` **removed** (`span` "/ BACKGROUND REMOVER" · 1 узл.)
- `body > div:1 > div:1 > div:2` **text**: ours `` → ref `PNG PROCESSING SINGLE TOOL` - `body > main:1 > header:1 > div:3 > span:1` **text**: ours `AUTO PIPELINE` → ref `AUTO PROCESSING`
- `body > div:1 > div:1 > div:2 > div:1` **added** (`div` · 1 узл.) - `body > main:1 > div:2 > section:1` **tag**: ours `section` → ref `div`
- `body > div:1 > div:1 > div:2 > div:2` **added** (`div` · 162 узл.) - `body > main:1 > div:2 > section:1` **text**: ours `` → ref `PNG PROCESSING SINGLE TOOL`
- `body > div:1 > div:1 > div:2 > span:1` **removed** (`span` "/" · 1 узл.) - `body > main:1 > div:2 > section:1 > section:1` **added** (`section` · 138 узл.)
- `body > div:1 > div:1 > footer:3` **added** (`footer` · 3 узл.) - `body > main:1 > div:2 > section:1 > span:1` **removed** (`span` "/" · 1 узл.)
- `body > div:1 > div:1 > div:2` **removed** (`div` · 6 узл.) - `body > main:1 > div:2 > section:2` **tag**: ours `section` → ref `div`
- `body > div:1 > div:1 > div:3` **removed** (`div` · 71 узл.) - `body > main:1 > div:2 > section:2 > header:1` **added** (`header` · 5 узл.)
- `body > div:1 > header:1` **removed** (`header` · 21 узл.) - `body > main:1 > div:2 > section:2 > div:2 > div:1` **added** (`div` · 4 узл.)
- `body > div:1 > footer:3` **removed** (`footer` · 5 узл.) - `body > main:1 > div:2 > section:2 > div:2 > div:2` **added** (`div` · 1 узл.)
- `body > main:1 > div:2 > section:2 > div:2 > div:3` **added** (`div` · 1 узл.)
- `body > main:1 > div:2 > section:2 > div:2 > h1:1` **removed** (`h1` "Remove background." · 1 узл.)
- `body > main:1 > div:2 > section:2 > div:2 > p:2` **removed** (`p` "Select a background color and tune the edge detection. Changes are processed automatically in your browser." · 1 узл.)
- `body > main:1 > div:2 > section:2 > span:2` **removed** (`span` "LIVE PREVIEW" · 2 узл.)
- `body > main:1 > div:2 > div:3` **removed** (`div` · 71 узл.)
- `body > main:1 > footer:3 > span:2` **added** (`span` "pipeline is local-only" · 2 узл.)
- `body > main:1 > footer:3 > span:2` **removed** (`span` "background remover · local-only" · 1 узл.)
- `body > div:2` **added** (`div` · 1 узл.)
+1 -1
View File
@@ -23,6 +23,6 @@
%sveltekit.head% %sveltekit.head%
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div> %sveltekit.body%
</body> </body>
</html> </html>
@@ -1,27 +0,0 @@
<script lang="ts">
import BlueprintGrid from "./BlueprintGrid.svelte";
let { children } = $props();
</script>
<div class="app-shell">
<BlueprintGrid />
<div class="app-content">
{@render children()}
</div>
</div>
<style>
.app-shell {
position: relative;
min-height: 100dvh;
background: var(--background);
color: var(--foreground);
font-family: var(--font-sans);
}
.app-content {
position: relative;
z-index: 1;
}
</style>
+4 -1
View File
@@ -1,16 +1,19 @@
<script lang="ts"> <script lang="ts">
import type { Snippet } from "svelte"; import type { Snippet } from "svelte";
import { Check } from "@lucide/svelte";
type Tone = "default" | "accent" | "success" | "amber"; type Tone = "default" | "accent" | "success" | "amber";
interface Props { interface Props {
tone?: Tone; tone?: Tone;
check?: boolean;
children: Snippet; children: Snippet;
} }
let { tone = "default", children }: Props = $props(); let { tone = "default", check = false, children }: Props = $props();
</script> </script>
<span class="badge badge--{tone}"> <span class="badge badge--{tone}">
{#if check}<Check size={12} />{/if}
{#if children}{@render children()}{/if} {#if children}{@render children()}{/if}
</span> </span>
@@ -1,22 +0,0 @@
<div class="blueprint-grid" aria-hidden="true"></div>
<style>
.blueprint-grid {
position: absolute;
inset: 0;
pointer-events: none;
background-image:
linear-gradient(
to right,
color-mix(in srgb, var(--line) 45%, transparent) 1px,
transparent 1px
),
linear-gradient(
to bottom,
color-mix(in srgb, var(--line) 45%, transparent) 1px,
transparent 1px
);
background-size: 32px 32px;
opacity: 0.55;
}
</style>
+31 -19
View File
@@ -1,12 +1,14 @@
<script lang="ts"> <script lang="ts">
import Field from "./Field.svelte"; import { ChevronDown } from "@lucide/svelte";
interface Props { interface Props {
label: string; label: string;
value: string; value: string;
dark?: boolean;
chevron?: boolean;
oninput?: (value: string) => void; oninput?: (value: string) => void;
} }
let { label, value = $bindable(), oninput }: Props = $props(); let { label, value = $bindable(), dark = false, chevron = false, oninput }: Props = $props();
function handle(e: Event) { function handle(e: Event) {
value = (e.target as HTMLInputElement).value; value = (e.target as HTMLInputElement).value;
@@ -14,43 +16,53 @@
} }
</script> </script>
<Field {label}> <div class="control-block">
<label>{label}</label>
<div class="color-field"> <div class="color-field">
<input type="color" class="color-swatch" {value} oninput={handle} /> <span class="swatch" class:dark style="background:{value}"></span>
<input <input type="text" aria-label={label} {value} oninput={handle} spellcheck="false" />
type="text" {#if chevron}<ChevronDown size={14} />{/if}
class="color-hex"
{value}
oninput={handle}
spellcheck="false"
/>
</div> </div>
</Field> </div>
<style> <style>
.control-block {
display: flex;
flex-direction: column;
gap: 8px;
}
label {
font: 10px var(--font-mono);
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
}
.color-field { .color-field {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.4rem; gap: 0.4rem;
} }
.color-swatch { .swatch {
width: 2rem; width: 1.6rem;
height: 1.6rem; height: 1.6rem;
padding: 0;
border: 1px solid var(--line); border: 1px solid var(--line);
border-radius: var(--radius); border-radius: var(--radius);
background: transparent; flex: none;
cursor: pointer;
} }
.color-hex { .color-field input {
flex: 1; flex: 1;
min-width: 0;
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 11px; font-size: 11px;
color: var(--foreground); color: var(--foreground);
background: var(--background); background: var(--background);
border: 1px solid var(--line); border: 1px solid var(--line);
border-radius: var(--radius); border-radius: var(--radius);
padding: 0.3rem 0.5rem; padding: 0.35rem 0.5rem;
text-transform: uppercase; text-transform: uppercase;
} }
.color-field :global(svg) {
color: var(--muted);
flex: none;
}
</style> </style>
@@ -1,23 +1,34 @@
<script lang="ts"> <script lang="ts">
import { Download } from "@lucide/svelte"; import { Download, ArrowDownToLine } from "@lucide/svelte";
import Button from "./Button.svelte";
interface Props { interface Props {
label: string; label: string;
size?: string;
onclick?: () => void; onclick?: () => void;
} }
let { label, size, onclick }: Props = $props(); let { label, onclick }: Props = $props();
</script> </script>
<Button icon={Download} variant="primary" onclick={() => onclick?.()}> <button class="download-btn" type="button" onclick={() => onclick?.()}>
{label}{#if size}<span class="dl-size">{size}</span>{/if} <Download size={16} />
</Button> {label}
<ArrowDownToLine size={14} />
</button>
<style> <style>
.dl-size { .download-btn {
font-family: var(--font-mono); display: inline-flex;
font-size: 10px; align-items: center;
opacity: 0.8; gap: 8px;
border: 1px solid var(--blue);
background: var(--blue);
color: #fff;
font: 12px var(--font-mono);
letter-spacing: 0.04em;
padding: 9px 14px;
border-radius: var(--radius);
cursor: pointer;
}
.download-btn:hover {
background: color-mix(in srgb, var(--blue) 88%, #000);
} }
</style> </style>
+9 -8
View File
@@ -9,22 +9,23 @@
let { compact = false, children }: Props = $props(); let { compact = false, children }: Props = $props();
</script> </script>
<div class="field-grid" class:compact> <div class="controls" class:compact>
{@render children()} {@render children()}
</div> </div>
<style> <style>
.field-grid { .controls {
display: grid; display: flex;
grid-template-columns: 1.1fr 1fr 1.2fr; flex-direction: column;
gap: 16px; gap: 14px;
} }
.field-grid.compact { .controls.compact {
display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 14px;
} }
@media (max-width: 800px) { @media (max-width: 800px) {
.field-grid, .controls.compact {
.field-grid.compact {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
+1 -1
View File
@@ -8,7 +8,7 @@
<div class="meta-row"> <div class="meta-row">
<span class="meta-caption">{caption}</span> <span class="meta-caption">{caption}</span>
<span class="meta-value">{value}</span> <b class="meta-value">{value}</b>
</div> </div>
<style> <style>
+23 -8
View File
@@ -3,14 +3,19 @@
interface Props { interface Props {
label: string; label: string;
caption: string;
children: Snippet; children: Snippet;
} }
let { label, children }: Props = $props();
let { label, caption, children }: Props = $props();
</script> </script>
<div class="preview-tile"> <div class="preview-tile">
<span class="tile-label">{label}</span>
<div class="tile-canvas">{@render children()}</div> <div class="tile-canvas">{@render children()}</div>
<div class="tile-label">
<span>{label}</span>
<b>{caption}</b>
</div>
</div> </div>
<style> <style>
@@ -23,15 +28,25 @@
padding: 0.4rem; padding: 0.4rem;
background: var(--panel); background: var(--panel);
} }
.tile-label {
font: 10px var(--font-mono);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--blue);
}
.tile-canvas { .tile-canvas {
border-radius: var(--radius); border-radius: var(--radius);
overflow: hidden; overflow: hidden;
min-height: 120px; min-height: 120px;
} }
.tile-label {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.5rem;
}
.tile-label span {
font: 10px var(--font-mono);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--blue);
}
.tile-label b {
font: 400 10px var(--font-mono);
color: var(--muted);
}
</style> </style>
@@ -7,9 +7,10 @@
title: string; title: string;
meta?: string; meta?: string;
actions?: Snippet; actions?: Snippet;
unwrapActions?: boolean;
} }
let { label, title, meta, actions }: Props = $props(); let { label, title, meta, actions, unwrapActions = false }: Props = $props();
</script> </script>
<div class="section-label"> <div class="section-label">
@@ -18,9 +19,13 @@
<strong>{title}{#if meta}<em>{meta}</em>{/if}</strong> <strong>{title}{#if meta}<em>{meta}</em>{/if}</strong>
</div> </div>
{#if actions} {#if actions}
<div class="section-actions"> {#if unwrapActions}
{@render actions()} {@render actions()}
</div> {:else}
<div class="section-actions">
{@render actions()}
</div>
{/if}
{/if} {/if}
</div> </div>
+1 -1
View File
@@ -22,7 +22,7 @@
<button <button
type="button" type="button"
class="segment" class="segment"
class:active={value === opt.value} class:selected={value === opt.value}
aria-pressed={value === opt.value} aria-pressed={value === opt.value}
onclick={() => select(opt.value)} onclick={() => select(opt.value)}
> >
+21 -24
View File
@@ -1,6 +1,4 @@
<script lang="ts"> <script lang="ts">
import Field from "./Field.svelte";
interface Props { interface Props {
label: string; label: string;
value: number; value: number;
@@ -26,34 +24,33 @@
} }
</script> </script>
<Field {label}> <div class="control-block">
<div class="slider-field"> <label>{label} <output>{value}{suffix}</output></label>
<input type="range" {min} {max} {step} {value} oninput={handle} /> <input type="range" {min} {max} {step} {value} oninput={handle} />
<span class="slider-value"> </div>
{value}
{#if suffix}
{suffix}
{/if}
</span>
</div>
</Field>
<style> <style>
.slider-field { .control-block {
display: flex;
flex-direction: column;
gap: 8px;
}
label {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.6rem; justify-content: space-between;
width: 100%; gap: 0.5rem;
font: 10px var(--font-mono);
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
}
output {
font: 11px var(--font-mono);
color: var(--foreground);
} }
input[type="range"] { input[type="range"] {
flex: 1; width: 100%;
accent-color: var(--blue); accent-color: var(--blue);
} }
.slider-value {
font-family: var(--font-mono);
font-size: 11px;
color: var(--foreground);
min-width: 4ch;
text-align: right;
}
</style> </style>
+1 -1
View File
@@ -1,4 +1,4 @@
<span class="status-dot" aria-hidden="true"></span> <i class="status-dot" aria-hidden="true"></i>
<style> <style>
.status-dot { .status-dot {
+19 -21
View File
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { Snippet } from "svelte"; import type { Snippet } from "svelte";
import { GripVertical, X } from "@lucide/svelte"; import { GripVertical, X, Ellipsis } from "@lucide/svelte";
interface Props { interface Props {
index: number; index: number;
@@ -15,10 +15,8 @@
</script> </script>
<article class="step-card"> <article class="step-card">
<div class="step-rail"> <div class="step-index">{index.toString().padStart(2, "0")}</div>
<span class="step-index">{index.toString().padStart(2, "0")}</span> <GripVertical size={16} class="drag" aria-hidden="true" />
<span class="drag" aria-hidden="true"><GripVertical size={16} /></span>
</div>
<div class="step-body"> <div class="step-body">
<div class="step-heading"> <div class="step-heading">
<div class="step-heading-text"> <div class="step-heading-text">
@@ -35,43 +33,46 @@
> >
<X size={16} /> <X size={16} />
</button> </button>
<Ellipsis size={17} />
</div> </div>
</div> </div>
{#if children} {#if children}
<div class="step-content">{@render children()}</div> {@render children()}
{/if} {/if}
</div> </div>
</article> </article>
<style> <style>
.step-card { .step-card {
display: flex; display: grid;
grid-template-columns: 46px 1fr;
grid-template-rows: auto 1fr;
border: 1px solid var(--line); border: 1px solid var(--line);
border-radius: var(--radius); border-radius: var(--radius);
background: var(--panel); background: var(--panel);
overflow: hidden; overflow: hidden;
} }
.step-rail {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
min-width: 46px;
padding: 17px 0 17px 15px;
border-right: 1px solid var(--line);
}
.step-index { .step-index {
grid-column: 1;
grid-row: 1;
font: 600 22px var(--font-mono); font: 600 22px var(--font-mono);
line-height: 1; line-height: 1;
color: var(--blue); color: var(--blue);
padding: 17px 0 6px 15px;
border-right: 1px solid var(--line);
} }
.drag { .drag {
grid-column: 1;
grid-row: 2;
color: var(--muted); color: var(--muted);
cursor: grab; cursor: grab;
display: inline-flex; display: inline-flex;
padding: 0 0 17px 15px;
border-right: 1px solid var(--line);
} }
.step-body { .step-body {
flex: 1; grid-column: 2;
grid-row: 1 / span 2;
min-width: 0; min-width: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -115,10 +116,7 @@
.step-remove:hover { .step-remove:hover {
color: var(--danger); color: var(--danger);
} }
.step-content { .step-body > :not(.step-heading) {
padding: 0.75rem; padding: 0.75rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
} }
</style> </style>
+1 -1
View File
@@ -21,7 +21,7 @@
aria-label={label} aria-label={label}
onclick={handle} onclick={handle}
> >
<span class="toggle-knob"></span> <i class="toggle-knob"></i>
</button> </button>
<style> <style>
+2 -2
View File
@@ -10,10 +10,10 @@
let { label, checked = $bindable(), onchange }: Props = $props(); let { label, checked = $bindable(), onchange }: Props = $props();
</script> </script>
<label class="toggle-row"> <div class="toggle-row">
<span>{label}</span> <span>{label}</span>
<Toggle bind:checked {onchange} {label} /> <Toggle bind:checked {onchange} {label} />
</label> </div>
<style> <style>
.toggle-row { .toggle-row {
+8 -3
View File
@@ -16,11 +16,11 @@
</script> </script>
<header class="topbar"> <header class="topbar">
<div class="brand"> <a class="brand" href={resolve("/")}>
<span class="brand-mark">EP</span> <span class="brand-mark">EP</span>
<span>easy-png-tools</span> <span>easy-png-tools</span>
{#if crumb}<span class="version">{crumb}</span>{/if} {#if crumb}<span class="version">{crumb}</span>{/if}
</div> </a>
<nav class="nav"> <nav class="nav">
<a href={resolve("/preview/demo")}>Workspace</a> <a href={resolve("/preview/demo")}>Workspace</a>
<a href={resolve("/preview/list-tools")}>Catalog</a> <a href={resolve("/preview/list-tools")}>Catalog</a>
@@ -47,7 +47,7 @@
class="lang-btn" class="lang-btn"
class:active={lang === "RU"} class:active={lang === "RU"}
onclick={() => (lang = "RU")}>RU</button onclick={() => (lang = "RU")}>RU</button
> ><span class="lang-div">/</span>
<button <button
type="button" type="button"
class="lang-btn" class="lang-btn"
@@ -138,6 +138,11 @@
.lang-btn + .lang-btn { .lang-btn + .lang-btn {
border-left: 1px solid var(--line); border-left: 1px solid var(--line);
} }
.lang-div {
color: var(--muted);
font: 10px var(--font-mono);
align-self: center;
}
.lang-btn.active { .lang-btn.active {
background: var(--foreground); background: var(--foreground);
color: var(--background); color: var(--background);
@@ -30,8 +30,8 @@
class="ws" class="ws"
style="--ws-cols:{columns};--ws-gap:{gap};--ws-pad:{padding};--ws-max:{maxWidth};--ws-sticky:{stickyTop};--ws-pad-mobile:{padMobile};--ws-gap-mobile:{gapMobile};" style="--ws-cols:{columns};--ws-gap:{gap};--ws-pad:{padding};--ws-max:{maxWidth};--ws-sticky:{stickyTop};--ws-pad-mobile:{padMobile};--ws-gap-mobile:{gapMobile};"
> >
<div class="ws-settings">{@render settings()}</div> <section class="ws-settings">{@render settings()}</section>
<div class="ws-preview">{@render preview()}</div> {@render preview()}
</div> </div>
<style> <style>
@@ -47,19 +47,11 @@
.ws-settings { .ws-settings {
min-width: 0; min-width: 0;
} }
.ws-preview {
position: sticky;
top: var(--ws-sticky);
min-width: 0;
}
@media (max-width: 800px) { @media (max-width: 800px) {
.ws { .ws {
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: var(--ws-gap-mobile); gap: var(--ws-gap-mobile);
padding: var(--ws-pad-mobile); padding: var(--ws-pad-mobile);
} }
.ws-preview {
position: static;
}
} }
</style> </style>
+11 -7
View File
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { page } from "$app/stores"; import { page } from "$app/stores";
import AppShell from "$lib/components/kit/AppShell.svelte";
import TopBar from "$lib/components/kit/TopBar.svelte"; import TopBar from "$lib/components/kit/TopBar.svelte";
import { Link2 } from "@lucide/svelte";
import "$lib/styles/design2.css"; import "$lib/styles/design2.css";
import type { Snippet } from "svelte"; import type { Snippet } from "svelte";
@@ -39,19 +39,23 @@
.replace(/^\//, "") .replace(/^\//, "")
.split("/") .split("/")
.filter(Boolean); .filter(Boolean);
const label = seg.length ? seg.join(" / ").toUpperCase() : "PREVIEW"; const map: Record<string, string> = { demo: "WORKSPACE" };
const label = seg.length
? seg.map((s) => map[s] ?? s.toUpperCase()).join(" / ")
: "PREVIEW";
return "/ " + label; return "/ " + label;
} }
</script> </script>
<div class="preview-root" data-theme={theme}> <main class="preview-root" data-theme={theme}>
<TopBar {theme} {crumb} ontoggle={toggle} /> <TopBar {theme} {crumb} ontoggle={toggle} />
<AppShell>{@render children()}</AppShell> {@render children()}
<footer class="preview-footer"> <footer class="preview-footer">
<span class="version">v0.1.0</span> <span>easy-png-tools <b>v2.4.0</b></span>
<span class="copy">© 2026</span> <span><Link2 size={13} /> pipeline is local-only</span>
<span>© 2024</span>
</footer> </footer>
</div> </main>
<style> <style>
.preview-footer { .preview-footer {
+38 -55
View File
@@ -1,19 +1,17 @@
<script lang="ts"> <script lang="ts">
import { Settings2, ChevronDown } from "@lucide/svelte"; import { Settings2, ChevronDown, Plus, SlidersHorizontal } from "@lucide/svelte";
import WorkspaceLayout from "$lib/components/kit/WorkspaceLayout.svelte"; import WorkspaceLayout from "$lib/components/kit/WorkspaceLayout.svelte";
import WorkspaceHeader from "./WorkspaceHeader.svelte"; import WorkspaceHeader from "./WorkspaceHeader.svelte";
import SectionLabel from "$lib/components/kit/SectionLabel.svelte"; import SectionLabel from "$lib/components/kit/SectionLabel.svelte";
import StepCard from "$lib/components/kit/StepCard.svelte"; import StepCard from "$lib/components/kit/StepCard.svelte";
import Badge from "$lib/components/kit/Badge.svelte"; import Badge from "$lib/components/kit/Badge.svelte";
import FieldGrid from "$lib/components/kit/FieldGrid.svelte"; import FieldGrid from "$lib/components/kit/FieldGrid.svelte";
import MonoLabel from "$lib/components/kit/MonoLabel.svelte";
import ColorField from "$lib/components/kit/ColorField.svelte"; import ColorField from "$lib/components/kit/ColorField.svelte";
import SliderField from "$lib/components/kit/SliderField.svelte"; import SliderField from "$lib/components/kit/SliderField.svelte";
import Segmented from "$lib/components/kit/Segmented.svelte"; import Segmented from "$lib/components/kit/Segmented.svelte";
import ToggleRow from "$lib/components/kit/ToggleRow.svelte"; import ToggleRow from "$lib/components/kit/ToggleRow.svelte";
import PreviewStack from "$lib/components/kit/PreviewStack.svelte"; import PreviewStack from "$lib/components/kit/PreviewStack.svelte";
import PreviewTile from "$lib/components/kit/PreviewTile.svelte"; import PreviewTile from "$lib/components/kit/PreviewTile.svelte";
import CheckerCanvas from "$lib/components/kit/CheckerCanvas.svelte";
import DownloadButton from "$lib/components/kit/DownloadButton.svelte"; import DownloadButton from "$lib/components/kit/DownloadButton.svelte";
import MetaList from "$lib/components/kit/MetaList.svelte"; import MetaList from "$lib/components/kit/MetaList.svelte";
import IconButton from "$lib/components/kit/IconButton.svelte"; import IconButton from "$lib/components/kit/IconButton.svelte";
@@ -78,9 +76,9 @@
file={{ name: "source.png", size: "1.8 MB" }} file={{ name: "source.png", size: "1.8 MB" }}
/> />
<SectionLabel label="PROCESSING PIPELINE" title="4 active steps" meta="• LIVE"> <SectionLabel label="PROCESSING PIPELINE" title="4 active steps" meta="• LIVE" unwrapActions>
{#snippet actions()} {#snippet actions()}
<button class="add-btn" onclick={() => {}}>+ Add tool</button> <button class="add-btn" onclick={() => {}}><Plus size={15} /> Add tool</button>
{/snippet} {/snippet}
</SectionLabel> </SectionLabel>
@@ -92,26 +90,26 @@
onremove={() => {}} onremove={() => {}}
> >
{#snippet tools()} {#snippet tools()}
<Badge tone="success">AUTO</Badge> <Badge tone="success" check>AUTO</Badge>
{/snippet} {/snippet}
<FieldGrid> <FieldGrid>
<ColorField label="Gradient color" bind:value={gradColor} /> <ColorField label="COLOR" chevron bind:value={gradColor} />
<SliderField <SliderField
label="DIRECTION" label="DIRECTION"
bind:value={direction} bind:value={direction}
min={0} min={0}
max={360} max={360}
suffix="°" suffix=" °"
/> />
<div class="control-block"> <div class="control-block">
<MonoLabel>OPACITY</MonoLabel> <label>OPACITY <output>{opacity}%</output></label>
<Segmented <Segmented
bind:value={opacity} bind:value={opacity}
options={[ options={[
{ value: "100", label: "100%" }, { value: "100", label: "100" },
{ value: "75", label: "75%" }, { value: "75", label: "75" },
{ value: "50", label: "50%" }, { value: "50", label: "50" },
{ value: "25", label: "25%" }, { value: "25", label: "25" },
]} ]}
/> />
</div> </div>
@@ -125,24 +123,26 @@
onremove={() => {}} onremove={() => {}}
> >
{#snippet tools()} {#snippet tools()}
<Badge tone="success">AUTO</Badge> <Badge tone="success" check>AUTO</Badge>
{/snippet} {/snippet}
<div class="transform-note">Automatic subject detection enabled</div> <div class="transform-note">
<SlidersHorizontal size={15} /> Automatic subject detection enabled
</div>
</StepCard> </StepCard>
<StepCard index={3} type="STYLE" title="Add outline" onremove={() => {}}> <StepCard index={3} type="STYLE" title="Add outline" onremove={() => {}}>
{#snippet tools()} {#snippet tools()}
<Badge tone="success">AUTO</Badge> <Badge tone="success" check>AUTO</Badge>
{/snippet} {/snippet}
<FieldGrid compact> <FieldGrid compact>
<SliderField <SliderField
label="Outline width" label="WIDTH"
bind:value={outlineWidth} bind:value={outlineWidth}
min={0} min={0}
max={8} max={8}
suffix="px" suffix=" px"
/> />
<ColorField label="Outline color" bind:value={outlineColor} /> <ColorField label="COLOR" dark bind:value={outlineColor} />
</FieldGrid> </FieldGrid>
</StepCard> </StepCard>
@@ -153,15 +153,15 @@
onremove={() => {}} onremove={() => {}}
> >
{#snippet tools()} {#snippet tools()}
<Badge tone="success">AUTO</Badge> <Badge tone="success" check>AUTO</Badge>
{/snippet} {/snippet}
<FieldGrid compact> <FieldGrid compact>
<SliderField <SliderField
label="Corner radius" label="RADIUS"
bind:value={radius} bind:value={radius}
min={0} min={0}
max={48} max={48}
suffix="px" suffix=" px"
/> />
<ToggleRow label="Preserve aspect ratio" bind:checked={preserveAspect} /> <ToggleRow label="Preserve aspect ratio" bind:checked={preserveAspect} />
</FieldGrid> </FieldGrid>
@@ -172,7 +172,7 @@
{/snippet} {/snippet}
{#snippet preview()} {#snippet preview()}
<div class="preview-panel"> <section class="preview-panel">
<SectionLabel label="PIPELINE OUTPUTS" title="Visual history"> <SectionLabel label="PIPELINE OUTPUTS" title="Visual history">
{#snippet actions()} {#snippet actions()}
<button class="history-toggle" onclick={() => {}}> <button class="history-toggle" onclick={() => {}}>
@@ -180,7 +180,6 @@
</button> </button>
<DownloadButton <DownloadButton
label="Download result" label="Download result"
size="1.2 MB"
onclick={() => {}} onclick={() => {}}
/> />
<MetaList <MetaList
@@ -200,29 +199,24 @@
<PreviewStack pad="16px 0"> <PreviewStack pad="16px 0">
{#each previewTiles as tile (tile.label)} {#each previewTiles as tile (tile.label)}
<PreviewTile label={tile.label}> <PreviewTile label={tile.label} caption={tile.caption}>
<CheckerCanvas size="large"> <div
<div class="ph"
class="ph" class:active={tile.active}
class:active={tile.active} style="background:{tile.bg}; border-radius:{tile.radius ??
style="background:{tile.bg}; border-radius:{tile.radius ?? '0'}; box-shadow:{tile.ring ?? 'none'};"
'0'}; box-shadow:{tile.ring ?? 'none'};" >
> <span class="sample-icon">PNG</span>
<span class="sample-icon">PNG</span> <span>easy-png-tools</span>
<span>easy-png-tools</span> </div>
</div> </PreviewTile>
</CheckerCanvas>
<div class="tile-label-wrap">
<span class="tile-caption">{tile.caption}</span>
</div>
</PreviewTile>
{/each} {/each}
</PreviewStack> </PreviewStack>
<p class="preview-note"> <p class="preview-note">
Output is generated in-browser. Your files never leave this device. Output is generated in-browser. Your files never leave this device.
</p> </p>
</div> </section>
{/snippet} {/snippet}
</WorkspaceLayout> </WorkspaceLayout>
@@ -259,6 +253,8 @@
background: var(--panel); background: var(--panel);
padding: 18px; padding: 18px;
min-width: 0; min-width: 0;
position: sticky;
top: 24px;
} }
.history-toggle { .history-toggle {
display: flex; display: flex;
@@ -298,20 +294,6 @@
padding: 3px 5px; padding: 3px 5px;
font-size: 9px; font-size: 9px;
} }
.tile-label-wrap {
display: flex;
justify-content: space-between;
gap: 8px;
padding: 8px;
border-top: 1px solid var(--line);
font: 9px var(--font-mono);
color: var(--muted);
}
.tile-caption {
color: var(--muted);
font-weight: 400;
text-align: right;
}
.preview-note { .preview-note {
margin: 12px 0 0; margin: 12px 0 0;
color: var(--muted); color: var(--muted);
@@ -319,3 +301,4 @@
line-height: 1.5; line-height: 1.5;
} }
</style> </style>
@@ -17,19 +17,17 @@
let { eyebrowA, eyebrowB, title, lede, file }: Props = $props(); let { eyebrowA, eyebrowB, title, lede, file }: Props = $props();
</script> </script>
<div class="ws-header"> <div class="eyebrow">
<div class="title-row"> {eyebrowA} <span>/</span> {eyebrowB}
<div> </div>
<div class="eyebrow"> <div class="title-row">
{eyebrowA}{#if eyebrowB}<span>/</span>{eyebrowB}{/if} <div>
</div> <h1>{title}</h1>
<h1>{title}</h1> {#if lede}<p class="lede">{lede}</p>{/if}
{#if lede}<p class="lede">{lede}</p>{/if}
</div>
{#if file}
<FileChip name={file.name} size={file.size} />
{/if}
</div> </div>
{#if file}
<FileChip name={file.name} size={file.size} />
{/if}
</div> </div>
<style> <style>