Self-review M5/M6/UX1: accurate preview, invalidate on change, reset overrides

Fixes found reviewing the previous commit:

- M5 preview was inaccurate: loadCommandPreview omitted formatId/formatHasAudio
  and trim, so the "exact command" differed from what download() actually spawns
  whenever a probe-selected format or a trim range was set. toggleCommandPreview
  now mirrors download()'s StartDownloadOptions (format + trim included).
- Stale preview: a shown command only cleared on URL change. Now it also clears
  on kind, quality, format, trim, download-options change, and on probe — so the
  displayed command is never out of sync with the form.
- Command button is now a real toggle (Show command / Hide command) instead of a
  no-op "Command · shown" label.
- Per-download overrides are now one-shot: incognito + downloadOptions reset to
  defaults after a download, matching trim/schedule and preventing incognito from
  silently carrying into the next download.
- Style: replaced two inline style={{}} usages with makeStyles classes
  (commandError), hardcoded 'monospace' -> tokens.fontFamilyMonospace, dropped an
  unnecessary fragment, and let long command lines wrap (pre-wrap/break-all).

typecheck + 243 tests + eslint + prettier green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 20:46:33 -04:00
parent cfa76314d7
commit e0465bc45e
+53 -22
View File
@@ -264,12 +264,20 @@ const useStyles = makeStyles({
backgroundColor: tokens.colorNeutralBackground2,
...shorthands.borderRadius(tokens.borderRadiusLarge),
border: `1px solid ${tokens.colorNeutralStroke2}`,
fontFamily: 'monospace',
fontFamily: tokens.fontFamilyMonospace,
fontSize: tokens.fontSizeBase200,
overflowX: 'auto',
whiteSpace: 'pre-wrap',
wordBreak: 'break-all',
color: tokens.colorNeutralForeground1
},
commandError: {
color: tokens.colorStatusDangerForeground1
},
optionsPanel: {
display: 'flex',
flexDirection: 'column',
gap: '12px',
padding: '12px',
backgroundColor: tokens.colorNeutralBackground2,
...shorthands.borderRadius(tokens.borderRadiusLarge),
@@ -403,20 +411,28 @@ export function DownloadBar(): React.JSX.Element {
setItemKinds({})
}
async function loadCommandPreview(): Promise<void> {
const trimmed = url.trim()
if (!trimmed) {
// Fetch (or toggle off) the exact yt-dlp command line for the current form
// state. The options mirror what download() sends so the preview matches the
// command that will actually run -- including a probe-selected format and any
// trim spec (M5).
async function toggleCommandPreview(): Promise<void> {
if (commandPreview) {
setCommandPreview(null)
return
}
const trimmed = url.trim()
if (!trimmed) return
setPreviewLoading(true)
try {
const result = await window.api.previewCommand({
id: 'preview',
url: trimmed,
kind,
quality,
options: downloadOptions
kind: usingFormats ? 'video' : kind,
quality: usingFormats && selectedFormat ? selectedFormat.label : quality,
formatId: usingFormats ? selectedFormat?.id : undefined,
formatHasAudio: usingFormats ? selectedFormat?.hasAudio : undefined,
options: downloadOptions,
trim: trim.trim() || undefined
})
setCommandPreview(result)
} catch (e) {
@@ -431,13 +447,15 @@ export function DownloadBar(): React.JSX.Element {
// Any probed info -- or a duplicate warning -- is stale once the URL changes.
if (info || probeError || playlist) clearProbe()
if (dup) setDup(null)
if (commandPreview) setCommandPreview(null)
setCommandPreview(null)
confirmDup.current = false
}
function onKindChange(next: MediaKind): void {
setKind(next)
setQuality(QUALITY_OPTIONS[next][0])
// The command changes with kind/quality, so any shown preview is now stale.
setCommandPreview(null)
}
async function fetchFormats(): Promise<void> {
@@ -447,6 +465,8 @@ export function DownloadBar(): React.JSX.Element {
setProbeError(null)
setInfo(null)
setPlaylist(null)
// Probing may switch the URL to the format-picker path, changing the command.
setCommandPreview(null)
try {
const res = await window.api.probe(trimmed)
if (res.ok && res.kind === 'playlist' && res.playlist) {
@@ -573,6 +593,11 @@ export function DownloadBar(): React.JSX.Element {
setShowSchedule(false)
setShowAdvanced(false)
setCommandPreview(null)
// Per-download overrides are one-shot (like trim/schedule): reset so the next
// download starts from the global defaults, and incognito never silently
// carries over to a following download.
setIncognito(false)
setDownloadOptions(DEFAULT_DOWNLOAD_OPTIONS)
clearProbe()
}
@@ -814,7 +839,10 @@ export function DownloadBar(): React.JSX.Element {
>
<Textarea
value={trim}
onChange={(_, d) => setTrim(d.value)}
onChange={(_, d) => {
setTrim(d.value)
setCommandPreview(null)
}}
placeholder={'0:30-1:45\n3:00-3:30'}
resize="vertical"
/>
@@ -855,22 +883,18 @@ export function DownloadBar(): React.JSX.Element {
size="small"
appearance="subtle"
icon={previewLoading ? <Spinner size="tiny" /> : <WindowConsoleRegular />}
onClick={loadCommandPreview}
onClick={toggleCommandPreview}
disabled={!url.trim() || previewLoading}
>
{commandPreview ? 'Command · shown' : 'Show command'}
{commandPreview ? 'Hide command' : 'Show command'}
</Button>
</div>
{commandPreview && (
<div className={styles.commandPreviewPanel}>
{commandPreview.ok ? (
<>
<div>{commandPreview.command}</div>
</>
commandPreview.command
) : (
<div style={{ color: tokens.colorStatusDangerForeground1 }}>
Error: {commandPreview.error}
</div>
<span className={styles.commandError}>Error: {commandPreview.error}</span>
)}
</div>
)}
@@ -881,14 +905,15 @@ export function DownloadBar(): React.JSX.Element {
onChange={(_, d) => setIncognito(!!d.checked)}
label="Incognito mode (no logging, no history, no cookies)"
/>
<div style={{ marginTop: '12px' }}>
<DownloadOptionsForm
value={downloadOptions}
onChange={setDownloadOptions}
onChange={(next) => {
setDownloadOptions(next)
setCommandPreview(null)
}}
kind={kind}
/>
</div>
</div>
)}
</div>
)}
@@ -916,7 +941,10 @@ export function DownloadBar(): React.JSX.Element {
size="large"
value={selectedFormat?.id ?? ''}
options={info.formats.map((f) => ({ value: f.id, label: f.label }))}
onChange={setFormatId}
onChange={(v) => {
setFormatId(v)
setCommandPreview(null)
}}
/>
) : (
<Select
@@ -925,7 +953,10 @@ export function DownloadBar(): React.JSX.Element {
size="large"
value={quality}
options={QUALITY_OPTIONS[kind].map((q) => ({ value: q, label: q }))}
onChange={setQuality}
onChange={(v) => {
setQuality(v)
setCommandPreview(null)
}}
/>
)}
</div>