feat: uniform control height, chrome auto-hide, streaming quality
Every control in the app now shares one height (CONTROL_H), with width still growing to fit its label. The player transport is larger, Back is an icon, and the footer buttons match the prev/next size. Player chrome — transport and edge arrows — fades after 2.6s of inactivity and never hides while paused. The title-bar strip collapses in macOS window fullscreen, where the traffic lights are gone and it was just a blank white bar. Streaming quality is now selectable alongside download quality. A cap is applied by rewriting YouTube's HLS master playlist down to the best variant at or below the chosen height, keeping its audio group. That playlist is served from a small loopback HTTP server: Safari's native HLS is backed by AVFoundation, which cannot read blob: or custom-scheme URLs, so a Blob URL silently fails to play. Settings closes with an icon button and its selects match the shared control height.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { LiveDownload } from "../hooks/useDownloads";
|
||||
import type { FeedItem } from "../types";
|
||||
import { humanEta } from "./format";
|
||||
import { CONTROL_H } from "./ui";
|
||||
|
||||
interface Props {
|
||||
item: FeedItem;
|
||||
@@ -11,7 +12,9 @@ interface Props {
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
const CHIP = "rounded-lg px-2.5 py-1.5 text-[11px] font-medium shrink-0 cursor-pointer";
|
||||
const CHIP =
|
||||
`inline-flex ${CONTROL_H} shrink-0 cursor-pointer items-center justify-center rounded-lg ` +
|
||||
"px-2.5 text-[12px] font-medium";
|
||||
|
||||
export default function DownloadButton({
|
||||
item, live, online, onDownload, onCancel, onDelete,
|
||||
@@ -38,22 +41,18 @@ export default function DownloadButton({
|
||||
const known = state === "running" && pct != null;
|
||||
return (
|
||||
<button onClick={onCancel} title={humanEta(live?.eta ?? null) || "Cancel download"}
|
||||
className={`${CHIP} group w-[104px] border border-slate-300 text-slate-500
|
||||
hover:border-red-500 hover:text-red-600
|
||||
dark:border-slate-700 dark:text-slate-400 dark:hover:border-red-500 dark:hover:text-red-400`}>
|
||||
<span className="hidden group-hover:block">Cancel</span>
|
||||
<span className="block group-hover:hidden">
|
||||
<span className="mb-1 block font-mono tabular-nums">
|
||||
{known ? `${pct!.toFixed(0)}%` : state === "queued" ? "Queued" : "Starting"}
|
||||
</span>
|
||||
<span className="block h-1 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-700">
|
||||
<span
|
||||
className={`block h-full rounded-full bg-sky-500 transition-[width] duration-100 ${
|
||||
known ? "" : "animate-pulse"
|
||||
}`}
|
||||
style={{ width: known ? `${pct}%` : "35%" }}
|
||||
/>
|
||||
</span>
|
||||
className={`${CHIP} group relative w-[104px] overflow-hidden border border-slate-300
|
||||
text-slate-500 hover:border-red-500 hover:text-red-600
|
||||
dark:border-slate-700 dark:text-slate-400 dark:hover:border-red-500
|
||||
dark:hover:text-red-400`}>
|
||||
{/* Progress fills the chip itself, so the control stays one line tall. */}
|
||||
<span
|
||||
className="absolute inset-y-0 left-0 bg-sky-500/15 transition-[width] duration-100"
|
||||
style={{ width: known ? `${pct}%` : "35%" }}
|
||||
/>
|
||||
<span className="relative hidden group-hover:inline">Cancel</span>
|
||||
<span className="relative inline font-mono tabular-nums group-hover:hidden">
|
||||
{known ? `${pct!.toFixed(0)}%` : state === "queued" ? "Queued" : "Starting"}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user