diff --git a/bun.lockb b/bun.lockb index a4ad605..1064691 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 733113e..2015120 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@fontsource/azeret-mono": "^5.1.0", "@fontsource/lexend": "^5.1.1", "@imagemagick/magick-wasm": "^0.0.31", + "client-zip": "^2.4.5", "clsx": "^2.1.1", "lucide-svelte": "^0.456.0", "svelte-adapter-bun": "^0.5.2", diff --git a/src/app.css b/src/app.css index 6183cc4..c89d1f4 100644 --- a/src/app.css +++ b/src/app.css @@ -24,3 +24,13 @@ body { @apply text-foreground bg-background font-body overflow-x-hidden; width: 100vw; } + +@layer components { + select { + @apply appearance-none; + } + + .btn { + @apply font-display flex items-center justify-center overflow-hidden relative cursor-pointer px-4 border-2 border-solid bg-background border-foreground-muted-alt rounded-xl p-2 focus:!outline-none hover:scale-105 transition-transform duration-200 active:scale-95; + } +} diff --git a/src/lib/animation/index.ts b/src/lib/animation/index.ts index c574e9b..6392734 100644 --- a/src/lib/animation/index.ts +++ b/src/lib/animation/index.ts @@ -29,6 +29,8 @@ const choose = ( ? outValue : defaultValue; +type Combination = `${T} ${U}`; + export const blur = ( _: HTMLElement, config: @@ -50,6 +52,10 @@ export const blur = ( }; delay: number; opacity: boolean; + origin: Combination< + "top" | "bottom" | "left" | "right" | "center", + "top" | "bottom" | "left" | "right" | "center" + >; }> | undefined, dir: { @@ -120,7 +126,7 @@ export const blur = ( : ``; return `filter: blur(${(1 - t) * (config?.blurMultiplier || 1)}px); opacity: ${config?.opacity ? t : 1}; transform: ${ translate - };`; + }; ${config?.origin ? `transform-origin: ${config.origin};` : ""}`; }, easing: config?.easing, }; diff --git a/src/lib/components/functional/Dropdown.svelte b/src/lib/components/functional/Dropdown.svelte new file mode 100644 index 0000000..8d9b28b --- /dev/null +++ b/src/lib/components/functional/Dropdown.svelte @@ -0,0 +1,128 @@ + + +
+ + {#if open} +
+ {#each options as option} + + {/each} +
+ {/if} +
diff --git a/src/lib/converters/converter.ts b/src/lib/converters/converter.ts index c2eda76..9c16dbc 100644 --- a/src/lib/converters/converter.ts +++ b/src/lib/converters/converter.ts @@ -4,6 +4,10 @@ import type { IFile, OmitBetterStrict } from "$lib/types"; * Base class for all converters. */ export class Converter { + /** + * The public name of the converter. + */ + public name: string = "Unknown"; /** * List of supported formats. */ diff --git a/src/lib/converters/vips.ts b/src/lib/converters/vips.ts index e9b7f4e..ebe11f5 100644 --- a/src/lib/converters/vips.ts +++ b/src/lib/converters/vips.ts @@ -7,6 +7,7 @@ import type { VipsWorkerMessage, OmitBetterStrict } from "$lib/types"; export class VipsConverter extends Converter { private worker: Worker = browser ? new VipsWorker() : null!; private id = 0; + public name = "Vips"; public supportedFormats = [ ".jpg", ".jpeg", diff --git a/src/lib/store/index.svelte.ts b/src/lib/store/index.svelte.ts index 78487c8..7878b4f 100644 --- a/src/lib/store/index.svelte.ts +++ b/src/lib/store/index.svelte.ts @@ -1,3 +1,5 @@ +import type { IFile } from "$lib/types"; + class Files { public files = $state< { @@ -6,6 +8,7 @@ class Files { to: string; blobUrl: string; id: string; + result?: (IFile & { blobUrl: string; animating: boolean }) | null; }[] >([]); public conversionTypes = $state([]); diff --git a/src/routes/convert/+page.svelte b/src/routes/convert/+page.svelte index 0dca94d..8ec5a91 100644 --- a/src/routes/convert/+page.svelte +++ b/src/routes/convert/+page.svelte @@ -1,5 +1,6 @@
@@ -30,98 +147,168 @@

No files uploaded. Head to the Upload tab to begin!

- {/if} -
- {#each reversed as file, i (file.id)} + {:else} +
-
-
- {file.file.name} -
-
- {#if converters[0].supportedFormats.includes(file.from)} - from - {file.from} - to - - {:else} - {file.from} - - - is not supported! - - {/if} - +

Options

+
+
+

Converter

+ converter.name, + )} + bind:selected={converterName} + />
- {#if converters[0].supportedFormats.includes(file.from)} - +

Quick Actions

+
+
+

Set all formats

+ { + files.conversionTypes = Array.from( + { length: files.files.length }, + () => o, + ); + + files.files.forEach((file) => { + file.result = null; + }); + }} + /> +
+
+ + +
+
+
+ {#each reversed as file, i (file.id)} +
-
- + class="flex items-center justify-between w-full z-50 relative" + > +
+ {file.file.name} +
+
+ {#if converters[0].supportedFormats.includes(file.from)} + from + {file.from} + to + + { + file.result = null; + }} + /> + {:else} + {file.from} + + + is not supported! + + {/if} + +
+ {#if converters[0].supportedFormats.includes(file.from)} + +
+
+
+ +
+
+ {/if}
- {/if} -
- {/each} -
+
+ {/each} +
+
+ {/if}