mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 21:46:35 +00:00
refactor: separate tool page for A\B testing
This commit is contained in:
@@ -14,14 +14,12 @@
|
|||||||
import { executeStep } from '$lib/tools/executor';
|
import { executeStep } from '$lib/tools/executor';
|
||||||
import { t } from '$lib/i18n/t';
|
import { t } from '$lib/i18n/t';
|
||||||
import { toolDescription, toolTitle } from '$lib/i18n/tool-strings';
|
import { toolDescription, toolTitle } from '$lib/i18n/tool-strings';
|
||||||
import ParamsCard from './tool/ParamsCard.svelte';
|
import type { StageStatus } from './stage/stage-props';
|
||||||
import ResultCard from './tool/ResultCard.svelte';
|
import ToolStageClassic from './stage/ToolStageClassic.svelte';
|
||||||
import SourceCard from './tool/SourceCard.svelte';
|
|
||||||
import TextInputCard from './tool/TextInputCard.svelte';
|
|
||||||
|
|
||||||
let { tool, restoreChain = false }: { tool: ToolEntry; restoreChain?: boolean } = $props();
|
let { tool, restoreChain = false }: { tool: ToolEntry; restoreChain?: boolean } = $props();
|
||||||
|
|
||||||
type Status = 'idle' | 'loaded' | 'processing' | 'error';
|
type Status = StageStatus;
|
||||||
|
|
||||||
let status = $state<Status>('idle');
|
let status = $state<Status>('idle');
|
||||||
let source = $state<PixelImage | null>(null);
|
let source = $state<PixelImage | null>(null);
|
||||||
@@ -267,61 +265,34 @@
|
|||||||
<div class="error-banner" role="alert">{errorText}</div>
|
<div class="error-banner" role="alert">{errorText}</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="panel tool-block">
|
<ToolStageClassic
|
||||||
<div class="tool-stage" class:single={isSourceless}>
|
tool={tool}
|
||||||
{#if !isSourceless}
|
source={source}
|
||||||
<span class="edge-legend source-legend" aria-hidden="true">{t('toolPage.legendSource')}</span>
|
result={result}
|
||||||
<div class="cell">
|
displayImage={shownBase}
|
||||||
{#if isTextSource && !source}
|
info={info}
|
||||||
<TextInputCard onSubmit={handleTextSubmit} />
|
status={status}
|
||||||
{:else}
|
isInfo={isInfo}
|
||||||
<SourceCard
|
isSourceless={isSourceless}
|
||||||
{source}
|
isTextSource={isTextSource}
|
||||||
onFile={handleFile}
|
textResult={textResult}
|
||||||
onError={(e) => (errorText = errorMessage(e))}
|
sanitized={sanitized}
|
||||||
onReset={reset}
|
canChainBase={canChainBase}
|
||||||
pipetteActive={!!pipetteTargetId}
|
hasChain={chain.length > 0}
|
||||||
onPickColor={handlePickColor}
|
hasMask={hasMask}
|
||||||
/>
|
bind:showMask
|
||||||
{/if}
|
bind:values
|
||||||
</div>
|
pipetteTargetId={pipetteTargetId}
|
||||||
{/if}
|
handleFile={handleFile}
|
||||||
<span class="edge-legend result-legend" aria-hidden="true">
|
onTextSubmit={handleTextSubmit}
|
||||||
{isInfo ? t('toolPage.legendSummary') : t('toolPage.legendResult')}
|
onSourceError={(e) => (errorText = errorMessage(e))}
|
||||||
</span>
|
reset={reset}
|
||||||
<div class="cell">
|
toggleChain={toggleChain}
|
||||||
<ResultCard
|
handlePipetteToggle={handlePipetteToggle}
|
||||||
{tool}
|
handlePickColor={handlePickColor}
|
||||||
sourceLoaded={isSourceless ? true : !!source}
|
showError={showError}
|
||||||
{status}
|
errorMessage={errorMessage}
|
||||||
{result}
|
/>
|
||||||
displayImage={shownBase}
|
|
||||||
{info}
|
|
||||||
{isInfo}
|
|
||||||
params={sanitized}
|
|
||||||
textResult={textResult}
|
|
||||||
onChainToggle={canChainBase ? toggleChain : undefined}
|
|
||||||
hasChain={chain.length > 0}
|
|
||||||
onDownloadError={showError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if (source || isSourceless) && !isInfo && (tool.params.length > 0 || hasMask)}
|
|
||||||
<div class="params-sep">
|
|
||||||
<span class="edge-legend" aria-hidden="true">{t('toolPage.legendParams')}</span>
|
|
||||||
</div>
|
|
||||||
<ParamsCard
|
|
||||||
tool={tool}
|
|
||||||
params={tool.params}
|
|
||||||
bind:values
|
|
||||||
{pipetteTargetId}
|
|
||||||
onPipetteToggle={handlePipetteToggle}
|
|
||||||
hasMask={hasMask}
|
|
||||||
bind:showMask
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="chain-stack">
|
<div class="chain-stack">
|
||||||
{#each chain as step, index (step.id)}
|
{#each chain as step, index (step.id)}
|
||||||
@@ -374,27 +345,6 @@
|
|||||||
margin-bottom: var(--space-3);
|
margin-bottom: var(--space-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-stage {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.source-legend {
|
|
||||||
left: 25%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-legend {
|
|
||||||
left: 75%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 48rem) {
|
|
||||||
.source-legend,
|
|
||||||
.result-legend {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-slot {
|
.empty-slot {
|
||||||
padding: var(--space-3);
|
padding: var(--space-3);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { t } from '$lib/i18n/t';
|
||||||
|
import ParamsCard from '../tool/ParamsCard.svelte';
|
||||||
|
import ResultCard from '../tool/ResultCard.svelte';
|
||||||
|
import SourceCard from '../tool/SourceCard.svelte';
|
||||||
|
import TextInputCard from '../tool/TextInputCard.svelte';
|
||||||
|
import type { StageProps } from './stage-props';
|
||||||
|
|
||||||
|
let {
|
||||||
|
tool,
|
||||||
|
source,
|
||||||
|
result,
|
||||||
|
displayImage,
|
||||||
|
info,
|
||||||
|
status,
|
||||||
|
isInfo,
|
||||||
|
isSourceless,
|
||||||
|
isTextSource,
|
||||||
|
textResult,
|
||||||
|
sanitized,
|
||||||
|
canChainBase,
|
||||||
|
hasChain,
|
||||||
|
hasMask,
|
||||||
|
showMask = $bindable(false),
|
||||||
|
values = $bindable(),
|
||||||
|
pipetteTargetId,
|
||||||
|
handleFile,
|
||||||
|
onTextSubmit,
|
||||||
|
onSourceError,
|
||||||
|
reset,
|
||||||
|
toggleChain,
|
||||||
|
handlePipetteToggle,
|
||||||
|
handlePickColor,
|
||||||
|
showError,
|
||||||
|
errorMessage
|
||||||
|
}: StageProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="panel tool-block">
|
||||||
|
<div class="tool-stage" class:single={isSourceless}>
|
||||||
|
{#if !isSourceless}
|
||||||
|
<span class="edge-legend source-legend" aria-hidden="true">{t('toolPage.legendSource')}</span>
|
||||||
|
<div class="cell">
|
||||||
|
{#if isTextSource && !source}
|
||||||
|
<TextInputCard onSubmit={onTextSubmit} />
|
||||||
|
{:else}
|
||||||
|
<SourceCard
|
||||||
|
{source}
|
||||||
|
onFile={handleFile}
|
||||||
|
onError={onSourceError}
|
||||||
|
onReset={reset}
|
||||||
|
pipetteActive={!!pipetteTargetId}
|
||||||
|
onPickColor={handlePickColor}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<span class="edge-legend result-legend" aria-hidden="true">
|
||||||
|
{isInfo ? t('toolPage.legendSummary') : t('toolPage.legendResult')}
|
||||||
|
</span>
|
||||||
|
<div class="cell">
|
||||||
|
<ResultCard
|
||||||
|
{tool}
|
||||||
|
sourceLoaded={isSourceless ? true : !!source}
|
||||||
|
{status}
|
||||||
|
{result}
|
||||||
|
displayImage={displayImage}
|
||||||
|
{info}
|
||||||
|
{isInfo}
|
||||||
|
params={sanitized}
|
||||||
|
textResult={textResult}
|
||||||
|
onChainToggle={canChainBase ? toggleChain : undefined}
|
||||||
|
{hasChain}
|
||||||
|
onDownloadError={showError}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if (source || isSourceless) && !isInfo && (tool.params.length > 0 || hasMask)}
|
||||||
|
<div class="params-sep">
|
||||||
|
<span class="edge-legend" aria-hidden="true">{t('toolPage.legendParams')}</span>
|
||||||
|
</div>
|
||||||
|
<ParamsCard
|
||||||
|
{tool}
|
||||||
|
params={tool.params}
|
||||||
|
bind:values
|
||||||
|
{pipetteTargetId}
|
||||||
|
onPipetteToggle={handlePipetteToggle}
|
||||||
|
{hasMask}
|
||||||
|
bind:showMask
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tool-stage {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-legend {
|
||||||
|
left: 25%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-legend {
|
||||||
|
left: 75%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 48rem) {
|
||||||
|
.source-legend,
|
||||||
|
.result-legend {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import type { ImageInfo } from '$lib/core/analyze';
|
||||||
|
import type { PixelImage } from '$lib/core/types';
|
||||||
|
import type { ToolEntry } from '$lib/registry';
|
||||||
|
|
||||||
|
export type StageStatus = 'idle' | 'loaded' | 'processing' | 'error';
|
||||||
|
|
||||||
|
export interface StageProps {
|
||||||
|
tool: ToolEntry;
|
||||||
|
source: PixelImage | null;
|
||||||
|
result: PixelImage | null;
|
||||||
|
displayImage: PixelImage | null;
|
||||||
|
info: ImageInfo | null;
|
||||||
|
status: StageStatus;
|
||||||
|
isInfo: boolean;
|
||||||
|
isSourceless: boolean;
|
||||||
|
isTextSource: boolean;
|
||||||
|
textResult: string | null;
|
||||||
|
sanitized: Record<string, any>;
|
||||||
|
canChainBase: boolean;
|
||||||
|
hasChain: boolean;
|
||||||
|
hasMask: boolean;
|
||||||
|
showMask: boolean;
|
||||||
|
values: Record<string, any>;
|
||||||
|
pipetteTargetId: string | null;
|
||||||
|
handleFile: (file: File) => Promise<void> | void;
|
||||||
|
onTextSubmit: (text: string) => Promise<void> | void;
|
||||||
|
onSourceError: (e: unknown) => void;
|
||||||
|
reset: () => void;
|
||||||
|
toggleChain: () => void;
|
||||||
|
handlePipetteToggle: (id: string) => void;
|
||||||
|
handlePickColor: (hex: string) => void;
|
||||||
|
showError: (e: unknown) => void;
|
||||||
|
errorMessage: (e: unknown) => string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user