refactor: separate tool page

This commit is contained in:
2026-08-22 20:52:03 +05:00
parent dcc7c346c4
commit 5560435b55
4 changed files with 213 additions and 101 deletions
@@ -0,0 +1,41 @@
<script lang="ts">
import type { ParamDef } from '$lib/registry';
import ParamForm from '../ParamForm.svelte';
import Button from '../ui/Button.svelte';
interface Props {
params: ParamDef[];
values: Record<string, any>;
busy: boolean;
onApply: () => void;
}
let { params, values = $bindable(), busy, onApply }: Props = $props();
</script>
<div class="panel root">
<h2 class="heading-section">Параметры</h2>
{#if params.length > 0}
<ParamForm {params} bind:values />
<Button onclick={onApply} {busy} busyText="Обработка…" fullWidth>Применить</Button>
{:else}
<p class="hint text-caption text-muted">
У этого инструмента нет параметров — результат уже готов.
</p>
{/if}
</div>
<style>
.root {
margin-top: var(--space-4);
padding: var(--space-4);
}
h2 {
margin-bottom: var(--space-2);
}
.hint {
margin: 0;
}
</style>
@@ -0,0 +1,92 @@
<script lang="ts">
import type { ImageInfo } from '$lib/core/analyze';
import type { PixelImage } from '$lib/core/types';
import { outputOf, type ToolEntry } from '$lib/registry';
import DownloadButton from '../DownloadButton.svelte';
import InfoPanel from '../InfoPanel.svelte';
import Preview from '../Preview.svelte';
import EmptyState from '../ui/EmptyState.svelte';
type Status = 'idle' | 'loaded' | 'processing' | 'error';
interface Props {
tool: ToolEntry;
sourceLoaded: boolean;
status: Status;
result: PixelImage | null;
info: ImageInfo | null;
isInfo: boolean;
params: Record<string, unknown>;
onDownloadError: (e: unknown) => void;
}
let {
tool,
sourceLoaded,
status,
result,
info,
isInfo,
params,
onDownloadError
}: Props = $props();
</script>
<div class="container">
<h2 class="heading-section">{isInfo ? 'Сводка' : 'Результат'}</h2>
{#if !sourceLoaded}
<div class="media">
<EmptyState
title="Результат появится здесь"
hint="Сначала загрузите исходное изображение слева"
/>
</div>
{:else if !isInfo && status === 'processing' && !result}
<div class="media">
<EmptyState
title="Обработка…"
hint="Изображение обрабатывается, это займёт немного времени"
/>
</div>
{:else if isInfo}
<div class="media">
{#if info}
<InfoPanel {info} />
{/if}
</div>
{:else}
<div class="media">
<Preview image={result} />
</div>
<DownloadButton
image={result}
format={outputOf(tool)}
baseName={tool.id}
{params}
onError={onDownloadError}
/>
{/if}
</div>
<style>
.container {
display: flex;
flex-direction: column;
gap: var(--space-2);
min-width: 0;
height: 100%;
}
h2 {
margin-bottom: var(--space-1);
}
.media {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 16rem;
}
</style>
@@ -0,0 +1,53 @@
<script lang="ts">
import type { PixelImage } from '$lib/core/types';
import DropOverlay from '../DropOverlay.svelte';
import DropZone from '../DropZone.svelte';
import Preview from '../Preview.svelte';
import Button from '../ui/Button.svelte';
interface Props {
source: PixelImage | null;
onFile: (file: File) => void;
onError: (message: string) => void;
onReset: () => void;
}
let { source, onFile, onError, onReset }: Props = $props();
</script>
<div class="container">
<h2 class="heading-section">Исходник</h2>
{#if !source}
<DropZone {onFile} {onError} />
{:else}
<DropOverlay {onFile} {onError}>
<div class="media">
<Preview image={source} />
</div>
<Button variant="secondary" onclick={onReset}>Заменить изображение</Button>
</DropOverlay>
{/if}
</div>
<style>
.container {
display: flex;
flex-direction: column;
gap: var(--space-2);
min-width: 0;
height: 100%;
}
h2 {
margin-bottom: var(--space-1);
}
.media {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 16rem;
}
</style>