Repository navigation
v0.7.0 — English + Polish UI, AMD encoding, dependency sweep
Localization and AMD hardware encoding, on top of a three-month dependency and security sweep. Every UI string now ships in English and Polish behind a language picker that lives in both Settings and the onboarding Welcome step. AMD GPUs finally get a hardware path — the encoder had no AMF branch at all, so an AMD-only machine on the default hwAccel:'nvenc' emitted h264_nvenc and failed with "unknown encoder"; AMF now has real args and the GPU probe is wired into the orchestrator so a detected encoder actually gets used. The probe also stopped taking ffmpeg's word for it: every detected encoder is confirmed with a 1-frame test-encode before it is offered. Alongside that, thirty-odd dependency commits took pnpm audit from 1 critical / 15 high / 20 moderate to zero, and carried Electron to 43, React to 19 and Astro to 7.
Added
- UI localization (English + Polish) — every user-facing string across screens, chrome components and the 14-step onboarding is extracted into 14 namespaced en/pl locale files and routed through
useTranslation. i18next initialises synchronously before React mounts (the init side-effect is imported ahead ofAppin the renderer entry, so a module-scope lookup can never race it). Decorative kanji/romaji eyebrows are left inline. A self-containedLanguagePickerappears in Settings and on the onboarding Welcome step. uiLanguagesetting — nullable, OS-detect-then-sticky: unset means "follow the OS", and the first explicit pick freezes the choice.useUiLanguage/useUiLanguageSyncread a localStorage boot mirror for the first paint and treat electron-store as the durable source. The main-process queue-complete notification is localized off the persisted value, since it can't reach the renderer's i18next instance. Includes key-parity, plural and configured-instance tests, so a locale file that drifts out of sync fails CI.- AMF (AMD) H.264/HEVC encoding — new
H264AmfSettings/HevcAmfSettingsunion members andh264_amf/hevc_amfbranches inargs.ts, written in AMF's own rate-control vocabulary (-quality {speed|balanced|quality} -rc cqp -qp_i/-qp_p/-qp_b, pixel formatyuv420p,p010lefor 10-bit HEVC) and verified against ffmpeg'samfencsource rather than analogized from NVENC.{codec:'av1',hwAccel:'amf'}is intentionally non-representable — ffmpeg has noav1_amf. resolveHwAccel()and probe-driven encoder selection — a best-effortprobeGpudependency is wired into the orchestrator and soft-fails back to the requested settings when the probe is unavailable, mirroring the font-extractor pattern. This is what closes the AMD gap end to end: detection already worked, nothing consumed it.- Verified encoder detection —
verifyEncoderWorks()runs a minimal 1-frame lavfi→null test-encode per detected encoder and keeps it only on a clean exit; non-zero exit, spawn error and timeout (SIGTERM→SIGKILL) all drop it. An advertised-but-broken hardware encoder is now caught at probe time instead of at the user's first real job. Results are cached in electron-store behind a version key with a 7-day TTL, so the test runs once rather than on every launch. The IPC contract gains averifiedflag and a zodgpuProbeResultSchema. - Per-dialog last-used directory —
UserSettings.lastDialogDirsmaps aDialogDirKind(video/subtitle/save-file/output-folder) to the directory it was last opened at, so the media library, the subtitle folder and the output folder each keep separate memory. Folder picks remember the folder itself (reopening lands inside it); file picks rememberdirname(). A cancelled dialog remembers nothing, a directory that no longer resolves is dropped at read time rather than handed to Electron dead, and an explicitdefaultPathstill wins over the remembered one. - Test coverage — desktop 321 passing / 2 skipped (was 263), web 51 (was 28). Includes
dialog.test.ts(20 cases) for a channel that previously had none.
Changed
- Electron 42.9.3 → 43.4.1. Five of the six entries in Electron's 43.0 breaking-change list resolve to zero call sites here (three are Linux-only and the build targets only win/mac; the
NativeImage.toBitmap()sRGB change and thechrome.scriptingfallback-frame change have no matching usage). The sixth — dialogs defaulting to the Downloads folder instead of the OS's remembered location — is what the per-dialog directory memory above was written to absorb, and it landed first, so the major itself carries no known UX regression. - React 18 → 19 across
@moekoder/weband@moekoder/landing, bumped in lockstep so a single@types/reactmajor exists in the store. No source changes were needed — the renderer was already oncreateRootand every ref passes an initial argument. - Astro 6 → 7 on the landing app (Rust compiler, Vite 8), with
@astrojs/react5 → 6. Alsotailwind-merge2 → 3 (Tailwind v4 was already in place, so v2 was a latent class-group mismatch),sharp0.34 → 0.35,concurrently9 → 10,lint-staged16 → 17,@types/yauzl2 → 3, andelectron-builderpinned to 26.15.7. engines.noderaised to>=22.22.1— required by lint-staged 17's Node floor.- Icon set and OG card regenerated — the libvips 8.16 → 8.18.3 shift under sharp 0.35 moved a few pixels in
icon-16.png/icon-32.png, and satori 0.33.0 made HarfBuzz shaping unconditional, which changes text measurement. Both artifacts are tracked, so both were re-rendered from the current toolchain. - GPU-probe verifications run sequentially. They were concurrent, which is faster in principle; see Fixed.
Fixed
- Concurrent probe verification dropped working encoders — running the test-encodes through
Promise.allSettledlet them race on GPU-context initialisation and hit driver concurrent-session limits (NVENC / AMF / QSV), so a perfectly good encoder could be marked unavailable. They now run one at a time; a 1-frame test is under 100 ms, so total probe latency stays sub-second. - Polish word order in container labels —
"{c.name} {containerSuffix}"rendered "MP4 kontener". The two fragments are now a singlecontainerLabelkey with a{{name}}placeholder, so each locale places the noun itself ("{{name}} container"/"kontener {{name}}"). - Install-stage label was force-lowercased —
.toLowerCase()on a translated string applies locale-independent casing rules (wrong for languages like Turkish) and overrides the translator's intent. The stage label renders as authored. - Queue retry button's accessible name — the retry
aria-labelpulled from thecommonnamespace while its sibling buttons used queue-namespace keys; it now usesqueue.retryLabel. - Inconsistent Windows install-path casing in onboarding copy — the engine-gate destination and
dl.install.subdisagreed (moekodervsMoeKoder); both now matchAPP_NAME, which is the actual%LOCALAPPDATA%folder name. - Landing OG card spacing under satori 0.33.0 — HarfBuzz shaping stopped applying letter-spacing to collapsible whitespace, collapsing the word gaps in the eyebrow and meta rows into letter gaps, and re-measured the 萌コーダー run 18px narrower, sliding the decorative rule left into the kana. Whitespace runs are now NBSP (which is shaped as a real glyph and still takes letter-spacing) and the width delta is compensated with padding. The card differs from the 0.26.0 baseline by 5 pixels out of 756,000, all antialiasing.
Security
pnpm audit on the full graph went from 1 critical / 15 high / 20 moderate / 1 low to 0 vulnerabilities, with no ignoreCves suppressions left in place.
electron-updater6.8.3 → 6.8.9 — the highest-priority fix in the sweep, since it runs on end-user machines. Pullsbuilder-util-runtime9.7.0, closing GHSA-p2f4-r6v6-j797 (a cross-origin redirect leaksAuthorization/PRIVATE-TOKENheaders) plus a js-yaml quadratic-DoS advisory.- Electron 42.5.0 → 42.8.1 ahead of the 43 bump closed CVE-2026-70606 (protocol-handler session-isolation bypass). No custom protocol handlers exist here, so the surface wasn't reachable either way.
pnpm.overridesfloors raised and bounded —fast-uri,shell-quote,form-data,brace-expansion,undici,tmp,svgo,yaml,js-yaml,nanoid,vite,@babel/core,devalue,ip-address. Several floors were unbounded above and pnpm had resolved them past what their consumers declare (nanoidto an ESM-only v6 behind postcss's^3.3.17,@babel/coreto v8 behind@vitejs/plugin-react's^7.29.0); each is now capped at the reviewed major.- A stale
electron-builder-squirrel-windows@26.8.1chain was collapsed — it dragged in its own oldapp-builder-libwith vulnerabletar,js-yaml,fast-uriandbuilder-util-runtime, duplicating packages the top-level resolution had already fixed. Because it andapp-builder-libpin each other as exact mutual peers, pnpm kept reusing the stale snapshot; adding it as an explicit devDependency gave the resolver a real edge and cleared the criticaltaradvisory along with several highs. - Dev-path fixes —
vite8.0.13 → 8.1.5 (CVE-2026-53571,server.fs.denybypass on Windows alternate paths),esbuild0.28.0 → 0.28.1 (GHSA-g7r4-m6w7-qqqr, arbitrary file read via the dev server),wait-on9.0.10 → 9.1.0 (clears all 8 axios advisories), andastro7.0.4 → 7.1.3 (3 moderates, cascading svgo and postcss fixes). - Full audit trail:
docs/deps/2026-08-06-audit-standard-2026-08.md,docs/deps/2026-08-20-audit-standard.md,docs/migrate/2026-08-20-electron-43.md,docs/migrate/2026-08-20-satori-0.33.0.md.
Known Limitations
- The AMF encoder path has not been validated on real AMD hardware. There is no AMD GPU in CI, so
args.test.tsasserts arg shape only — the flags were derived from ffmpeg'samfencsource, not from a successful encode. If you have an AMD card, a 1-frame encode confirming ffmpeg accepts these flags is the single most useful bug report you can file against this release. - Multiple subtitle tracks in a single mux is still out of scope — one external
.assper mux. It was pencilled in for v0.7 and did not make it. - Extraction is still copy-or-transcode between text formats only; image subtitles (PGS / VOBSUB) can't be exported.
- Same as v0.5.x and v0.6.0: binaries are unsigned, the auto-updater is Windows-only, and there's no Linux build yet. Embedded-font extraction still runs per-job (no
<userData>/fonts/<sourceHash>/cache).