Fix paste broken by isAnalyzing + UI polish batch

Critical fix:
- Input field no longer disabled during URL analysis — the race condition
  fix (isAnalyzing=true on paste) was disabling the input mid-paste,
  causing the browser to drop the pasted text. Input now only disabled
  during submission.

UI polish:
- All action row elements standardized to 42px height
- Mobile toggle pills wider (min-width: 42px, matches gear icon)
- URL clear button (floating X) in the input field
- Footer visible in mobile view (padding above bottom nav)
- FormatPicker mobile: ellipsis on codec text, wrapped layout at narrow widths
This commit is contained in:
xpltd 2026-03-22 17:01:35 -05:00
parent f72b649acf
commit 9cfa9818f9
3 changed files with 94 additions and 17 deletions

View file

@ -40,6 +40,12 @@ onMounted(async () => {
opacity: 0.7; opacity: 0.7;
} }
@media (max-width: 767px) {
.app-footer {
padding-bottom: calc(var(--mobile-nav-height) + var(--space-md));
}
}
.sep { .sep {
margin: 0 var(--space-sm); margin: 0 var(--space-sm);
opacity: 0.5; opacity: 0.5;

View file

@ -144,6 +144,7 @@ function selectFormat(id: string | null): void {
cursor: pointer; cursor: pointer;
min-height: var(--touch-min); min-height: var(--touch-min);
transition: background-color 0.15s ease; transition: background-color 0.15s ease;
gap: var(--space-sm);
} }
.format-option:hover { .format-option:hover {
@ -157,6 +158,10 @@ function selectFormat(id: string | null): void {
.format-label { .format-label {
font-size: var(--font-size-base); font-size: var(--font-size-base);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.format-hint { .format-hint {
@ -165,9 +170,31 @@ function selectFormat(id: string | null): void {
} }
.format-codecs { .format-codecs {
font-size: var(--font-size-sm); font-size: var(--font-size-xs);
color: var(--color-text-muted); color: var(--color-text-muted);
font-family: var(--font-mono); font-family: var(--font-mono);
white-space: nowrap;
flex-shrink: 0;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 540px) {
.format-option {
flex-wrap: wrap;
gap: 2px;
}
.format-label {
flex: 1 1 100%;
font-size: var(--font-size-sm);
}
.format-codecs {
font-size: 0.6875rem;
max-width: none;
}
} }
.format-empty { .format-empty {

View file

@ -299,16 +299,27 @@ function formatTooltip(fmt: string): string {
<template> <template>
<div class="url-input"> <div class="url-input">
<!-- URL field --> <!-- URL field with clear button -->
<input <div class="url-field-wrap">
v-model="url" <input
type="url" v-model="url"
placeholder="Paste a URL to download…" type="url"
class="url-field" placeholder="Paste a URL to download…"
@paste="handlePaste" class="url-field"
@keydown.enter="submitDownload" @paste="handlePaste"
:disabled="isAnalyzing || store.isSubmitting" @keydown.enter="submitDownload"
/> :disabled="store.isSubmitting"
/>
<button
v-if="url.trim()"
class="url-clear"
@click="url = ''"
title="Clear URL"
aria-label="Clear URL"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<!-- Action row: gear, media toggle, download button --> <!-- Action row: gear, media toggle, download button -->
<div class="action-row"> <div class="action-row">
@ -456,15 +467,46 @@ function formatTooltip(fmt: string): string {
width: 100%; width: 100%;
} }
.url-field-wrap {
position: relative;
width: 100%;
}
.url-field { .url-field {
width: 100%; width: 100%;
font-size: var(--font-size-base); font-size: var(--font-size-base);
padding-right: 40px; /* room for clear button */
} }
/* Action row: gear | toggle | download */ .url-clear {
.action-row { position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center;
width: 28px;
height: 28px;
padding: 0;
background: transparent;
color: var(--color-text-muted);
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
opacity: 0.6;
transition: opacity 0.15s ease, color 0.15s ease;
}
.url-clear:hover {
opacity: 1;
color: var(--color-text);
}
/* Action row: gear | toggle | download — all 42px height */
.action-row {
display: flex;
align-items: stretch;
gap: var(--space-sm); gap: var(--space-sm);
} }
@ -472,8 +514,8 @@ function formatTooltip(fmt: string): string {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 38px; width: 42px;
height: 38px; min-height: 42px;
padding: 0; padding: 0;
flex-shrink: 0; flex-shrink: 0;
background: var(--color-surface); background: var(--color-surface);
@ -513,7 +555,7 @@ function formatTooltip(fmt: string): string {
border: none; border: none;
border-radius: 0; border-radius: 0;
font-size: var(--font-size-sm); font-size: var(--font-size-sm);
min-height: 38px; min-height: 42px;
transition: all 0.15s ease; transition: all 0.15s ease;
} }
@ -540,7 +582,7 @@ function formatTooltip(fmt: string): string {
white-space: nowrap; white-space: nowrap;
padding: var(--space-sm) var(--space-lg); padding: var(--space-sm) var(--space-lg);
font-weight: 600; font-weight: 600;
min-height: 38px; min-height: 42px;
background: var(--color-accent); background: var(--color-accent);
color: var(--color-bg); color: var(--color-bg);
} }
@ -832,6 +874,8 @@ button:disabled {
.toggle-pill { .toggle-pill {
padding: var(--space-xs) var(--space-sm); padding: var(--space-xs) var(--space-sm);
min-width: 42px;
justify-content: center;
} }
} }