feat: reactive tool flow, with debounce
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
const isInfo = $derived(tool.resultType === 'info');
|
||||
const sanitized = $derived(sanitizeParams(tool, values));
|
||||
|
||||
let runToken = 0;
|
||||
let lastRunSource: PixelImage | null = null;
|
||||
let lastRunValuesJson = '';
|
||||
|
||||
async function handleFile(file: File) {
|
||||
errorText = '';
|
||||
status = 'processing';
|
||||
@@ -41,20 +45,27 @@
|
||||
|
||||
async function runTool() {
|
||||
if (!source || isInfo) return;
|
||||
const token = ++runToken;
|
||||
lastRunSource = source;
|
||||
lastRunValuesJson = JSON.stringify(sanitized);
|
||||
try {
|
||||
result = await tool.run(source, sanitized);
|
||||
const next = await tool.run(source, sanitized);
|
||||
if (token !== runToken) return;
|
||||
result = next;
|
||||
status = 'loaded';
|
||||
} catch (e) {
|
||||
if (token !== runToken) return;
|
||||
showError(e);
|
||||
}
|
||||
}
|
||||
|
||||
function apply() {
|
||||
if (!source || isInfo || status === 'processing') return;
|
||||
errorText = '';
|
||||
status = 'processing';
|
||||
runTool();
|
||||
}
|
||||
$effect(() => {
|
||||
const valuesJson = JSON.stringify(sanitized);
|
||||
if (source === lastRunSource && valuesJson === lastRunValuesJson) return;
|
||||
if (!source || isInfo) return;
|
||||
const timer = setTimeout(() => void runTool(), 300);
|
||||
return () => clearTimeout(timer);
|
||||
});
|
||||
|
||||
function showError(e: unknown) {
|
||||
status = source ? 'loaded' : 'idle';
|
||||
@@ -123,7 +134,7 @@
|
||||
</div>
|
||||
|
||||
{#if source && !isInfo}
|
||||
<ParamsCard params={tool.params} bind:values busy={status === 'processing'} onApply={apply} />
|
||||
<ParamsCard params={tool.params} bind:values />
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
<script lang="ts">
|
||||
import type { ParamDef } from '$lib/registry';
|
||||
import ParamForm from '../ParamForm.svelte';
|
||||
import Button from '../ui/Button.svelte';
|
||||
import type { ParamDef } from '$lib/registry';
|
||||
|
||||
interface Props {
|
||||
params: ParamDef[];
|
||||
values: Record<string, any>;
|
||||
busy: boolean;
|
||||
onApply: () => void;
|
||||
}
|
||||
|
||||
let { params, values = $bindable(), busy, onApply }: Props = $props();
|
||||
let { params, values = $bindable() }: 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">
|
||||
У этого инструмента нет параметров — результат уже готов.
|
||||
|
||||
@@ -57,6 +57,9 @@
|
||||
{:else}
|
||||
<div class="media">
|
||||
<Preview image={result} />
|
||||
{#if status === 'processing'}
|
||||
<span class="recalc" aria-live="polite">Пересчёт…</span>
|
||||
{/if}
|
||||
</div>
|
||||
<DownloadButton
|
||||
image={result}
|
||||
@@ -88,5 +91,17 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 16rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.recalc {
|
||||
position: absolute;
|
||||
top: var(--space-2);
|
||||
right: var(--space-2);
|
||||
padding: 2px var(--space-2);
|
||||
border-radius: var(--radius-s);
|
||||
background: color-mix(in srgb, var(--accent) 12%, var(--surface));
|
||||
color: var(--accent);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user