feat: ui of image sequence/fps in format dropdown

This commit is contained in:
Maya 2026-05-04 15:27:29 +03:00
parent cf77cf6fd6
commit 343d5f2d65
No known key found for this signature in database
4 changed files with 84 additions and 48 deletions

View File

@ -59,6 +59,10 @@
"image": "image", "image": "image",
"extract_error": "Error extracting {filename}: {error}" "extract_error": "Error extracting {filename}: {error}"
}, },
"image_sequence": {
"image_sequence": "Image Sequence",
"fps": "FPS"
},
"large_file_warning": "Due to browser / device limitations, video to audio conversion is disabled for this file as it is larger than {limit}GB. We recommend using Firefox or Safari for files of this size since they have less limitations.", "large_file_warning": "Due to browser / device limitations, video to audio conversion is disabled for this file as it is larger than {limit}GB. We recommend using Firefox or Safari for files of this size since they have less limitations.",
"external_warning": { "external_warning": {
"title": "External server warning", "title": "External server warning",

View File

@ -1,24 +1,29 @@
<script lang="ts"> <script lang="ts">
import type { HTMLInputAttributes } from "svelte/elements"; interface Props {
class?: string;
interface Props extends Omit< value?: string | number;
HTMLInputAttributes, checked?: boolean;
keyof HTMLInputAttributes type?: string;
> { disabled?: boolean | null;
extension?: string; extension?: string;
prefix?: string; prefix?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any inputClass?: string;
[key: string]: any; thin?: boolean;
multiline?: boolean;
[name: string]: unknown;
} }
let { let {
class: className, class: className,
inputClass,
bgColor = "bg-panel",
value = $bindable(), value = $bindable(),
checked = $bindable(), checked = $bindable(),
type = "text", type = "text",
disabled = false, disabled = false,
extension, extension,
prefix, prefix,
thin = false,
multiline = false, multiline = false,
...rest ...rest
}: Props = $props(); }: Props = $props();
@ -26,54 +31,54 @@
<div class="relative flex w-full {className}"> <div class="relative flex w-full {className}">
{#if type === "checkbox"} {#if type === "checkbox"}
<div class="relative w-full h-full"> <input
<input {...rest}
{...rest} type="checkbox"
type="checkbox" bind:checked
bind:checked {disabled}
{disabled} class="w-full p-3 rounded-lg border-2 border-button
class="w-full p-3 rounded-lg bg-panel border-2 border-button {bgColor}
{prefix ? 'pl-[2rem]' : 'pl-3'} {prefix ? 'pl-[2rem]' : 'pl-3'}
{extension ? 'pr-[4rem]' : 'pr-3'} {extension ? 'pr-[4rem]' : 'pr-3'}
{disabled && 'opacity-50 cursor-not-allowed'} appearance-none" {disabled && 'opacity-50 cursor-not-allowed'} appearance-none
/> {inputClass}"
{#if checked} />
<div {#if checked}
class="absolute w-7 h-7 inset-0 flex items-center justify-center pointer-events-none" <div
> class="absolute w-7 h-7 inset-0 flex items-center justify-center pointer-events-none"
<svg >
class="w-6 h-6" <svg class="w-6 h-6" fill="var(--bg-panel)" viewBox="0 0 20 20">
fill="var(--bg-panel)" <path
viewBox="0 0 20 20" fill-rule="evenodd"
> d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
<path clip-rule="evenodd"
fill-rule="evenodd" />
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" </svg>
clip-rule="evenodd" </div>
/> {/if}
</svg>
</div>
{/if}
</div>
{:else if multiline} {:else if multiline}
<textarea <textarea
{...rest} {...rest}
bind:value bind:value
{disabled} {disabled}
class="w-full p-3 rounded-lg bg-panel border-2 border-button class="w-full rounded-lg bg-panel border-2 border-button
{prefix ? 'pl-[2rem]' : 'pl-3'} {thin ? 'py-1.5' : 'p-3'}
{extension ? 'pr-[4rem]' : 'pr-3'} {prefix ? 'pl-[2rem]' : thin ? 'pl-2.5' : 'pl-3'}
{disabled && 'opacity-50 cursor-not-allowed'}" {extension ? (thin ? 'pr-[3.5rem]' : 'pr-[4rem]') : thin ? 'pr-2.5' : 'pr-3'}
{disabled && 'opacity-50 cursor-not-allowed'}
{inputClass}"
></textarea> ></textarea>
{:else} {:else}
<input <input
{...rest} {...rest}
bind:value bind:value
{disabled} {disabled}
class="w-full p-3 rounded-lg bg-panel border-2 border-button class="w-full rounded-lg bg-panel border-2 border-button
{prefix ? 'pl-[2rem]' : 'pl-3'} {thin ? 'py-1.5' : 'p-3'}
{extension ? 'pr-[4rem]' : 'pr-3'} {prefix ? 'pl-[2rem]' : thin ? 'pl-2.5' : 'pl-3'}
{disabled && 'opacity-50 cursor-not-allowed'}" {extension ? (thin ? 'pr-[3rem]' : 'pr-[4rem]') : thin ? 'pr-2.5' : 'pr-3'}
{disabled && 'opacity-50 cursor-not-allowed'}
{inputClass}"
/> />
{/if} {/if}
@ -84,10 +89,15 @@
</div> </div>
{/if} {/if}
{#if extension} {#if extension}
<div class="absolute right-0 top-0 bottom-0 flex items-center px-4"> <div
class="absolute right-0 top-0 bottom-0 flex items-center {thin
? 'px-2'
: 'px-4'}"
>
<span <span
class="text-sm bg-button text-black dynadark:text-white px-2 py-1 rounded" class="bg-button text-black dynadark:text-white rounded {thin
>{extension}</span ? 'text-xs px-2 py-0.5'
: 'text-sm px-2 py-1'}">{extension}</span
> >
</div> </div>
{/if} {/if}

View File

@ -10,6 +10,7 @@
import { VertFile } from "$lib/types"; import { VertFile } from "$lib/types";
import SettingsModal from "./popups/SettingsModal.svelte"; import SettingsModal from "./popups/SettingsModal.svelte";
import { log } from "$lib/util/logger"; import { log } from "$lib/util/logger";
import FancyInput from "./FancyInput.svelte";
type Props = { type Props = {
categories: Categories; categories: Categories;
@ -488,13 +489,34 @@
<!-- format options --> <!-- format options -->
<!-- TODO: image sequence & fps --> <!-- TODO: image sequence & fps -->
{#if file?.name.toLowerCase().endsWith(".zip")} {#if file?.name.toLowerCase().endsWith(".zip")}
<div class="border-t border-separator text-base p-2"> <div
class="flex flex-col gap-2 p-2 border-t border-separator text-base"
>
<button <button
class="w-full p-2 text-center rounded-lg bg-accent text-black" class="w-full p-2 text-center rounded-lg bg-accent text-black"
onclick={() => extract()} onclick={() => extract()}
> >
{m["convert.archive_file.extract"]()} {m["convert.archive_file.extract"]()}
</button> </button>
<div class="flex items-center gap-3">
<div
class="flex items-center gap-2 flex-1 min-w-0 h-full"
>
<FancyInput type="checkbox" class="!w-fit" />
<label for="extract-sequence" class="text-sm">
{m["convert.image_sequence.image_sequence"]()}
</label>
</div>
<div class="w-[80px] shrink-0">
<FancyInput
thin
inputClass="!h-9 !text-xs"
type="number"
extension="FPS"
placeholder="15"
/>
</div>
</div>
</div> </div>
{:else} {:else}
<div class="border-t border-separator text-base p-2"> <div class="border-t border-separator text-base p-2">

View File

@ -398,7 +398,7 @@ body {
} }
input[type="checkbox"] { input[type="checkbox"] {
@apply appearance-none w-5 h-5 rounded-md bg-button border-2 border-button cursor-pointer transition-colors duration-200; @apply appearance-none w-5 h-5 rounded-md border-2 border-button cursor-pointer transition-colors duration-200;
} }
input[type="checkbox"]:hover { input[type="checkbox"]:hover {