commit fe9a33301463c5ec9d1ff5396279b9677e07f962
parent bc0161c26db215b060048898e9808fec4cbcc4cf
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Thu, 24 Sep 2026 13:08:09 -0400
widget: ActiveJobsStrip draws JobRow compact; private row and bars deleted
The monitor widget's Active jobs strip draws `<JobRow variant="compact">`
with its config mapped onto `show` (tasks unless compact, jobBar,
headingProgress, eta; no actions, no links). Its private JobRow,
JobProgressBar, TaskBar, TASK_KIND_VERB / TASK_KIND_FILL, METRIC_PREFIX and
jobProgressText are deleted — the glyph labels live on as the compact
variants in jobs/components/JobProgressBars.tsx. Region name "Active jobs"
and the "N running, M queued" meta are unchanged. One visible addition: a
row's `detail` (who asked) now shows in the widget line too, as it does on
every other job row.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 11 insertions(+), 230 deletions(-)
diff --git a/editor/app/widget/components/MonitorWidget.tsx b/editor/app/widget/components/MonitorWidget.tsx
@@ -1,7 +1,7 @@
"use client";
import { Fragment, useState, type CSSProperties, type ReactNode } from "react";
-import { formatDuration, formatBytes } from "yt-dlp-transcript-common/lib/format";
+import { formatBytes } from "yt-dlp-transcript-common/lib/format";
import { usePolledPayload, useNow } from "../../lib/usePolledPayload";
import { fmtTime } from "../lib/relativeTime";
import type {
@@ -9,11 +9,7 @@ import type {
DiskStatusView,
} from "yt-dlp-transcript-common/views/activeJobs";
import type { JobRowView } from "yt-dlp-transcript-common/views/jobRowView";
-import type {
- JobProgressMetric,
- JobTaskKind,
-} from "yt-dlp-transcript-common/jobs/registry";
-import { jobKindLabel } from "../../jobs/jobKindLabels";
+import { JobRow } from "../../jobs/components/JobRow";
import type { WorkersPayload, WorkerView } from "yt-dlp-transcript-common/views/workers";
import { InlineActionButton } from "../../components/actions/InlineActionButton";
import type { WidgetActionablePayload } from "yt-dlp-transcript-common/views/widgetActionable";
@@ -1017,10 +1013,15 @@ function ActiveJobsStrip({
<li key={j.id}>
<JobRow
job={j}
- compact={compact}
- jobBar={jobBar}
- headingProgress={headingProgress}
- eta={eta}
+ variant="compact"
+ show={{
+ tasks: !compact,
+ jobBar,
+ headingProgress,
+ eta,
+ actions: false,
+ links: false,
+ }}
/>
</li>
))}
@@ -1030,226 +1031,6 @@ function ActiveJobsStrip({
);
}
-function JobRow({
- job,
- compact,
- jobBar,
- headingProgress,
- eta,
-}: {
- job: JobRowView;
- compact: boolean;
- jobBar: boolean;
- headingProgress: boolean;
- eta: boolean;
-}) {
- const statusClass =
- job.status === "running"
- ? "bg-info-soft text-info"
- : "bg-muted text-muted-foreground";
- return (
- <div className="flex flex-col gap-1">
- <div className="flex flex-wrap items-center gap-1.5 text-xs">
- <span
- className={`uppercase tracking-wide px-1.5 py-0.5 rounded text-[10px] ${statusClass}`}
- >
- {job.status}
- </span>
- <span className="font-mono" title={job.kind}>
- {jobKindLabel(job.kind)}
- </span>
- {job.background && (
- <span
- className="uppercase tracking-wide px-1 py-0.5 rounded text-[9px] bg-warning-soft text-warning"
- title="Background work — yields to a manual sync/download"
- >
- auto
- </span>
- )}
- {job.channelSlug && (
- <span className="font-mono text-muted-foreground truncate max-w-[12rem]">
- {job.channelSlug}
- </span>
- )}
- {headingProgress && job.progress && (
- <span className="font-mono text-muted-foreground shrink-0">
- {jobProgressText(job.progress, eta)}
- </span>
- )}
- </div>
- {jobBar && job.progress && (
- <JobProgressBar progress={job.progress} showEta={eta} />
- )}
- {!compact &&
- job.tasks &&
- job.tasks.length > 0 &&
- job.tasks.map((t) => <TaskBar key={t.id} task={t} />)}
- </div>
- );
-}
-
-// Per-task-kind glyph/fill, as Records over JobTaskKind so a new kind is a
-// compile error rather than silently rendering as a transcription.
-const TASK_KIND_VERB: Record<JobTaskKind, string> = {
- download: "\u2193",
- transcribe: "\u270e",
- digest: "\u00b6",
- backfill: "\u21ba",
- // A right arrow: a relocate task is a channel's media moving to another disk.
- relocate: "\u21e2",
-};
-
-const TASK_KIND_FILL: Record<JobTaskKind, string> = {
- download: "bg-success/60",
- transcribe: "bg-success",
- digest: "bg-info",
- backfill: "bg-warning",
- relocate: "bg-info/70",
-};
-
-// Per-metric glyph for the compact widget line. A Record over JobProgressMetric
-// so a new metric is a compile error, not a mislabelled bar (the two copies of
-// this ternary previously had to be kept in sync by hand).
-const METRIC_PREFIX: Record<JobProgressMetric, string> = {
- downloads: "\u2193 ",
- transcripts: "",
- digests: "\u00b6 ",
- backfills: "\u21ba ",
- scans: "\u2315 ",
-};
-
-// One-line textual summary of a job's batch progress, e.g. "↓ 5/10 · ~2m left".
-// Shared by the job heading (headingProgress) and the job bar's caption so the
-// label/ETA formatting stays in one place.
-function jobProgressText(
- progress: NonNullable<JobRowView["progress"]>,
- showEta: boolean,
-): string {
- const label = `${METRIC_PREFIX[progress.metric]}${progress.current}/${progress.target}`;
- const remaining = progress.target - progress.current;
- const etaText =
- !showEta || remaining <= 0
- ? null
- : typeof progress.etaSeconds === "number"
- ? `~${formatDuration(Math.max(1, Math.round(progress.etaSeconds)))} left`
- : "…";
- return etaText ? `${label} · ${etaText}` : label;
-}
-
-function JobProgressBar({
- progress,
- showEta,
-}: {
- progress: NonNullable<JobRowView["progress"]>;
- showEta: boolean;
-}) {
- const label = `${METRIC_PREFIX[progress.metric]}${progress.current}/${progress.target}`;
- const remaining = progress.target - progress.current;
- const etaText =
- !showEta || remaining <= 0
- ? null
- : typeof progress.etaSeconds === "number"
- ? `~${formatDuration(Math.max(1, Math.round(progress.etaSeconds)))} left`
- : "…";
- return (
- <div className="flex flex-col gap-0.5">
- <div
- role="progressbar"
- aria-valuemin={progress.initial}
- aria-valuemax={progress.target}
- aria-valuenow={progress.current}
- aria-label={label}
- className="relative h-1.5 w-full overflow-hidden rounded bg-muted"
- >
- <div
- className="absolute inset-y-0 left-0 bg-success"
- style={{ width: `${progress.pct}%` }}
- />
- </div>
- <div className="flex items-baseline justify-between gap-2 text-[11px] text-muted-foreground">
- <span>{label}</span>
- {etaText && <span className="shrink-0">{etaText}</span>}
- </div>
- </div>
- );
-}
-
-function TaskBar({
- task,
-}: {
- task: NonNullable<JobRowView["tasks"]>[number];
-}) {
- const now = useNow();
- const probing = task.phase === "probing";
- const verb = probing ? "🔍" : TASK_KIND_VERB[task.kind];
- // While probing, fill against the estimated probe duration (violet "scanning"
- // bar) rather than the frozen download fraction. See TaskProgressBar.
- const probeFraction =
- probing &&
- now !== null &&
- typeof task.probeStartedAt === "number" &&
- typeof task.probeEtaSeconds === "number" &&
- task.probeEtaSeconds > 0
- ? Math.max(
- 0,
- Math.min(1, (now - task.probeStartedAt) / (task.probeEtaSeconds * 1000)),
- )
- : undefined;
- const hasFraction = probing
- ? typeof probeFraction === "number"
- : typeof task.fraction === "number";
- const fraction = probing ? probeFraction : task.fraction;
- const pct = hasFraction ? Math.round((fraction as number) * 100) : 0;
- const fillClass = probing
- ? "bg-violet-400 dark:bg-violet-500"
- : TASK_KIND_FILL[task.kind];
- const pulseClass = probing
- ? "bg-violet-400 dark:bg-violet-500"
- : "bg-warning";
- const elapsed =
- now === null
- ? null
- : formatDuration(Math.max(0, Math.round((now - task.startedAt) / 1000))) ||
- "0:00";
- const meta = [
- elapsed,
- hasFraction ? `${pct}%` : null,
- probing ? "probing" : task.detail,
- ]
- .filter(Boolean)
- .join(" · ");
- return (
- <div className="flex flex-col gap-0.5 pl-2 border-l-2 border-border">
- <div className="flex items-baseline justify-between gap-2 text-[11px]">
- <span className="truncate">
- <span className="text-muted-foreground">{verb} </span>
- <span className="font-mono">{task.label}</span>
- </span>
- <span className="font-mono text-muted-foreground shrink-0">{meta}</span>
- </div>
- <div
- role="progressbar"
- aria-valuenow={hasFraction ? pct : undefined}
- aria-label={`${task.label}`}
- className="relative h-1 w-full overflow-hidden rounded bg-muted"
- >
- {hasFraction ? (
- <div
- className={`absolute inset-y-0 left-0 ${fillClass}`}
- style={{ width: `${pct}%` }}
- />
- ) : (
- // No fraction yet (just started, an engine that reports no %, or the
- // first audio probe): a full-width pulse reads as "working, no
- // progress number" — distinct from the emerald fill so it can't be
- // mistaken for ~33%.
- <div className={`absolute inset-0 animate-pulse ${pulseClass}`} />
- )}
- </div>
- </div>
- );
-}
-
// The settings gear and its in-place config overlay. The gear anchors to the
// widget's own (relative) container; the overlay covers the whole widget window
// (fixed inset-0) and hosts the same form the builder uses, so a pinned widget