mirror of https://github.com/VERT-sh/VERT.git
fix: layout jump, feat: image preview
This commit is contained in:
parent
cda3825f6a
commit
db5d5414db
|
|
@ -21,5 +21,5 @@
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply text-foreground bg-background font-body overflow-x-hidden;
|
@apply text-foreground bg-background font-body overflow-x-hidden;
|
||||||
padding-left: calc(100vw - 100%);
|
width: 100vw;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@
|
||||||
if (!event.dataTransfer) return;
|
if (!event.dataTransfer) return;
|
||||||
if (!files) files = Array.from(event.dataTransfer.files);
|
if (!files) files = Array.from(event.dataTransfer.files);
|
||||||
else files.push(...Array.from(event.dataTransfer.files));
|
else files.push(...Array.from(event.dataTransfer.files));
|
||||||
|
onupload?.();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
<script lang="ts">
|
||||||
|
type Props = {
|
||||||
|
iterations: number;
|
||||||
|
endIntensity: number;
|
||||||
|
direction: "top" | "left" | "bottom" | "right";
|
||||||
|
fadeTo?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
let {
|
||||||
|
iterations,
|
||||||
|
endIntensity,
|
||||||
|
direction,
|
||||||
|
fadeTo = "transparent",
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
|
const getGradientDirection = () => {
|
||||||
|
switch (direction) {
|
||||||
|
case "top":
|
||||||
|
return "to top";
|
||||||
|
case "left":
|
||||||
|
return "to left";
|
||||||
|
case "bottom":
|
||||||
|
return "to bottom";
|
||||||
|
case "right":
|
||||||
|
return "to right";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const blurSteps = $derived(
|
||||||
|
Array.from({ length: iterations }, (_, i) => {
|
||||||
|
const blurIntensity =
|
||||||
|
(endIntensity / 2 ** (iterations - 1)) * 2 ** i;
|
||||||
|
const gradientStart = (i / iterations) * 100;
|
||||||
|
const gradientEnd = ((i + 1) / iterations) * 100;
|
||||||
|
|
||||||
|
return {
|
||||||
|
blurIntensity,
|
||||||
|
mask: `linear-gradient(${getGradientDirection()}, rgba(0, 0, 0, 0) ${gradientStart}%, rgba(0, 0, 0, 1) ${gradientEnd}%)`,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="w-full h-full relative">
|
||||||
|
{#each blurSteps as { blurIntensity, mask }, index}
|
||||||
|
<div
|
||||||
|
class="absolute w-full h-full"
|
||||||
|
style="
|
||||||
|
z-index: {index + 2};
|
||||||
|
backdrop-filter: blur({blurIntensity}px);
|
||||||
|
mask: {mask};
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
{/each}
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
z-index: {iterations + 2};
|
||||||
|
backdrop-filter: blur({endIntensity}px);
|
||||||
|
mask: linear-gradient({getGradientDirection()}, rgba(0, 0, 0, 0) ${(iterations /
|
||||||
|
(iterations + 1)) *
|
||||||
|
100}%, rgba(0, 0, 0, 1) 100%);
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="absolute top-0 left-0 w-full h-full z-50"
|
||||||
|
style="background: linear-gradient({getGradientDirection()}, transparent 0%, {fadeTo} 100%);"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
class Files {
|
class Files {
|
||||||
public files = $state<File[]>([]);
|
public files = $state<
|
||||||
|
{
|
||||||
|
file: File;
|
||||||
|
to: string;
|
||||||
|
blobUrl: string;
|
||||||
|
}[]
|
||||||
|
>([]);
|
||||||
public conversionTypes = $state<string[]>([]);
|
public conversionTypes = $state<string[]>([]);
|
||||||
public downloadFns = $state<(() => void)[]>([]);
|
|
||||||
public beenToConverterPage = $state(false);
|
public beenToConverterPage = $state(false);
|
||||||
public shouldShowAlert = $derived(
|
public shouldShowAlert = $derived(
|
||||||
!this.beenToConverterPage && this.files.length > 0,
|
!this.beenToConverterPage && this.files.length > 0,
|
||||||
|
|
|
||||||
|
|
@ -25,18 +25,30 @@
|
||||||
const linkIndex = $derived(
|
const linkIndex = $derived(
|
||||||
Object.keys(links).findIndex((link) => links[link] === data.pathname),
|
Object.keys(links).findIndex((link) => links[link] === data.pathname),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const maybeNavToHome = (e: DragEvent) => {
|
||||||
|
if (e.dataTransfer?.types.includes("Files")) {
|
||||||
|
e.preventDefault();
|
||||||
|
goto("/");
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full h-full flex items-center pt-48 flex-col gap-10">
|
<div
|
||||||
|
role="main"
|
||||||
|
class="w-full h-full flex items-center p-8 flex-col gap-8"
|
||||||
|
ondragenter={maybeNavToHome}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="w-full max-w-screen-lg p-1 border-solid border-2 rounded-2xl border-foreground-muted-alt grid"
|
class="w-full max-w-screen-lg p-1 border-solid border-2 rounded-2xl border-foreground-muted-alt flex"
|
||||||
style="grid-template-columns: auto 1fr"
|
|
||||||
>
|
>
|
||||||
<div
|
<div class="p-1">
|
||||||
class="px-4 m-1 mr-3 flex items-center bg-accent-background fill-accent-foreground rounded-xl"
|
<div
|
||||||
>
|
class="px-4 relative w-full h-full mr-3 justify-center items-center bg-accent-background fill-accent-foreground rounded-xl md:flex hidden"
|
||||||
<div class="h-6">
|
>
|
||||||
<Logo />
|
<div class="h-6 items-center flex justify-center">
|
||||||
|
<Logo />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -53,7 +65,7 @@
|
||||||
></div>
|
></div>
|
||||||
{#each Object.entries(links) as [name, link] (link)}
|
{#each Object.entries(links) as [name, link] (link)}
|
||||||
<button
|
<button
|
||||||
class="w-1/2 h-full flex items-center justify-center rounded-xl relative font-display overflow-hidden"
|
class="w-1/2 px-2 h-full flex items-center justify-center rounded-xl relative font-display overflow-hidden"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
const keys = Object.keys(links);
|
const keys = Object.keys(links);
|
||||||
const currentIndex = keys.findIndex(
|
const currentIndex = keys.findIndex(
|
||||||
|
|
@ -90,9 +102,11 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full max-w-screen-lg grid grid-cols-1 grid-rows-1 relative">
|
<div
|
||||||
|
class="w-full max-w-screen-lg h-full grid grid-cols-1 grid-rows-1 relative"
|
||||||
|
>
|
||||||
{#key data.pathname}
|
{#key data.pathname}
|
||||||
<div class="w-full">
|
<div class="w-full h-full">
|
||||||
<div
|
<div
|
||||||
class="absolute top-0 left-0 w-full h-full flex justify-center"
|
class="absolute top-0 left-0 w-full h-full flex justify-center"
|
||||||
in:blur={{
|
in:blur={{
|
||||||
|
|
|
||||||
|
|
@ -4,39 +4,58 @@
|
||||||
import { converters } from "$lib/converters";
|
import { converters } from "$lib/converters";
|
||||||
import { files } from "$lib/store/index.svelte";
|
import { files } from "$lib/store/index.svelte";
|
||||||
|
|
||||||
const convertAllFiles = async () => {
|
let ourFiles = $state<File[]>();
|
||||||
const promises = files.files?.map(async (file, i) => {
|
|
||||||
let conversionType = files.conversionTypes[i];
|
const runUpload = () => {
|
||||||
const converter = converters[0];
|
files.files = [
|
||||||
const convertedFile = await converter.convert(
|
...files.files,
|
||||||
{
|
...(ourFiles || []).map((f) => ({
|
||||||
name: file.name,
|
file: f,
|
||||||
buffer: await file.arrayBuffer(),
|
to: converters[0].supportedFormats[0],
|
||||||
},
|
blobUrl: URL.createObjectURL(f),
|
||||||
conversionType,
|
})),
|
||||||
);
|
];
|
||||||
files.downloadFns[i] = () => {
|
|
||||||
const url = URL.createObjectURL(
|
ourFiles = [];
|
||||||
new Blob([convertedFile.buffer]),
|
|
||||||
);
|
if (files.files.length > 0 && !files.beenToConverterPage)
|
||||||
const a = document.createElement("a");
|
goto("/convert");
|
||||||
a.href = url;
|
|
||||||
if (conversionType.startsWith("."))
|
|
||||||
conversionType = conversionType.slice(1);
|
|
||||||
a.download = `${file.name}.${conversionType}`;
|
|
||||||
a.target = "_self";
|
|
||||||
a.click();
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
if (promises) await Promise.all(promises);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// const convertAllFiles = async () => {
|
||||||
|
// const promises = files.files?.map(async (file, i) => {
|
||||||
|
// let conversionType = files.conversionTypes[i];
|
||||||
|
// const converter = converters[0];
|
||||||
|
// const convertedFile = await converter.convert(
|
||||||
|
// {
|
||||||
|
// name: file.name,
|
||||||
|
// buffer: await file.arrayBuffer(),
|
||||||
|
// },
|
||||||
|
// conversionType,
|
||||||
|
// );
|
||||||
|
// files.downloadFns[i] = () => {
|
||||||
|
// const url = URL.createObjectURL(
|
||||||
|
// new Blob([convertedFile.buffer]),
|
||||||
|
// );
|
||||||
|
// const a = document.createElement("a");
|
||||||
|
// a.href = url;
|
||||||
|
// if (conversionType.startsWith("."))
|
||||||
|
// conversionType = conversionType.slice(1);
|
||||||
|
// a.download = `${file.name}.${conversionType}`;
|
||||||
|
// a.target = "_self";
|
||||||
|
// a.click();
|
||||||
|
// URL.revokeObjectURL(url);
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// if (promises) await Promise.all(promises);
|
||||||
|
// };
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Uploader
|
<div
|
||||||
bind:files={files.files}
|
class="flex w-full h-full items-center justify-center pb-32 [@media(max-height:768px)]:block"
|
||||||
onupload={() => files.files.length > 0 && goto("/convert")}
|
>
|
||||||
/>
|
<Uploader bind:files={ourFiles} onupload={runUpload} />
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* for this page specifically */
|
/* for this page specifically */
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import ProgressiveBlur from "$lib/components/visual/effects/ProgressiveBlur.svelte";
|
||||||
import { converters } from "$lib/converters";
|
import { converters } from "$lib/converters";
|
||||||
import { files } from "$lib/store/index.svelte";
|
import { files } from "$lib/store/index.svelte";
|
||||||
|
|
||||||
|
|
@ -11,28 +12,52 @@
|
||||||
No files uploaded. Head to the Upload tab to begin!
|
No files uploaded. Head to the Upload tab to begin!
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#each files.files as file, i}
|
{#each files.files.reverse() as file, i}
|
||||||
<div
|
<div
|
||||||
class="flex items-center w-full border-2 border-solid border-foreground-muted-alt rounded-xl pl-4 pr-2 py-2"
|
class="flex relative items-center w-full border-2 border-solid border-foreground-muted-alt rounded-xl pl-4 pr-2 py-2"
|
||||||
>
|
>
|
||||||
<div class="flex items-center flex-grow">
|
<div class="flex items-center w-full z-50 relative">
|
||||||
{file.name}
|
<div
|
||||||
|
class="flex items-center flex-grow"
|
||||||
|
style="text-shadow: 0px 0px 6px white, 0px 0px 12px white"
|
||||||
|
>
|
||||||
|
{file.file.name}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 flex-shrink-0">
|
||||||
|
<span>from</span>
|
||||||
|
<span
|
||||||
|
class="py-2 px-3 font-display bg-foreground text-background rounded-xl"
|
||||||
|
>.{file.file.name.split(".").slice(-1)}</span
|
||||||
|
>
|
||||||
|
<span>to</span>
|
||||||
|
<select
|
||||||
|
class="font-display border-2 border-solid border-foreground-muted-alt rounded-xl p-2 focus:!outline-none"
|
||||||
|
bind:value={files.conversionTypes[i]}
|
||||||
|
>
|
||||||
|
{#each converters[0].supportedFormats as conversionType}
|
||||||
|
<option value={conversionType}
|
||||||
|
>{conversionType}</option
|
||||||
|
>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2 flex-shrink-0">
|
<!-- god knows why, but setting opacity > 0.98 causes a z-ordering issue in firefox ??? -->
|
||||||
<span>from</span>
|
<div
|
||||||
<span
|
class="absolute top-0 -z-50 left-0 w-full h-full rounded-[10px] overflow-hidden opacity-[0.98]"
|
||||||
class="py-2 px-3 font-display bg-foreground text-background rounded-xl"
|
>
|
||||||
>.{file.name.split(".").slice(-1)}</span
|
<div
|
||||||
>
|
class="bg-cover bg-center w-full h-full"
|
||||||
<span>to</span>
|
style="background-image: url({file.blobUrl});"
|
||||||
<select
|
></div>
|
||||||
class="font-display border-2 border-solid border-foreground-muted-alt rounded-xl p-2 focus:!outline-none"
|
<div class="absolute top-0 right-0 w-5/6 h-full">
|
||||||
bind:value={files.conversionTypes[i]}
|
<ProgressiveBlur
|
||||||
>
|
direction="right"
|
||||||
{#each converters[0].supportedFormats as conversionType}
|
endIntensity={64}
|
||||||
<option value={conversionType}>{conversionType}</option>
|
iterations={6}
|
||||||
{/each}
|
fadeTo="rgba(255, 255, 255, 0.8)"
|
||||||
</select>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue