Repository navigation
Releases: HatmanStack/local-nano
Release list
v0.4.9
[0.4.9] - 2026-08-30
Tells the user why nothing happened when the panel cannot open in the current tab. 0.4.8 made the toggle inject the content script into tabs that lack it, but Chrome refuses to script its own surfaces — chrome:// pages, the Web Store, the PDF viewer, a blank new tab — and that refusal was swallowed. Clicking the icon on a fresh empty tab therefore still did nothing at all, with no way to tell that opening a site first would fix it.
Added
- The toolbar icon reports a tab the panel cannot run in. When Chrome refuses the injection, the icon gets a red
!badge and its tooltip reads "Local Nano only runs on a web page — open a site in this tab, then click again". Both are scoped to that one tab, so other tabs are unaffected, and both clear as soon as the panel does open there. This needs no new permission — it uses theactionAPI the toolbar button already relies on.
Changed
- The restricted marker is cleared exactly, not on every toggle. Clearing reads the tab's effective tooltip and only restores it when this extension had actually flagged that tab. Resetting the tooltip on every successful toggle would pin a tab-scoped copy of the current title to each tab, and those copies would then misreport the keyboard binding once it changed — the tooltip is a documented diagnostic for exactly that, so it must not go stale.
v0.4.8
[0.4.8] - 2026-08-30
Fixes the panel not opening at all after a fresh install or an auto-update. A declared content script is only injected on navigation, so every tab that was already open when the extension was installed or updated had no listener in it. The toolbar click's chrome.tabs.sendMessage failed with "Could not establish connection. Receiving end does not exist.", that error was deliberately swallowed to keep a mid-load race quiet, and so the click did nothing whatsoever — no panel, no error, no hint that reloading the tab would fix it. Chrome updates extensions in the background, which meant a working install could go silently dead in every open tab without the user touching anything.
Fixed
- The toggle now heals a tab that has no content script. When a toggle send fails because nothing is listening, the service worker injects
dist/content.jsinto that tab and retries, so the panel opens on the first click instead of being dropped. Host access comes fromactiveTab, granted only for the toolbar click or keyboard command that triggered it, so no broad host permission is needed. A tab that can never be scripted (chrome://, the Web Store, the PDF viewer) still fails quietly — there is no panel to open there. Unrelated send failures are left alone rather than being retried blindly. - The build can no longer ship a mismatched ONNX Runtime.
build.mjscopied ORT's.wasmbinaries from the top-levelonnxruntime-webdevDependency while esbuild bundled ORT's JS glue from the copy nested under@huggingface/transformers. Dependabot had walked that pin from1.26.0-dev.20260416to1.26.0to1.27.0, so a cleannpm ciproduced a package whose wasm and glue were different builds — the wasm backend cannot initialize on that pairing, and the model would silently never load. The build now resolves those artifacts through Transformers.js's own resolution root, so the pair cannot split however npm hoists the tree, andtests/ort-version-match.test.tsfails the build if the pin ever drifts again. - Model downloads no longer rely on Hugging Face's CDN being CORS-permissive.
huggingface.conow redirects weight files to its Xet CDN onhf.co(e.g.us.aws.cdn.hf.co), which the declared host permissions did not cover — the retiredcdn-lfs.huggingface.coentry was still there in its place.https://*.hf.co/*is now declared and the dead entry dropped.
Added
activeTabandscriptingpermissions. Used only to inject the extension's own bundled content script, only into the tab you just toggled, and only when that tab has no panel in it. No remote or generated code is ever injected.- A re-injection guard in the content script. Running
dist/content.jstwice in one tab — possible now that the service worker can inject it — builds only one panel instead of stacking a second on top of the first.
v0.4.7
[0.4.7] - 2026-06-17
Defaults to a small model that loads on the common case, makes gemma an opt-in choice, and makes any load interruptible. The 0.4.6 RAM check could not catch a device with plenty of system RAM but a small GPU memory budget (16 GB RAM, hardware WebGPU, maxBufferSize 4096 MiB) where gemma-4-E2B still dies with VK_ERROR_OUT_OF_DEVICE_MEMORY. No WebGPU adapter limit predicts that (maxBufferSize is a per-buffer ceiling, not usable VRAM; deviceMemory is system RAM), so rather than speculatively load the 2B model and hope, the auto-default is now a small model and gemma is selected explicitly.
Changed
- Small model by default; gemma is opt-in. With no stored preference, a real WebGPU device auto-loads
Qwen3-0.6B(~0.5 GB, confirmed loading on the dev integrated GPU) and a WASM/software-fallback device auto-loadsQwen2.5-0.5B; the ~2Bgemma-4-E2Bis never loaded speculatively. An explicit pick in the settings picker is always honored, and the picker marks the small model as the default and labels gemma as the larger, strong-GPU option. Choosing gemma shows an up-front advisory that it may not fit an integrated GPU — shown only until gemma has loaded successfully once on this device, so a working setup is not nagged. - Dropped the
webgpu/fp16gemma rung. The gemma ladder is nowwebgpu/q4f16 → webgpu/q8 → wasm/q8. fp16 was ~2x q8, so on any device where q8 failed for memory it could not succeed either; removing it makes an opt-in gemma that cannot fit fail fast instead of churning.
Added
- Stop button on the loading bubble. Any in-flight model load can be interrupted: Stop force-recreates the offscreen document (freeing the partial GPU allocation) and cancels the walk cleanly — no tier recorded known-bad, no advance, no terminal bubble — returning the panel to idle and unlocking the picker. It stops a model LOAD only; a live generation is still stopped via the action button (ADR-P7).
- A model switch no longer deadlocks behind a hung load. A load that hangs (e.g. an opt-in gemma whose GPU OOM surfaces as an uncaptured device error, never a rejection) previously blocked any switch forever; a switch now supersedes the hung load and proceeds to the chosen model.
v0.4.5
[0.4.5] - 2026-06-08
Reverts the reactive device-loss machinery added in 0.4.2 and 0.4.3 back to the simpler, proven 0.4.1 load and stream path, and arms idle resource release at model load. (0.4.4 was an internal rollback build and was never published.)
Changed
- Reverted the 0.4.2 and 0.4.3 device-loss layers. Removed the
navigator.gpucapture wrapper (gpu-capture.ts), theSESSION_POISONED-driven offscreen recreate, the panel/offscreen pin ports (panel-pin.ts), and the zero-chunk poisoned-stream detection (stream-finalize.ts); the load and stream path returns to 0.4.1 behavior. That machinery added offscreen-recreate churn and a GPU-device wrapper that ran before every load, and did not address the failure it was chasing — a device or tab low on RAM cannot allocate the multi-GB model regardless of session health, which is the actual cause of the load failures observed on constrained ChromeOS hardware. Device-loss handling will be reworked separately. The reverted code is preserved in history at tagv0.4.3.
Fixed
- Idle release now arms on model load, not only after a generation. The panel auto-warms the model on open, so a user who loaded the model and walked away without sending a message previously left a multi-GB session resident with no release scheduled (
touchIdlefired only at generation start and end). The inactivity countdown now starts at load completion, so an unused model is reclaimed after the configured timeout (default 15 min, configurable in settings) instead of staying resident until the next interaction.
v0.4.3
[0.4.3] - 2026-06-04
Roots out the WebGPU device-loss failure that 0.4.2 caught reactively. Captures the GPUDevice handle through a transparent navigator.gpu monkey-patch in the offscreen document, listens for device.lost, marks the session poisoned, and rebuilds lazily on the next ensure. Pins the offscreen document open while any panel is visible so the 30-second no-port reap cannot close it across a tab switch. Promotes the offscreen's zero-chunk stream completion from a silent ok:true to a typed terminal failure so the existing reactive recovery runs.
Fixed
- Layer A: GPUDevice.lost listener. The offscreen installs a transparent navigator.gpu monkey-patch at module top, captures the GPUDevice the polyfill flows through to, and attaches a .lost handler that marks the offscreen session poisoned and pushes SESSION_POISONED to the service worker. The next ENSURE_OFFSCREEN_REQUEST recreates the offscreen document (when not busy, per ADR-P7) before the user's send is dispatched.
- Layer B: SW-pinned offscreen port while panels are open. The content script holds a long-lived port to the SW while the panel is visible; the SW holds a long-lived port to the offscreen while at least one panel-pin port is open. The pin port's existence prevents Chrome's 30-second no-port reap from closing the offscreen document across a tab switch.
- Layer C: authoritative zero-chunk stream detection. A natural stream completion with zero tokens now surfaces as STREAM_DONE { ok: false, error: 'no tokens emitted; session may be poisoned' }. classifyFailure classes that as terminal so the existing reactive recovery in src/session.ts re-warms via the serialized primitive and retries the prompt once.
- Diagnostic. The Copy diagnostic gains a new deviceLostAt field so future bug reports carry the ISO timestamp of the most recent device.lost event observed in the offscreen.
v0.4.0
[0.4.0] - 2026-05-26
Lets you choose which on-device model to run, and stops holding the model in memory when you're not using it. A new gear/settings popover offers a curated model catalog — pick one and click Load to switch — and the offscreen model is now released after a configurable idle timeout and re-warmed on return, reclaiming the multi-GB WebGPU allocation that previously lived for the entire browser session.
Added
- User-selectable model picker. A gear button in the panel header opens a settings popover listing a curated model catalog. Selection is select-then-Load: clicking a row only marks the choice; the Load button commits it, force-recreating the offscreen document and re-walking the fallback ladder headed by the chosen model. The choice persists in
chrome.storage.local(keylocal-nano:model-pref:v1) and — unlike the per-device capability record — is NOT invalidated on an extension-version bump, since a model preference is a user choice, not a device fact. No preference set means today's capability-based auto-pick (onnx-community/gemma-4-E2B-it-ONNX). The picker chooses the model only; dtype/device stay automatic (the existing load-time ladder steps them capable→lean), and the model rows are keyboard-operable. - Curated model catalog. Two live entries — the
gemma-4-E2B-itdefault (webgpu/q4f16) and the smalleronnx-community/Qwen2.5-0.5B-Instruct(wasm/q8, the CPU/WASM-only "smallest that answers") — span a both-directions size spectrum.Qwen3.5-0.8Band a larger-model slot ship gated OFF (QWEN3_08B_ENABLED,LARGER_MODEL_ENABLED, bothfalse), mirroring the existingSMALLER_MODEL_ENABLEDgate, pending manual WebGPU smoke-vetting; only combinationsdocs/models.mdconfirms working are ever live. - Idle resource release. The offscreen model (a multi-GB WebGPU session) is now freed after a period of inactivity instead of being held for the whole browser session. A
chrome.alarmstimer — measured from the last generation, so it survives the MV3 service-worker eviction a plain timer would not — fires the service worker, which verifies no generation is in flight (reschedules if one is) and then closes the entire offscreen document, a hard release that actually reclaims VRAM. The offscreen document never closes itself, keeping the worker's readiness flag honest. The next use re-warms: recovery works from the send path (not just panel-open), bounded to a single retry so a dead device cannot spin. The timeout is configurable in the popover — 5 / 15 / 60 min or Never (default 15) — and "Never" disables release for users who would rather keep the model warm.
Notes
- A model switch and an idle re-warm share ONE serialized teardown+re-warm primitive under a single lock, so two model loads can never overlap (the v0.2.0
VK_ERROR_OUT_OF_DEVICE_MEMORYcame from concurrent sessions). A failed switch reverts the stored preference so the next session does not boot into a model that never loaded. - All inference still runs on-device. The only network access remains the one-time model-weights download from Hugging Face; switching to a catalog model not yet cached triggers a one-time download for that model.
- The gated catalog entries (
Qwen3.5-0.8B, the larger-model slot) stay off until a manual WebGPU smoke pass confirms each tier loads and answers — CI cannot exercise WebGPU, so on-hardware behavior (real VRAM reclamation, release-then-return) is gated on the manual smoke matrix.
v0.3.0
[0.3.0] - 2026-05-24
Hardens the model-load path for a wide Chrome Web Store audience. When the model can't load on a device, it now self-heals through a dtype/device fallback ladder, picks a model by device capability, shows real download progress, and — when nothing works — fails with a clear, actionable message and a copyable diagnostic instead of a silent dead panel. Also renames the extension to Local Nano to match its branding.
Added
- Automatic dtype/device fallback ladder. On a model-LOAD failure the panel walks
q4f16 → q8 → fp16on WebGPU, thenq8on WASM, recreating the offscreen document between rungs so a crashed or GPU-poisoned document never blocks the next attempt and two loads never overlap. The resolved tier is persisted per device (chrome.storage.local, keylocal-nano:capability:v1) so a later cold start skips straight to a known-good tier and avoids a deterministically crashing one. Auto-fallback applies only at load time; a mid-stream/runtime error never auto-rebuilds. - Capability-based model selection. The WebGPU adapter (max buffer size / software-fallback) is classified at first load so a smaller model can be chosen for weak devices. The smaller-model rung ships gated off (
SMALLER_MODEL_ENABLED = false) pending manual WebGPU vetting; the live default remainsonnx-community/gemma-4-E2B-it-ONNX. - Phased first-run download progress. "Downloading model NN%" driven by the polyfill's real
downloadprogress, then an indeterminate "Loading into GPU…" phase, relayed from the offscreen document to the panel. - Graceful terminal failure + manual recovery. A model-load crash (the offscreen document can hard-crash, not just throw) is detected client-side and surfaced as an actionable in-panel message with a manual Retry that force-recreates the offscreen document — never the old churny auto-rebuild. A weights-download/network failure gets a distinct, retryable "check your connection" message.
- Copy-only diagnostic. An always-available, copyable diagnostic (device, adapter limits, chosen model + active dtype tier, the ladder path taken, error class, extension/Chrome version). Nothing leaves the device — copy-to-clipboard only.
Changed
- Renamed to "Local Nano" in the manifest and the in-panel window header, matching the repo and hero-art branding.
Fixed
- 4xx HTTP statuses from the weights fetch (e.g. 403 gated, 404 bad id) are permanent for the tier and now advance the ladder rather than looping on a misleading "check your connection"; 5xx remains a retryable network condition.
- The Copy-diagnostic control lives in the panel header instead of overlaying the close button, and clicking it after the extension context is invalidated (reload or auto-update) no longer throws —
chrome.runtime.getManifest()is read defensively.
Notes
onnxruntime-webstays pinned to the dev build1.26.0-dev.20260416-b7804b056c— the exact build@huggingface/transformers@4.2.0depends on, so they dedupe to one copy. Stable1.26.0was evaluated and rejected for this release: it did not fix the q4 SIGILL, and pinning it would de-duplicate the runtime (transformers keeps its dev pin) and mismatch the bundleddist/ort/wasm against the ORT JS transformers actually loads. The pin is intentional; revisit only alongside a transformers upgrade plus a full WebGPU smoke pass.- All inference still runs on-device; the only network access is the one-time model-weights download from Hugging Face.
v0.2.4
[0.2.4] - 2026-05-23
Polishes the selection-rewrite UX and prepares the first Chrome Web Store submission. An earlier automatic GPU-OOM "guard" (zero-chunk-as-failure + session teardown + rebuild-and-retry) was removed: on a memory-constrained adapter the session churn tended to make out-of-memory worse, not better. The session now loads once and is never auto-destroyed; GPU errors surface plainly instead.
Added
- Model preloads when the panel opens. Previously the first message ate the 30–90s WebGPU upload. Now opening the panel (Ctrl/Cmd+Shift+K) kicks off the load in the background with a live "Loading…" indicator (bouncing dots + an elapsed counter); the input stays editable while the Send button is gated until the model is ready. The preload is best-effort — if it fails it degrades quietly to lazy loading on the first message rather than raising an alarm, and the load is never killed on a timer.
- Proactive "Clear conversation" warning. The panel tracks how large the conversation has grown and warns before the next turn is likely to exhaust VRAM, with a one-click button to reset the session. The warning threshold is derived per-session from the actual WebGPU adapter (or
device/ an optionalhistoryTokenWarnThresholdin.env.json). - Undo / Accept on rewrites. A finished rewrite now offers both Undo (restore the original text) and Accept (commit and reset selection state for the next edit), instead of Undo alone.
- Brand icon (
icons/) and annpm run packagestep that zips a Web-Store-ready upload (manifest +dist/+ icons).
Changed
- Reduced requested permissions to
["storage", "offscreen"]. RemovedactiveTabandscripting(the declarative<all_urls>content script already grants the page access the extension uses; nothing calls thechrome.scriptingAPI) and thecdn.jsdelivr.nethost permission (ONNX Runtime WASM loads from the bundleddist/ort/, never jsdelivr). - Default
dtypechanged fromq4toq4f16in.env.example.json. See the Notes below — theq4ONNX kernel hits an illegal-instruction crash in ONNX Runtime Web's WASM SIMD path on some Chrome/Dawn builds;q4f16routes through different kernels and avoids it, with slightly better quality (fp16 activations, same 4-bit weights).
Removed
- The automatic GPU-OOM guard: zero-chunk streams are no longer treated as failures, the offscreen session is no longer torn down on a stream error, and the chat/rewrite paths no longer rebuild-and-retry on a device-loss-shaped error. A failed turn now shows its error plainly. Manual recovery is still available via the "Clear conversation" button.
Fixed
- Highlighting text then clicking into the chat input no longer drops the selection — focus-shift
selectionchangeevents are ignored so the captured snapshot survives.
Notes
- All inference still runs on-device; the only network access remains the one-time model-weights download from Hugging Face. No new permissions touch the network beyond what 0.2.2 already declared (and jsdelivr was dropped).
- See docs/chrome-web-store.md for the submission checklist and permission justifications.
q4kernel crash (debugging note). A model load withdtype: "q4"was crashing the offscreen document with a WebAssemblySIGILL(illegal instruction) inside ONNX Runtime Web's quantized-matmul SIMD kernel — reproducible in a bare webpage with no extension, so it's upstream of this project (a V8/Dawn codegen issue on the affected device, not our code, the model, or the GPU; raw WebGPU buffer allocation and dependency integrity both checked out).q4f16uses different kernels and sidesteps the bad instruction. Ifq4f16ever regresses similarly,fp16andq8are the next dtypes to try.
v0.2.2
[0.2.2] - 2026-05-19
Moves the on-device LanguageModel session out of the content script and into a hidden offscreen document, so the model loads once and is shared across tabs/pages instead of reloading WebGPU on every navigation. Per-URL chat history continues to live in chrome.storage.local; the polyfill session is the shared resource.
Adds explicit recovery for WebGPU device loss. When the offscreen doc loses its GPU device — typical after a tab/window switch — the polyfill swallows the underlying ONNX Runtime Web error and closes the stream with zero chunks. The offscreen layer now treats zero-chunk results as failure and tears the session down; the chat layer catches the failure, re-seeds a fresh session with the persisted conversation, and retries the prompt once. The user sees "GPU device lost — restoring session…" instead of an empty bubble and the conversation resumes in place.
Added
offscreen.html/offscreen.ts— hidden offscreen document that hosts the polyfill session and streams tokens over achrome.runtime.Port.src/offscreen/protocol.ts— wire types forENSURE_OFFSCREEN_*,STREAM_*, andREBUILD_SESSION_*messages.src/offscreen/client.tsandsrc/offscreen/stream-client.ts— content-script-facing client and the context-agnostic port-streaming helper it shares with the service worker.src/background/offscreen.ts— service-worker side of the offscreen lifecycle (idempotentensureOffscreen, dedupe of concurrent calls).- One-shot device-loss retry path in
src/session.ts, including a transient "restoring session" hint.
Changed
manifest.json— adds theoffscreenpermission and acontent_security_policyallowingwasm-unsafe-eval(required for ONNX Runtime Web's WebAssembly).manifest.jsonversion bumped 0.1.1 → 0.2.2 to catch up withpackage.json(the 0.2.1 revert bumped one but not the other).src/session.ts— drops its direct polyfill imports; streams through the offscreen client.
Notes
- Same model, same UX surface as 0.2.1; the change is architectural. No new commands, no new menus.
- Cross-URL conversation continuity is still scoped to the offscreen session's lifetime — the polyfill session is shared, but UI history switches per URL.
v0.2.1
[0.2.1] - 2026-05-18
Reverts the entire v0.2.0 DOM-aware-actions release. The feature was too ambitious for the in-browser GPU memory budget: spinning up a second LanguageModel session for every right-click action on top of the long-lived chat session pushed WebGPU into VK_ERROR_OUT_OF_DEVICE_MEMORY cascades during normal use. The tree is reset to the v0.1.1 baseline (chat-only, one model session per tab).
The rewrite-selection capability is on the roadmap and will return in a future iteration designed against the memory budget — likely by reusing the chat session for transforms rather than allocating a parallel one, and by capping max_new_tokens per-call.
Removed
- All v0.2.0 surface area:
chrome.contextMenusregistration,ask_about_selection/rewrite_selection/translate_selectionkeyboard commands,runTransformephemeral sessions, the Preview component, the dom-apply layer, the selection-capture layer, anddocs/dom-actions.md. src/transform-prompts.ts,src/transform.ts,src/dom-actions.ts,src/dom-apply.ts,src/ui/preview.ts,src/background/menus.ts, andsrc/heavy.ts(the heavy-loader factored out for v0.2; the lazy import moves back inline intosrc/session.ts).SessionHandlesurface oninitSession— the chat-only build doesn't needopenPanel/prefillAndSend/mountPreview.
Notes
- The v0.2.0 GitHub release and tag remain published. Anyone who pinned v0.2.0 still has it; this release just rolls
mainback to a stable baseline. package.jsonversion bumped 0.1.1 → 0.2.1 to match the changelog; semver-wise this is a backwards-incompatible removal under a 0.x version, which is the intended signal.