Skip to content

v1.1.0

Latest

Choose a tag to compare

@owenpkent owenpkent released this 19 May 19:42

Installing on Windows: about the SmartScreen prompt.
On first launch you may see a blue "Windows protected your PC" screen. This is normal for newer apps and does not mean anything is wrong. Alpha-OSK is digitally signed by OK Studio Inc. Click More info and you will see that publisher name, then click Run anyway to install. The prompt stops appearing on its own as more people install. (Microsoft removed the SmartScreen fast-pass for code-signed apps in 2024, so reputation now builds from download volume, not from the certificate.)


[1.1.0] (2026-05-19)

Headlines: a new "Data Backup" feature (export your model, lifetime stats, and imported vocabulary packs to a portable .zip; import on a new machine to restore everything in place), pill-click learning now passes through the same 3-sighting candidate gate as free-typed words (a single accidental click can no longer permanently change the model), a clickwrap MIT license page in the Windows installer, live download / install progress in the auto-updater popup and the relauncher splash, and a documentation overhaul (segmented onboarding README, screenshot gallery, and white-paper figures / Data Backup section / macOS section).

Added

  • README onboarding rewrite. Plain-language tagline replaces the engine-acronym lead. The dark-theme keyboard screenshot moved to a hero position above the fold. New three-column "Pick your path" card routes visitors by audience (end users → Install + First launch, developers → Architecture + CLAUDE.md, reviewers → white paper + Status). New "First launch" walkthrough (six numbered steps from installer to first word to settings tour) and a ten-question FAQ covering the actual common stumbles (Slack/Discord/IDE keystroke issues, move/resize, pause learning, theme, opacity, data location, telemetry, vocab packs, emoji). Install restructured into a Platform / File / Notes table. Screenshot gallery moved below First launch.
  • White-paper refresh. Five inline figures sourced from the live app (full keyboard, Appearance panel, Data & Privacy panel, analytics dashboard, Word Cloud). New §5.5 Data backup (export / import) covering archive contents, replace-not-merge import semantics, the rescue archive, and the allow-list extraction validation. Telemetry renumbered §5.5 → §5.6 and Auto-update threat model §5.6 → §5.7 with all inline cross-references updated. New §7.5 macOS (in progress). Stale test count 450+ bumped to 640+. Top-of-doc version line clarified as "Document revision" distinct from app version. 78 pre-existing em dashes stripped per the global writing-style preference (paired dashes to parentheticals, bullet term-definitions to colons, others to sentence breaks).
  • assets/screenshots/ new directory with six images sourced from the live app: dark theme keyboard, Amethyst theme keyboard, Appearance settings panel, Data & Privacy settings panel, Word Cloud tab, Dashboard tab.
  • Data export / import (back up your model, move it between machines). New "Data Backup" section in Settings → Data & Privacy with Export Data… and Import Data… buttons. Export bundles the prediction model (ngram_model.json + ppm_model.json), lifetime analytics (analytics.json), and every imported vocabulary pack into a single .zip archive with a manifest (schema version, app version, ISO-8601 UTC timestamp, file list, pack ids). Import reads the archive, shows a preview card (app version + export date + file/pack counts), and on confirmation replaces the user's current data, after first writing a timestamped rescue export of the existing state to <config_dir>/exports/rescue-<ts>.zip so the user can roll back by importing that file. The predictor and lifetime analytics live-reload from disk after import (no restart needed); enabled-pack state resets so the user re-enables packs on the new machine. telemetry.json is deliberately excluded from exports: copying the anon_id across machines would link contributions, which the telemetry consent docs explicitly promise not to do, so a fresh anon_id is generated on the new machine when telemetry is re-enabled. Settings (theme, layout, toggles, window size) are not exported either; they live in the Qt settings layer (Windows registry / Linux config) and are quick to reconfigure manually, while the irreplaceable bit is the model. Security hardening on import: allow-list extraction (a hand-edited archive can't smuggle telemetry.json or arbitrary files past the extractor), zip-slip rejection (.. components, absolute paths, drive prefixes, backslashes), per-file (75 MB) / total uncompressed (500 MB) / archive-on-disk (200 MB) size caps that trip on file_size metadata before any bytes hit disk, and forward-compatibility rejection when the manifest's schema_version exceeds what this build supports ("upgrade Alpha-OSK first" rather than half-applied state). Atomic-rename writes through .importing tempfiles so a partial copy can't corrupt the existing file. New module src/data_export.py, new bridge slots (exportUserData, inspectUserExport, importUserData, getDefaultExportDir, getSuggestedExportName), new HybridPredictor.reload_from_disk() and TypingAnalytics.reload_from_disk(). 20 regression tests in tests/test_data_export.py cover the round-trip, every security gate, and the telemetry-exclusion invariant. Full design in CLAUDE.md § Data Backup (Export / Import).
  • Pill-click learning now passes through the 3-sighting candidate gate. Clicking a prediction pill for a word that the engine generated but the user has never actually typed (a fuzzy-recogniser completion of a typo, a PPM character-level extrapolation, a vocab-pack word the user hadn't fired before) used to inject the word into user_vocab permanently with weight 5 on the very first click, so a single accidental click could permanently change the model. Symptom: words showed up in the Model Visualization dashboard after one keypress when the user expected they wouldn't be added until "really" learned. New NgramPredictor.learn_from_pill_click(word) mirrors the gate already used by free-typing in learn(): known words (already in the Google 10K base dict or user_vocab) get the immediate +5 boost as before, but unknown words go through _candidate_counts and only promote into user_vocab after three clicks (landing with cumulative weight 3 × 5 = 15). HybridPredictor.learn_from_selection routes through this; trailing bigram / trigram reinforcement still fires immediately on each click since the context edge was validated by the click and a bigram pointing into a not-yet-promoted unigram surfaces the word only in that specific context, which is exactly the loop that drives "click more times to promote." Right-click → Show more and vocab-pack import deliberately bypass the gate (those are explicit user-boost actions, not implicit signals; gating them would break unprefer's rollback math). Plus: candidates now carry a _candidate_last_seen timestamp, and _sweep_stale_candidates (called from _apply_decay) drops entries older than 30 days so an accidental sighting doesn't sit in the pool indefinitely waiting for the multiplicative decay to drain it. Timestamps persist across save/load; older save files without the field have entries backfilled on the next sweep rather than instantly expired. Coverage in tests/test_ngram_predictor.py::TestLearnFromPillClick and TestStaleCandidateSweep, plus tests/test_hybrid_predictor.py::test_learn_from_selection_gates_unknown_word / _promotes_after_threshold.
  • Clickwrap license page in the Windows installer. New LICENSE.rtf (MIT) shipped under build/windows/ and wired into the generated NSIS script as MUI_PAGE_LICENSE between WELCOME and DIRECTORY with MUI_LICENSEPAGE_CHECKBOX enabled, so the Next button stays disabled until the user ticks "I have read and accept the terms of the license agreement." Customised MUI_LICENSEPAGE_TEXT_TOP clarifies the action ("You must accept them to install Alpha-OSK"). The repo-root LICENSE (already MIT) is unchanged; the RTF is a formatted copy with bold headers, an accessibility-tool disclaimer, and a privacy / auto-update summary, since the NSIS license control renders RTF better than plain text and the recent NSIS high-contrast-mode fix targets it. Silent install (/S, used by the auto-updater) bypasses pages entirely so the EULA never blocks an upgrade. First-install gets the clickwrap, every subsequent auto-update flows through silently.
  • Streaming download progress for the auto-updater. KeyboardBridge.updateDownloadProgress(bytes, total) is emitted from the install worker thread as the signed installer downloads. The popup that appears when the user clicks the title-bar ↓ icon now shows live MB / total MB / percentage and a determinate ProgressBar; when the server omits Content-Length it falls back to a labelled byte count + indeterminate bar. Emits are throttled at 256 KB to keep the queued-signal connection out of the hot path on a fast link (an 85 MB installer would otherwise fire ~1300 progress events). Closes the silent-download gap that used to read as "did the Install button do anything?".
  • Indeterminate progress bar on the post-install relauncher splash. The "Updating Alpha-OSK" window that bridges the gap between OSK-vanishes and OSK-relaunches now shows a moving marquee bar under the phase-aware status text. NSIS silent install suppresses its own UI, so we cannot show a real percentage during the install phase; the constant motion is the difference between "is this stuck?" and "still working" and is the same pattern every commercial installer uses. The bar settles full on the Done phase and empty on a failure phase so the eye can tell the work has stopped before the splash dwell expires. Splash widget grew 30 px in height to fit; layout, theming, and the existing ✕ dismiss behaviour are unchanged.

Fixed

  • Worker ws transitive bumped to 8.20.1 to clear GHSA-58qx-3vcg-4xpx. OSV-Scanner flagged a medium-severity (CVSS 4.4) DoS vector in ws@8.18.0, reachable through the wrangler@4.92.0 → miniflare@4.20260515.0 → ws dev-only chain. Pinned away via an overrides: { "ws": "^8.20.1" } block in backend/cf-worker/package.json and a regenerated package-lock.json. Dev-only (the deployed worker on Cloudflare's edge runtime does not include miniflare), but the merge-gating OSV-Scanner CI job from 1.0.18 was failing every push until the bump landed. Local osv-scanner --lockfile=backend/cf-worker/package-lock.json --lockfile=requirements-dev.txt now reports zero vulnerabilities.