mirror of https://github.com/VERT-sh/VERT.git
feat: conversion page redesign
This commit is contained in:
parent
b1faad1c8c
commit
549c61a108
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
type Props = {
|
||||||
|
progress: number;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
let { progress, min, max }: Props = $props();
|
||||||
|
|
||||||
|
const percent = $derived(((progress - min) / (max - min)) * 100);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="w-full h-1 bg-foreground-muted rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
class="h-full bg-accent-background"
|
||||||
|
style="width: {percent}%; transition: 500ms linear width;"
|
||||||
|
></div>
|
||||||
|
</div>
|
|
@ -41,15 +41,21 @@ export class VipsConverter extends Converter {
|
||||||
|
|
||||||
public async convert(input: VertFile, to: string): Promise<VertFile> {
|
public async convert(input: VertFile, to: string): Promise<VertFile> {
|
||||||
log(["converters", this.name], `converting ${input.name} to ${to}`);
|
log(["converters", this.name], `converting ${input.name} to ${to}`);
|
||||||
const res = await this.sendMessage({
|
const msg = {
|
||||||
type: "convert",
|
type: "convert",
|
||||||
input,
|
input: {
|
||||||
|
file: input.file,
|
||||||
|
name: input.name,
|
||||||
|
to: input.to,
|
||||||
|
from: input.from,
|
||||||
|
},
|
||||||
to,
|
to,
|
||||||
});
|
} as WorkerMessage;
|
||||||
|
const res = await this.sendMessage(msg);
|
||||||
|
|
||||||
if (res.type === "finished") {
|
if (res.type === "finished") {
|
||||||
log(["converters", this.name], `converted ${input.name} to ${to}`);
|
log(["converters", this.name], `converted ${input.name} to ${to}`);
|
||||||
return res.output;
|
return new VertFile(new File([res.output], input.name), to);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.type === "error") {
|
if (res.type === "error") {
|
||||||
|
@ -81,8 +87,13 @@ export class VipsConverter extends Converter {
|
||||||
}, 60000);
|
}, 60000);
|
||||||
|
|
||||||
this.worker.addEventListener("message", onMessage);
|
this.worker.addEventListener("message", onMessage);
|
||||||
|
const msg = { ...message, id, worker: null };
|
||||||
this.worker.postMessage({ ...message, id });
|
log(["converters", this.name], `sending message`, msg);
|
||||||
|
try {
|
||||||
|
this.worker.postMessage(msg);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ interface ConvertMessage {
|
||||||
|
|
||||||
interface FinishedMessage {
|
interface FinishedMessage {
|
||||||
type: "finished";
|
type: "finished";
|
||||||
output: VertFile;
|
output: ArrayBufferLike;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoadedMessage {
|
interface LoadedMessage {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import type { Converter } from "$lib/converters/converter.svelte";
|
||||||
|
|
||||||
export class VertFile {
|
export class VertFile {
|
||||||
public id: string = Math.random().toString(36).slice(2, 8);
|
public id: string = Math.random().toString(36).slice(2, 8);
|
||||||
|
|
||||||
|
@ -10,16 +12,46 @@ export class VertFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
public progress = $state(0);
|
public progress = $state(0);
|
||||||
// public result: VertFile | null = null;
|
|
||||||
public result = $state<VertFile | null>(null);
|
public result = $state<VertFile | null>(null);
|
||||||
|
|
||||||
public to = $state("");
|
public to = $state("");
|
||||||
|
|
||||||
|
public converter: Converter | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly file: File,
|
public readonly file: File,
|
||||||
to: string,
|
to: string,
|
||||||
|
converter?: Converter,
|
||||||
public readonly blobUrl?: string,
|
public readonly blobUrl?: string,
|
||||||
) {
|
) {
|
||||||
this.to = to;
|
this.to = to;
|
||||||
|
this.converter = converter ?? null;
|
||||||
|
this.convert = this.convert.bind(this);
|
||||||
|
this.download = this.download.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async convert() {
|
||||||
|
console.log(this.converter);
|
||||||
|
if (!this.converter) throw new Error("No converter found");
|
||||||
|
this.result = null;
|
||||||
|
this.progress = 0;
|
||||||
|
const res = await this.converter.convert(this, this.to);
|
||||||
|
this.result = res;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async download() {
|
||||||
|
if (!this.result) throw new Error("No result found");
|
||||||
|
const blob = URL.createObjectURL(
|
||||||
|
new Blob([await this.result.file.arrayBuffer()], {
|
||||||
|
type: this.to.slice(1),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = blob;
|
||||||
|
a.download = `VERT-Converted_${new Date().toISOString()}${this.to}`;
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(blob);
|
||||||
|
a.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
import {
|
import { type WorkerMessage, type OmitBetterStrict } from "$lib/types";
|
||||||
type WorkerMessage,
|
|
||||||
type OmitBetterStrict,
|
|
||||||
VertFile,
|
|
||||||
} from "$lib/types";
|
|
||||||
import Vips from "wasm-vips";
|
import Vips from "wasm-vips";
|
||||||
|
|
||||||
const vipsPromise = Vips({
|
const vipsPromise = Vips({
|
||||||
|
@ -32,10 +28,7 @@ const handleMessage = async (
|
||||||
image.delete();
|
image.delete();
|
||||||
return {
|
return {
|
||||||
type: "finished",
|
type: "finished",
|
||||||
output: new VertFile(
|
output: output.buffer,
|
||||||
new File([output.buffer], message.input.name),
|
|
||||||
message.to,
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
import Uploader from "$lib/components/functional/Uploader.svelte";
|
import Uploader from "$lib/components/functional/Uploader.svelte";
|
||||||
import { converters } from "$lib/converters";
|
import { converters } from "$lib/converters";
|
||||||
import { log } from "$lib/logger/index.js";
|
import { log } from "$lib/logger";
|
||||||
import { files } from "$lib/store/index.svelte";
|
import { files } from "$lib/store/index.svelte";
|
||||||
import { VertFile } from "$lib/types/file.svelte.js";
|
import { VertFile } from "$lib/types/file.svelte";
|
||||||
import { Check } from "lucide-svelte";
|
import { Check } from "lucide-svelte";
|
||||||
|
|
||||||
const { data } = $props();
|
const { data } = $props();
|
||||||
|
@ -48,6 +48,7 @@
|
||||||
new VertFile(
|
new VertFile(
|
||||||
f,
|
f,
|
||||||
to,
|
to,
|
||||||
|
converter,
|
||||||
URL.createObjectURL(blob!),
|
URL.createObjectURL(blob!),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -58,7 +59,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
img.onerror = async () => {
|
img.onerror = async () => {
|
||||||
resolve(new VertFile(f, to));
|
resolve(new VertFile(f, to, converter));
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
import { blur, duration, flip } from "$lib/animation";
|
import { blur, duration, flip } from "$lib/animation";
|
||||||
import Dropdown from "$lib/components/functional/Dropdown.svelte";
|
import Dropdown from "$lib/components/functional/Dropdown.svelte";
|
||||||
import ProgressiveBlur from "$lib/components/visual/effects/ProgressiveBlur.svelte";
|
import ProgressiveBlur from "$lib/components/visual/effects/ProgressiveBlur.svelte";
|
||||||
|
import ProgressBar from "$lib/components/visual/ProgressBar.svelte";
|
||||||
import { converters } from "$lib/converters";
|
import { converters } from "$lib/converters";
|
||||||
import type { Converter } from "$lib/converters/converter.svelte";
|
import type { Converter } from "$lib/converters/converter.svelte";
|
||||||
import { log } from "$lib/logger";
|
import { log } from "$lib/logger";
|
||||||
import { files } from "$lib/store/index.svelte";
|
import { files } from "$lib/store/index.svelte";
|
||||||
|
import type { VertFile } from "$lib/types";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { ArrowRight, XIcon } from "lucide-svelte";
|
import { ArrowRight, XIcon } from "lucide-svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
@ -19,6 +21,7 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
let isSm = $state(false);
|
let isSm = $state(false);
|
||||||
|
let isLg = $state(false);
|
||||||
|
|
||||||
let processings = $state<boolean[]>([]);
|
let processings = $state<boolean[]>([]);
|
||||||
|
|
||||||
|
@ -49,8 +52,10 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
isSm = window.innerWidth < 640;
|
isSm = window.innerWidth < 640;
|
||||||
|
isLg = window.innerWidth > 1024;
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener("resize", () => {
|
||||||
isSm = window.innerWidth < 640;
|
isSm = window.innerWidth < 640;
|
||||||
|
isLg = window.innerWidth > 1024;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -71,20 +76,9 @@
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
for (let i = 0; i < files.files.length; i++) {
|
for (let i = 0; i < files.files.length; i++) {
|
||||||
promises.push(
|
promises.push(
|
||||||
(async () => {
|
(async (i) => {
|
||||||
const file = files.files[i];
|
await convert(files.files[i]);
|
||||||
const converter = converters.find(
|
})(i),
|
||||||
(c) =>
|
|
||||||
c.supportedFormats.includes(file.from) &&
|
|
||||||
c.supportedFormats.includes(file.to),
|
|
||||||
);
|
|
||||||
if (!converter) throw new Error("No converter found");
|
|
||||||
const to = file.to;
|
|
||||||
processings[i] = true;
|
|
||||||
const converted = await converter.convert(file, to);
|
|
||||||
file.result = converted;
|
|
||||||
processings[i] = false;
|
|
||||||
})(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +88,14 @@
|
||||||
log(["converter"], `converted all files in ${seconds}s`);
|
log(["converter"], `converted all files in ${seconds}s`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const convert = async (file: VertFile) => {
|
||||||
|
file.progress = 0;
|
||||||
|
const index = files.files.findIndex((f) => f === file);
|
||||||
|
processings[index] = true;
|
||||||
|
await file.convert();
|
||||||
|
processings[index] = false;
|
||||||
|
};
|
||||||
|
|
||||||
const downloadAll = async () => {
|
const downloadAll = async () => {
|
||||||
const dlFiles: any[] = [];
|
const dlFiles: any[] = [];
|
||||||
for (let i = 0; i < files.files.length; i++) {
|
for (let i = 0; i < files.files.length; i++) {
|
||||||
|
@ -233,149 +235,249 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#each reversedFiles as file, i (file.id)}
|
<div
|
||||||
{@const converter = (() => {
|
class="w-full lg:grid flex flex-col lg:gap-8 gap-4"
|
||||||
return converters.find((c) =>
|
style="grid-template-columns: 1fr 1fr;"
|
||||||
c.supportedFormats.includes(file.from),
|
>
|
||||||
);
|
{#each reversedFiles as file, i (file.id)}
|
||||||
})()}
|
{@const converter = (() => {
|
||||||
<div
|
return converters.find((c) =>
|
||||||
class="w-full rounded-xl relative"
|
c.supportedFormats.includes(file.from),
|
||||||
animate:flip={{ duration, easing: quintOut }}
|
);
|
||||||
out:blur={{
|
})()}
|
||||||
duration,
|
|
||||||
easing: quintOut,
|
|
||||||
blurMultiplier: 16,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class={clsx(
|
class="w-full rounded-xl relative lg:h-48"
|
||||||
"sm:h-16 sm:py-0 py-4 px-3 flex relative overflow-hidden flex-shrink-0 items-center w-full rounded-xl",
|
animate:flip={{ duration, easing: quintOut }}
|
||||||
{
|
out:blur={{
|
||||||
"initial-fade": !finisheds[i],
|
duration,
|
||||||
processing:
|
easing: quintOut,
|
||||||
processings[files.files.length - i - 1],
|
blurMultiplier: 16,
|
||||||
},
|
}}
|
||||||
)}
|
|
||||||
style="--delay: {i * 50}ms; z-index: {files.files
|
|
||||||
.length - i}; border: solid 3px {file.result
|
|
||||||
? 'var(--accent-bg)'
|
|
||||||
: 'var(--fg-muted-alt)'}; transition: border 1000ms ease; transition: filter {duration}ms var(--transition), transform {duration}ms var(--transition);"
|
|
||||||
>
|
>
|
||||||
<!-- <div
|
<div
|
||||||
|
class={clsx(
|
||||||
|
"sm:h-full lg:py-0 py-4 px-3 flex relative flex-shrink-0 items-center w-full rounded-xl",
|
||||||
|
{
|
||||||
|
"initial-fade": !finisheds[i],
|
||||||
|
processing: processings[i] && !isLg,
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
style="--delay: {i * 50}ms; z-index: {files.files
|
||||||
|
.length - i}; border: solid 2px {file.result
|
||||||
|
? 'var(--accent-bg)'
|
||||||
|
: 'var(--fg-muted-alt)'}; transition: border 1000ms ease; transition: filter {duration}ms var(--transition), transform {duration}ms var(--transition);"
|
||||||
|
>
|
||||||
|
<!-- <div
|
||||||
class="absolute top-0 left-0 bg-red-500 h-full"
|
class="absolute top-0 left-0 bg-red-500 h-full"
|
||||||
style="width: {file.progress}%; transition: width 500ms linear;"
|
style="width: {file.progress}%; transition: width 500ms linear;"
|
||||||
></div> -->
|
></div> -->
|
||||||
<div
|
|
||||||
class="flex gap-8 sm:gap-0 sm:flex-row flex-col items-center justify-between w-full z-50 relative sm:h-fit h-full"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class={clsx(
|
class="flex gap-8 lg:flex-grow lg:h-full sm:gap-0 lg:py-4 lg:justify-normal sm:flex-row lg:flex-col flex-col items-center justify-between w-full z-50 relative sm:h-fit h-full"
|
||||||
"py-2 px-3 rounded-xl transition-colors duration-300 sm:w-fit w-full sm:text-left text-center",
|
|
||||||
{
|
|
||||||
"bg-accent-background text-accent-foreground":
|
|
||||||
file.result,
|
|
||||||
"bg-background text-foreground":
|
|
||||||
!file.result,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{file.file.name}
|
<div class="w-full lg:flex-grow">
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="flex items-center gap-3 sm:justify-normal w-full sm:w-fit flex-shrink-0"
|
|
||||||
>
|
|
||||||
{#if converter && converter.supportedFormats.includes(file.from)}
|
|
||||||
<span class="sm:block hidden">from</span>
|
|
||||||
<span
|
|
||||||
class="py-2 px-3 font-display bg-foreground text-background rounded-xl sm:block hidden"
|
|
||||||
>{file.from}</span
|
|
||||||
>
|
|
||||||
<span class="sm:block hidden">to</span>
|
|
||||||
<div class="sm:block hidden">
|
|
||||||
<Dropdown
|
|
||||||
options={converter.supportedFormats}
|
|
||||||
bind:selected={files.files[
|
|
||||||
files.files.length - i - 1
|
|
||||||
].to}
|
|
||||||
onselect={() => {
|
|
||||||
file.result = null;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="w-full sm:hidden block h-11">
|
|
||||||
<div
|
|
||||||
class="py-2 px-3 font-display bg-foreground text-background rounded-xl"
|
|
||||||
>
|
|
||||||
{file.from}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="w-full sm:hidden h-full flex justify-center items-center"
|
class={clsx(
|
||||||
|
"py-2 px-3 lg:w-full lg:flex rounded-xl transition-colors duration-300 sm:w-fit w-full flex-shrink sm:text-left text-center",
|
||||||
|
{
|
||||||
|
"bg-accent-background text-accent-foreground":
|
||||||
|
file.result,
|
||||||
|
"bg-background text-foreground":
|
||||||
|
!file.result,
|
||||||
|
},
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ArrowRight
|
<h3
|
||||||
class="w-6 h-6 text-accent-foreground"
|
class="lg:flex-grow flex-shrink whitespace-nowrap overflow-hidden text-ellipsis sm:max-w-96 lg:max-w-none"
|
||||||
/>
|
>
|
||||||
</div>
|
{file.file.name}
|
||||||
<div class="w-full sm:hidden block h-full">
|
</h3>
|
||||||
<Dropdown
|
<button
|
||||||
options={converter.supportedFormats}
|
onclick={() => {
|
||||||
bind:selected={files.files[
|
// delete the file from the list
|
||||||
files.files.length - 1 - i
|
files.files =
|
||||||
].to}
|
files.files.filter(
|
||||||
onselect={() => {
|
(f) => f !== file,
|
||||||
file.result = null;
|
);
|
||||||
|
if (files.files.length === 0)
|
||||||
|
goto("/");
|
||||||
}}
|
}}
|
||||||
/>
|
class="ml-2 mr-1 lg:block hidden flex-shrink-0"
|
||||||
|
>
|
||||||
|
<XIcon size="18" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
</div>
|
||||||
<span
|
<div
|
||||||
class="py-2 px-3 font-display bg-foreground-failure text-white rounded-xl"
|
class="flex items-center gap-3 sm:justify-normal w-full sm:w-fit flex-shrink-0 lg:w-full"
|
||||||
>{file.from}</span
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-col items-center gap-3 w-full"
|
||||||
>
|
>
|
||||||
|
{#if processings[i]}
|
||||||
|
<div
|
||||||
|
class="w-full lg:block hidden"
|
||||||
|
transition:blur={{
|
||||||
|
blurMultiplier: 6,
|
||||||
|
duration,
|
||||||
|
easing: quintOut,
|
||||||
|
scale: {
|
||||||
|
start: 0.9,
|
||||||
|
end: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProgressBar
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
progress={file.result
|
||||||
|
? 100
|
||||||
|
: file.progress}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div
|
||||||
|
class="flex items-center gap-3 w-full"
|
||||||
|
>
|
||||||
|
{#if converter && converter.supportedFormats.includes(file.from)}
|
||||||
|
<span
|
||||||
|
class="sm:block hidden lg:hidden"
|
||||||
|
>from</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="py-2 lg:hidden px-3 font-display bg-foreground text-background rounded-xl sm:block hidden"
|
||||||
|
>{file.from}</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="sm:block lg:hidden hidden"
|
||||||
|
>to</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="hidden lg:block whitespace-nowrap"
|
||||||
|
>Convert to</span
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sm:block hidden lg:w-full"
|
||||||
|
>
|
||||||
|
<Dropdown
|
||||||
|
options={converter.supportedFormats}
|
||||||
|
bind:selected={files
|
||||||
|
.files[
|
||||||
|
files.files.length -
|
||||||
|
i -
|
||||||
|
1
|
||||||
|
].to}
|
||||||
|
onselect={() => {
|
||||||
|
file.result = null;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="w-full sm:hidden block h-11"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="py-2 px-3 font-display bg-foreground text-background rounded-xl"
|
||||||
|
>
|
||||||
|
{file.from}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="w-full sm:hidden h-full flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<ArrowRight
|
||||||
|
class="w-6 h-6 text-accent-foreground"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="w-full sm:hidden block h-full"
|
||||||
|
>
|
||||||
|
<Dropdown
|
||||||
|
options={converter.supportedFormats}
|
||||||
|
bind:selected={files
|
||||||
|
.files[
|
||||||
|
files.files.length -
|
||||||
|
1 -
|
||||||
|
i
|
||||||
|
].to}
|
||||||
|
onselect={() => {
|
||||||
|
file.result = null;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<span
|
||||||
|
class="py-2 px-3 font-display bg-foreground-failure text-white rounded-xl"
|
||||||
|
>{file.from}</span
|
||||||
|
>
|
||||||
|
|
||||||
<span class="text-foreground-failure">
|
<span
|
||||||
is not supported!
|
class="text-foreground-failure"
|
||||||
</span>
|
>
|
||||||
{/if}
|
is not supported!
|
||||||
<button
|
</span>
|
||||||
onclick={() => {
|
{/if}
|
||||||
// delete the file from the list
|
</div>
|
||||||
files.files = files.files.filter(
|
<!-- <div
|
||||||
(f) => f !== file,
|
class="hidden lg:flex gap-4 w-full"
|
||||||
);
|
>
|
||||||
if (files.files.length === 0) goto("/");
|
<button
|
||||||
}}
|
class="btn flex-grow flex-shrink-0"
|
||||||
class="ml-2 mr-1 sm:block hidden"
|
onclick={() => convert(file)}
|
||||||
>
|
>
|
||||||
<XIcon size="18" />
|
Convert
|
||||||
</button>
|
</button>
|
||||||
</div>
|
<button
|
||||||
</div>
|
class="btn flex-grow flex-shrink-0"
|
||||||
{#if converter && converter.supportedFormats.includes(file.from)}
|
disabled={!file.result}
|
||||||
<!-- god knows why, but setting opacity > 0.98 causes a z-ordering issue in firefox ??? -->
|
onclick={file.download}
|
||||||
<div
|
>
|
||||||
class="absolute top-0 -z-50 left-0 w-full h-full rounded-[10px] overflow-hidden opacity-[0.98]"
|
Download
|
||||||
>
|
</button>
|
||||||
<div
|
</div> -->
|
||||||
class="bg-cover bg-center w-full h-full"
|
</div>
|
||||||
style="background-image: url({file.blobUrl});"
|
<button
|
||||||
></div>
|
onclick={() => {
|
||||||
<div
|
// delete the file from the list
|
||||||
class="absolute sm:top-0 bottom-0 sm:right-0 sm:w-5/6 h-5/6 w-full sm:h-full"
|
files.files = files.files.filter(
|
||||||
>
|
(f) => f !== file,
|
||||||
<ProgressiveBlur
|
);
|
||||||
direction={isSm ? "bottom" : "right"}
|
if (files.files.length === 0)
|
||||||
endIntensity={128}
|
goto("/");
|
||||||
iterations={6}
|
}}
|
||||||
fadeTo="var(--bg-transparent)"
|
class="ml-2 mr-1 sm:block hidden lg:hidden"
|
||||||
/>
|
>
|
||||||
|
<XIcon size="18" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{#if converter && converter.supportedFormats.includes(file.from)}
|
||||||
|
<!-- god knows why, but setting opacity > 0.98 causes a z-ordering issue in firefox ??? -->
|
||||||
|
<div
|
||||||
|
class="absolute top-0 -z-50 left-0 w-full h-full rounded-[10px] overflow-hidden opacity-[0.98]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bg-cover bg-center w-full h-full"
|
||||||
|
style="background-image: url({file.blobUrl});"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="absolute sm:top-0 bottom-0 sm:left-0 h-5/6 w-full sm:h-full"
|
||||||
|
>
|
||||||
|
<ProgressiveBlur
|
||||||
|
direction={isSm
|
||||||
|
? "bottom"
|
||||||
|
: isLg
|
||||||
|
? "bottom"
|
||||||
|
: "right"}
|
||||||
|
endIntensity={isLg ? 64 : 128}
|
||||||
|
iterations={6}
|
||||||
|
fadeTo="var(--bg-transparent)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/each}
|
||||||
{/each}
|
</div>
|
||||||
<div class="w-full h-4 flex-shrink-0"></div>
|
<div class="w-full h-4 flex-shrink-0"></div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -406,9 +508,28 @@
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes processing {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
filter: blur(0px);
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
filter: blur(4px);
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
filter: blur(0px);
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.processing {
|
.processing {
|
||||||
transform: scale(1.05);
|
animation: processing 2000ms infinite;
|
||||||
filter: blur(4px);
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue