docs(design): specify download progress treatment

Document and mock the redesigned downloading state for the launcher. The
reference now replaces the action button slot with a dedicated progress
primitive, covers both card and detail-modal layouts, and records the sizing,
number formatting, container-query fallback, and sample-data expectations that
implementation work should follow.

This commit keeps the design package separate from application code so the
next UI/backend changes can be reviewed against a stable reference.

Test Plan:
- git diff --cached --check

Refs: local design reference update
This commit is contained in:
2026-05-20 23:09:46 +02:00
parent 51216b7281
commit e308009a08
5 changed files with 438 additions and 18 deletions
+34 -10
View File
@@ -18,7 +18,7 @@ const GAMES = [
{
id: 'avp', title: 'Aliens vs. Predator', size: 35.0, version: '2019.10.01',
desc: "Three campaigns, three nightmares. Be the alien stalking the dark, the predator hunting both, or the marine just trying to make it home with a working flashlight.",
state: 'none', players: '216', tags: ['FPS', 'Horror', 'Multiplayer'],
state: 'downloading', progress: 0.32, speed: 49.4, players: '216', tags: ['FPS', 'Horror', 'Multiplayer'],
cover: { c1: '#064e3b', c2: '#020617', accent: '#34d399', mood: 'dark' },
},
{
@@ -120,7 +120,7 @@ const GAMES = [
{
id: 'quake3', title: 'Quake III Arena', size: 0.5, version: '2010.08.15',
desc: "Arena FPS in its most distilled form. Rocket jumps, rail-gun duels, and a netcode that's still the benchmark.",
state: 'none', players: '216', tags: ['FPS', 'Arena', 'LAN'],
state: 'downloading', progress: 0.71, speed: 12.8, players: '216', tags: ['FPS', 'Arena', 'LAN'],
cover: { c1: '#7f1d1d', c2: '#0a0a0a', accent: '#fbbf24', mood: 'dark' },
},
{
@@ -152,25 +152,45 @@ const GAMES = [
// Helpers
const fmtSize = (gb) => gb < 1 ? `${Math.round(gb * 1024)} MB` : `${gb.toFixed(1)} GB`;
const STATE_META = {
installed: { label: 'Installed', dot: '#22c55e' },
local: { label: 'Local', dot: '#f59e0b' },
none: { label: '', dot: 'transparent' },
installed: { label: 'Installed', dot: '#22c55e' },
local: { label: 'Local', dot: '#f59e0b' },
downloading: { label: 'Downloading', dot: 'var(--accent)' },
none: { label: '', dot: 'transparent' },
};
const ACTION_FOR_STATE = {
installed: { label: 'Play', kind: 'play' },
local: { label: 'Install', kind: 'install' },
none: { label: 'Download', kind: 'download' },
installed: { label: 'Play', kind: 'play' },
local: { label: 'Install', kind: 'install' },
downloading: { label: 'Downloading', kind: 'downloading' },
none: { label: 'Download', kind: 'download' },
};
// Format helpers for download UI
const fmtSpeed = (mbps) => mbps >= 100 ? `${Math.round(mbps)} MB/s` : `${mbps.toFixed(1)} MB/s`;
const fmtSpeedShort = (mbps) => `${Math.round(mbps)} MB/s`;
const fmtBytes = (gb) => {
if (gb < 1) return `${Math.round(gb * 1024)} MB`;
// No trailing zeros: 35 GB, 11.4 GB, 2.35 GB
if (gb >= 10) return `${gb.toFixed(1).replace(/\.0$/,'')} GB`;
return `${gb.toFixed(2).replace(/0$/,'').replace(/\.$/,'')} GB`;
};
const fmtEta = (sec) => {
if (!isFinite(sec) || sec <= 0) return '—';
if (sec < 60) return `${Math.round(sec)} s`;
const m = Math.round(sec / 60);
if (m < 60) return `${m} min`;
return `${Math.floor(m/60)} h ${m%60} min`;
};
const isLocalish = (s) => s === 'installed' || s === 'local' || s === 'downloading';
const countByFilter = (games) => ({
all: games.length,
local: games.filter(g => g.state === 'installed' || g.state === 'local').length,
local: games.filter(g => isLocalish(g.state)).length,
installed: games.filter(g => g.state === 'installed').length,
});
const filterGames = (games, key) => {
if (key === 'all') return games;
if (key === 'local') return games.filter(g => g.state === 'installed' || g.state === 'local');
if (key === 'local') return games.filter(g => isLocalish(g.state));
if (key === 'installed') return games.filter(g => g.state === 'installed');
return games;
};
@@ -189,4 +209,8 @@ window.ACTION_FOR_STATE = ACTION_FOR_STATE;
window.countByFilter = countByFilter;
window.filterGames = filterGames;
window.fmtSize = fmtSize;
window.fmtSpeed = fmtSpeed;
window.fmtSpeedShort = fmtSpeedShort;
window.fmtBytes = fmtBytes;
window.fmtEta = fmtEta;
window.STORAGE = STORAGE;