fix: separate preview tile component

This commit is contained in:
2026-09-12 17:17:25 +05:00
parent 05165031c3
commit 35df076732
3 changed files with 124 additions and 95 deletions
+14 -42
View File
@@ -1,12 +1,13 @@
<script lang="ts"> <script lang="ts">
import { toDataUrl } from "$lib/core/io"; import { toDataUrl } from "$lib/core/io";
import type { PixelImage } from "$lib/core/types"; import type { PixelImage } from "$lib/core/types";
import type { FileResult } from "$lib/registry"; import type { FileResult, ResultKind } from "$lib/registry";
import { Check, RefreshCw } from "@lucide/svelte"; import { Check } from "@lucide/svelte";
import SchemaTextResult from "./SchemaTextResult.svelte"; import SchemaTextResult from "./SchemaTextResult.svelte";
import PreviewTile from "./layout/PreviewTile.svelte";
interface Props { interface Props {
resultKind?: "image" | "text" | "verdict" | "files"; resultKind?: ResultKind;
result: PixelImage | null; result: PixelImage | null;
fileResult?: FileResult | null; fileResult?: FileResult | null;
textResult?: string | null; textResult?: string | null;
@@ -33,20 +34,13 @@
})) }))
: [], : [],
); );
let parts = $derived(
resultKind === "files" && fileResult ? fileResult.files.length : undefined,
);
</script> </script>
<figure class="tile"> <PreviewTile label="RESULT" viewMode={resultKind} loading={running} {parts}>
<figcaption>
<span>
RESULT
{#if resultKind === "files" && fileResult}
<span class="count">{fileResult.files.length} parts</span>
{/if}
{#if running}
<RefreshCw class="rotating" size="16" />
{/if}
</span>
</figcaption>
<div class="canvas" class:checker={resultKind === "image"}> <div class="canvas" class:checker={resultKind === "image"}>
{#if resultKind === "text" && textResult} {#if resultKind === "text" && textResult}
<div class="text-result-wrap"> <div class="text-result-wrap">
@@ -83,20 +77,9 @@
<span class="empty">no result yet</span> <span class="empty">no result yet</span>
{/if} {/if}
</div> </div>
</figure> </PreviewTile>
<style> <style>
.tile figcaption {
display: flex;
align-items: center;
gap: var(--space-m);
margin-bottom: var(--space-m);
font: var(--font-size-s) var(--font-mono);
color: var(--color-text-muted);
& :global(.rotating) {
animation: rotate var(--duration-l) linear infinite;
}
}
.canvas { .canvas {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -109,8 +92,10 @@
color: var(--color-text-muted); color: var(--color-text-muted);
} }
.canvas img { .canvas img {
width: 100%; width: auto;
height: 100%; max-width: 100%;
height: auto;
max-height: 100vh;
object-fit: contain; object-fit: contain;
display: block; display: block;
} }
@@ -124,10 +109,6 @@
.empty { .empty {
font: var(--font-size-s) var(--font-mono); font: var(--font-size-s) var(--font-mono);
} }
.count {
margin-left: var(--space-m);
color: var(--color-main);
}
.parts-grid { .parts-grid {
display: grid; display: grid;
grid-template-columns: repeat( grid-template-columns: repeat(
@@ -170,13 +151,4 @@
padding: var(--space-l); padding: var(--space-l);
box-sizing: border-box; box-sizing: border-box;
} }
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style> </style>
+19 -53
View File
@@ -2,6 +2,7 @@
import { toDataUrl } from "$lib/core/io"; import { toDataUrl } from "$lib/core/io";
import type { PixelImage } from "$lib/core/types"; import type { PixelImage } from "$lib/core/types";
import type { InputMode } from "$lib/registry"; import type { InputMode } from "$lib/registry";
import PreviewTile from "./layout/PreviewTile.svelte";
import SchemaTextSource from "./SchemaTextSource.svelte"; import SchemaTextSource from "./SchemaTextSource.svelte";
interface Props { interface Props {
@@ -24,61 +25,26 @@
const sourceUrl = $derived(source ? toDataUrl(source) : null); const sourceUrl = $derived(source ? toDataUrl(source) : null);
</script> </script>
<figure class="tile"> <PreviewTile label="SOURCE" viewMode={mode}>
<figcaption><span>SOURCE</span></figcaption> {#if mode === "text"}
<div class="canvas" class:checker={mode === "image"}> <div class="text-source-wrap">
{#if mode === "text"} <SchemaTextSource
<div class="text-source-wrap"> value={textSource}
<SchemaTextSource disabled={running}
value={textSource} oninput={ontextinput ?? (() => {})}
disabled={running} onrender={onrendertext ?? (() => {})}
oninput={ontextinput ?? (() => {})} />
onrender={onrendertext ?? (() => {})} </div>
/> {:else if mode === "image" && sourceUrl}
</div> <img src={sourceUrl} alt="source" />
{:else if mode === "image" && sourceUrl} {:else if mode === "image"}
<img src={sourceUrl} alt="source" /> <span class="empty">choose an image</span>
{:else if mode === "image"} {:else}
<span class="empty">choose an image</span> <span class="empty">no source — configure the parameters</span>
{:else} {/if}
<span class="empty">no source — configure the parameters</span> </PreviewTile>
{/if}
</div>
</figure>
<style> <style>
.tile figcaption {
display: flex;
align-items: center;
gap: var(--space-m);
margin-bottom: var(--space-m);
font: var(--font-size-s) var(--font-mono);
color: var(--color-text-muted);
}
.canvas {
display: flex;
align-items: center;
justify-content: center;
min-height: clamp(var(--space-brand), 30vh, 60vh);
border: var(--size-border) solid var(--color-border);
border-radius: var(--radius-s);
overflow: hidden;
background: var(--color-background-muted);
color: var(--color-text-muted);
}
.canvas img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}
.canvas.checker {
background: repeating-conic-gradient(
var(--color-checker-main) 0 25%,
var(--color-checker-alt) 0 50%
)
50% / 28px 28px;
}
.empty { .empty {
font: var(--font-size-s) var(--font-mono); font: var(--font-size-s) var(--font-mono);
} }
@@ -0,0 +1,91 @@
<script lang="ts">
import type { InputMode, ResultKind } from "$lib/registry";
import { RefreshCw } from "@lucide/svelte";
import type { Snippet } from "svelte";
interface Props {
label: string;
viewMode: InputMode | ResultKind;
children: Snippet;
loading?: boolean;
parts?: number;
}
let {
label,
viewMode: mode,
children,
loading = false,
parts = undefined,
}: Props = $props();
// TODO: make canvas aspect ratio auto, from 16:9 to 9:16 max
</script>
<figure class="tile">
<figcaption>
<span>
{label}
{#if parts !== undefined}
<span class="count">{parts} parts</span>
{/if}
{#if loading}
<RefreshCw class="rotating" size="12" />
{/if}
</span>
</figcaption>
<div class="canvas" class:checker={mode === "image"}>
{@render children()}
</div>
</figure>
<style>
.tile figcaption {
display: flex;
align-items: center;
gap: var(--space-m);
margin-bottom: var(--space-m);
font: var(--font-size-s) var(--font-mono);
color: var(--color-text-muted);
& :global(.rotating) {
animation: rotate var(--duration-l) linear infinite;
}
}
.count {
margin-left: var(--space-m);
color: var(--color-main);
}
.canvas {
display: flex;
align-items: center;
justify-content: center;
min-height: clamp(var(--space-brand), 30vh, 60vh);
border: var(--size-border) solid var(--color-border);
border-radius: var(--radius-s);
overflow: hidden;
background: var(--color-background-muted);
color: var(--color-text-muted);
}
.canvas :global(img) {
width: auto;
max-width: 100%;
height: auto;
max-height: 100vh;
object-fit: contain;
display: block;
}
.canvas.checker {
background: repeating-conic-gradient(
var(--color-checker-main) 0 25%,
var(--color-checker-alt) 0 50%
)
50% / 28px 28px;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>