mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 21:46:35 +00:00
feat: add watermarks and text tools
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
import ToolSearch from './search/ToolSearch.svelte';
|
||||
import { createAutoRunner } from '$lib/tools/auto-run';
|
||||
import { executeStep } from '$lib/tools/executor';
|
||||
import { clearOverlay, setOverlay } from '$lib/tools/overlay-store.svelte';
|
||||
import { t } from '$lib/i18n/t';
|
||||
import { toolDescription, toolTitle } from '$lib/i18n/tool-strings';
|
||||
import type { StageStatus } from './stage/stage-props';
|
||||
@@ -51,6 +52,7 @@
|
||||
let showMask = $state(false);
|
||||
let info = $state<ImageInfo | null>(null);
|
||||
let errorText = $state('');
|
||||
let overlayImage = $state<PixelImage | null>(null);
|
||||
// svelte-ignore state_referenced_locally
|
||||
let values = $state<Record<string, any>>({ ...defaultParams(tool), ...presetBaseValues });
|
||||
// svelte-ignore state_referenced_locally
|
||||
@@ -272,7 +274,16 @@
|
||||
info = null;
|
||||
errorText = '';
|
||||
status = 'idle';
|
||||
values = defaultParams(tool);
|
||||
clearOverlay();
|
||||
values = { ...defaultParams(tool), ...presetBaseValues };
|
||||
}
|
||||
|
||||
async function handleOverlayFile(file: File) {
|
||||
try {
|
||||
setOverlay(await decodeFile(file));
|
||||
} catch (e) {
|
||||
errorText = errorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
function handlePaste(event: ClipboardEvent) {
|
||||
@@ -332,6 +343,10 @@
|
||||
handlePickColor={handlePickColor}
|
||||
showError={showError}
|
||||
errorMessage={errorMessage}
|
||||
overlayImage={overlayImage}
|
||||
onOverlayFile={(f) => void handleOverlayFile(f)}
|
||||
onOverlayError={(e) => (errorText = errorMessage(e))}
|
||||
onOverlayClear={clearOverlay}
|
||||
/>
|
||||
|
||||
<div class="chain-stack">
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import DownloadButton from '../DownloadButton.svelte';
|
||||
import Button from '../ui/Button.svelte';
|
||||
import EmptyState from '../ui/EmptyState.svelte';
|
||||
import OverlayCard from '../tool/OverlayCard.svelte';
|
||||
import ParamsCard from '../tool/ParamsCard.svelte';
|
||||
import ResultCard from '../tool/ResultCard.svelte';
|
||||
import SourceCard from '../tool/SourceCard.svelte';
|
||||
@@ -43,6 +44,10 @@
|
||||
handlePipetteToggle?: (id: string) => void;
|
||||
handlePickColor?: (hex: string) => void;
|
||||
showError?: (e: unknown) => void;
|
||||
overlayImage?: PixelImage | null;
|
||||
onOverlayFile?: (file: File) => void;
|
||||
onOverlayError?: (e: unknown) => void;
|
||||
onOverlayClear?: () => void;
|
||||
index?: number;
|
||||
input?: PixelImage | null;
|
||||
result?: PixelImage | null;
|
||||
@@ -81,6 +86,10 @@
|
||||
handlePipetteToggle,
|
||||
handlePickColor,
|
||||
showError,
|
||||
overlayImage = null,
|
||||
onOverlayFile,
|
||||
onOverlayError,
|
||||
onOverlayClear,
|
||||
index = 0,
|
||||
input = null,
|
||||
result = null,
|
||||
@@ -138,6 +147,14 @@
|
||||
pipetteActive={!!pipetteTargetId}
|
||||
onPickColor={(hex) => handlePickColor?.(hex)}
|
||||
/>
|
||||
{#if tool.needsOverlaySource && source}
|
||||
<OverlayCard
|
||||
overlay={overlayImage}
|
||||
onFile={(f) => onOverlayFile?.(f)}
|
||||
onError={(e) => onOverlayError?.(e)}
|
||||
onClear={() => onOverlayClear?.()}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import { t } from '$lib/i18n/t';
|
||||
import type { PixelImage } from '$lib/core/types';
|
||||
import DropZone from '../DropZone.svelte';
|
||||
import Preview from '../Preview.svelte';
|
||||
import Button from '../ui/Button.svelte';
|
||||
|
||||
interface Props {
|
||||
overlay: PixelImage | null;
|
||||
onFile: (file: File) => void;
|
||||
onError: (e: unknown) => void;
|
||||
onClear: () => void;
|
||||
}
|
||||
|
||||
let { overlay, onFile, onError, onClear }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="overlay-card">
|
||||
<span class="edge-legend overlay-legend" aria-hidden="true">{t('ui.overlayTitle')}</span>
|
||||
{#if !overlay}
|
||||
<DropZone {onFile} {onError} label={t('ui.overlayDrop')} />
|
||||
{:else}
|
||||
<div class="preview-wrap">
|
||||
<Preview image={overlay} />
|
||||
<Button variant="secondary" onclick={onClear}>{t('ui.overlayRemove')}</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.overlay-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
margin-top: var(--space-3);
|
||||
padding-top: var(--space-3);
|
||||
border-top: 1px dashed var(--border);
|
||||
}
|
||||
|
||||
.overlay-legend {
|
||||
top: -0.65em;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.overlay-card :global(.dropzone) {
|
||||
min-height: 7rem;
|
||||
font-size: var(--text-s);
|
||||
}
|
||||
|
||||
.preview-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.preview-wrap :global(.media) {
|
||||
max-height: 12rem;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user