1
0

Standardize line endings
All checks were successful
Build & Test WASM / build-and-test (push) Successful in 1m45s

This commit is contained in:
2026-03-16 20:43:13 +01:00
parent 30def04cfa
commit 91f09b42a8
14 changed files with 2256 additions and 2230 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,254 +1,254 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KoboPatch Web UI</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<link rel="stylesheet" href="style.css?ts=1773672116">
<script src="https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js"></script>
</head>
<body>
<main>
<header class="hero">
<h1>KoboPatch <span class="hero-accent">Web UI</span> <span class="beta-pill">beta</span></h1>
<p class="subtitle">Custom patches for your Kobo e-reader</p>
</header>
<!-- Step indicator -->
<nav id="step-nav" class="step-nav" hidden>
<ol>
<li data-step="1" class="active">Device</li>
<li data-step="2">Patches</li>
<li data-step="3">Build</li>
<li data-step="4">Install</li>
</ol>
</nav>
<!-- Step 1a: Connect device (automatic, Chromium only) -->
<section id="step-connect" class="step" hidden>
<div class="warning">
<b>Patching modifies system files on your Kobo, and <u>will void your warranty</u>.</b> This process allows for custom modifications to be applied, or undone. If something has gone wrong,
you may need to <a href="https://help.kobo.com/hc/en-us/articles/360017605314-Manual-reset-your-Kobo-Clara-HD-Kobo-Nia-Kobo-Elipsa-Kobo-Clara-2E-Kobo-Elipsa-2E" target="_blank">manually reset your device</a>.
</div>
<p>
Connect your Kobo e-reader via USB. It should appear as a removable drive.
Then click the button below and select the root of the Kobo drive.
</p>
<button id="btn-connect" class="primary">Select Kobo Drive</button>
<p class="fallback-hint">
Don't want to use Chrome?
<a href="#" id="btn-manual-from-auto">Select your software version manually</a> instead.
</p>
</section>
<!-- Step 1b (manual): Select device model + firmware -->
<section id="step-manual" class="step" hidden>
<div class="warning">
<b>Patching modifies system files on your Kobo, and <u>will void your warranty</u>.</b> This process allows for custom modifications to be applied, or undone. If something has gone wrong,
you may need to <a href="https://help.kobo.com/hc/en-us/articles/360017605314-Manual-reset-your-Kobo-Clara-HD-Kobo-Nia-Kobo-Elipsa-Kobo-Clara-2E-Kobo-Elipsa-2E" target="_blank">manually reset your device</a>.
</div>
<p class="fallback-hint">
<strong>First, select the version number that is currently installed on your device.</strong> If it does not appear in this dropdown list, your software version is not supported for patching via this website.
</p>
<select id="manual-version">
<option value="">-- Select software version --</option>
</select>
<p id="manual-version-hint" class="fallback-hint">
You can identify the version number shown on your Kobo under <strong>More &gt; Settings &gt; Device information</strong> and by checking <strong>Software version</strong>. You should only apply a patch if the version number is a complete match. Only the listed version numbers are supported by this tool.
</p>
<select id="manual-model" hidden>
<option value="">-- Select your Kobo model --</option>
</select>
<p id="manual-model-hint" class="fallback-hint" hidden>
You can identify your model by the serial number prefix shown on your Kobo under <strong>More &gt; Settings &gt; Device information</strong>. Match the first characters (e.g. N428) to the list above. If your device isn't listed, it is not supported by this tool.
</p>
<p id="manual-chrome-hint" class="info-banner" hidden>
<b>Tip</b>: if you use Chrome or Edge, this tool can auto-detect your device
and write patched files directly to it, which makes this a lot easier and less error-prone. Before continuing, make sure you've double checked if the device and model number are correct.
</p>
<div class="step-actions" style="margin-top: 25px">
<button id="btn-manual-confirm" class="primary" disabled>I understand, continue &#x203A;</button>
</div>
</section>
<!-- Step 1c: Device detected (auto mode info card) -->
<section id="step-device" class="step" hidden>
<div id="device-info" class="info-card">
<div class="info-row">
<span class="label">Model</span>
<span id="device-model" class="value">--</span>
</div>
<div class="info-row">
<span class="label">Serial</span>
<span id="device-serial" class="value">--</span>
</div>
<div class="info-row">
<span class="label">Software</span>
<span id="device-firmware" class="value">--</span>
</div>
</div>
<p id="device-status"></p>
<div class="step-actions">
<button id="btn-device-restore" class="secondary">Restore Unpatched Software</button>
<button id="btn-device-next" class="primary">Configure Patches &#x203A;</button>
</div>
</section>
<!-- Step 2: Configure patches -->
<section id="step-patches" class="step" hidden>
<p>Enable or disable patches below. Patches in the same group are mutually exclusive.</p>
<div id="patch-container" class="patch-container-scroll"></div>
<div class="step-actions">
<button id="btn-patches-back" class="secondary">&#x2039; Back</button>
<button id="btn-patches-next" class="primary" disabled>Continue &#x203A;</button>
</div>
<p id="patch-count-hint" class="fallback-hint"></p>
</section>
<!-- Step 3: Review & Build -->
<section id="step-firmware" class="step" hidden>
<p id="firmware-auto-info">
Software update <strong id="firmware-version-label"></strong> for
<strong id="firmware-device-label"></strong>
<span id="firmware-description">will be downloaded automatically from Kobo's servers and will be patched after the download completes.</span>
</p>
<p id="selected-patches-heading" hidden>The following patches will be applied:</p>
<ul id="selected-patches-list" class="selected-patches-list"></ul>
<details class="log-details">
<summary>Software download URL</summary>
<code id="firmware-download-url"></code>
<p id="firmware-verify-notice" class="fallback-hint">
You can verify if this URL matches your Kobo's model on
<a href="https://help.kobo.com/hc/en-us/articles/35059171032727" target="_blank">Kobo's support page</a>. The most important bit is that "koboXX" matches, for example "kobo13" for Kobo Libra Color.
</p>
</details>
<div class="step-actions">
<button id="btn-build-back" class="secondary">&#x2039; Back</button>
<button id="btn-build" class="primary">Build Patched Software</button>
</div>
</section>
<!-- Step 4: Building -->
<section id="step-building" class="step" hidden>
<div class="build-header">
<div class="spinner"></div>
<p id="build-progress">Starting...</p>
</div>
<pre id="build-log" class="build-log"></pre>
<p id="build-wait-hint" class="fallback-hint">Please wait while the patch is being applied...</p>
</section>
<!-- Step 5: Install -->
<section id="step-done" class="step" hidden>
<p id="build-status" class="install-summary"></p>
<div id="existing-tgz-warning" class="warning" hidden>
An <b>existing</b> KoboRoot.tgz file was found on your Kobo. This means an update has not been applied yet. If you choose to write the new file to your Kobo, the existing one will be overwritten.
</div>
<details class="log-details">
<summary>Build log</summary>
<pre id="done-log" class="build-log done-log"></pre>
</details>
<div id="build-actions">
<button id="btn-write" class="primary">Write to Kobo</button>
<button id="btn-download" class="secondary">Download KoboRoot.tgz</button>
</div>
<div id="write-instructions" class="install-instructions" hidden>
<p class="hint">
KoboRoot.tgz has been written to your Kobo.
<strong>Safely eject</strong> the device before unplugging the USB cable — it will reboot and apply the patches automatically.
</p>
</div>
<div id="download-instructions" class="install-instructions" hidden>
<ol class="install-steps">
<li>Connect your Kobo via USB so it appears as a removable drive.</li>
<li>Copy <strong>KoboRoot.tgz</strong> into the <strong>.kobo</strong> folder on the Kobo drive.</li>
<li><strong>Safely eject</strong> the Kobo — do not just unplug the cable, as this can corrupt data.</li>
<li>The device will reboot and apply the patches automatically.</li>
</ol>
</div>
</section>
<!-- Error state -->
<section id="step-error" class="step" hidden>
<h2>Something went wrong</h2>
<p id="error-message" class="error"></p>
<pre id="error-log" class="error-log" hidden></pre>
<button id="btn-retry" class="secondary">Start Over</button>
</section>
</main>
<footer class="site-footer">
<p>
<a href="#" id="btn-how-it-works">How does this work?</a><br/>
<br/>
Built on <a href="https://github.com/pgaskin/kobopatch" target="_blank">kobopatch</a> by pgaskin.
Patches and discussion on the <a href="https://www.mobileread.com/forums/forumdisplay.php?f=247" target="_blank">MobileRead forums</a>.
</p>
<p>
Source code available <a href="https://github.com/nicoverbruggen/kobopatch-webui" target="_blank">on GitHub</a>. This project is not affiliated with Rakuten Kobo Inc.
</p>
</footer>
<dialog id="how-it-works-dialog" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>How does this work?</h2>
<button id="btn-close-dialog" class="modal-close" aria-label="Close">&times;</button>
</div>
<div class="modal-body">
<p>
KoboPatch Web UI is a fully client-side web application for applying custom
<a href="https://github.com/pgaskin/kobopatch" target="_blank">kobopatch</a> patches
to Kobo e-readers. Nothing is uploaded to a server &mdash; everything runs in your browser.
</p>
<h3>The patching process</h3>
<ol>
<li><strong>Device selection</strong> &mdash; On Chromium-based browsers (Chrome, Edge), the app can
auto-detect your Kobo via the File System Access API when connected over USB.
On other browsers, you manually select your model and software version.</li>
<li><strong>Patch configuration</strong> &mdash; You choose which patches to enable or disable.
Patches in the same group are mutually exclusive (radio buttons).
The patches themselves are community-contributed via the
<a href="https://www.mobileread.com/forums/forumdisplay.php?f=247" target="_blank">MobileRead forums</a>.</li>
<li><strong>Build</strong> &mdash; The correct software update is downloaded directly from Kobo's servers
(<code>ereaderfiles.kobo.com</code>). The patcher, compiled from Go to WebAssembly, runs
inside a Web Worker so the UI stays responsive. It extracts
<code>KoboRoot.tgz</code>, applies your selected patches as in-place byte replacements
to the ELF binaries, validates the results (ELF headers, file sizes, archive consistency),
and produces a new <code>KoboRoot.tgz</code>.</li>
<li><strong>Install</strong> &mdash; On Chromium, the patched file is written directly to the
<code>.kobo</code> folder on the device. On other browsers, you download the file
and copy it manually. After safely ejecting, the Kobo reboots and applies the update.</li>
</ol>
<h3>Restoring original software</h3>
<p>
You can also use this tool to restore the original unpatched software. In that case,
no patches are applied &mdash; the original <code>KoboRoot.tgz</code> is extracted
from the software update as-is.
</p>
<h3>Safety</h3>
<p>
Each patched binary is validated before being included in the output: ELF magic bytes,
32-bit ARM architecture, file size (must match the original), and archive integrity are
all checked. If anything looks wrong, the build fails with an error. That said, patching
modifies system files, so you should know how to
<a href="https://help.kobo.com/hc/en-us/articles/360017605314" target="_blank">manually reset your Kobo</a>
if needed.
</p>
</div>
</div>
</dialog>
<!-- wasm_exec.js loaded by patch-worker.js inside the Web Worker -->
<script src="kobo-device.js?ts=1773672116"></script>
<script src="kobopatch.js?ts=1773672116"></script>
<script src="patch-ui.js?ts=1773672116"></script>
<script src="app.js?ts=1773672116"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KoboPatch Web UI</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<link rel="stylesheet" href="style.css?ts=1773672116">
<script src="https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js"></script>
</head>
<body>
<main>
<header class="hero">
<h1>KoboPatch <span class="hero-accent">Web UI</span> <span class="beta-pill">beta</span></h1>
<p class="subtitle">Apply patches to your Kobo Libra Colour, Kobo Clara Colour and Kobo Clara BW.</p>
</header>
<!-- Step indicator -->
<nav id="step-nav" class="step-nav" hidden>
<ol>
<li data-step="1" class="active">Device</li>
<li data-step="2">Patches</li>
<li data-step="3">Build</li>
<li data-step="4">Install</li>
</ol>
</nav>
<!-- Step 1a: Connect device (automatic, Chromium only) -->
<section id="step-connect" class="step" hidden>
<div class="warning">
<b>Patching modifies system files on your Kobo, and <u>will void your warranty</u>.</b> This process allows for custom modifications to be applied, or undone. If something has gone wrong,
you may need to <a href="https://help.kobo.com/hc/en-us/articles/360017605314-Manual-reset-your-Kobo-Clara-HD-Kobo-Nia-Kobo-Elipsa-Kobo-Clara-2E-Kobo-Elipsa-2E" target="_blank">manually reset your device</a>.
</div>
<p>
Connect your Kobo e-reader via USB. It should appear as a removable drive.
Then click the button below and select the root of the Kobo drive.
</p>
<button id="btn-connect" class="primary">Select Kobo Drive</button>
<p class="fallback-hint">
Don't want to use Chrome?
<a href="#" id="btn-manual-from-auto">Select your software version manually</a> instead.
</p>
</section>
<!-- Step 1b (manual): Select device model + firmware -->
<section id="step-manual" class="step" hidden>
<div class="warning">
<b>Patching modifies system files on your Kobo, and <u>will void your warranty</u>.</b> This process allows for custom modifications to be applied, or undone. If something has gone wrong,
you may need to <a href="https://help.kobo.com/hc/en-us/articles/360017605314-Manual-reset-your-Kobo-Clara-HD-Kobo-Nia-Kobo-Elipsa-Kobo-Clara-2E-Kobo-Elipsa-2E" target="_blank">manually reset your device</a>.
</div>
<p class="fallback-hint">
<strong>First, select the version number that is currently installed on your device.</strong> If it does not appear in this dropdown list, your software version is not supported for patching via this website.
</p>
<select id="manual-version">
<option value="">-- Select software version --</option>
</select>
<p id="manual-version-hint" class="fallback-hint">
You can identify the version number shown on your Kobo under <strong>More &gt; Settings &gt; Device information</strong> and by checking <strong>Software version</strong>. You should only apply a patch if the version number is a complete match. Only the listed version numbers are supported by this tool.
</p>
<select id="manual-model" hidden>
<option value="">-- Select your Kobo model --</option>
</select>
<p id="manual-model-hint" class="fallback-hint" hidden>
You can identify your model by the serial number prefix shown on your Kobo under <strong>More &gt; Settings &gt; Device information</strong>. Match the first characters (e.g. N428) to the list above. If your device isn't listed, it is not supported by this tool.
</p>
<p id="manual-chrome-hint" class="info-banner" hidden>
<b>Tip</b>: if you use Chrome or Edge, this tool can auto-detect your device
and write patched files directly to it, which makes this a lot easier and less error-prone. Before continuing, make sure you've double checked if the device and model number are correct.
</p>
<div class="step-actions" style="margin-top: 25px">
<button id="btn-manual-confirm" class="primary" disabled>I understand, continue &#x203A;</button>
</div>
</section>
<!-- Step 1c: Device detected (auto mode info card) -->
<section id="step-device" class="step" hidden>
<div id="device-info" class="info-card">
<div class="info-row">
<span class="label">Model</span>
<span id="device-model" class="value">--</span>
</div>
<div class="info-row">
<span class="label">Serial</span>
<span id="device-serial" class="value">--</span>
</div>
<div class="info-row">
<span class="label">Software</span>
<span id="device-firmware" class="value">--</span>
</div>
</div>
<p id="device-status"></p>
<div class="step-actions">
<button id="btn-device-restore" class="secondary">Restore Unpatched Software</button>
<button id="btn-device-next" class="primary">Configure Patches &#x203A;</button>
</div>
</section>
<!-- Step 2: Configure patches -->
<section id="step-patches" class="step" hidden>
<p>Enable or disable patches below. Patches in the same group are mutually exclusive.</p>
<div id="patch-container" class="patch-container-scroll"></div>
<div class="step-actions">
<button id="btn-patches-back" class="secondary">&#x2039; Back</button>
<button id="btn-patches-next" class="primary" disabled>Continue &#x203A;</button>
</div>
<p id="patch-count-hint" class="fallback-hint"></p>
</section>
<!-- Step 3: Review & Build -->
<section id="step-firmware" class="step" hidden>
<p id="firmware-auto-info">
Software update <strong id="firmware-version-label"></strong> for
<strong id="firmware-device-label"></strong>
<span id="firmware-description">will be downloaded automatically from Kobo's servers and will be patched after the download completes.</span>
</p>
<p id="selected-patches-heading" hidden>The following patches will be applied:</p>
<ul id="selected-patches-list" class="selected-patches-list"></ul>
<details class="log-details">
<summary>Software download URL</summary>
<code id="firmware-download-url"></code>
<p id="firmware-verify-notice" class="fallback-hint">
You can verify if this URL matches your Kobo's model on
<a href="https://help.kobo.com/hc/en-us/articles/35059171032727" target="_blank">Kobo's support page</a>. The most important bit is that "koboXX" matches, for example "kobo13" for Kobo Libra Color.
</p>
</details>
<div class="step-actions">
<button id="btn-build-back" class="secondary">&#x2039; Back</button>
<button id="btn-build" class="primary">Build Patched Software</button>
</div>
</section>
<!-- Step 4: Building -->
<section id="step-building" class="step" hidden>
<div class="build-header">
<div class="spinner"></div>
<p id="build-progress">Starting...</p>
</div>
<pre id="build-log" class="build-log"></pre>
<p id="build-wait-hint" class="fallback-hint">Please wait while the patch is being applied...</p>
</section>
<!-- Step 5: Install -->
<section id="step-done" class="step" hidden>
<p id="build-status" class="install-summary"></p>
<div id="existing-tgz-warning" class="warning" hidden>
An <b>existing</b> KoboRoot.tgz file was found on your Kobo. This means an update has not been applied yet. If you choose to write the new file to your Kobo, the existing one will be overwritten.
</div>
<details class="log-details">
<summary>Build log</summary>
<pre id="done-log" class="build-log done-log"></pre>
</details>
<div id="build-actions">
<button id="btn-write" class="primary">Write to Kobo</button>
<button id="btn-download" class="secondary">Download KoboRoot.tgz</button>
</div>
<div id="write-instructions" class="install-instructions" hidden>
<p class="hint">
KoboRoot.tgz has been written to your Kobo.
<strong>Safely eject</strong> the device before unplugging the USB cable — it will reboot and apply the patches automatically.
</p>
</div>
<div id="download-instructions" class="install-instructions" hidden>
<ol class="install-steps">
<li>Connect your Kobo via USB so it appears as a removable drive.</li>
<li>Copy <strong>KoboRoot.tgz</strong> into the <strong>.kobo</strong> folder on the Kobo drive.</li>
<li><strong>Safely eject</strong> the Kobo — do not just unplug the cable, as this can corrupt data.</li>
<li>The device will reboot and apply the patches automatically.</li>
</ol>
</div>
</section>
<!-- Error state -->
<section id="step-error" class="step" hidden>
<h2>Something went wrong</h2>
<p id="error-message" class="error"></p>
<pre id="error-log" class="error-log" hidden></pre>
<button id="btn-retry" class="secondary">Start Over</button>
</section>
</main>
<footer class="site-footer">
<p>
<a href="#" id="btn-how-it-works">How does this work?</a><br/>
<br/>
Built on <a href="https://github.com/pgaskin/kobopatch" target="_blank">kobopatch</a> by pgaskin.
Patches and discussion on the <a href="https://www.mobileread.com/forums/forumdisplay.php?f=247" target="_blank">MobileRead forums</a>.
</p>
<p>
Source code available <a href="https://github.com/nicoverbruggen/kobopatch-webui" target="_blank">on GitHub</a>. This project is not affiliated with Rakuten Kobo Inc.
</p>
</footer>
<dialog id="how-it-works-dialog" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>How does this work?</h2>
<button id="btn-close-dialog" class="modal-close" aria-label="Close">&times;</button>
</div>
<div class="modal-body">
<p>
KoboPatch Web UI is a fully client-side web application for applying custom
<a href="https://github.com/pgaskin/kobopatch" target="_blank">kobopatch</a> patches
to Kobo e-readers. Nothing is uploaded to a server &mdash; everything runs in your browser.
</p>
<h3>The patching process</h3>
<ol>
<li><strong>Device selection</strong> &mdash; On Chromium-based browsers (Chrome, Edge), the app can
auto-detect your Kobo via the File System Access API when connected over USB.
On other browsers, you manually select your model and software version.</li>
<li><strong>Patch configuration</strong> &mdash; You choose which patches to enable or disable.
Patches in the same group are mutually exclusive (radio buttons).
The patches themselves are community-contributed via the
<a href="https://www.mobileread.com/forums/forumdisplay.php?f=247" target="_blank">MobileRead forums</a>.</li>
<li><strong>Build</strong> &mdash; The correct software update is downloaded directly from Kobo's servers
(<code>ereaderfiles.kobo.com</code>). The patcher, compiled from Go to WebAssembly, runs
inside a Web Worker so the UI stays responsive. It extracts
<code>KoboRoot.tgz</code>, applies your selected patches as in-place byte replacements
to the ELF binaries, validates the results (ELF headers, file sizes, archive consistency),
and produces a new <code>KoboRoot.tgz</code>.</li>
<li><strong>Install</strong> &mdash; On Chromium, the patched file is written directly to the
<code>.kobo</code> folder on the device. On other browsers, you download the file
and copy it manually. After safely ejecting, the Kobo reboots and applies the update.</li>
</ol>
<h3>Restoring original software</h3>
<p>
You can also use this tool to restore the original unpatched software. In that case,
no patches are applied &mdash; the original <code>KoboRoot.tgz</code> is extracted
from the software update as-is.
</p>
<h3>Safety</h3>
<p>
Each patched binary is validated before being included in the output: ELF magic bytes,
32-bit ARM architecture, file size (must match the original), and archive integrity are
all checked. If anything looks wrong, the build fails with an error. That said, patching
modifies system files, so you should know how to
<a href="https://help.kobo.com/hc/en-us/articles/360017605314" target="_blank">manually reset your Kobo</a>
if needed.
</p>
</div>
</div>
</dialog>
<!-- wasm_exec.js loaded by patch-worker.js inside the Web Worker -->
<script src="kobo-device.js?ts=1773672116"></script>
<script src="kobopatch.js?ts=1773672116"></script>
<script src="patch-ui.js?ts=1773672116"></script>
<script src="app.js?ts=1773672116"></script>
</body>
</html>

View File

@@ -1,175 +1,175 @@
/**
* Known Kobo device serial prefixes mapped to model names.
* Source: https://help.kobo.com/hc/en-us/articles/360019676973
* The serial number prefix (first 3-4 characters) identifies the model.
*/
const KOBO_MODELS = {
// Current eReaders
'N428': 'Kobo Libra Colour',
'N367': 'Kobo Clara Colour',
'N365': 'Kobo Clara BW',
'P365': 'Kobo Clara BW',
'N605': 'Kobo Elipsa 2E',
'N506': 'Kobo Clara 2E',
'N778': 'Kobo Sage',
'N418': 'Kobo Libra 2',
'N604': 'Kobo Elipsa',
'N306': 'Kobo Nia',
'N873': 'Kobo Libra H2O',
'N782': 'Kobo Forma',
'N249': 'Kobo Clara HD',
'N867': 'Kobo Aura H2O Edition 2',
'N709': 'Kobo Aura ONE',
'N236': 'Kobo Aura Edition 2',
'N587': 'Kobo Touch 2.0',
'N437': 'Kobo Glo HD',
'N250': 'Kobo Aura H2O',
'N514': 'Kobo Aura',
'N613': 'Kobo Glo',
'N705': 'Kobo Mini',
'N416': 'Kobo Original',
// Older models with multiple revisions
'N905': 'Kobo Touch',
'N647': 'Kobo Wireless',
'N47B': 'Kobo Wireless',
// Aura HD uses 5-char prefix
'N204': 'Kobo Aura HD',
};
/**
* Supported firmware version for patching.
*/
const SUPPORTED_FIRMWARE = '4.45.23646';
/**
* Firmware download URLs by version and serial prefix.
* Source: https://help.kobo.com/hc/en-us/articles/35059171032727
*
* The kobo prefix (kobo12, kobo13, kobo14) is stable per device family.
* The date path segment (e.g. Mar2026) changes per release.
* help.kobo.com may lag behind; verify URLs when adding new versions.
*/
const FIRMWARE_DOWNLOADS = {
'4.45.23646': {
'N428': 'https://ereaderfiles.kobo.com/firmwares/kobo13/Mar2026/kobo-update-4.45.23646.zip',
'N365': 'https://ereaderfiles.kobo.com/firmwares/kobo12/Mar2026/kobo-update-4.45.23646.zip',
'N367': 'https://ereaderfiles.kobo.com/firmwares/kobo12/Mar2026/kobo-update-4.45.23646.zip',
'P365': 'https://ereaderfiles.kobo.com/firmwares/kobo14/Mar2026/kobo-update-4.45.23646.zip',
},
};
/**
* Get the firmware download URL for a given serial prefix and firmware version.
* Returns null if no URL is available.
*/
function getFirmwareURL(serialPrefix, version) {
const versionMap = FIRMWARE_DOWNLOADS[version];
if (!versionMap) return null;
return versionMap[serialPrefix] || null;
}
/**
* Get all device models that have firmware downloads for a given version.
* Returns array of { prefix, model } objects.
*/
function getDevicesForVersion(version) {
const versionMap = FIRMWARE_DOWNLOADS[version];
if (!versionMap) return [];
const devices = [];
for (const prefix of Object.keys(versionMap)) {
const model = KOBO_MODELS[prefix] || 'Unknown';
devices.push({ prefix, model: model + ' (' + prefix + ')' });
}
return devices;
}
class KoboDevice {
constructor() {
this.directoryHandle = null;
this.deviceInfo = null;
}
/**
* Check if the File System Access API is available.
*/
static isSupported() {
return 'showDirectoryPicker' in window;
}
/**
* Prompt the user to select the Kobo drive root directory.
* Validates that it looks like a Kobo by checking for .kobo/version.
*/
async connect() {
this.directoryHandle = await window.showDirectoryPicker({
mode: 'readwrite',
});
// Verify this looks like a Kobo root
let koboDir;
try {
koboDir = await this.directoryHandle.getDirectoryHandle('.kobo');
} catch {
throw new Error(
'This does not appear to be a Kobo device. Could not find the .kobo directory.'
);
}
let versionFile;
try {
versionFile = await koboDir.getFileHandle('version');
} catch {
throw new Error(
'Could not find .kobo/version. Is this the root of your Kobo drive?'
);
}
const file = await versionFile.getFile();
const content = await file.text();
this.deviceInfo = KoboDevice.parseVersion(content.trim());
return this.deviceInfo;
}
/**
* Parse the .kobo/version file content.
*
* Format: serial,version1,firmware,version3,version4,hardware_uuid
* Example: N4284B5215352,4.9.77,4.45.23646,4.9.77,4.9.77,00000000-0000-0000-0000-000000000390
*/
static parseVersion(content) {
const parts = content.split(',');
if (parts.length < 6) {
throw new Error(
'Unexpected version file format. Expected 6 comma-separated fields, got ' + parts.length
);
}
const serial = parts[0];
const firmware = parts[2];
const hardwareId = parts[5];
// Try matching 4-char prefix first, then 3-char for models like N204B
const serialPrefix = KOBO_MODELS[serial.substring(0, 4)]
? serial.substring(0, 4)
: serial.substring(0, 3);
const model = KOBO_MODELS[serialPrefix] || 'Unknown Kobo (' + serial.substring(0, 4) + ')';
const isSupported = firmware === SUPPORTED_FIRMWARE;
return {
serial,
serialPrefix,
firmware,
hardwareId,
model,
isSupported,
};
}
/**
* Disconnect / release the directory handle.
*/
disconnect() {
this.directoryHandle = null;
this.deviceInfo = null;
}
}
/**
* Known Kobo device serial prefixes mapped to model names.
* Source: https://help.kobo.com/hc/en-us/articles/360019676973
* The serial number prefix (first 3-4 characters) identifies the model.
*/
const KOBO_MODELS = {
// Current eReaders
'N428': 'Kobo Libra Colour',
'N367': 'Kobo Clara Colour',
'N365': 'Kobo Clara BW',
'P365': 'Kobo Clara BW',
'N605': 'Kobo Elipsa 2E',
'N506': 'Kobo Clara 2E',
'N778': 'Kobo Sage',
'N418': 'Kobo Libra 2',
'N604': 'Kobo Elipsa',
'N306': 'Kobo Nia',
'N873': 'Kobo Libra H2O',
'N782': 'Kobo Forma',
'N249': 'Kobo Clara HD',
'N867': 'Kobo Aura H2O Edition 2',
'N709': 'Kobo Aura ONE',
'N236': 'Kobo Aura Edition 2',
'N587': 'Kobo Touch 2.0',
'N437': 'Kobo Glo HD',
'N250': 'Kobo Aura H2O',
'N514': 'Kobo Aura',
'N613': 'Kobo Glo',
'N705': 'Kobo Mini',
'N416': 'Kobo Original',
// Older models with multiple revisions
'N905': 'Kobo Touch',
'N647': 'Kobo Wireless',
'N47B': 'Kobo Wireless',
// Aura HD uses 5-char prefix
'N204': 'Kobo Aura HD',
};
/**
* Firmware download URLs by version and serial prefix.
* Source: https://help.kobo.com/hc/en-us/articles/35059171032727
*
* The kobo prefix (kobo12, kobo13, kobo14) is stable per device family.
* The date path segment (e.g. Mar2026) changes per release.
* help.kobo.com may lag behind; verify URLs when adding new versions.
*/
const FIRMWARE_DOWNLOADS = {
'4.45.23646': {
'N428': 'https://ereaderfiles.kobo.com/firmwares/kobo13/Mar2026/kobo-update-4.45.23646.zip',
'N365': 'https://ereaderfiles.kobo.com/firmwares/kobo12/Mar2026/kobo-update-4.45.23646.zip',
'N367': 'https://ereaderfiles.kobo.com/firmwares/kobo12/Mar2026/kobo-update-4.45.23646.zip',
'P365': 'https://ereaderfiles.kobo.com/firmwares/kobo14/Mar2026/kobo-update-4.45.23646.zip',
},
};
/**
* Supported firmware versions for patching (derived from FIRMWARE_DOWNLOADS).
*/
const SUPPORTED_FIRMWARE = Object.keys(FIRMWARE_DOWNLOADS);
/**
* Get the firmware download URL for a given serial prefix and firmware version.
* Returns null if no URL is available.
*/
function getFirmwareURL(serialPrefix, version) {
const versionMap = FIRMWARE_DOWNLOADS[version];
if (!versionMap) return null;
return versionMap[serialPrefix] || null;
}
/**
* Get all device models that have firmware downloads for a given version.
* Returns array of { prefix, model } objects.
*/
function getDevicesForVersion(version) {
const versionMap = FIRMWARE_DOWNLOADS[version];
if (!versionMap) return [];
const devices = [];
for (const prefix of Object.keys(versionMap)) {
const model = KOBO_MODELS[prefix] || 'Unknown';
devices.push({ prefix, model: model + ' (' + prefix + ')' });
}
return devices;
}
class KoboDevice {
constructor() {
this.directoryHandle = null;
this.deviceInfo = null;
}
/**
* Check if the File System Access API is available.
*/
static isSupported() {
return 'showDirectoryPicker' in window;
}
/**
* Prompt the user to select the Kobo drive root directory.
* Validates that it looks like a Kobo by checking for .kobo/version.
*/
async connect() {
this.directoryHandle = await window.showDirectoryPicker({
mode: 'readwrite',
});
// Verify this looks like a Kobo root
let koboDir;
try {
koboDir = await this.directoryHandle.getDirectoryHandle('.kobo');
} catch {
throw new Error(
'This does not appear to be a Kobo device. Could not find the .kobo directory.'
);
}
let versionFile;
try {
versionFile = await koboDir.getFileHandle('version');
} catch {
throw new Error(
'Could not find .kobo/version. Is this the root of your Kobo drive?'
);
}
const file = await versionFile.getFile();
const content = await file.text();
this.deviceInfo = KoboDevice.parseVersion(content.trim());
return this.deviceInfo;
}
/**
* Parse the .kobo/version file content.
*
* Format: serial,version1,firmware,version3,version4,hardware_uuid
* Example: N4284B5215352,4.9.77,4.45.23646,4.9.77,4.9.77,00000000-0000-0000-0000-000000000390
*/
static parseVersion(content) {
const parts = content.split(',');
if (parts.length < 6) {
throw new Error(
'Unexpected version file format. Expected 6 comma-separated fields, got ' + parts.length
);
}
const serial = parts[0];
const firmware = parts[2];
const hardwareId = parts[5];
// Try matching 4-char prefix first, then 3-char for models like N204B
const serialPrefix = KOBO_MODELS[serial.substring(0, 4)]
? serial.substring(0, 4)
: serial.substring(0, 3);
const model = KOBO_MODELS[serialPrefix] || 'Unknown Kobo (' + serial.substring(0, 4) + ')';
const isSupported = SUPPORTED_FIRMWARE.includes(firmware);
return {
serial,
serialPrefix,
firmware,
hardwareId,
model,
isSupported,
};
}
/**
* Disconnect / release the directory handle.
*/
disconnect() {
this.directoryHandle = null;
this.deviceInfo = null;
}
}

View File

@@ -136,14 +136,22 @@ function parsePatchConfig(configYAML) {
/**
* Scan the patches/ directory for available patch zips.
* Returns an array of { filename, version } sorted by version descending.
* Returns an array of { filename, version } objects.
* Each entry in index.json may list multiple versions; these are flattened
* so that each version gets its own entry pointing to the same filename.
*/
async function scanAvailablePatches() {
try {
const resp = await fetch('patches/index.json');
if (!resp.ok) return [];
const list = await resp.json();
return list;
const result = [];
for (const entry of list) {
for (const version of entry.versions) {
result.push({ filename: entry.filename, version });
}
}
return result;
} catch {
return [];
}

View File

@@ -1,6 +1,7 @@
[
{
"filename": "patches_4.4523646.zip",
"version": "4.45.23646"
}
"filename": "patches_4.45.23646.zip",
"versions": ["4.45.23646"],
"date": "03-2026"
},
]

File diff suppressed because it is too large Load Diff