mirror of https://github.com/VERT-sh/VERT.git
refactor: run prettier
This commit is contained in:
parent
35949a6d04
commit
c139eae565
106
src/app.html
106
src/app.html
|
|
@ -1,53 +1,53 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="%lang%">
|
<html lang="%lang%">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<link rel="apple-touch-icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="apple-touch-icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-startup-image"
|
rel="apple-touch-startup-image"
|
||||||
href="%sveltekit.assets%/lettermark.jpg"
|
href="%sveltekit.assets%/lettermark.jpg"
|
||||||
/>
|
/>
|
||||||
<meta name="mobile-web-app-capable" content="yes" />
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta
|
<meta
|
||||||
name="apple-mobile-web-app-status-bar-style"
|
name="apple-mobile-web-app-status-bar-style"
|
||||||
content="black-translucent"
|
content="black-translucent"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
// Apply theme before DOM is loaded
|
// Apply theme before DOM is loaded
|
||||||
let theme = localStorage.getItem("theme");
|
let theme = localStorage.getItem("theme");
|
||||||
const prefersDark = window.matchMedia(
|
const prefersDark = window.matchMedia(
|
||||||
"(prefers-color-scheme: dark)",
|
"(prefers-color-scheme: dark)",
|
||||||
).matches;
|
).matches;
|
||||||
console.log(
|
console.log(
|
||||||
`Theme: ${theme || "N/A"}, prefers dark: ${prefersDark}`,
|
`Theme: ${theme || "N/A"}, prefers dark: ${prefersDark}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (theme !== "light" && theme !== "dark") {
|
if (theme !== "light" && theme !== "dark") {
|
||||||
console.log("Invalid theme, setting to default");
|
console.log("Invalid theme, setting to default");
|
||||||
theme = prefersDark ? "dark" : "light";
|
theme = prefersDark ? "dark" : "light";
|
||||||
localStorage.setItem("theme", theme);
|
localStorage.setItem("theme", theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Applying theme: ${theme}`);
|
console.log(`Applying theme: ${theme}`);
|
||||||
document.documentElement.classList.add(theme);
|
document.documentElement.classList.add(theme);
|
||||||
|
|
||||||
// Lock dark reader if it's set to dark mode
|
// Lock dark reader if it's set to dark mode
|
||||||
if (theme === "dark") {
|
if (theme === "dark") {
|
||||||
const lock = document.createElement("meta");
|
const lock = document.createElement("meta");
|
||||||
lock.name = "darkreader-lock";
|
lock.name = "darkreader-lock";
|
||||||
document.head.appendChild(lock);
|
document.head.appendChild(lock);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,8 @@
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
file.conversionSettings.imageSequence = imageSequence;
|
file.conversionSettings.imageSequence = imageSequence;
|
||||||
file.conversionSettings.imageSequenceFPS = imageSequenceFPS;
|
file.conversionSettings.imageSequenceFPS = imageSequenceFPS;
|
||||||
file.conversionSettings.imageSequenceTransparency = imageSequenceTransparency;
|
file.conversionSettings.imageSequenceTransparency =
|
||||||
|
imageSequenceTransparency;
|
||||||
});
|
});
|
||||||
|
|
||||||
const normalize = (str: string) => str.replace(/^\./, "").toLowerCase();
|
const normalize = (str: string) => str.replace(/^\./, "").toLowerCase();
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
</div>
|
</div>
|
||||||
<h2 class="text-center text-2xl font-semibold mt-4">
|
<h2 class="text-center text-2xl font-semibold mt-4">
|
||||||
{m["upload.uploader.text"]({
|
{m["upload.uploader.text"]({
|
||||||
action: m["upload.uploader.convert"]()
|
action: m["upload.uploader.convert"](),
|
||||||
})}
|
})}
|
||||||
</h2>
|
</h2>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<p class="text-black">
|
<p class="text-black">
|
||||||
{m["convert.external_warning.text"]({
|
{m["convert.external_warning.text"]({
|
||||||
filename: toast.additional.filename,
|
filename: toast.additional.filename,
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -38,40 +38,50 @@
|
||||||
<p>{@html sanitize(m["convert.errors.vertd.details.body"]())}</p>
|
<p>{@html sanitize(m["convert.errors.vertd.details.body"]())}</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="text-black dynadark:text-white">
|
<span class="text-black dynadark:text-white">
|
||||||
{@html sanitize(m["convert.errors.vertd.details.job_id"]({
|
{@html sanitize(
|
||||||
jobId: additional.jobId,
|
m["convert.errors.vertd.details.job_id"]({
|
||||||
}))}
|
jobId: additional.jobId,
|
||||||
|
}),
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="text-black dynadark:text-white">
|
<span class="text-black dynadark:text-white">
|
||||||
{@html sanitize(m["convert.errors.vertd.details.from"]({
|
{@html sanitize(
|
||||||
from: additional.from,
|
m["convert.errors.vertd.details.from"]({
|
||||||
}))}
|
from: additional.from,
|
||||||
|
}),
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="text-black dynadark:text-white">
|
<span class="text-black dynadark:text-white">
|
||||||
{@html sanitize(m["convert.errors.vertd.details.to"]({ to: additional.to }))}
|
{@html sanitize(
|
||||||
|
m["convert.errors.vertd.details.to"]({ to: additional.to }),
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="text-black dynadark:text-white">
|
<span class="text-black dynadark:text-white">
|
||||||
{@html sanitize(link(
|
{@html sanitize(
|
||||||
["view_link"],
|
link(
|
||||||
m["convert.errors.vertd.details.error_message"](),
|
["view_link"],
|
||||||
[errorBlobUrl || "#"],
|
m["convert.errors.vertd.details.error_message"](),
|
||||||
|
[errorBlobUrl || "#"],
|
||||||
|
[true],
|
||||||
|
["text-blue-500 font-normal"],
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{@html sanitize(
|
||||||
|
link(
|
||||||
|
["privacy_link"],
|
||||||
|
m["convert.errors.vertd.details.footer"](),
|
||||||
|
"/privacy",
|
||||||
[true],
|
[true],
|
||||||
["text-blue-500 font-normal"],
|
),
|
||||||
))}
|
)}
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{@html sanitize(link(
|
|
||||||
["privacy_link"],
|
|
||||||
m["convert.errors.vertd.details.footer"](),
|
|
||||||
"/privacy",
|
|
||||||
[true],
|
|
||||||
))}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,7 @@
|
||||||
{m["footer.discord_server"]()}
|
{m["footer.discord_server"]()}
|
||||||
</a>
|
</a>
|
||||||
<p>•</p>
|
<p>•</p>
|
||||||
<a
|
<a class="hover:underline font-normal" href="/privacy/">
|
||||||
class="hover:underline font-normal"
|
|
||||||
href="/privacy/"
|
|
||||||
>
|
|
||||||
{m["footer.privacy_policy"]()}
|
{m["footer.privacy_policy"]()}
|
||||||
</a>
|
</a>
|
||||||
{#if commitHash}
|
{#if commitHash}
|
||||||
|
|
|
||||||
|
|
@ -1,212 +1,212 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import { page } from "$app/state";
|
import { page } from "$app/state";
|
||||||
import { duration, fade } from "$lib/util/animation";
|
import { duration, fade } from "$lib/util/animation";
|
||||||
import {
|
import {
|
||||||
effects,
|
effects,
|
||||||
files,
|
files,
|
||||||
goingLeft,
|
goingLeft,
|
||||||
setTheme,
|
setTheme,
|
||||||
} from "$lib/store/index.svelte";
|
} from "$lib/store/index.svelte";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {
|
import {
|
||||||
InfoIcon,
|
InfoIcon,
|
||||||
MoonIcon,
|
MoonIcon,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
SunIcon,
|
SunIcon,
|
||||||
UploadIcon,
|
UploadIcon,
|
||||||
type Icon as IconType,
|
type Icon as IconType,
|
||||||
} from "lucide-svelte";
|
} from "lucide-svelte";
|
||||||
import { quintOut } from "svelte/easing";
|
import { quintOut } from "svelte/easing";
|
||||||
import Panel from "../../visual/Panel.svelte";
|
import Panel from "../../visual/Panel.svelte";
|
||||||
import Logo from "../../visual/svg/Logo.svelte";
|
import Logo from "../../visual/svg/Logo.svelte";
|
||||||
import { beforeNavigate } from "$app/navigation";
|
import { beforeNavigate } from "$app/navigation";
|
||||||
import Tooltip from "$lib/components/visual/Tooltip.svelte";
|
import Tooltip from "$lib/components/visual/Tooltip.svelte";
|
||||||
import { m } from "$lib/paraglide/messages";
|
import { m } from "$lib/paraglide/messages";
|
||||||
|
|
||||||
const items = $derived<
|
const items = $derived<
|
||||||
{
|
{
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
activeMatch: (pathname: string) => boolean;
|
activeMatch: (pathname: string) => boolean;
|
||||||
icon: typeof IconType;
|
icon: typeof IconType;
|
||||||
badge?: number;
|
badge?: number;
|
||||||
}[]
|
}[]
|
||||||
>([
|
>([
|
||||||
{
|
{
|
||||||
name: m["navbar.upload"](),
|
name: m["navbar.upload"](),
|
||||||
url: "/",
|
url: "/",
|
||||||
activeMatch: (pathname) => pathname === "/",
|
activeMatch: (pathname) => pathname === "/",
|
||||||
icon: UploadIcon,
|
icon: UploadIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: m["navbar.convert"](),
|
name: m["navbar.convert"](),
|
||||||
url: "/convert/",
|
url: "/convert/",
|
||||||
activeMatch: (pathname) =>
|
activeMatch: (pathname) =>
|
||||||
pathname === "/convert/" || pathname === "/convert",
|
pathname === "/convert/" || pathname === "/convert",
|
||||||
icon: RefreshCw,
|
icon: RefreshCw,
|
||||||
badge: files.files.length,
|
badge: files.files.length,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: m["navbar.settings"](),
|
name: m["navbar.settings"](),
|
||||||
url: "/settings/",
|
url: "/settings/",
|
||||||
activeMatch: (pathname) => pathname.startsWith("/settings"),
|
activeMatch: (pathname) => pathname.startsWith("/settings"),
|
||||||
icon: SettingsIcon,
|
icon: SettingsIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: m["navbar.about"](),
|
name: m["navbar.about"](),
|
||||||
url: "/about/",
|
url: "/about/",
|
||||||
activeMatch: (pathname) => pathname.startsWith("/about"),
|
activeMatch: (pathname) => pathname.startsWith("/about"),
|
||||||
icon: InfoIcon,
|
icon: InfoIcon,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let links = $state<HTMLAnchorElement[]>([]);
|
let links = $state<HTMLAnchorElement[]>([]);
|
||||||
let container = $state<HTMLDivElement>();
|
let container = $state<HTMLDivElement>();
|
||||||
let containerRect = $derived(container?.getBoundingClientRect());
|
let containerRect = $derived(container?.getBoundingClientRect());
|
||||||
let isInitialized = $state(false);
|
let isInitialized = $state(false);
|
||||||
|
|
||||||
const linkRects = $derived(links.map((l) => l.getBoundingClientRect()));
|
const linkRects = $derived(links.map((l) => l.getBoundingClientRect()));
|
||||||
|
|
||||||
const selectedIndex = $derived(
|
const selectedIndex = $derived(
|
||||||
items.findIndex((i) => i.activeMatch(page.url.pathname)),
|
items.findIndex((i) => i.activeMatch(page.url.pathname)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const isSecretPage = $derived(selectedIndex === -1);
|
const isSecretPage = $derived(selectedIndex === -1);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (containerRect && linkRects.length > 0 && links.length > 0) {
|
if (containerRect && linkRects.length > 0 && links.length > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
}, 10);
|
}, 10);
|
||||||
} else {
|
} else {
|
||||||
isInitialized = false;
|
isInitialized = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeNavigate((e) => {
|
beforeNavigate((e) => {
|
||||||
const oldIndex = items.findIndex((i) =>
|
const oldIndex = items.findIndex((i) =>
|
||||||
i.activeMatch(e.from?.url.pathname || ""),
|
i.activeMatch(e.from?.url.pathname || ""),
|
||||||
);
|
);
|
||||||
const newIndex = items.findIndex((i) =>
|
const newIndex = items.findIndex((i) =>
|
||||||
i.activeMatch(e.to?.url.pathname || ""),
|
i.activeMatch(e.to?.url.pathname || ""),
|
||||||
);
|
);
|
||||||
if (newIndex < oldIndex) {
|
if (newIndex < oldIndex) {
|
||||||
goingLeft.set(true);
|
goingLeft.set(true);
|
||||||
} else {
|
} else {
|
||||||
goingLeft.set(false);
|
goingLeft.set(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet link(item: (typeof items)[0], index: number)}
|
{#snippet link(item: (typeof items)[0], index: number)}
|
||||||
{@const Icon = item.icon}
|
{@const Icon = item.icon}
|
||||||
<a
|
<a
|
||||||
bind:this={links[index]}
|
bind:this={links[index]}
|
||||||
href={item.url}
|
href={item.url}
|
||||||
aria-label={item.name}
|
aria-label={item.name}
|
||||||
class={clsx(
|
class={clsx(
|
||||||
"min-w-16 md:min-w-32 h-full relative z-10 rounded-xl flex flex-1 items-center justify-center gap-3 overflow-hidden",
|
"min-w-16 md:min-w-32 h-full relative z-10 rounded-xl flex flex-1 items-center justify-center gap-3 overflow-hidden",
|
||||||
{
|
{
|
||||||
"bg-panel-highlight":
|
"bg-panel-highlight":
|
||||||
item.activeMatch(page.url.pathname) && !browser,
|
item.activeMatch(page.url.pathname) && !browser,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
>
|
>
|
||||||
<div class="grid grid-rows-1 grid-cols-1">
|
<div class="grid grid-rows-1 grid-cols-1">
|
||||||
{#key item.name}
|
{#key item.name}
|
||||||
<div
|
<div
|
||||||
class="w-full row-start-1 col-start-1 h-full flex items-center justify-center gap-3"
|
class="w-full row-start-1 col-start-1 h-full flex items-center justify-center gap-3"
|
||||||
in:fade={{
|
in:fade={{
|
||||||
duration,
|
duration,
|
||||||
easing: quintOut,
|
easing: quintOut,
|
||||||
}}
|
}}
|
||||||
out:fade={{
|
out:fade={{
|
||||||
duration,
|
duration,
|
||||||
easing: quintOut,
|
easing: quintOut,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<Icon />
|
<Icon />
|
||||||
{#if item.badge}
|
{#if item.badge}
|
||||||
<div
|
<div
|
||||||
class="absolute overflow-hidden grid grid-rows-1 grid-cols-1 -top-1 font-display -right-1 w-fit px-1.5 h-4 rounded-full bg-badge text-on-badge font-medium"
|
class="absolute overflow-hidden grid grid-rows-1 grid-cols-1 -top-1 font-display -right-1 w-fit px-1.5 h-4 rounded-full bg-badge text-on-badge font-medium"
|
||||||
style="font-size: 0.7rem;"
|
style="font-size: 0.7rem;"
|
||||||
transition:fade={{
|
transition:fade={{
|
||||||
duration,
|
duration,
|
||||||
easing: quintOut,
|
easing: quintOut,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#key item.badge}
|
{#key item.badge}
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-center w-full h-full col-start-1 row-start-1"
|
class="flex items-center justify-center w-full h-full col-start-1 row-start-1"
|
||||||
in:fade={{
|
in:fade={{
|
||||||
duration,
|
duration,
|
||||||
easing: quintOut,
|
easing: quintOut,
|
||||||
}}
|
}}
|
||||||
out:fade={{
|
out:fade={{
|
||||||
duration,
|
duration,
|
||||||
easing: quintOut,
|
easing: quintOut,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.badge}
|
{item.badge}
|
||||||
</div>
|
</div>
|
||||||
{/key}
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
class="font-medium hidden hyphens-auto break-all md:flex min-w-0"
|
class="font-medium hidden hyphens-auto break-all md:flex min-w-0"
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{/key}
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
<div bind:this={container}>
|
<div bind:this={container}>
|
||||||
<Panel class="max-w-[778px] w-screen h-20 flex items-center gap-3 relative">
|
<Panel class="max-w-[778px] w-screen h-20 flex items-center gap-3 relative">
|
||||||
{@const linkRect = linkRects.at(selectedIndex) || linkRects[0]}
|
{@const linkRect = linkRects.at(selectedIndex) || linkRects[0]}
|
||||||
{#if linkRect && isInitialized}
|
{#if linkRect && isInitialized}
|
||||||
<div
|
<div
|
||||||
class="absolute bg-panel-highlight rounded-xl"
|
class="absolute bg-panel-highlight rounded-xl"
|
||||||
style="width: {linkRect.width}px; height: {linkRect.height}px; top: {linkRect.top -
|
style="width: {linkRect.width}px; height: {linkRect.height}px; top: {linkRect.top -
|
||||||
(containerRect?.top || 0)}px; left: {linkRect.left -
|
(containerRect?.top || 0)}px; left: {linkRect.left -
|
||||||
(containerRect?.left || 0)}px; opacity: {isSecretPage
|
(containerRect?.left || 0)}px; opacity: {isSecretPage
|
||||||
? 0
|
? 0
|
||||||
: 1}; {$effects
|
: 1}; {$effects
|
||||||
? `transition: left var(--transition) ${duration}ms, top var(--transition) ${duration}ms, opacity var(--transition) ${duration}ms;`
|
? `transition: left var(--transition) ${duration}ms, top var(--transition) ${duration}ms, opacity var(--transition) ${duration}ms;`
|
||||||
: ''}"
|
: ''}"
|
||||||
></div>
|
></div>
|
||||||
{/if}
|
{/if}
|
||||||
<a
|
<a
|
||||||
class="w-28 h-full bg-accent rounded-xl items-center justify-center hidden md:flex"
|
class="w-28 h-full bg-accent rounded-xl items-center justify-center hidden md:flex"
|
||||||
href="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<div class="h-5 w-full">
|
<div class="h-5 w-full">
|
||||||
<Logo />
|
<Logo />
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
{#each items as item, i (item.url)}
|
{#each items as item, i (item.url)}
|
||||||
{@render link(item, i)}
|
{@render link(item, i)}
|
||||||
{/each}
|
{/each}
|
||||||
<div class="w-0.5 bg-separator h-full hidden md:flex"></div>
|
<div class="w-0.5 bg-separator h-full hidden md:flex"></div>
|
||||||
<Tooltip text={m["navbar.toggle_theme"]()} position="right">
|
<Tooltip text={m["navbar.toggle_theme"]()} position="right">
|
||||||
<button
|
<button
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
const isDark =
|
const isDark =
|
||||||
document.documentElement.classList.contains("dark");
|
document.documentElement.classList.contains("dark");
|
||||||
setTheme(isDark ? "light" : "dark");
|
setTheme(isDark ? "light" : "dark");
|
||||||
}}
|
}}
|
||||||
class="w-14 h-full items-center justify-center hidden md:flex"
|
class="w-14 h-full items-center justify-center hidden md:flex"
|
||||||
>
|
>
|
||||||
<SunIcon class="dynadark:hidden block" />
|
<SunIcon class="dynadark:hidden block" />
|
||||||
<MoonIcon class="dynadark:block hidden" />
|
<MoonIcon class="dynadark:block hidden" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Panel>
|
</Panel>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,12 @@ import type {
|
||||||
SettingDefinition,
|
SettingDefinition,
|
||||||
} from "$lib/types/conversion-settings";
|
} from "$lib/types/conversion-settings";
|
||||||
|
|
||||||
export type WorkerStatus = "not-ready" | "downloading" | "ready" | "partially-ready" | "error";
|
export type WorkerStatus =
|
||||||
|
| "not-ready"
|
||||||
|
| "downloading"
|
||||||
|
| "ready"
|
||||||
|
| "partially-ready"
|
||||||
|
| "error";
|
||||||
|
|
||||||
export class FormatInfo {
|
export class FormatInfo {
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
@ -56,7 +61,9 @@ export class Converter {
|
||||||
* Can be overridden per converter for format-specific settings.
|
* Can be overridden per converter for format-specific settings.
|
||||||
* @param input The input file.
|
* @param input The input file.
|
||||||
*/
|
*/
|
||||||
public async getAvailableSettings(input?: VertFile): Promise<SettingDefinition[]> {
|
public async getAvailableSettings(
|
||||||
|
input?: VertFile,
|
||||||
|
): Promise<SettingDefinition[]> {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,7 +71,9 @@ export class Converter {
|
||||||
* Get default settings for a conversion.
|
* Get default settings for a conversion.
|
||||||
* @param input The input file.
|
* @param input The input file.
|
||||||
*/
|
*/
|
||||||
public async getDefaultSettings(input?: VertFile): Promise<ConversionSettings> {
|
public async getDefaultSettings(
|
||||||
|
input?: VertFile,
|
||||||
|
): Promise<ConversionSettings> {
|
||||||
const defaults: ConversionSettings = {};
|
const defaults: ConversionSettings = {};
|
||||||
const settings = await this.getAvailableSettings(input);
|
const settings = await this.getAvailableSettings(input);
|
||||||
settings.forEach((setting) => {
|
settings.forEach((setting) => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import { toArgs, animatedImageFormats } from "$lib/converters/ffmpeg/ffmpeg.codecs";
|
import {
|
||||||
|
toArgs,
|
||||||
|
animatedImageFormats,
|
||||||
|
} from "$lib/converters/ffmpeg/ffmpeg.codecs";
|
||||||
import type { ConversionSettings } from "$lib/types/conversion-settings";
|
import type { ConversionSettings } from "$lib/types/conversion-settings";
|
||||||
import { videoFormats } from "../vertd/vertd.svelte";
|
import { videoFormats } from "../vertd/vertd.svelte";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export const converterCategories = {
|
||||||
video: ["mediabunny", "vertd"],
|
video: ["mediabunny", "vertd"],
|
||||||
audio: ["ffmpeg"],
|
audio: ["ffmpeg"],
|
||||||
doc: ["pandoc"],
|
doc: ["pandoc"],
|
||||||
}
|
};
|
||||||
|
|
||||||
export function getConverterByFormat(format: string) {
|
export function getConverterByFormat(format: string) {
|
||||||
for (const converter of converters) {
|
for (const converter of converters) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Panel from "$lib/components/visual/Panel.svelte";
|
import Panel from "$lib/components/visual/Panel.svelte";
|
||||||
import { CONTACT_EMAIL, DISCORD_URL, GITHUB_URL_VERT } from "$lib/util/consts";
|
import {
|
||||||
|
CONTACT_EMAIL,
|
||||||
|
DISCORD_URL,
|
||||||
|
GITHUB_URL_VERT,
|
||||||
|
} from "$lib/util/consts";
|
||||||
import { effects } from "$lib/store/index.svelte";
|
import { effects } from "$lib/store/index.svelte";
|
||||||
import {
|
import {
|
||||||
GithubIcon,
|
GithubIcon,
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,14 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-muted">
|
<p class="text-muted">
|
||||||
{@html sanitize(link(
|
{@html sanitize(
|
||||||
"discord_link",
|
link(
|
||||||
m["about.sponsors.description"](),
|
"discord_link",
|
||||||
DISCORD_URL,
|
m["about.sponsors.description"](),
|
||||||
true
|
DISCORD_URL,
|
||||||
))}
|
true,
|
||||||
|
),
|
||||||
|
)}
|
||||||
<span class="inline-block mx-[2px] relative top-[2px]">
|
<span class="inline-block mx-[2px] relative top-[2px]">
|
||||||
<button
|
<button
|
||||||
id="email"
|
id="email"
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,8 @@
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p class="text-base font-bold">
|
<p class="text-base font-bold">
|
||||||
{m["settings.appearance.language.title"]()}
|
{m["settings.appearance.language.title"]()}
|
||||||
{#if currentLocale !== "en"} (Language){/if}
|
{#if currentLocale !== "en"}
|
||||||
|
(Language){/if}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-muted font-normal italic">
|
<p class="text-sm text-muted font-normal italic">
|
||||||
{m["settings.appearance.language.description"]()}
|
{m["settings.appearance.language.description"]()}
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,11 @@
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p class="text-base font-bold">
|
<p class="text-base font-bold">
|
||||||
{m["settings.conversion.filename.format"]()}
|
{m["settings.conversion.filename.format"]()}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-muted font-normal">
|
<p class="text-sm text-muted font-normal">
|
||||||
{@html sanitize(
|
{@html sanitize(
|
||||||
m["settings.conversion.filename.description"](),
|
m["settings.conversion.filename.description"](),
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -87,7 +87,9 @@
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p class="text-base font-bold">
|
<p class="text-base font-bold">
|
||||||
{m["settings.conversion.default_format.label"]()}
|
{m[
|
||||||
|
"settings.conversion.default_format.label"
|
||||||
|
]()}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-muted font-normal">
|
<p class="text-sm text-muted font-normal">
|
||||||
{m[
|
{m[
|
||||||
|
|
@ -226,7 +228,9 @@
|
||||||
size="24"
|
size="24"
|
||||||
class="inline-block mr-2"
|
class="inline-block mr-2"
|
||||||
/>
|
/>
|
||||||
{m["settings.conversion.metadata.keep"]()}
|
{m[
|
||||||
|
"settings.conversion.metadata.keep"
|
||||||
|
]()}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
@ -242,7 +246,9 @@
|
||||||
size="24"
|
size="24"
|
||||||
class="inline-block mr-2"
|
class="inline-block mr-2"
|
||||||
/>
|
/>
|
||||||
{m["settings.conversion.metadata.remove"]()}
|
{m[
|
||||||
|
"settings.conversion.metadata.remove"
|
||||||
|
]()}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,9 @@
|
||||||
m["settings.privacy.site_data.clear_all_data_confirm"](),
|
m["settings.privacy.site_data.clear_all_data_confirm"](),
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
text: m["settings.privacy.site_data.clear_all_data_cancel"](),
|
text: m[
|
||||||
|
"settings.privacy.site_data.clear_all_data_cancel"
|
||||||
|
](),
|
||||||
action: () => {},
|
action: () => {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -95,7 +97,9 @@
|
||||||
ToastManager.add({
|
ToastManager.add({
|
||||||
type: "success",
|
type: "success",
|
||||||
message:
|
message:
|
||||||
m["settings.privacy.site_data.all_data_cleared"](),
|
m[
|
||||||
|
"settings.privacy.site_data.all_data_cleared"
|
||||||
|
](),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error(
|
error(
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ export interface SettingDefinition {
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
step?: number;
|
step?: number;
|
||||||
options?: Array<{ value: any; label: any; }>; // for select/range types
|
options?: Array<{ value: any; label: any }>; // for select/range types
|
||||||
description?: string;
|
description?: string;
|
||||||
hasCustomInput?: boolean; // for select types with a "custom" option
|
hasCustomInput?: boolean; // for select types with a "custom" option
|
||||||
customInputKey?: string; // key to use for custom input value in settings object
|
customInputKey?: string; // key to use for custom input value in settings object
|
||||||
forceFullWidth?: boolean; // force setting to take up full width (usually grid 2)
|
forceFullWidth?: boolean; // force setting to take up full width (usually grid 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConversionSettings {
|
export interface ConversionSettings {
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,50 @@
|
||||||
import { VertFile } from "./file.svelte";
|
import { VertFile } from "./file.svelte";
|
||||||
|
|
||||||
interface ConvertMessage {
|
interface ConvertMessage {
|
||||||
type: "convert";
|
type: "convert";
|
||||||
input: {
|
input:
|
||||||
file: File;
|
| {
|
||||||
name: string;
|
file: File;
|
||||||
from: string;
|
name: string;
|
||||||
to: string;
|
from: string;
|
||||||
} | VertFile;
|
to: string;
|
||||||
to: string;
|
}
|
||||||
conversionSettings: string; // JSON stringified ConversionSettings
|
| VertFile;
|
||||||
}
|
to: string;
|
||||||
|
conversionSettings: string; // JSON stringified ConversionSettings
|
||||||
interface FinishedMessage {
|
}
|
||||||
type: "finished";
|
|
||||||
output: ArrayBufferLike | Uint8Array;
|
interface FinishedMessage {
|
||||||
zip?: boolean;
|
type: "finished";
|
||||||
}
|
output: ArrayBufferLike | Uint8Array;
|
||||||
|
zip?: boolean;
|
||||||
interface LoadMessage {
|
}
|
||||||
type: "load";
|
|
||||||
wasm: ArrayBuffer;
|
interface LoadMessage {
|
||||||
}
|
type: "load";
|
||||||
|
wasm: ArrayBuffer;
|
||||||
interface LoadedMessage {
|
}
|
||||||
type: "loaded";
|
|
||||||
}
|
interface LoadedMessage {
|
||||||
|
type: "loaded";
|
||||||
interface ReadyMessage {
|
}
|
||||||
type: "ready";
|
|
||||||
}
|
interface ReadyMessage {
|
||||||
|
type: "ready";
|
||||||
interface ErrorMessage {
|
}
|
||||||
type: "error";
|
|
||||||
error: string;
|
interface ErrorMessage {
|
||||||
}
|
type: "error";
|
||||||
|
error: string;
|
||||||
export type WorkerMessage = (
|
}
|
||||||
| ConvertMessage
|
|
||||||
| FinishedMessage
|
export type WorkerMessage = (
|
||||||
| LoadMessage
|
| ConvertMessage
|
||||||
| LoadedMessage
|
| FinishedMessage
|
||||||
| ReadyMessage
|
| LoadMessage
|
||||||
| ErrorMessage
|
| LoadedMessage
|
||||||
) & {
|
| ReadyMessage
|
||||||
id: string; // unused? rn just using file id, probably meant to be incrementing w/ every message posted?
|
| ErrorMessage
|
||||||
};
|
) & {
|
||||||
|
id: string; // unused? rn just using file id, probably meant to be incrementing w/ every message posted?
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -89,4 +89,4 @@ export const formatBytes = (bytes: number): string => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${value.toFixed(value >= 100 ? 0 : value >= 10 ? 1 : 2)} ${units[unitIndex]}`;
|
return `${value.toFixed(value >= 100 ? 0 : value >= 10 ? 1 : 2)} ${units[unitIndex]}`;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,150 +1,150 @@
|
||||||
// THIS CODE IS FROM https://github.com/captbaritone/webamp/blob/15b0312cb794973a0e615d894df942452e920c36/packages/ani-cursor/src/parser.ts
|
// THIS CODE IS FROM https://github.com/captbaritone/webamp/blob/15b0312cb794973a0e615d894df942452e920c36/packages/ani-cursor/src/parser.ts
|
||||||
// LICENSED UNDER MIT. (c) Jordan Eldredge and Webamp contributors
|
// LICENSED UNDER MIT. (c) Jordan Eldredge and Webamp contributors
|
||||||
|
|
||||||
// this code is ripped from their project because i didn't want to
|
// this code is ripped from their project because i didn't want to
|
||||||
// re-invent the wheel, BUT the library they provide (ani-cursor)
|
// re-invent the wheel, BUT the library they provide (ani-cursor)
|
||||||
// doesn't expose the internals.
|
// doesn't expose the internals.
|
||||||
|
|
||||||
import { RIFFFile } from "riff-file";
|
import { RIFFFile } from "riff-file";
|
||||||
import { unpackArray, unpackString } from "byte-data";
|
import { unpackArray, unpackString } from "byte-data";
|
||||||
|
|
||||||
type Chunk = {
|
type Chunk = {
|
||||||
format: string;
|
format: string;
|
||||||
chunkId: string;
|
chunkId: string;
|
||||||
chunkData: {
|
chunkData: {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
};
|
};
|
||||||
subChunks: Chunk[];
|
subChunks: Chunk[];
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://www.informit.com/articles/article.aspx?p=1189080&seqNum=3
|
// https://www.informit.com/articles/article.aspx?p=1189080&seqNum=3
|
||||||
type AniMetadata = {
|
type AniMetadata = {
|
||||||
cbSize: number; // Data structure size (in bytes)
|
cbSize: number; // Data structure size (in bytes)
|
||||||
nFrames: number; // Number of images (also known as frames) stored in the file
|
nFrames: number; // Number of images (also known as frames) stored in the file
|
||||||
nSteps: number; // Number of frames to be displayed before the animation repeats
|
nSteps: number; // Number of frames to be displayed before the animation repeats
|
||||||
iWidth: number; // Width of frame (in pixels)
|
iWidth: number; // Width of frame (in pixels)
|
||||||
iHeight: number; // Height of frame (in pixels)
|
iHeight: number; // Height of frame (in pixels)
|
||||||
iBitCount: number; // Number of bits per pixel
|
iBitCount: number; // Number of bits per pixel
|
||||||
nPlanes: number; // Number of color planes
|
nPlanes: number; // Number of color planes
|
||||||
iDispRate: number; // Default frame display rate (measured in 1/60th-of-a-second units)
|
iDispRate: number; // Default frame display rate (measured in 1/60th-of-a-second units)
|
||||||
bfAttributes: number; // ANI attribute bit flags
|
bfAttributes: number; // ANI attribute bit flags
|
||||||
};
|
};
|
||||||
|
|
||||||
type ParsedAni = {
|
type ParsedAni = {
|
||||||
rate: number[] | null;
|
rate: number[] | null;
|
||||||
seq: number[] | null;
|
seq: number[] | null;
|
||||||
images: Uint8Array[];
|
images: Uint8Array[];
|
||||||
metadata: AniMetadata;
|
metadata: AniMetadata;
|
||||||
artist: string | null;
|
artist: string | null;
|
||||||
title: string | null;
|
title: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DWORD = { bits: 32, be: false, signed: false, fp: false };
|
const DWORD = { bits: 32, be: false, signed: false, fp: false };
|
||||||
|
|
||||||
export function parseAni(arr: Uint8Array): ParsedAni {
|
export function parseAni(arr: Uint8Array): ParsedAni {
|
||||||
const riff = new RIFFFile();
|
const riff = new RIFFFile();
|
||||||
|
|
||||||
riff.setSignature(arr);
|
riff.setSignature(arr);
|
||||||
|
|
||||||
const signature = riff.signature as Chunk;
|
const signature = riff.signature as Chunk;
|
||||||
if (signature.format !== "ACON") {
|
if (signature.format !== "ACON") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Expected format. Expected "ACON", got "${signature.format}"`,
|
`Expected format. Expected "ACON", got "${signature.format}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get a chunk by chunkId and transform it if it's non-null.
|
// Helper function to get a chunk by chunkId and transform it if it's non-null.
|
||||||
function mapChunk<T>(
|
function mapChunk<T>(
|
||||||
chunkId: string,
|
chunkId: string,
|
||||||
mapper: (chunk: Chunk) => T,
|
mapper: (chunk: Chunk) => T,
|
||||||
): T | null {
|
): T | null {
|
||||||
const chunk = riff.findChunk(chunkId) as Chunk | null;
|
const chunk = riff.findChunk(chunkId) as Chunk | null;
|
||||||
return chunk == null ? null : mapper(chunk);
|
return chunk == null ? null : mapper(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readImages(chunk: Chunk, frameCount: number): Uint8Array[] {
|
function readImages(chunk: Chunk, frameCount: number): Uint8Array[] {
|
||||||
return chunk.subChunks.slice(0, frameCount).map((c) => {
|
return chunk.subChunks.slice(0, frameCount).map((c) => {
|
||||||
if (c.chunkId !== "icon") {
|
if (c.chunkId !== "icon") {
|
||||||
throw new Error(`Unexpected chunk type in fram: ${c.chunkId}`);
|
throw new Error(`Unexpected chunk type in fram: ${c.chunkId}`);
|
||||||
}
|
}
|
||||||
return arr.slice(c.chunkData.start, c.chunkData.end);
|
return arr.slice(c.chunkData.start, c.chunkData.end);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const metadata = mapChunk("anih", (c) => {
|
const metadata = mapChunk("anih", (c) => {
|
||||||
const words = unpackArray(
|
const words = unpackArray(
|
||||||
arr,
|
arr,
|
||||||
DWORD,
|
DWORD,
|
||||||
c.chunkData.start,
|
c.chunkData.start,
|
||||||
c.chunkData.end,
|
c.chunkData.end,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
cbSize: words[0],
|
cbSize: words[0],
|
||||||
nFrames: words[1],
|
nFrames: words[1],
|
||||||
nSteps: words[2],
|
nSteps: words[2],
|
||||||
iWidth: words[3],
|
iWidth: words[3],
|
||||||
iHeight: words[4],
|
iHeight: words[4],
|
||||||
iBitCount: words[5],
|
iBitCount: words[5],
|
||||||
nPlanes: words[6],
|
nPlanes: words[6],
|
||||||
iDispRate: words[7],
|
iDispRate: words[7],
|
||||||
bfAttributes: words[8],
|
bfAttributes: words[8],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
throw new Error("Did not find anih");
|
throw new Error("Did not find anih");
|
||||||
}
|
}
|
||||||
|
|
||||||
const rate = mapChunk("rate", (c) => {
|
const rate = mapChunk("rate", (c) => {
|
||||||
return unpackArray(arr, DWORD, c.chunkData.start, c.chunkData.end);
|
return unpackArray(arr, DWORD, c.chunkData.start, c.chunkData.end);
|
||||||
});
|
});
|
||||||
// chunkIds are always four chars, hence the trailing space.
|
// chunkIds are always four chars, hence the trailing space.
|
||||||
const seq = mapChunk("seq ", (c) => {
|
const seq = mapChunk("seq ", (c) => {
|
||||||
return unpackArray(arr, DWORD, c.chunkData.start, c.chunkData.end);
|
return unpackArray(arr, DWORD, c.chunkData.start, c.chunkData.end);
|
||||||
});
|
});
|
||||||
|
|
||||||
const lists = riff.findChunk("LIST", true) as Chunk[] | null;
|
const lists = riff.findChunk("LIST", true) as Chunk[] | null;
|
||||||
const imageChunk = lists?.find((c) => c.format === "fram");
|
const imageChunk = lists?.find((c) => c.format === "fram");
|
||||||
if (imageChunk == null) {
|
if (imageChunk == null) {
|
||||||
throw new Error("Did not find fram LIST");
|
throw new Error("Did not find fram LIST");
|
||||||
}
|
}
|
||||||
|
|
||||||
let images = readImages(imageChunk, metadata.nFrames);
|
let images = readImages(imageChunk, metadata.nFrames);
|
||||||
|
|
||||||
let title = null;
|
let title = null;
|
||||||
let artist = null;
|
let artist = null;
|
||||||
|
|
||||||
const infoChunk = lists?.find((c) => c.format === "INFO");
|
const infoChunk = lists?.find((c) => c.format === "INFO");
|
||||||
if (infoChunk != null) {
|
if (infoChunk != null) {
|
||||||
infoChunk.subChunks.forEach((c) => {
|
infoChunk.subChunks.forEach((c) => {
|
||||||
switch (c.chunkId) {
|
switch (c.chunkId) {
|
||||||
case "INAM":
|
case "INAM":
|
||||||
title = unpackString(
|
title = unpackString(
|
||||||
arr,
|
arr,
|
||||||
c.chunkData.start,
|
c.chunkData.start,
|
||||||
c.chunkData.end,
|
c.chunkData.end,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "IART":
|
case "IART":
|
||||||
artist = unpackString(
|
artist = unpackString(
|
||||||
arr,
|
arr,
|
||||||
c.chunkData.start,
|
c.chunkData.start,
|
||||||
c.chunkData.end,
|
c.chunkData.end,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "LIST":
|
case "LIST":
|
||||||
// Some cursors with an artist of "Created with Take ONE 3.5 (unregisterred version)" seem to have their frames here for some reason?
|
// Some cursors with an artist of "Created with Take ONE 3.5 (unregisterred version)" seem to have their frames here for some reason?
|
||||||
if (c.format === "fram") {
|
if (c.format === "fram") {
|
||||||
images = readImages(c, metadata.nFrames);
|
images = readImages(c, metadata.nFrames);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Unexpected subchunk
|
// Unexpected subchunk
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { images, rate, seq, metadata, artist, title };
|
return { images, rate, seq, metadata, artist, title };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import { error } from "$lib/util/logger";
|
import { error } from "$lib/util/logger";
|
||||||
|
|
||||||
export function readSettings<T extends object = Record<string, unknown>>(): Partial<T> {
|
export function readSettings<
|
||||||
|
T extends object = Record<string, unknown>,
|
||||||
|
>(): Partial<T> {
|
||||||
if (!browser) return {};
|
if (!browser) return {};
|
||||||
|
|
||||||
const raw = localStorage.getItem("settings");
|
const raw = localStorage.getItem("settings");
|
||||||
|
|
@ -20,4 +22,4 @@ export function readSettings<T extends object = Record<string, unknown>>(): Part
|
||||||
localStorage.removeItem("settings");
|
localStorage.removeItem("settings");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,10 @@ const pandocToFiles = (entries: PandocEntries, parent = ""): File[] => {
|
||||||
const nestedFiles = pandocToFiles(entry.entries, fullPath);
|
const nestedFiles = pandocToFiles(entry.entries, fullPath);
|
||||||
flattened.push(...nestedFiles);
|
flattened.push(...nestedFiles);
|
||||||
} else {
|
} else {
|
||||||
const file = new File([new Uint8Array(Array.from(entry.data))], fullPath);
|
const file = new File(
|
||||||
|
[new Uint8Array(Array.from(entry.data))],
|
||||||
|
fullPath,
|
||||||
|
);
|
||||||
flattened.push(file);
|
flattened.push(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@
|
||||||
import { goto, beforeNavigate, afterNavigate } from "$app/navigation";
|
import { goto, beforeNavigate, afterNavigate } from "$app/navigation";
|
||||||
|
|
||||||
import { PUB_PLAUSIBLE_URL, PUB_HOSTNAME } from "$env/static/public";
|
import { PUB_PLAUSIBLE_URL, PUB_HOSTNAME } from "$env/static/public";
|
||||||
import { DISABLE_ALL_EXTERNAL_REQUESTS, VERT_NAME } from "$lib/util/consts.js";
|
import {
|
||||||
|
DISABLE_ALL_EXTERNAL_REQUESTS,
|
||||||
|
VERT_NAME,
|
||||||
|
} from "$lib/util/consts.js";
|
||||||
import * as Layout from "$lib/components/layout";
|
import * as Layout from "$lib/components/layout";
|
||||||
import * as Navbar from "$lib/components/layout/Navbar";
|
import * as Navbar from "$lib/components/layout/Navbar";
|
||||||
import { Settings } from "$lib/sections/settings/index.svelte";
|
import { Settings } from "$lib/sections/settings/index.svelte";
|
||||||
|
|
@ -96,7 +99,10 @@
|
||||||
|
|
||||||
// detect if insecure context
|
// detect if insecure context
|
||||||
if (!window.isSecureContext) {
|
if (!window.isSecureContext) {
|
||||||
log(["layout"], "Insecure context (HTTP) detected, some features may not work as expected -- you may want to enable \"PUB_DISABLE_FAILURE_BLOCKS\" on local deployments.");
|
log(
|
||||||
|
["layout"],
|
||||||
|
'Insecure context (HTTP) detected, some features may not work as expected -- you may want to enable "PUB_DISABLE_FAILURE_BLOCKS" on local deployments.',
|
||||||
|
);
|
||||||
ToastManager.add({
|
ToastManager.add({
|
||||||
type: "warning",
|
type: "warning",
|
||||||
message: m["toast.insecure_context"](),
|
message: m["toast.insecure_context"](),
|
||||||
|
|
@ -155,7 +161,10 @@
|
||||||
property="twitter:description"
|
property="twitter:description"
|
||||||
content="With VERT, you can quickly convert any image, video, audio, and document file. No ads, no tracking, open source, and all processing is done on your device."
|
content="With VERT, you can quickly convert any image, video, audio, and document file. No ads, no tracking, open source, and all processing is done on your device."
|
||||||
/>
|
/>
|
||||||
<meta property="twitter:image" content="https://vert.sh/VERT_Feature.webp" />
|
<meta
|
||||||
|
property="twitter:image"
|
||||||
|
content="https://vert.sh/VERT_Feature.webp"
|
||||||
|
/>
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<link rel="canonical" href="https://vert.sh/" />
|
<link rel="canonical" href="https://vert.sh/" />
|
||||||
{#if enablePlausible}
|
{#if enablePlausible}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,10 @@
|
||||||
link(
|
link(
|
||||||
["about_link", "stripe_link"],
|
["about_link", "stripe_link"],
|
||||||
m["privacy.donations.description"](),
|
m["privacy.donations.description"](),
|
||||||
["/about", "https://stripe.com/docs/disputes/prevention/advanced-fraud-detection"],
|
[
|
||||||
|
"/about",
|
||||||
|
"https://stripe.com/docs/disputes/prevention/advanced-fraud-detection",
|
||||||
|
],
|
||||||
[false, true],
|
[false, true],
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedSettings = readSavedSettings();
|
const parsedSettings = readSavedSettings();
|
||||||
if (parsedSettings && JSON.stringify(parsedSettings) === JSON.stringify(settings))
|
if (
|
||||||
|
parsedSettings &&
|
||||||
|
JSON.stringify(parsedSettings) === JSON.stringify(settings)
|
||||||
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue