Files
Knutwurst 23e4dda1e2 Redesign game cards and the Other versions dialog
Cards show installed, update and newest version side by side, and
problems as notes with their action. Other versions became a button.
The dialog groups versions into available, needs newer firmware,
installed and older (folded), with larger type throughout. Text is at
least 13px, buttons 44px tall, and every control has a focus ring.
Phones get two status tiles per row and full-width card content.
2026-10-17 15:19:00 +02:00

303 lines
15 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>PatchDL</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<svg class="icon-sprite" aria-hidden="true">
<symbol id="icon-download" viewBox="0 0 24 24">
<path d="M12 3v11" /><path d="M7 10l5 5 5-5" /><path d="M5 20h14" />
</symbol>
<symbol id="icon-refresh" viewBox="0 0 24 24">
<path d="M20 6v6h-6" /><path d="M4 18v-6h6" />
<path d="M19 12A7 7 0 0 0 7 7" /><path d="M5 12a7 7 0 0 0 12 5" />
</symbol>
<symbol id="icon-settings" viewBox="0 0 24 24">
<path d="M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" />
<path d="M4 12h2M18 12h2M12 4v2M12 18v2M6.5 6.5 8 8M16 16l1.5 1.5M17.5 6.5 16 8M8 16l-1.5 1.5" />
</symbol>
<symbol id="icon-search" viewBox="0 0 24 24">
<path d="M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15z" /><path d="M16 16l5 5" />
</symbol>
<symbol id="icon-save" viewBox="0 0 24 24">
<path d="M5 4h12l2 2v14H5V4z" /><path d="M8 4v6h8V4" /><path d="M8 20v-6h8v6" />
</symbol>
<symbol id="icon-log" viewBox="0 0 24 24">
<path d="M7 4h10l3 3v13H7V4z" /><path d="M17 4v4h4" /><path d="M10 12h7M10 16h5" />
</symbol>
<symbol id="icon-grid" viewBox="0 0 24 24">
<path d="M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z" />
</symbol>
<symbol id="icon-close" viewBox="0 0 24 24">
<path d="M6 6l12 12M18 6L6 18" />
</symbol>
<symbol id="icon-arrow" viewBox="0 0 24 24">
<path d="M5 12h14" /><path d="M13 6l6 6-6 6" />
</symbol>
<symbol id="icon-layers" viewBox="0 0 24 24">
<path d="M12 3l9 5-9 5-9-5 9-5z" /><path d="M3 13l9 5 9-5" />
</symbol>
<symbol id="icon-alert" viewBox="0 0 24 24">
<path d="M12 3l10 18H2L12 3z" /><path d="M12 10v5" /><path d="M12 18h.01" />
</symbol>
<symbol id="icon-check" viewBox="0 0 24 24">
<path d="M5 12.5l4.5 4.5L19 7" />
</symbol>
<symbol id="icon-lock" viewBox="0 0 24 24">
<path d="M6 11h12v10H6z" /><path d="M8.5 11V8a3.5 3.5 0 0 1 7 0v3" />
</symbol>
<symbol id="icon-external" viewBox="0 0 24 24">
<path d="M14 4h6v6" /><path d="M20 4l-9 9" /><path d="M18 14v6H4V6h6" />
</symbol>
<symbol id="icon-chevron" viewBox="0 0 24 24">
<path d="M9 6l6 6-6 6" />
</symbol>
</svg>
<div class="app-shell">
<aside class="rail" aria-label="Navigation" data-i18n-aria-label="nav.label">
<div class="brand">
<div class="brand-mark">PD</div>
<div>
<strong>PatchDL</strong>
<span><span class="brand-by" data-i18n="brand.by">by Knutwurst</span> · <span id="brandVersion">--</span></span>
</div>
</div>
<nav class="nav-list" aria-label="Sections" data-i18n-aria-label="nav.sections">
<button class="nav-link is-active" data-view="games" aria-label="Games" data-i18n-aria-label="nav.games" aria-current="page">
<svg><use href="#icon-grid"></use></svg><span data-i18n="nav.games">Games</span>
</button>
<button class="nav-link" data-view="settings" aria-label="Settings" data-i18n-aria-label="nav.settings">
<svg><use href="#icon-settings"></use></svg><span data-i18n="nav.settings">Settings</span>
</button>
<button class="nav-link" data-view="logs" aria-label="Logs" data-i18n-aria-label="nav.logs">
<svg><use href="#icon-log"></use></svg><span data-i18n="nav.logs">Logs</span>
</button>
</nav>
<div class="rail-foot">
<span id="railFw">FW --</span>
<span id="railSpace" data-i18n="rail.freeUnknown">-- free</span>
<button type="button" class="link-button rail-donate" data-donate data-i18n="donate.button">Buy me a coffee</button>
</div>
</aside>
<main class="main">
<!-- ============ GAMES ============ -->
<section class="view is-active" data-view="games">
<header class="topbar">
<div>
<p class="eyebrow" data-i18n="games.eyebrow">PS5 &amp; PS4 game updates</p>
<h1 data-i18n="games.title">Games</h1>
</div>
<div class="topbar-actions">
<button class="primary-button" id="updateAllBtn" title="Download (and optionally install) every game that has an available, allowed update" data-i18n-title="games.updateAllTitle">
<svg><use href="#icon-download"></use></svg>
<span data-i18n="games.updateAll">Update all</span>
</button>
<button class="icon-button" id="refreshBtn" title="Refresh status and games" data-i18n-title="games.refreshTitle" aria-label="Refresh" data-i18n-aria-label="games.refresh">
<svg><use href="#icon-refresh"></use></svg>
</button>
</div>
</header>
<aside id="globalDl" class="global-dl" hidden aria-live="polite">
<div class="global-dl-row">
<span class="global-dl-pulse" aria-hidden="true"></span>
<div class="global-dl-text">
<div class="global-dl-line1">
<span class="global-dl-eyebrow" id="globalDlState" data-i18n="dl.downloading">Downloading</span>
<strong class="global-dl-name" id="globalDlName">—</strong>
<span class="global-dl-position" id="globalDlPosition" hidden></span>
</div>
<div class="global-dl-line2">
<span id="globalDlPct">0%</span>
<span class="global-dl-sep" aria-hidden="true">·</span>
<span id="globalDlSpeed">—</span>
<span class="global-dl-sep" aria-hidden="true">·</span>
<span id="globalDlEta">—</span>
</div>
</div>
</div>
<div class="global-dl-track">
<i class="global-dl-bar" id="globalDlBar" style="width:0%"></i>
</div>
</aside>
<section class="status-strip" aria-label="System status" data-i18n-aria-label="status.label">
<article class="metric">
<span data-i18n="metric.firmware">Firmware</span>
<strong id="firmwareValue">--</strong>
<em id="firmwareBuild" data-i18n="metric.firmwareBuild">System version</em>
</article>
<article class="metric">
<span data-i18n="metric.dns">DNS Guard</span>
<strong id="dnsValue">--</strong>
<em data-i18n="metric.dnsNote">System DNS left untouched</em>
</article>
<article class="metric">
<span data-i18n="metric.cdn">CDN Access</span>
<strong id="resolverValue">--</strong>
<em data-i18n="metric.cdnNote">PatchDL resolver only</em>
</article>
<article class="metric">
<span data-i18n="metric.storage">Storage</span>
<strong id="spaceValue">--</strong>
<em id="downloadDirValue" data-i18n="metric.downloadTarget">Download target</em>
</article>
</section>
<div class="toolbar">
<div class="search-box">
<svg><use href="#icon-search"></use></svg>
<input id="searchInput" type="search" placeholder="Search title, Title ID, or Content ID" data-i18n-placeholder="search.placeholder" />
</div>
<div class="segmented" role="group" aria-label="Filter" data-i18n-aria-label="filter.label">
<button class="is-selected" data-filter="updates" aria-pressed="true"><span data-i18n="filter.updates">Updates</span> <span class="chip-count"></span></button>
<button data-filter="uptodate" aria-pressed="false"><span data-i18n="filter.uptodate">Up to date</span> <span class="chip-count"></span></button>
<button data-filter="blocked" aria-pressed="false"><span data-i18n="filter.blocked">Not supported</span> <span class="chip-count"></span></button>
<button data-filter="all" aria-pressed="false"><span data-i18n="filter.all">All</span> <span class="chip-count"></span></button>
</div>
</div>
<section class="game-grid" id="gameGrid"></section>
</section>
<!-- ============ SETTINGS ============ -->
<section class="view" data-view="settings">
<header class="topbar">
<div>
<p class="eyebrow" data-i18n="settings.eyebrow">Policy</p>
<h1 data-i18n="settings.title">Settings</h1>
</div>
<div class="topbar-actions">
<button class="primary-button" id="saveBtn">
<svg><use href="#icon-save"></use></svg>
<span data-i18n="settings.save">Save</span>
</button>
</div>
</header>
<div class="panel">
<div class="settings-grid">
<label class="field">
<span data-i18n="settings.defaultPolicy">Default Policy</span>
<select id="defaultPolicy">
<option value="deny" data-i18n="settings.policyDeny">Deny by default</option>
<option value="allow" data-i18n="settings.policyAllow">Allow by default</option>
</select>
</label>
<label class="field">
<span data-i18n="settings.downloadFolder">Download Folder</span>
<input id="downloadDir" type="text" spellcheck="false" readonly
title="Patches always download to the console's internal storage"
data-i18n-title="settings.downloadFolderTitle" />
</label>
<label class="field">
<span data-i18n="settings.language">Language</span>
<select id="language"></select>
</label>
<label class="switch-row">
<span>
<strong data-i18n="settings.installAfter">Install after download</strong>
<em data-i18n="settings.installAfterHint">Global default, overridable per game</em>
</span>
<span class="toggle">
<input id="installAfterDownload" type="checkbox" aria-label="Install after download" data-i18n-aria-label="settings.installAfter" />
<span class="track"></span>
</span>
</label>
<label class="switch-row">
<span>
<strong data-i18n="settings.deleteAfter">Delete PKG after install</strong>
<em data-i18n="settings.deleteAfterHint">Only after a successful install</em>
</span>
<span class="toggle">
<input id="deleteAfterInstall" type="checkbox" aria-label="Delete package after install" data-i18n-aria-label="settings.deleteAfterAria" />
<span class="track"></span>
</span>
</label>
<label class="switch-row">
<span>
<strong data-i18n="settings.verify">Verify downloaded pieces (SHA-256)</strong>
<em data-i18n="settings.verifyHint">Checks each manifest piece; off by default, verify on-device first</em>
</span>
<span class="toggle">
<input id="verifyDownloads" type="checkbox" aria-label="Verify downloaded pieces" data-i18n-aria-label="settings.verifyAria" />
<span class="track"></span>
</span>
</label>
<label class="switch-row">
<span>
<strong data-i18n="settings.shortcut">Home-screen shortcut</strong>
<em id="shortcutHint" data-i18n="settings.shortcutHint">Add a PS5 home-screen icon that opens this UI in the browser</em>
</span>
<span class="toggle">
<input id="homeShortcut" type="checkbox" aria-label="Install home-screen shortcut" data-i18n-aria-label="settings.shortcutAria" />
<span class="track"></span>
</span>
</label>
<div class="switch-row">
<span>
<strong data-i18n="settings.connections">Parallel download connections</strong>
<em data-i18n="settings.connectionsHint">1–16 · applies live, no payload restart</em>
</span>
<div class="stepper" role="group" aria-label="Parallel download connections" data-i18n-aria-label="settings.connections">
<button type="button" class="stepper-btn" id="connMinus" aria-label="Fewer connections" data-i18n-aria-label="settings.connFewer">−</button>
<output class="stepper-value" id="connValue" aria-live="polite">4</output>
<button type="button" class="stepper-btn" id="connPlus" aria-label="More connections" data-i18n-aria-label="settings.connMore">+</button>
</div>
</div>
</div>
<div class="allowlist">
<span data-i18n="settings.allowlist">Internal CDN Allowlist</span>
<div id="allowlistHosts"></div>
</div>
<div class="source-policy">
<span data-i18n="settings.installProtection">Install Protection</span>
<div id="sourcePolicyList"></div>
</div>
<div class="storage-list">
<span><span data-i18n="settings.storage">Downloads on console</span> <em id="storageTotal"></em></span>
<div id="storageList"></div>
</div>
<div class="donate-row">
<span data-i18n="settings.donateText">Like PatchDL? You can buy me a coffee.</span>
<button type="button" class="row-button is-ghost" data-donate data-i18n="donate.button">Buy me a coffee</button>
</div>
</div>
</section>
<!-- ============ LOGS ============ -->
<section class="view" data-view="logs">
<header class="topbar">
<div>
<p class="eyebrow" data-i18n="logs.eyebrow">Runtime</p>
<h1 data-i18n="logs.title">Logs</h1>
</div>
<div class="topbar-actions">
<button class="icon-button" id="clearLogBtn" title="Clear log" data-i18n-title="logs.clear" aria-label="Clear log" data-i18n-aria-label="logs.clear">
<svg><use href="#icon-log"></use></svg>
</button>
</div>
</header>
<pre id="logOutput" tabindex="0"></pre>
</section>
</main>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="i18n.js"></script>
<script src="app.js"></script>
</body>
</html>