diff --git a/docs/DESIGN.md b/docs/DESIGN.md
index 53680f95..f3588f18 100644
--- a/docs/DESIGN.md
+++ b/docs/DESIGN.md
@@ -774,3 +774,7 @@ of a full-width panel and nothing on the right.
| 2026-10-10 | Clip mode in the players (E38) | Scissors in the control row + `C`, `[` `]`; the panel in the player with the watched video as preview; overview + zoomed strip on `ui/RangeTrack` (ends are sliders); a toast + the Ready notification after Cut | A ⋯ menu entry; permanent In / Out buttons; a big dialog with its own preview (a second Plex transcode / YouTube iframe) | Owner's picks A, P1, a; a film is hours and a clip seconds, so only a zoomed strip gives a hand the precision. The Downloads editor's cuts moved onto the same track (keyboard-steppable now) |
| 2026-10-10 | Crop in clip mode (E38) | A Crop toggle in the clip panel: a frame over the playing picture (`ui/CropFrame`, the editor's crop too), Free / 16:9 / 9:16 / 1:1, Whole picture; maximised YouTube keeps the panel in the bar's place; the wide side panel shuts while clipping | A frame always on the picture in clip mode (K2); a free shape only | Owner's K1 + a: a crop is rare, so it never covers the picture unasked; a phone or square clip needs a held shape. The iframe hides its picture's size: drawn on the 16:9 box, the cut fits it to the real picture |
| 2026-10-10 | Marks on the player chrome | `--media-mark` (amber) and `--media-danger` (light red), the same in both themes; `media-marks` points the focus ring (`--focus`) and the chosen mark (`--chosen`: a track's tick, a tab's underline; the label stays white) at the amber on the player and every chrome surface; `media-scope` (a form) also the errors at the red | The theme's accent ring, chosen text, warn and danger | On the chrome (#171717) a light theme's accent ring (Slate 2.84:1), warn (2.99:1) and danger (2.77:1) all fall under 3:1; the accent as chosen text under 4.5:1 |
+| 2026-10-11 | Files page (one library) | List rows by default, the media grid a toggle remembered per folder; a sidebar of automatic source folders, own folders, Shared with me and the storage meter; the paste-a-link box in the toolbar | A media grid by default | The library holds videos AND plain files: rows read both, show more per screen and scan by size and date |
+| 2026-10-11 | Library list columns | Every kind of value in its own column (name, source, phone-safe, retention, size, added, actions), one grid with a subgrid per row so they line up; an action that does not apply keeps its slot empty; a right-click menu on every row (the ⋯ opens the same menu) | Badges flowing after the title; icons that shift when one is missing | Owner: values of one kind must fall in one vertical line; a row's every action reachable by right click |
+| 2026-10-11 | Running downloads | An app-wide tray, bottom right, shown only while something runs or failed (reason + retry), foldable to a pill | A rail icon with a count and a popover | Downloading became an action you start anywhere; a running one must be visible without going to look |
+| 2026-10-11 | Download done | The notification says whether it plays on a phone and carries Link másolása + Megnyitás (the folder, the item focused); never an automatic jump | A plain "ready" notice; auto-navigation to the item | The daily path is paste → done → copy the link; a jump would pull the user out of what they were doing |
diff --git a/frontend/src/design-lab/DesignLab.tsx b/frontend/src/design-lab/DesignLab.tsx
index 33a3233b..bf846765 100644
--- a/frontend/src/design-lab/DesignLab.tsx
+++ b/frontend/src/design-lab/DesignLab.tsx
@@ -4,6 +4,7 @@ import { BatchA } from "./batchA";
import { BatchB } from "./batchB";
import { BatchC } from "./batchC";
import { BatchD } from "./batchD";
+import { FilesRound } from "./filesRound";
import { Chip } from "../components/ui/Chip";
import { Slider } from "../components/ui/NativeControls";
import { SURFACE } from "../components/ui/surfaces";
@@ -74,6 +75,9 @@ export default function DesignLab() {
/>
+
+
Open · Files, the one library
+
+ The page that replaces Downloads. The recommended variant has the accent edge.
+
+ );
+}
+
+// ── F1: the list and the grid ────────────────────────────────────────────────────────────────────
+
+// One grid for the whole list, each row a subgrid: every kind of value sits in its own column, so the
+// badges, the sizes and the action icons line up down the page. An action that does not apply keeps
+// its slot empty (a file has no "copy the play link").
+const LIST_COLUMNS =
+ "grid grid-cols-[4rem_minmax(0,1fr)_7rem_6.5rem_7.5rem_4.5rem_4.5rem_auto] items-center gap-x-3";
+const ROW = "col-span-full grid grid-cols-subgrid items-center rounded-lg px-2 py-1.5";
+
+function ListHead() {
+ const th = "text-2xs uppercase tracking-wider text-muted";
+ return (
+
+
+ Név
+ Forrás
+ Telefon
+ Megőrzés
+ Méret
+ Hozzáadva
+
+
+ );
+}
diff --git a/frontend/src/design-lab/labKit.tsx b/frontend/src/design-lab/labKit.tsx
index b5332f59..1bad5c48 100644
--- a/frontend/src/design-lab/labKit.tsx
+++ b/frontend/src/design-lab/labKit.tsx
@@ -1,10 +1,11 @@
import { useSyncExternalStore, type ReactNode } from "react";
import clsx from "clsx";
+import { Badge } from "../components/ui/Badge";
import { SURFACE } from "../components/ui/surfaces";
-// Building blocks of the design lab. Every decision is taken, so the lab is the reference half
-// only: each is the chosen rendering with the rule it became. A new design round brings
-// back the side-by-side / pair (as before) for its open questions. Everything
+// Building blocks of the design lab. A taken decision is a : the chosen rendering with the
+// rule it became. An open design round shows its questions as s with their s side
+// by side, until the owner picks. Everything
// is static markup on purpose: a rendering that only exists here must not need app state.
/** A decision already taken: the chosen rendering only, with the rule it became. The lab's
@@ -36,6 +37,76 @@ export function Reference({
);
}
+/** An open question of a design round: what the app does today, then the variants side by side. */
+export function Decision({
+ id,
+ code,
+ title,
+ question,
+ now,
+ children,
+}: {
+ id: string;
+ code: string;
+ title: string;
+ question: string;
+ /** What the app does today. */
+ now: ReactNode;
+ children: ReactNode;
+}) {
+ return (
+
+
+
+ {code}
+
{title}
+
+
{question}
+
+ Today:
+ {now}
+
+
+
{children}
+
+ );
+}
+
+/** One answer to a Decision; the recommended one carries the accent edge. */
+export function Variant({
+ letter,
+ name,
+ recommended = false,
+ why,
+ children,
+}: {
+ letter: string;
+ name: string;
+ recommended?: boolean;
+ why: string;
+ children: ReactNode;
+}) {
+ return (
+
+
+
+ {letter}
+
+
{name}
+ {recommended && Recommended}
+
+
{children}
+
{why}
+
+ );
+}
+
/** A small caption above a sample inside a variant. */
export function Sample({ label, children }: { label: string; children: ReactNode }) {
return (