Updated documentation
Some checks failed
Build & Test WASM / build-and-test (push) Has been cancelled
Some checks failed
Build & Test WASM / build-and-test (push) Has been cancelled
This commit is contained in:
82
README.md
82
README.md
@@ -1,58 +1,66 @@
|
||||
# KoboPatch Web UI
|
||||
|
||||
A fully client-side web application for applying [kobopatch](https://github.com/pgaskin/kobopatch) patches to Kobo e-readers. No backend required — runs entirely in the browser using WebAssembly.
|
||||
Fully client-side web app for applying [kobopatch](https://github.com/pgaskin/kobopatch) patches to Kobo e-readers. No backend — runs entirely in the browser via WebAssembly. Can be hosted as a static site.
|
||||
|
||||
## Features
|
||||
## User flow
|
||||
|
||||
- **Auto mode** (Chromium): detect your Kobo model and firmware via the File System Access API, then write the patched file directly back to the device
|
||||
- **Manual mode** (all browsers): select your model and firmware version from dropdowns, download the result
|
||||
- Firmware is downloaded automatically from Kobo's servers
|
||||
- Step-by-step wizard with live build progress
|
||||
- Patch descriptions and PatchGroup mutual exclusion
|
||||
1. Select device (auto-detect via File System Access API on Chromium, or manual dropdowns on any browser)
|
||||
2. Configure patches (enable/disable, PatchGroup mutual exclusion via radio buttons)
|
||||
3. Build — firmware auto-downloaded from Kobo's CDN (`ereaderfiles.kobo.com`, CORS open), patched via WASM in a Web Worker
|
||||
4. Write `KoboRoot.tgz` to device (Chromium auto mode) or download manually
|
||||
|
||||
## How it works
|
||||
## File structure
|
||||
|
||||
1. Connect your Kobo via USB (or select your model/firmware manually)
|
||||
2. Enable/disable patches in the configurator
|
||||
3. Click **Build** — firmware is fetched from Kobo's CDN, patches are applied via WASM in a Web Worker
|
||||
4. Write `KoboRoot.tgz` to your device or download it manually
|
||||
5. Safely eject and reboot your Kobo
|
||||
```
|
||||
src/public/ # Webroot — serve this directory
|
||||
index.html # Single-page app, 3-step wizard (Device → Patches → Build)
|
||||
style.css
|
||||
app.js # Step navigation, flow orchestration, firmware download with progress
|
||||
kobo-device.js # KOBO_MODELS (serial prefix → name), FIRMWARE_DOWNLOADS (version+prefix → URL),
|
||||
# getDevicesForVersion(), getFirmwareURL(), KoboDevice class (File System Access API)
|
||||
patch-ui.js # PatchUI class: loads patch zips (JSZip), parses YAML, renders toggle UI,
|
||||
# generates kobopatch.yaml config with overrides
|
||||
kobopatch.js # KobopatchRunner: spawns Web Worker per build, handles progress/done/error messages
|
||||
patch-worker.js # Web Worker: loads wasm_exec.js + kobopatch.wasm, runs patchFirmware(),
|
||||
# posts progress back, transfers result buffer zero-copy
|
||||
wasm_exec.js # Go WASM support runtime (copied from Go SDK by setup.sh, gitignored)
|
||||
kobopatch.wasm # Compiled WASM binary (built by build.sh, gitignored)
|
||||
patches/
|
||||
index.json # [{ "version": "4.45.23646", "filename": "patches_4.45.23646.zip" }]
|
||||
patches_*.zip # Each contains kobopatch.yaml + src/*.yaml patch files
|
||||
|
||||
## Building
|
||||
kobopatch-wasm/ # WASM build
|
||||
main.go # Go entry point: jsPatchFirmware() → patchFirmware() pipeline
|
||||
# Accepts configYAML, firmwareZip, patchFiles, optional progressFn
|
||||
# Returns { tgz: Uint8Array, log: string }
|
||||
go.mod
|
||||
setup.sh # Clones kobopatch source, copies wasm_exec.js
|
||||
build.sh # GOOS=js GOARCH=wasm go build, copies .wasm to src/public/,
|
||||
# sets ?ts= cache-bust timestamp in patch-worker.js
|
||||
```
|
||||
|
||||
### Prerequisites
|
||||
## Adding a new firmware version
|
||||
|
||||
- Go 1.21+ (for compiling kobopatch to WASM)
|
||||
1. Add the patch zip to `src/public/patches/` and update `index.json`
|
||||
2. Add firmware download URLs to `FIRMWARE_DOWNLOADS` in `kobo-device.js` (keyed by version then serial prefix)
|
||||
3. The kobo CDN prefix per device family (e.g. `kobo12`, `kobo13`) is stable; the date path segment changes per release
|
||||
|
||||
### Setup & build
|
||||
## Building the WASM binary
|
||||
|
||||
Requires Go 1.21+.
|
||||
|
||||
```bash
|
||||
cd kobopatch-wasm
|
||||
./setup.sh # clones kobopatch source, copies wasm_exec.js
|
||||
./build.sh # compiles WASM, copies artifacts to src/public/
|
||||
./setup.sh # first time only
|
||||
./build.sh # compiles WASM, copies to src/public/
|
||||
```
|
||||
|
||||
### Running locally
|
||||
|
||||
Any static file server works:
|
||||
## Running locally
|
||||
|
||||
```bash
|
||||
python3 -m http.server -d src/public/ 8888
|
||||
```
|
||||
|
||||
Then open `http://localhost:8888`.
|
||||
## Credits
|
||||
|
||||
## Supported devices
|
||||
|
||||
Currently supports firmware **4.45.23646** for:
|
||||
|
||||
- Kobo Libra Colour
|
||||
- Kobo Clara BW (N365)
|
||||
- Kobo Clara BW (P365)
|
||||
- Kobo Clara Colour
|
||||
|
||||
Additional firmware versions can be added by placing patch zips in `src/public/patches/` and updating `index.json` and the firmware URL map in `kobo-device.js`.
|
||||
|
||||
## License
|
||||
|
||||
kobopatch is by [pgaskin](https://github.com/pgaskin/kobopatch). Patches are community-contributed via [MobileRead](https://www.mobileread.com/).
|
||||
kobopatch by [pgaskin](https://github.com/pgaskin/kobopatch). Patches from [MobileRead](https://www.mobileread.com/).
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
const buildStatus = document.getElementById('build-status');
|
||||
const writeSuccess = document.getElementById('write-success');
|
||||
const firmwareVersionLabel = document.getElementById('firmware-version-label');
|
||||
const firmwareDeviceLabel = document.getElementById('firmware-device-label');
|
||||
const patchCountHint = document.getElementById('patch-count-hint');
|
||||
|
||||
const allSteps = [stepConnect, stepManual, stepDevice, stepPatches, stepFirmware, stepBuilding, stepDone, stepError];
|
||||
@@ -86,6 +87,7 @@
|
||||
function configureFirmwareStep(version, prefix) {
|
||||
firmwareURL = prefix ? getFirmwareURL(prefix, version) : null;
|
||||
firmwareVersionLabel.textContent = version;
|
||||
firmwareDeviceLabel.textContent = KOBO_MODELS[prefix] || prefix;
|
||||
document.getElementById('firmware-download-url').textContent = firmwareURL || '';
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<select id="manual-model" hidden>
|
||||
<option value="">-- Select your Kobo model --</option>
|
||||
</select>
|
||||
<button id="btn-manual-confirm" class="primary" disabled>Continue</button>
|
||||
<button id="btn-manual-confirm" class="primary" disabled>Continue ›</button>
|
||||
</section>
|
||||
|
||||
<!-- Step 1c: Device detected (auto mode info card) -->
|
||||
@@ -70,7 +70,7 @@
|
||||
</div>
|
||||
<div id="device-status"></div>
|
||||
<div class="step-actions">
|
||||
<button id="btn-device-next" class="primary">Configure Patches</button>
|
||||
<button id="btn-device-next" class="primary">Configure Patches ›</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -79,8 +79,8 @@
|
||||
<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">Back</button>
|
||||
<button id="btn-patches-next" class="primary" disabled>Continue</button>
|
||||
<button id="btn-patches-back" class="secondary">‹ Back</button>
|
||||
<button id="btn-patches-next" class="primary" disabled>Continue ›</button>
|
||||
</div>
|
||||
<p id="patch-count-hint" class="fallback-hint"></p>
|
||||
</section>
|
||||
@@ -88,11 +88,14 @@
|
||||
<!-- Step 3: Review & Build -->
|
||||
<section id="step-firmware" class="step" hidden>
|
||||
<p id="firmware-auto-info">
|
||||
Firmware <strong id="firmware-version-label"></strong> will be downloaded
|
||||
automatically from Kobo's servers:<br>
|
||||
Firmware <strong id="firmware-version-label"></strong> for
|
||||
<strong id="firmware-device-label"></strong> will be downloaded
|
||||
automatically from Kobo's servers and will be patched after the download completes:<br>
|
||||
<code id="firmware-download-url"></code><br>
|
||||
You can verify this URL on
|
||||
<a href="https://help.kobo.com/hc/en-us/articles/35059171032727" target="_blank">Kobo's support page</a>.
|
||||
<span id="firmware-verify-notice">
|
||||
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.
|
||||
</span>
|
||||
</p>
|
||||
<!--
|
||||
<p id="firmware-manual-info" hidden>
|
||||
@@ -105,7 +108,7 @@
|
||||
<input type="file" id="firmware-input" accept=".zip" hidden>
|
||||
-->
|
||||
<div class="step-actions">
|
||||
<button id="btn-build-back" class="secondary">Back</button>
|
||||
<button id="btn-build-back" class="secondary">‹ Back</button>
|
||||
<button id="btn-build" class="primary">Build Patched Firmware</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -163,10 +163,15 @@ h2 {
|
||||
/* Step action buttons (back/next) */
|
||||
.step-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.step-actions .primary:first-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
button {
|
||||
font-size: 0.9rem;
|
||||
@@ -512,6 +517,10 @@ input[type="file"] {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#firmware-verify-notice {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
# Kobopatch Web UI - Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
Fully client-side web app. No backend server needed — can be hosted as a static site.
|
||||
kobopatch is compiled from Go to WebAssembly and runs entirely in the browser.
|
||||
|
||||
```
|
||||
Browser
|
||||
├── index.html + CSS + JS
|
||||
├── patch-worker.js (Web Worker for off-thread patching)
|
||||
├── kobopatch.wasm (Go compiled to WASM, loaded by worker)
|
||||
├── Patch config zips (in src/public/patches/)
|
||||
├── Firmware auto-download (fetched from ereaderfiles.kobo.com)
|
||||
└── File System Access API (read/write Kobo USB drive, Chromium only)
|
||||
```
|
||||
|
||||
## User Flow
|
||||
|
||||
### Auto mode (Chromium)
|
||||
1. Connect Kobo via USB
|
||||
2. Click "Select Kobo Drive" → browser reads `.kobo/version`
|
||||
3. App detects model + firmware version from serial prefix
|
||||
4. App shows available patches with toggles, grouped by target binary
|
||||
5. User configures patches (enable/disable)
|
||||
6. Click "Build" → firmware downloaded from Kobo CDN → WASM patches in Web Worker
|
||||
7. App writes resulting `KoboRoot.tgz` to `.kobo/` on device (or download)
|
||||
8. User ejects device and reboots Kobo
|
||||
|
||||
### Manual mode (all browsers)
|
||||
1. Select firmware version from dropdown
|
||||
2. Select Kobo model from dropdown (determines firmware download URL)
|
||||
3. Configure patches, click "Build"
|
||||
4. Download `KoboRoot.tgz` and manually copy to `.kobo/` on device
|
||||
|
||||
## Components
|
||||
|
||||
### `kobo-device.js` — Device Access & Firmware URLs
|
||||
- File System Access API for reading `.kobo/version`
|
||||
- Serial prefix → model name mapping (`KOBO_MODELS`)
|
||||
- Firmware download URLs per version and device prefix (`FIRMWARE_DOWNLOADS`)
|
||||
- Writing `KoboRoot.tgz` back to `.kobo/`
|
||||
|
||||
### `patch-ui.js` — Patch Configuration
|
||||
- Parses patch YAML files from zip archives (handles CRLF line endings)
|
||||
- Renders patch list with toggles grouped by target file
|
||||
- Enforces PatchGroup mutual exclusion (radio buttons)
|
||||
- Generates kobopatch.yaml config with overrides from UI state
|
||||
|
||||
### `patch-worker.js` — Web Worker
|
||||
- Loads `wasm_exec.js` and `kobopatch.wasm` off the main thread
|
||||
- Receives patch config + firmware via `postMessage`
|
||||
- Sends progress updates back to main thread for live UI rendering
|
||||
- Returns `KoboRoot.tgz` bytes via transferable buffer
|
||||
|
||||
### `kobopatch.wasm` — Patching Engine
|
||||
- Go source in `kobopatch-wasm/`, compiled with `GOOS=js GOARCH=wasm`
|
||||
- Custom WASM wrapper accepts in-memory inputs:
|
||||
- Config YAML (generated from UI state)
|
||||
- Firmware zip (auto-downloaded from Kobo CDN)
|
||||
- Patch YAML files (from bundled zip)
|
||||
- Optional progress callback (4th argument) for real-time status
|
||||
- Returns `{ tgz: Uint8Array, log: string }`
|
||||
- No filesystem or exec calls — everything in-memory
|
||||
|
||||
### `kobopatch.js` — Runner Interface
|
||||
- Abstracts WASM loading and invocation
|
||||
- Spawns Web Worker per build, handles progress/done/error messages
|
||||
- Transfers firmware buffer zero-copy via transferable
|
||||
|
||||
### Static Assets
|
||||
- Patch config zips in `src/public/patches/` with `index.json` index
|
||||
- `wasm_exec.js` (Go's WASM support JS)
|
||||
- The WASM binary itself
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
src/
|
||||
public/ # Webroot (static hosting)
|
||||
index.html # Single page app
|
||||
style.css # Styling
|
||||
app.js # Main controller / flow orchestration
|
||||
kobo-device.js # File System Access API + device identification + firmware URLs
|
||||
patch-ui.js # Patch list rendering + toggle logic
|
||||
kobopatch.js # WASM runner interface
|
||||
patch-worker.js # Web Worker for off-thread patching
|
||||
wasm_exec.js # Go WASM support (from Go SDK, gitignored)
|
||||
kobopatch.wasm # Compiled WASM binary (gitignored)
|
||||
patches/
|
||||
index.json # Available patch versions
|
||||
patches_*.zip # Patch config zips (kobopatch.yaml + src/*.yaml)
|
||||
kobopatch-wasm/
|
||||
main.go # WASM entry point + patching pipeline
|
||||
go.mod # Go module (replaces for kobopatch + yaml fork)
|
||||
setup.sh # Clones kobopatch source, copies wasm_exec.js
|
||||
build.sh # Compiles WASM, copies artifacts to src/public/
|
||||
kobopatch-src/ # Cloned kobopatch Go source (gitignored)
|
||||
wip/ # Planning docs
|
||||
.github/workflows/ # CI (GitHub Actions / Gitea compatible)
|
||||
```
|
||||
|
||||
## Key Constraints
|
||||
|
||||
- **Chromium-only for auto mode**: File System Access API not in Firefox/Safari
|
||||
- Manual mode fallback: select model + firmware version from dropdowns
|
||||
- **Firmware auto-download**: fetched from `ereaderfiles.kobo.com` (CORS allows `*`)
|
||||
- URLs hardcoded per device prefix in `FIRMWARE_DOWNLOADS`
|
||||
- Download URL displayed to user for verification
|
||||
- **WASM binary size**: ~9.9MB uncompressed
|
||||
- Mitigated by gzip compression on static hosting (~3-4MB)
|
||||
- **Memory usage**: firmware zips are ~150-300MB, patching happens in-memory
|
||||
- Should be fine on modern desktops (USB implies desktop use)
|
||||
|
||||
## Running
|
||||
|
||||
Any static file server, e.g.: `python3 -m http.server -d src/public/ 8888`
|
||||
@@ -1,65 +0,0 @@
|
||||
# Device Identification
|
||||
|
||||
## Source: `.kobo/version`
|
||||
|
||||
The file contains a single line with 6 comma-separated fields:
|
||||
|
||||
```
|
||||
N4284B5215352,4.9.77,4.45.23646,4.9.77,4.9.77,00000000-0000-0000-0000-000000000390
|
||||
```
|
||||
|
||||
| Index | Value | Meaning |
|
||||
|-------|-------|---------|
|
||||
| 0 | `N4284B5215352` | Device serial number |
|
||||
| 1 | `4.9.77` | Unknown (kernel version?) |
|
||||
| 2 | `4.45.23646` | **Firmware version** (what kobopatch matches against) |
|
||||
| 3 | `4.9.77` | Unknown |
|
||||
| 4 | `4.9.77` | Unknown |
|
||||
| 5 | `00000000-0000-0000-0000-000000000390` | Hardware platform UUID |
|
||||
|
||||
## Serial Prefix → Model Mapping
|
||||
|
||||
The first 3-4 characters of the serial identify the device model.
|
||||
Source: https://help.kobo.com/hc/en-us/articles/360019676973
|
||||
|
||||
### Current eReaders
|
||||
|
||||
| Prefix | Model |
|
||||
|--------|-------|
|
||||
| N428 | Kobo Libra Colour |
|
||||
| N367 | Kobo Clara Colour |
|
||||
| N365 / 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 |
|
||||
|
||||
### Older eReaders
|
||||
|
||||
| Prefix | Model |
|
||||
|--------|-------|
|
||||
| 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 |
|
||||
| N204 | Kobo Aura HD |
|
||||
| N613 | Kobo Glo |
|
||||
| N705 | Kobo Mini |
|
||||
| N905 | Kobo Touch |
|
||||
| N416 | Kobo Original |
|
||||
| N647 / N47B | Kobo Wireless |
|
||||
|
||||
## Firmware
|
||||
|
||||
- The user provides their own firmware zip (not hosted by us for legal reasons)
|
||||
- Both Libra Colour and Clara BW/Colour currently use firmware `4.45.23646`
|
||||
- The firmware zip filename matches what `kobopatch.yaml` references: `kobo-update-4.45.23646.zip`
|
||||
- Different device families may have different firmware zips even for the same version number
|
||||
@@ -1,85 +0,0 @@
|
||||
# Kobopatch Patch Format
|
||||
|
||||
## kobopatch.yaml (Main Config)
|
||||
|
||||
```yaml
|
||||
version: 4.45.23646
|
||||
in: src/kobo-update-4.45.23646.zip
|
||||
out: out/KoboRoot.tgz
|
||||
log: out/log.txt
|
||||
patchFormat: kobopatch
|
||||
|
||||
patches:
|
||||
src/nickel.yaml: usr/local/Kobo/nickel
|
||||
src/nickel_custom.yaml: usr/local/Kobo/nickel
|
||||
src/libadobe.so.yaml: usr/local/Kobo/libadobe.so
|
||||
src/libnickel.so.1.0.0.yaml: usr/local/Kobo/libnickel.so.1.0.0
|
||||
src/librmsdk.so.1.0.0.yaml: usr/local/Kobo/librmsdk.so.1.0.0
|
||||
src/cloud_sync.yaml: usr/local/Kobo/libnickel.so.1.0.0
|
||||
|
||||
overrides:
|
||||
src/nickel.yaml:
|
||||
Patch Name Here: yes
|
||||
Another Patch: no
|
||||
src/libnickel.so.1.0.0.yaml:
|
||||
Some Other Patch: yes
|
||||
```
|
||||
|
||||
The `overrides` section is what the web UI generates. Everything else stays fixed.
|
||||
|
||||
## Patch YAML Files
|
||||
|
||||
Each file contains one or more patches as top-level YAML keys:
|
||||
|
||||
```yaml
|
||||
Patch Name:
|
||||
- Enabled: no
|
||||
- PatchGroup: Optional Group Name # patches in same group are mutually exclusive
|
||||
- Description: |
|
||||
Multi-line description text.
|
||||
Can span multiple lines.
|
||||
- <patch instructions...> # FindZlib, ReplaceBytes, etc. (opaque to UI)
|
||||
```
|
||||
|
||||
### Fields the UI cares about
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| Name | yes | Top-level YAML key |
|
||||
| Enabled | yes | `yes` or `no` - default state |
|
||||
| Description | no | Human-readable description (single line or multi-line `\|` block) |
|
||||
| PatchGroup | no | Mutual exclusion group - only one patch per group can be enabled |
|
||||
|
||||
### Patch Files and Their Targets
|
||||
|
||||
| File | Binary Target | Patch Count |
|
||||
|------|--------------|-------------|
|
||||
| nickel.yaml | nickel (main UI) | ~17 patches |
|
||||
| nickel_custom.yaml | nickel | ~2 patches |
|
||||
| libnickel.so.1.0.0.yaml | libnickel.so | ~50+ patches (largest) |
|
||||
| libadobe.so.yaml | libadobe.so | 1 patch |
|
||||
| librmsdk.so.1.0.0.yaml | librmsdk.so | ~10 patches |
|
||||
| cloud_sync.yaml | libnickel.so | 1 patch |
|
||||
|
||||
## PatchGroup Rules
|
||||
|
||||
Patches with the same `PatchGroup` value within a file are mutually exclusive.
|
||||
Only one can be enabled at a time. The UI should render these as radio buttons.
|
||||
|
||||
Example from libnickel.so.1.0.0.yaml:
|
||||
- "My 10 line spacing values" (PatchGroup: Line spacing values alternatives)
|
||||
- "My 24 line spacing values" (PatchGroup: Line spacing values alternatives)
|
||||
|
||||
## YAML Parsing Strategy
|
||||
|
||||
PHP doesn't have `yaml_parse` available on this system. Options:
|
||||
1. Use a simple line-by-line parser that extracts only the fields we need
|
||||
2. Install php-yaml extension
|
||||
3. Use a pure PHP YAML library (e.g., Symfony YAML component)
|
||||
|
||||
The patch YAML structure is regular enough for a targeted parser:
|
||||
- Top-level keys (no indentation, ending with `:`) are patch names
|
||||
- `- Enabled: yes/no` on the next level
|
||||
- `- Description: |` followed by indented text, or `- Description: single line`
|
||||
- `- PatchGroup: group name`
|
||||
- Everything else can be ignored
|
||||
67
wip/todo.md
67
wip/todo.md
@@ -1,67 +0,0 @@
|
||||
# TODO
|
||||
|
||||
## Done
|
||||
|
||||
- [x] Device detection proof of concept (File System Access API)
|
||||
- [x] Serial prefix → model mapping (verified against official Kobo help page)
|
||||
- [x] Architecture planning (fully client-side WASM, no backend)
|
||||
- [x] Installed Go via Homebrew (v1.26.1)
|
||||
- [x] Verified all kobopatch tests pass natively + WASM
|
||||
- [x] Created `kobopatch-wasm/` with setup.sh, build.sh, go.mod, main.go
|
||||
- [x] WASM wrapper compiles successfully (9.9MB)
|
||||
- [x] GitHub/Gitea CI workflow (build + test)
|
||||
- [x] Patch UI: loads patches from zip, parses YAML, renders toggles
|
||||
- [x] PatchGroup mutual exclusion (radio buttons)
|
||||
- [x] Full app flow: connect → detect → configure patches → upload firmware → build → write/download
|
||||
- [x] Patches served from `src/public/patches/` with `index.json` for version discovery
|
||||
- [x] JSZip for client-side zip extraction
|
||||
- [x] Renamed `src/frontend` → `src/public` (webroot)
|
||||
- [x] Moved `patches/` into `src/public/patches/`
|
||||
- [x] Manual mode fallback for non-Chromium browsers (model + firmware version dropdowns)
|
||||
- [x] Auto-download firmware from Kobo's servers (CORS confirmed working)
|
||||
- [x] Firmware download URLs hardcoded per device prefix and version
|
||||
- [x] Firmware download URL displayed in build step for user verification
|
||||
- [x] Fixed CRLF line ending bug in patch YAML parser
|
||||
- [x] Copy `kobopatch.wasm` + `wasm_exec.js` to `src/public/` as part of build
|
||||
- [x] Progress reporting: download % with MB, WASM log output in terminal window
|
||||
- [x] WASM `patchFirmware` accepts optional progress callback (4th arg)
|
||||
- [x] Verified patched binaries are byte-identical between native and WASM builds
|
||||
- [x] Web Worker for WASM patching (non-blocking UI, live progress)
|
||||
- [x] Cache-busting timestamp on WASM file (`?ts=` query string)
|
||||
- [x] Matched log output to native kobopatch (no debug spam from patchfile.Log)
|
||||
- [x] Step navigation: 3-step indicator (Device → Patches → Build) with back/forward
|
||||
- [x] Discrete steps with proper state management
|
||||
- [x] Scrollable patch list (50vh max height with border)
|
||||
- [x] Toggleable patch descriptions (hidden by default, `?` button)
|
||||
- [x] UI polish: renamed to "KoboPatch Web UI", styled firmware URL, patch count hint
|
||||
- [x] Disambiguated identical model names in dropdown (serial prefix suffix)
|
||||
|
||||
## To Test
|
||||
|
||||
- [ ] End-to-end test in browser with real Kobo device (auto mode)
|
||||
- [ ] Verify File System Access API write to `.kobo/KoboRoot.tgz`
|
||||
- [ ] Test manual mode flow across Firefox/Safari/Chrome
|
||||
|
||||
## Remaining Work
|
||||
|
||||
- [ ] Better error messages for common failures
|
||||
- [ ] Test with multiple firmware versions / patch zips
|
||||
|
||||
## Future / Polish
|
||||
|
||||
- [ ] Host as static site (GitHub Pages / Netlify)
|
||||
- [ ] NickelMenu install/uninstall support
|
||||
- [ ] Dark mode support
|
||||
|
||||
## Architecture Change Log
|
||||
|
||||
- **Switched from PHP backend to fully client-side WASM.**
|
||||
Reason: avoid storing Kobo firmware files on a server (legal risk).
|
||||
- **Patches served from zip files in `src/public/patches/`.**
|
||||
App scans `patches/index.json` to find compatible patch zips for the detected firmware.
|
||||
- **Firmware auto-downloaded from Kobo's CDN.**
|
||||
`ereaderfiles.kobo.com` serves `Access-Control-Allow-Origin: *`, so direct `fetch()` works.
|
||||
User no longer needs to provide firmware manually. URLs hardcoded in `kobo-device.js`.
|
||||
- **Web Worker for WASM.**
|
||||
Moves patching off the main thread so progress updates render live during the build.
|
||||
`patch-worker.js` loads `wasm_exec.js` + `kobopatch.wasm`, communicates via `postMessage`.
|
||||
@@ -1,48 +0,0 @@
|
||||
# WASM Feasibility — Confirmed
|
||||
|
||||
## Test Results
|
||||
|
||||
All kobopatch tests pass under both native and WASM targets.
|
||||
|
||||
### Native (`go test ./...`)
|
||||
```
|
||||
ok github.com/pgaskin/kobopatch/kobopatch 0.003s
|
||||
ok github.com/pgaskin/kobopatch/patchfile/kobopatch 0.003s
|
||||
ok github.com/pgaskin/kobopatch/patchfile/patch32lsb 0.002s
|
||||
ok github.com/pgaskin/kobopatch/patchlib 0.796s
|
||||
```
|
||||
|
||||
### WASM (`GOOS=js GOARCH=wasm`, run via Node.js)
|
||||
```
|
||||
ok github.com/pgaskin/kobopatch/kobopatch 0.158s
|
||||
ok github.com/pgaskin/kobopatch/patchfile/kobopatch 0.162s
|
||||
ok github.com/pgaskin/kobopatch/patchfile/patch32lsb 0.133s
|
||||
ok github.com/pgaskin/kobopatch/patchlib 9.755s
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- `patchlib` tests are ~12x slower under WASM (9.7s vs 0.8s) — expected overhead
|
||||
- All pure Go dependencies compile to WASM without issues
|
||||
- No CGO, no OS-specific syscalls in the core libraries
|
||||
- Go WASM executor: `$(go env GOROOT)/lib/wasm/go_js_wasm_exec`
|
||||
- Node.js v25.8.1 used for WASM test execution
|
||||
|
||||
## What Works in WASM
|
||||
|
||||
- Binary patching (`patchlib`)
|
||||
- ARM Thumb-2 instruction assembly (`patchlib/asm`)
|
||||
- ELF symbol table parsing (`patchlib/syms`)
|
||||
- YAML patch format parsing (`patchfile/kobopatch`)
|
||||
- Binary patch format parsing (`patchfile/patch32lsb`)
|
||||
- CSS parsing (`patchlib/css`)
|
||||
- Zlib compression/decompression
|
||||
- tar.gz reading/writing
|
||||
- ZIP extraction
|
||||
|
||||
## What Won't Work in WASM (and doesn't need to)
|
||||
|
||||
- `os.Open` / `os.Create` — replace with in-memory I/O
|
||||
- `os.Chdir` — not needed, use in-memory paths
|
||||
- `exec.Command` (lrelease for translations) — skip, rare use case
|
||||
- `ioutil.TempDir` — not needed with in-memory approach
|
||||
Reference in New Issue
Block a user