The first code editor built for the AI era.
C++ + Rust · ~12 MB bare native executable · Zero Electron · 238 file types · 82 language lexers · Local AI formatters · MCP server
New in v0.1.119: notepatra-mcp — connect Claude Desktop, Claude Code, OpenAI Codex, and any MCP client to the running editor. 35 tools (Git, read-only SQL, Noter notes); every write human-approved in-window; nothing leaves your machine.
Website ·
Download ·
MCP ·
Features ·
The Story ·
Install ·
Plugins ·
AI ·
Contributing ·
Security ·
Changelog
The Story
I'm Prateek Singh. A developer who spent years on Linux wanting a small, fast, free native code editor that could fix things in seconds — broken JSON, messy SQL, tangled HTML — and finding only Wine hacks or bloated Electron editors eating 500 MB of RAM to show a text file.
Every text editor told me to pick two of three: fast, powerful, native. Vim is fast and native but cryptic. Modern Electron editors are powerful but heavy. The truly tiny native ones either don't have AI or don't run cross-platform.
So I built Notepatra.
Not a port. Not a wrapper. Something new — for everyone.
I asked: what would a small native code editor look like if it was built today, in 2026, when AI is part of every developer's workflow, and ran natively on Linux + macOS + Windows from one codebase?
The answer: a tiny native executable — ~12 MB bare (~12.4 MB on Linux x64) on every platform — with a Rust-powered core, Scintilla editing engine, and local-first AI integration (cloud backends optional). v0.1.119 downloads: 4.4 MB Linux x64 (tarball, Qt from the system), 27.7 MB on macOS (DMG with bundled Qt), 32.8–42.7 MB on Windows (MSI/zip/setup.exe with bundled Qt DLLs). An editor that can fix your broken JSON with regex in milliseconds — and when regex isn't enough, it asks your AI to figure it out — local by default, six cloud backends one click away when you want a frontier model. No telemetry. No subscription. No mandatory API key.
Notepatra started on Linux — because that's where the gap was. But great tools shouldn't have borders. Notepatra runs on Linux, Windows, and macOS. Same codebase. Same features. No one gets left behind.
Notepatra is what I wish had existed — on every platform.
Features
Editor — Battle-tested basics done right
- 238 file extensions mapped to 82 language lexers — Python, C/C++, C#, Java, Kotlin, JavaScript, TypeScript, Rust, Go, Swift, Dart, Solidity, Zig, Vala, Hack, Julia, R, Protobuf, F#, HCL/Terraform, Thrift, GraphQL, GDScript, Nim, Cython, Mojo, Crystal, Elixir, Scala, Groovy, Apex, SQL (6 dialect presets), HTML, CSS, JSON, JSON5, YAML, TOML, Markdown, Bash, Fish, Nushell, Fortran, VHDL, Verilog, MATLAB, LaTeX, BibTeX, Jinja, Liquid, Twig, Dockerfile, DotEnv, Gitignore, and more. Rust / Go / Swift / Kotlin / TypeScript / PowerShell ship as Notepatra-local lexers (not just C-family fallbacks)
- Tabbed editing — drag, reorder, middle-click close, double-click empty area for new tab
- Tab right-click menu — Close, Close All BUT This, Close All to the Left/Right, Close All, Save, Save As, Rename, Copy Full Path, Copy Filename, Copy Directory Path, Open Containing Folder, Open Terminal Here, Read-Only toggle, Color Tag (7 named colors + custom + Remove)
- 3 themes — Light, Dark, Monokai (Settings > Theme)
- Session persistence — close Notepatra, reopen tomorrow, same files, same cursor positions, same window size
- Crash recovery — if Notepatra crashes (it shouldn't, but life happens), your unsaved work is recovered on next launch
- File change detection — someone else edits your file? Notepatra asks: reload or keep yours?
- 2 GB file support — memory-mapped I/O via Rust; files up to ~1.86 GB load fully, larger open truncated + read-only
- Double-click word highlight — double-click any word, all occurrences light up in orange
- Ctrl+B brace matching — jump between matching
{} [] (), highlights both braces + selects everything between
- Macro recording — Start Recording (Ctrl+Shift+M), Stop, Playback (Ctrl+Shift+P), Run Multiple Times, Save/Load macros
- Code folding, bookmarks, auto-complete, indent guides, line numbers
- Custom scrollbars — clean, modern, rounded
Search — Find anything, anywhere
- Project Search (
Ctrl+Shift+G) — recursive search across file names AND file contents in any text-based file. Any size, any language (Python, SQL, C/C++, JS/TS, Rust, Go, HTML, JSON, YAML, Markdown, logs, config). Streams line-by-line so a 2 GB log searches the same as a 2 KB script. Each match shows exact line:col coordinates — double-click to jump the caret to the character.
- 5-tab Find/Replace dialog — Find, Replace, Find in Files, Mark, Go to
- 3 search modes — Normal, Extended (
\n, \r, \t, \xNN), Regular expression
- Find All in Current Document — results appear in bottom panel, double-click any result to jump to that exact line
- Find All in All Opened Documents — search across every open tab at once
- Replace All in All Opened Documents — one click, every file updated
- Find in Files — search entire directories recursively with file filters
- Mark All — highlight every occurrence with visual indicators
- Aho-Corasick search engine (Rust) — faster than regex for literal patterns
Plugins — The real power
Every plugin opens in its own tab. Real UI, not just a menu click.
| Button | What it does |
|---|
| Format | Pretty-print with preserved key order |
| Minify | Compact to one line |
| Fix + Format | Rust-powered auto-repair: fixes missing braces, trailing commas, single quotes, unquoted keys, missing { after [ — shows detailed report of every fix |
| AI Fix (Ollama) | Sends truly broken JSON to your local AI — fixes what regex can't |
| Button | What it does |
|---|
| Format (2/4 spaces) | Proper HTML indentation |
| Minify | Strip all whitespace |
| Fix + Format | Auto-close unclosed tags, detect missing closers, report issues |
| AI Fix (Ollama) | AI repairs broken nesting, malformed tags, missing attributes |
| Button | What it does |
|---|
| Check | Detailed report: line numbers of every mismatched () [] {}, keyword mismatches (begin/end, if/fi) |
| Auto-Fix | Adds missing closers in correct nesting order |
| AI Fix (Ollama) | AI understands your code structure and fixes all bracket issues |
- Format with UPPERCASE or lowercase keywords
- Configurable indent width
- Supports T-SQL, PL/SQL, MySQL, PostgreSQL, SQLite
Compare / Diff (inbuilt)
- Pick any two tabs or any tab vs file on disk
- Side-by-side Scintilla editors with a ComparePlus-style overview/nav bar
+ green for added, - red for deleted, # amber for changed
- Word-level LCS intra-line diff — within a changed line, the specific
tokens that were removed are highlighted in red on the left pane; the
tokens that were added are highlighted in green on the right pane. Works
on actual word boundaries, not just common-prefix/suffix.
- Dark-mode aware — colours track your Notepatra theme (Light / Dark /
Monokai) so diffs stay readable on both black and white backgrounds.
- Prev/Next diff navigation, inline overview-bar jump
- Ignore whitespace, ignore case, ignore empty lines checkboxes
- Unlock for editing mode — edit either pane and re-diff in place
- Powered by Rust Myers diff (line-level) + C++ LCS (word-level)
- Visual UX inspired by ComparePlus by Pavel Nedev — credit where credit is due.
Git Integration (inbuilt)
- Staged / Unstaged trees — porcelain v2 parser, inline
+ / − buttons per row to stage/unstage
- Branch chip with ahead/behind — shows
main ↑3 ↓1 when diverged
- Commit box (Ctrl+Enter) — line-count indicator, blocks commit when nothing's staged
- Sync row — one-click Pull / Push / Fetch with live ahead/behind refresh
- Collapsible history + stash menu — recent commits expandable; stash / pop / list / drop
- Git gutter — green/yellow/red markers in editor margin for changed lines
AI Powered — local-first, cloud-optional
Backend dropdown ships 6 entries — Ollama (local default · localhost:11434), llama.cpp (GGUF) (local · localhost:8080), OpenRouter (cloud · 100+ models), Ollama Cloud (cloud · gpt-oss:120b / qwen3-coder:480b / deepseek-v3.1:671b), OpenAI (cloud · GPT-4o / GPT-5 / o-series), Azure OpenAI (enterprise). Per-provider key slots — no cross-provider bleed. The llama.cpp entry also accepts a user-configured base URL via Settings → Preferences → AI, so you can route it to any OpenAI-compatible HTTP server you have installed separately. Local backends keep code on your machine; cloud backends are opt-in. No telemetry. No subscription. No mandatory API key.
AI Assistant — side-dock (Ctrl+Shift+A)
The AI chat lives in a persistent right-side dock, not an editor tab. One conversation, preserved across tab switches. Tick Coding Mode to open the 3-column coding layout (file tree · editor · AI chat) 3-pane layout.
Workspace awareness. Every prompt carries:
- the selection (or full file if no selection is active)
- the full text of the current file
- excerpts of every other open editor tab
- a flat listing of all files under the workspace root (
.git, node_modules, target, dist, etc. filtered out)
So the model can reason about files you haven't opened yet — "import from utils.py" works even when utils.py isn't in a tab. Budget-capped so small local models (3B, 4K–8K context) don't overflow.
One-click actions (hidden by default, click "▸ Quick actions" to reveal):
Explain · Find Bugs · Refactor · Write Tests · Add Comments · Generate Docs · Optimize · Translate. Or type a custom prompt. Responses stream in, each has a Copy link, and the last response can be inserted at the cursor or replace the selection with one click.
Speech-to-text — optional mic button, uses local arecord + whisper CLI when installed. No audio ever leaves the machine.
AI Setup
curl -fsSL https://ollama.com/install.sh | sh
ollama pull qwen2.5-coder:3b
ollama serve
Notepatra auto-detects the running Ollama and picks the most CPU-friendly model installed. For llama.cpp / OpenRouter / Ollama Cloud / OpenAI / Azure OpenAI, pick the backend from the dropdown at the top of the AI panel; base URL and API key are editable inline. The llama.cpp entry also accepts a user-configured base URL via Settings → Preferences → AI, so you can route it to any OpenAI-compatible HTTP server you have installed separately.
👁 AI Interaction Log — see exactly what your AI did (Features → AI Interaction Log…)
Total transparency, on by default. Every request and response to any AI backend — local (Ollama / llama.cpp) or cloud (OpenRouter / OpenAI / Azure / Ollama Cloud), including Noter's Extract — is written to a local SQLite log you can open and read: timestamp, backend, model, mode, full prompt + full response, token counts, latency. It never leaves your machine (zero network egress), entries auto-prune after 7 days, and a built-in credential scrubber masks API keys / bearer tokens / PEM private keys before anything is stored. The viewer has backend/model/mode filters, Export JSON, Prune now, and a one-click opt-out toggle. You are never guessing what got sent to a model — you can audit every byte. Privacy as transparency, not as a promise.
📝 Noter — meeting notes that turn into reminders (Ctrl+Alt+N)
A local-first, two-pane meeting workspace (notes list · editor) — no accounts, no bots; notes live under ~/Documents/Notepatra/Noter/.
- Write fast — a top toolbar drops in Action Items / What I plan / To-dos headers and checkbox bullets; check a line to strike it through.
- Extract (
Ctrl+Alt+E) runs your AI backend over the note and returns a summary plus action items / decisions / questions / risks. A task that mentions a time ("ship the build 10am tomorrow") comes back with that date/time pre-filled.
- Reminders — set one on a note (right-click) or schedule action items straight from Extract; they all collect in a central Reminders list grouped Overdue / Today / This week / Later, with desktop notifications at the due time while Notepatra is running (missed ones arrive as one summary at next launch). Click to open the note, pencil to reschedule, ✕ to delete. Re-running Extract flags what's already scheduled so you never pile up duplicates.
📐 Diagram tool — flow / ER / system diagrams from text (.npd)
A first-class diagramming surface that renders in the default binary on every platform (incl. macOS Apple Silicon and the Windows installer — native Qt renderer, no WebEngine), opened from the toolbar next to Noter. The tiny .npd text DSL is the source of truth; the canvas is a live projection of it (so undo/redo and version control just work).
- Create three ways — AI Generate describes it in plain English and a local model writes the
.npd, shown in a review pane before it touches the canvas (undo/redo after); or start from a Flow / ER / System template; or write .npd directly. Import Mermaid converts an existing flowchart.
- Rich visuals — 5 shapes (pill / box / decision diamond / database cylinder / icon), ~55 hand-drawn icons (~150 aliases) for system / ER / flowchart, directed + labelled + bidirectional arrows, label-overflow→hover, 4 palettes (+ default fallback), infinite pan/zoom canvas.
- Export — PNG / JPEG / PDF on every build, plus SVG / HTML where the Qt Svg module is present and WebP where the Qt WebP image plugin is present (the menu shows exactly what your build supports). A Help button has the full cheat-sheet;
samples/diagram_showcase.npd shows every element; the npd_render CLI renders any .npd to an image headless.
More Features
| Feature | Shortcut |
|---|
| Built-in Terminal | `Ctrl+`` — opens as a tab, runs real commands |
| REST Client | Ctrl+Shift+R — send HTTP requests, see responses with pretty JSON |
| Hex Editor | View > Hex Editor — color-coded hex dump of any binary file |
| Markdown Converter | Features > Markdown — convert selection to table, list, code block, bold, link, heading, or strip HTML to markdown |
| File Explorer | Ctrl+Shift+E — tree view sidebar |
| Function List | View > Function List — lists all functions/classes, double-click to navigate |
| Preferences | Settings > Preferences — 6 tabs of configuration |
Keyboard Shortcuts
| Category | Shortcut | Action |
|---|
| File | Ctrl+N | New |
| Ctrl+O | Open |
| Ctrl+S | Save |
| Ctrl+W | Close tab |
| Edit | Ctrl+D | Duplicate line |
| Ctrl+Shift+K | Delete line |
| Ctrl+/ | Toggle comment |
| Ctrl+Shift+U | UPPERCASE |
| Ctrl+U | lowercase |
| Search | Ctrl+F | Find |
| Ctrl+H | Replace |
| Ctrl+Shift+G | Project Search (folder-wide names + contents) |
| F3 / Shift+F3 | Find Next / Previous |
| Ctrl+G | Go to line |
| Ctrl+B | Go to matching brace |
| Ctrl+F2 / F2 | Toggle / Next bookmark |
| View | F11 | Full screen |
| Ctrl+= / Ctrl+- | Zoom in / out |
| Alt+0 | Fold all |
| Macro | Ctrl+Shift+M | Start recording |
| Ctrl+Shift+T | Stop recording |
| Ctrl+Shift+P | Playback |
| Features | `Ctrl+`` | Terminal |
| Ctrl+Shift+A | AI Assistant |
| Ctrl+Shift+E | File Explorer |
| Tabs | Ctrl+Tab | Next tab |
| Middle-click | Close tab |
| Double-click empty | New tab |
AI assistants (MCP) — new in v0.1.118
From v0.1.118, Notepatra ships notepatra-mcp — a stdio JSON-RPC 2.0 Model Context Protocol server that connects external AI assistants (Claude Desktop, Claude Code, OpenAI Codex, the OpenAI Agents SDK, and any spec-compliant MCP client) to the running editor over a local socket. Nothing leaves your machine: stdio to the client, local socket to the editor, no network connections.
35 tools in three tiers (as of v0.1.119):
| Tier | Tools | Gate |
|---|
| Read (18) | tabs, selection, status, recent files, in-tab + project search, Noter notes, reminders, read-only Git (status / diff / log / show / branch), .npd validation, read-only SQL (run_sql) | None — observation only |
| Act (9) | open file, new tab, go to line, set language, compare tabs, format JSON/SQL/HTML, open note | None — visible, non-destructive |
| Write (8) | insert text, replace selection, find-and-replace, save, create note, append note, set reminder, export diagram | Approve/Deny card inside the editor — 120 s auto-deny, FIFO one card at a time, no headless bypass |
find_in_tab and search_project also take an optional regex flag. run_sql is SELECT-only (rejected by the SQL classifier otherwise) and, on the Full/DuckDB edition, runs in an engine sandbox — the target file is materialized into an in-memory table, then DuckDB's external filesystem access is disabled (enable_external_access=false) before the untrusted query runs, so it cannot read host files. Since v0.1.119 the sidecar also supports Windows over a named pipe.
Hook it up in one line each (from v0.1.118):
claude mcp add notepatra -- notepatra-mcp --socket
[mcp_servers.notepatra]
command = "notepatra-mcp"
args = ["--socket"]
Full tool reference, Claude Desktop / Agents SDK snippets, security model, and honest limitations (editor must be running; Windows named-pipe transport lands in a later release; cloud-only connector surfaces can't reach a desktop editor): docs/mcp.html / notepatra.org/mcp.html.
Architecture
┌──────────────────────────────────────────────┐
│ C++ Layer (Qt5 + QScintilla) │
│ UI · Menus · Tabs · Dialogs · Editor │
│ Terminal · AI Panel · Compare · Plugins │
├──────────────────────────────────────────────┤
│ C FFI boundary │
├──────────────────────────────────────────────┤
│ Rust Core Library │
│ File I/O (mmap) · Search (Aho-Corasick) │
│ Diff (Myers) · JSON/HTML/SQL Formatters │
│ Bracket Fixer · Hash · Base64 · Encoding │
└──────────────────────────────────────────────┘
Why this hybrid?
- C++ because Qt and QScintilla are C++ — zero friction for UI
- Rust because file I/O, text processing, and parsing must never crash — Rust's ownership system guarantees memory safety
- Result: the speed of C++, the safety of Rust. The bare executable is ~12 MB on every platform (~12.4 MB Linux x64, similar on macOS / Windows). Latest v0.1.119 download sizes: 4.4 MB Linux x64 tar.gz · 4.1 MB Linux ARM64 tar.gz · 27.7 MB macOS DMG (with bundled Qt) · 42.7 MB Windows MSI · 32.8 MB Windows NSIS · 37.4 MB Windows portable zip. Installed footprint on Windows is ~75-85 MB after the MSI extracts bundled Qt + QScintilla DLLs — normal for any Qt-based installer.
Install
One-command install
Linux / macOS:
curl -fsSL https://notepatra.org/install.sh | sh
Windows (PowerShell):
irm https://notepatra.org/install.ps1 | iex
That's it. Auto-detects your OS, downloads the right binary, installs it, adds to PATH, creates shortcuts.
| Platform | Download | Size | What's inside |
|---|
| 🐧 Linux x64 | .tar.gz | 4.4 MB | Bare notepatra binary. Qt5 from your distro. |
| 🐧 Linux ARM64 | .tar.gz | 4.1 MB | Bare notepatra binary for aarch64 / ARM64 Linux. |
| 🍎 macOS Apple Silicon (M1–M4) | .dmg | 27.7 MB | Notepatra.app with Qt frameworks bundled. Drag to Applications. |
| 🪟 Windows x64 (MSI) | .msi | 42.7 MB | WiX-built MSI. Per-machine install, upgrade-code handled, file-type associations for .txt, .log, .md, .json, .py, .cpp etc., adds Notepatra to PATH. Best for enterprise / SCCM deploy. |
| 🪟 Windows x64 (installer) | .exe | 32.8 MB | NSIS installer. Registers in Settings → Apps → Installed apps. Uninstall via Control Panel works. |
| 🪟 Windows x64 (portable) | .zip | 37.4 MB | notepatra.exe + Qt DLLs + QScintilla DLL. Unzip and run anywhere. No installer, no registry. Optional: double-click register-associations.bat inside the zip to add Notepatra to the "Open with" menu for .txt/.md/.py/.json/etc. — HKCU only, no admin needed. Undo with unregister-associations.bat. |
⚠ Download size vs. installed size are different. The numbers above are download sizes — the .msi / .dmg / .tar.gz files you grab from GitHub Releases. After install, the on-disk footprint is larger because the installer extracts the bundled Qt DLLs, QScintilla DLL, and Rust core library out of the compressed payload. Typical installed size on Windows: ~75-85 MB. Linux installs are still tiny (~12 MB on disk) because Qt5 comes from your distro repo, not the tarball. macOS Notepatra.app on disk is ~50-60 MB after xattr removal.
Lite vs Full. The downloads above are the default Lite build. Each release also ships an opt-in Full variant (-full) that bundles the DuckDB query engine on every platform — plus, on Linux and Windows, the QtWebEngine inline Vega-Lite chart renderer (the generate_chart tool). macOS Full is DuckDB-only: Homebrew qt@5 no longer ships QtWebEngine and there is no Apple-Silicon Qt5 WebEngine, so inline Vega charts are a Linux/Windows Full feature. The native fenced ```chart (QtCharts) renderer and the .npd diagram tool work on every platform in both flavors.
Why are the download sizes different? Bare notepatra executable is ~12 MB on each platform (~12.4 MB Linux x64 — slightly smaller on Windows/macOS than on Linux because clang + MSVC emit denser code than gcc). On Linux, Qt5 is a standard system package (apt install qtbase5-dev libqscintilla2-qt5-dev), so the download is just the binary (~4.4 MB compressed). On macOS and Windows, Qt isn't pre-installed, so we bundle the Qt frameworks / DLLs alongside the executable for portability — same approach Krita, Kdenlive, and every cross-platform Qt app uses. Even with Qt bundled, Notepatra installs at ~12–85 MB depending on platform vs 300+ MB for VS Code.
macOS Intel: not shipped pre-built. Apple stopped selling Intel Macs in 2023 and the GitHub Actions macos-13 runner has been unreliable. Intel Mac users — git clone and run ./build.sh. Builds in ~3 minutes.
Admin / Fleet install — for IT, DevOps, and regulated teams
For one-time-install-then-every-user-sees-it on a shared machine, or silent push to a fleet via SCCM / Jamf / Ansible / Salt, every supported platform has a per-machine artefact:
| OS | Artefact | Silent admin install |
|---|
| 🪟 Windows | notepatra-x.x.x.msi | msiexec /i notepatra-0.1.119.msi /quiet — installs to C:\Program Files\Notepatra\, adds system PATH, registers HKCR file associations, all-users Start Menu. WiX-built, MajorUpgrade-aware, SCCM-friendly. |
| 🍎 macOS | Notepatra.dmg | Mount + sudo cp -R "/Volumes/Notepatra/Notepatra.app" /Applications/ from a deployment script. Or open the DMG manually and drag to /Applications (admin password). Notarised + stapled. |
| 🐧 Debian / Ubuntu / Mint / Pop!_OS (x64 + ARM64) | notepatra_0.1.119_amd64.deb | sudo apt install ./notepatra_0.1.119_amd64.deb — installs to /opt/notepatra/ + symlink at /usr/bin/notepatra, hicolor icons, .desktop registration. ARM64: replace amd64 → arm64. |
| 🐧 Fedora / RHEL / CentOS Stream / Rocky / Alma (x64 + ARM64) | notepatra-0.1.119-1.x86_64.rpm | sudo dnf install ./notepatra-0.1.119-1.x86_64.rpm — same layout as the .deb. ARM64: replace x86_64 → aarch64. Bundles QScintilla 2.14.1 alongside the binary because Fedora ships an incompatible packaging. |
| 🐧 Arch / openSUSE Tumbleweed / Manjaro / EndeavourOS / other glibc 2.38+ | Notepatra-0.1.118-x86_64.AppImage | chmod +x Notepatra-0.1.118-x86_64.AppImage && sudo cp Notepatra-0.1.118-x86_64.AppImage /opt/notepatra.AppImage && sudo ln -s /opt/notepatra.AppImage /usr/local/bin/notepatra. Requires glibc 2.38+ (Ubuntu 24.04+, Fedora 40+, Arch, Tumbleweed). Older distros: use the .deb / .rpm. |
All artefacts ship with cosign .sig + .pem for keyless Sigstore verification and SLSA build provenance. See Verify your download below.
🔒 notepatra-local-ai — cloud-free build for regulated environments
For teams that can't or won't send code to public LLM endpoints — regulated industries (finance, healthcare, legal, gov), data-sovereignty regions (EU GDPR, India RBI, China), air-gapped networks, or anyone who wants by-construction privacy — there's a dedicated cloud-free build, available for Linux and Windows:
The binary physically cannot reach api.openai.com, api.anthropic.com, openrouter.ai, api.mistral.ai, generativelanguage.googleapis.com, or any other public LLM endpoint. Every QNetworkAccessManager request goes through an allowlist that only accepts:
localhost / 127.0.0.1 / [::1]
- RFC1918 (
10.x, 172.16-31.x, 192.168.x)
- CGNAT range (
100.64.0.0/10 — covers Tailscale, corp VPN)
- IPv6 unique-local (
fc00::/7)
- DNS suffixes
.local, .lan, .internal, .intranet, .corp, .home
Local Ollama, local llama.cpp, self-hosted Ollama on the LAN, and any other OpenAI-compatible server you have installed locally or on your private network — all continue to work in the cloud-free build. Only public-cloud LLM endpoints are blocked. The cloud-URL paste box is stripped from the UI as well, so users can't even type a public host. Auditors can confirm by running strings notepatra | grep -c openai.com — zero hits.
On Linux the two flavors share the same notepatra binary name on disk; apt Conflicts ensures only one of notepatra / notepatra-local-ai is installed at a time, swap transactionally with sudo apt install ./notepatra-local-ai_0.1.119_amd64.deb. On Windows the two MSIs are independent products (different UpgradeCode + ProductName + install dir) so they can coexist if needed; admins typically push one or the other based on policy. notepatra --version self-identifies the build by name — only the bare lite build carries an edition suffix: Notepatra Lite v0.1.119 for the lite build and Notepatra v0.1.119 for the full build (DuckDB bundled), plus Notepatra Local AI Lite v0.1.119 / Notepatra Local AI v0.1.119 for the cloud-free (local-ai) builds; the same name shows in the window title bar and the About dialog.
Verify your download
Every release ships with SHA-256 checksums, Sigstore (cosign) signatures, and SLSA build provenance. The install.sh and install.ps1 scripts above already verify SHA-256 automatically and refuse to install on mismatch — but if you downloaded manually you should verify yourself.
curl -sL -O https://github.com/singhpratech/notepatra/releases/latest/download/SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missing
cosign verify-blob \
--certificate-identity-regexp '^https://github.com/singhpratech/notepatra/' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--certificate notepatra-linux-x64.tar.gz.pem \
--signature notepatra-linux-x64.tar.gz.sig \
notepatra-linux-x64.tar.gz
gh attestation verify notepatra-linux-x64.tar.gz --owner singhpratech
Full instructions, threat model, and disclosure policy in SECURITY.md.
Stay up to date — safe in-app updater
Notepatra checks github.com/singhpratech/notepatra/releases/latest on launch (silent on no-match) and pops a "A new version is available" dialog when something newer exists. Click Download and the updater will:
- Pick the right artifact for your OS + architecture — Linux x64 / ARM64 tar.gz, macOS DMG, Windows MSI (with NSIS
.exe and portable .zip as fallbacks).
- Stream-download it to
~/Downloads/*.part with a cancellable progress dialog.
- Fetch the release's
SHA256SUMS and verify the download's hash. If the hash does not match, the .part file is deleted and you are shown an error — nothing on your system is modified.
- Atomic-rename
.part → final name once verified.
- Hand off to the OS installer —
msiexec /i on Windows, open <dmg> on macOS (Finder drag to Applications), xdg-open on the Downloads folder on Linux so you replace the binary yourself.
Safety contract — the updater will never leave you with a broken install:
| Failure | What happens |
|---|
| No internet | Error dialog, zero disk writes |
| Download cancelled | .part deleted, nothing else touched |
| Power / crash mid-download | .part orphan in ~/Downloads, current binary untouched |
| SHA-256 mismatch | .part deleted, critical dialog shown, current binary untouched |
No SHA256SUMS in release | Refuses to auto-install, opens release page for manual verify |
| No matching platform asset | Refuses to auto-install, opens release page |
| OS installer cancelled or fails | Installer's own rollback — current binary untouched |
The Notepatra process never rewrites or replaces the running binary. Only the OS installer you explicitly clicked through may do that, and those installers all have their own transactional rollback (MSI MajorUpgrade, DMG copy-on-drag, user-driven file-manager swap on Linux).
Check manually: Help → Check for Updates or ? menu. The check is also visible on first launch (silent if up to date).
Windows: refresh "Open with" entry after upgrading from v0.1.23 → v0.1.24
If you upgraded from v0.1.23 or earlier and your right-click → Open with menu still shows Notepatra â€" native code editor (mojibaked text) and/or a red ❌ overlay on the icon, that's Windows shell-cache lag, not a Notepatra bug. Windows' MuiCache permanently caches the FileDescription string the first time it reads an executable's VERSIONINFO, and never re-reads it on upgrade. The new v0.1.24 binary embeds clean ASCII; Windows is just showing the cached old string.
One-time fix — open PowerShell (no admin needed, all changes are HKCU-scoped) and paste this whole block. Tested and confirmed working on Windows 11:
# 1. Wipe Notepatra's stale entries from MuiCache (the cache that has the â€" text)
$mui = "HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache"
Get-Item $mui | Select-Object -ExpandProperty Property | Where-Object { $_ -match "notepatra" } | ForEach-Object {
Remove-ItemProperty -Path $mui -Name $_ -Force
Write-Host "Cleared MuiCache: $_" -ForegroundColor Green
}
# 2. Wipe stale "Open with" associations pointing to old notepatra.exe paths
Remove-Item "HKCU:\Software\Classes\Applications\notepatra.exe" -Recurse -Force -ErrorAction SilentlyContinue
$exts = @(".txt",".log",".md",".json",".py",".cpp",".js",".html",".css",".xml",".sql",".sh",".yml",".yaml",".ini",".conf",".csv",".rs",".go",".java",".rb",".php",".c",".h",".hpp",".tsx",".ts",".jsx")
foreach ($ext in $exts) {
Remove-Item "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\$ext\OpenWithList" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\$ext\OpenWithProgids" -Recurse -Force -ErrorAction SilentlyContinue
}
Write-Host "Cleared OpenWithList for $($exts.Count) extensions" -ForegroundColor Green
# 3. Force shell to rebuild association cache
ie4uinit.exe -show
ie4uinit.exe -ClearIconCache
Write-Host "Rebuilt shell cache" -ForegroundColor Green
# 4. Restart Explorer (drops in-memory cache)
Stop-Process -Name explorer -Force
Start-Process explorer
Write-Host "Restarted Explorer — right-click any file now to verify" -ForegroundColor Cyan
What it does — line by line:
| Step | What & why |
|---|
| 1. MuiCache wipe | Clears the per-user cache where Windows stores FileDescription strings shown in "Open with" / File Properties → Details. This is the cache holding the â€" mojibake. |
| 2. Per-extension cache wipe | Removes OpenWithList + OpenWithProgids for 28 common file types. Forces Windows to re-query the .exe's actual VERSIONINFO next time the menu opens. |
3. ie4uinit.exe -show + -ClearIconCache | Built-in Windows tool that rebuilds shell association + icon caches. The red ❌ overlay disappears here. |
| 4. Restart Explorer | Drops the in-memory copy of the cache (the fourth and final layer). Without this, the menu can stay stale until you log out / reboot. |
Verify it worked: right-click any .txt or .json file → Open with → the Notepatra entry should now read Notepatra native code editor for the AI era with a clean icon. If you still see the old text after this, log out and back in (forces every kernel-side cache layer to flush).
New v0.1.24 installs on a clean machine never see this — it only affects upgrades from v0.1.23 or earlier where the mojibaked string was first cached.
Build from source
Linux (Ubuntu/Mint/Debian)
sudo apt install cmake qtbase5-dev libqscintilla2-qt5-dev
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
git clone https://github.com/singhpratech/notepatra.git
cd notepatra
cd rust-core && cargo build --release && cd ..
mkdir build && cd build && cmake .. && make -j$(nproc)
./notepatra
macOS
brew install qt@5 cmake
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
git clone https://github.com/singhpratech/notepatra.git
cd notepatra
./build.sh
Windows (MSVC)
Prerequisites
- Visual Studio 2022 with the "Desktop development with C++" workload
- Qt 5.15.2 for
msvc2019_64 — install via Qt Online Installer or aqtinstall
- CMake ≥ 3.16 —
winget install Kitware.CMake or cmake.org/download
- Rust stable — rustup.rs
Build QScintilla via the CMake wrapper (once)
git clone --depth 1 https://github.com/farleyrunkel/QScintilla.git $env:TEMP\qsci-src
cmake -S $env:TEMP\qsci-src -B $env:TEMP\qsci-src\build -G "Visual Studio 17 2022" -A x64 `
-DCMAKE_BUILD_TYPE=Release `
"-DCMAKE_PREFIX_PATH=C:\Qt\5.15.2\msvc2019_64" `
"-DCMAKE_INSTALL_PREFIX=$env:TEMP\qsci-install"
cmake --build $env:TEMP\qsci-src\build --config Release
cmake --install $env:TEMP\qsci-src\build --config Release
Build Notepatra
git clone https://github.com/singhpratech/notepatra.git
cd notepatra
cd rust-core; cargo build --release; cd ..
mkdir build; cd build
cmake .. -G "Visual Studio 17 2022" -A x64 `
"-DCMAKE_PREFIX_PATH=C:\Qt\5.15.2\msvc2019_64" `
"-DQSCINTILLA_INCLUDE=$env:TEMP\qsci-install\include" `
"-DQSCINTILLA_LIB=$env:TEMP\qsci-install\lib\qscintilla2_qt5.lib"
cmake --build . --config Release
Bundle Qt + QScintilla DLLs next to the exe
mkdir notepatra-win
copy build\Release\notepatra.exe notepatra-win\
windeployqt notepatra-win\notepatra.exe
copy $env:TEMP\qsci-install\bin\qscintilla2_qt5.dll notepatra-win\
.\notepatra-win\notepatra.exe
If you hit LNK2019 unresolved external symbol QsciScintilla::staticMetaObject — verify CMakeLists.txt defines QSCINTILLA_DLL for Windows targets. Without it, MSVC won't emit __declspec(dllimport) and the linker will fail to resolve symbols against the import library. This is the gotcha that took 12 CI iterations to find.
Plugin System
Drop a shared library in ~/.config/notepatra/plugins/ and restart.
- Linux:
.so files
- macOS:
.dylib files
- Windows:
.dll files
Write your own plugin in 30 seconds:
extern "C" {
const char* notepatra_plugin_name() { return "My Plugin"; }
const char* notepatra_plugin_version() { return "1.0"; }
const char* notepatra_plugin_author() { return "Your Name"; }
char* notepatra_plugin_run(const char* text, int len) {
}
}
g++ -shared -fPIC -o myplugin.so myplugin.cpp
clang++ -shared -o myplugin.dylib myplugin.cpp
cl /LD myplugin.cpp /Fe:myplugin.dll
Why not just use...?
| Editor | Download size | Native | Local AI | Built-in JSON fixer | 2 GB files | Linux | Win | Mac | Free |
|---|
| Notepad++ | ~4 MB | ✓ | ✗ | plugin only | ✗ | ✗ | ✓ | ✗ | ✓ |
| VS Code | ~300 MB | ✗ Electron | extension | extension | ✗ | ✓ | ✓ | ✓ | ✓ |
| Vim / Neovim | ~3 MB | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Sublime Text | ~30 MB | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ | $99 |
| Kate / Gedit | ~30 MB | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✓ |
| Notepatra | 4.1 / 27.5 / 42.7 MB | ✓ C++/Rust | ✓ Ollama | ✓ regex + AI | ✓ Rust mmap | ✓ | ✓ | ✓ | ✓ GPL-3 |
Notepatra download sizes are Linux x64 tar.gz / macOS DMG / Windows MSI from v0.1.119. Linux is just the binary (Qt is system-installed). macOS and Windows include bundled Qt. The bare notepatra executable inside is ~12 MB on every platform (Linux 12.4 MB, similar on macOS / Windows). Compressed download is much smaller because tar.gz / DMG / MSI all compress the binary plus shared libraries.
Tests
Focused automated regression tests are wired through CMake + CTest and run in CI — 70 test suites on the Full build (67 on Lite, which omits the two DuckDB suites and the WebEngine-gated Noter-export suite); all green, each with many assertions. A representative sample:
test_lexers — verifies every shipped QScintilla lexer produces real styling
test_palette — verifies the canonical 9-hue palette colors and bold/italic styles
test_fmtpanel_diff — verifies formatter panels keep diff state and emit signals
test_compare_widget — verifies the inbuilt Compare panel diff/edit/close paths
test_sqlfmt — 33 assertions across 11 SQL dialects through the AST pretty-printer
test_updater — verifies pickAssetForPlatform, SHA256 parsing, and asset scoring (18 assertions)
test_projectsearch — verifies the Rust-backed project search streaming path
test_projectsearch_ui — verifies the Project Search UI bindings
test_ollama — verifies live Ollama model detection (skips cleanly when offline)
test_aifix — exercises the AI-fix cleanup path against a real Ollama daemon (skips cleanly when offline)
test_llamacpp — exercises the llama.cpp backend path
test_ai_context — verifies the AI workspace-context summarizer
Run them locally with:
The Ollama / llama.cpp / AI-fix tests skip cleanly when no inference backend is running, so local and CI runs stay deterministic.
Releases
Notepatra follows Keep a Changelog and Semantic Versioning. Every release is tagged, signed, and published to GitHub Releases with binaries for Linux x64, Linux ARM64, macOS Apple Silicon, and Windows x64.
| Version | Date | Highlights |
|---|
| v0.1.119 | 2026-07-18 | MCP depth — 35 tools. The notepatra-mcp sidecar grows from 22 to 35 tools in three tiers — Read 18 / Act 9 / Write 8. New read tools: list_reminders, read-only Git (git_status / git_diff / git_log / git_show / git_branch), validate_npd, and run_sql (SELECT-only). New act tool: open_note. New human-approved write verbs: create_note, append_note, set_reminder, export_diagram. find_in_tab / search_project gained an optional regex flag. Windows named-pipe transport is now supported (Linux/macOS/Windows all connect via --socket; prebuilt Windows binaries not yet in the signed bundle — build from source / cargo install). run_sql security: SELECT-only via the SQL classifier and, on the Full/DuckDB edition, an engine sandbox — the file is materialized in-memory, then enable_external_access=false is set before the untrusted query runs, so it cannot read host files; an adversarial security pass hardened it before ship. Bare Lite binary unchanged at 12.4 MB (the new tools live in the sidecar). Full offscreen ctest 71/71, Lite 68/68, 60 sidecar cargo tests, live end-to-end across all 35 tools. |
| v0.1.118 | 2026-07-17 | MCP support — your editor, readable by your AI. New standalone notepatra-mcp sidecar: a spec-compliant stdio JSON-RPC 2.0 Model Context Protocol server exposing 22 tools in three tiers — read (10), act (8), and write (4) — plus open tabs / Noter notes as MCP resources and 3 ready-made prompts. Works with Claude Desktop, Claude Code, OpenAI Codex CLI, the OpenAI Agents SDK, and any spec-compliant stdio MCP client. Every write is human-gated: an Approve/Deny card inside the editor window, 120 s auto-deny, FIFO one-at-a-time, no headless bypass — the gate lives in the editor process, so no MCP client can write without a human click. Local socket + stdio only, no network connections; Linux/macOS first (Windows named-pipe transport in a future release). Prebuilt notepatra-mcp binaries ship as cosign-signed release artifacts; new docs page notepatra.org/mcp.html. Bare-binary size claim corrected 12.4 → 12.4 MB. Full ctest 68/68 + 47 sidecar cargo tests + live E2E 17/17. |
| v0.1.117 | 2026-07-17 | The honesty release — a 32-agent adversarial audit of every component, every confirmed defect fixed, ~4,100 LOC of dead code removed. No new deps, same bare binary (slightly smaller). Find & Replace tells the truth: Find in Files finally shows its results (they were written to a never-shown widget), whole-word is honored by Count / Replace All / Find in Files, line numbers are right in non-ASCII files, and dead controls are hidden until they work. Noter export unlocked: notes export as PDF / Markdown from the right-click menu (the exporter was fully built and tested — it just had no UI entry point); resizable pop-out with visible pin state; Dark/Monokai-correct dialogs. UTF-32 files with emoji no longer corrupt on save (byte-level round-trip regression-tested). Terminal gained a real Stop button; tab colors survive reorder; image attach is no longer gated by a hardcoded model list. ~150 Rust-core unit tests promoted into CI + release gates; git-panel test resurrected; full ctest 70/70. New for AI assistants: llms.txt + crawler-friendly robots.txt + CITATION.cff. |
| v0.1.116 | 2026-07-06 | Two focused, adversarially-verified fixes — no new features, same bare binary, no new deps. Compare view is readable again: the changed-word inline-diff highlight used a heavy red/green fill that buried the text; it's now a soft wash with a crisp red/green outline, WCAG AA in Light / Dark / Monokai. The updater picks the right edition: the release-asset picker previously scored every installer variant equally, so GitHub asset order could hand you the wrong edition; it now deterministically selects the installer matching this build's edition (Lite/Full × cloud/local-AI) across Windows / macOS / Linux. Full ctest 70/70, every fix red-state proven. |
| v0.1.115 | 2026-07-05 | Security, robustness & quality hardening — the AI coding assistant regrades B → A- and the Data analyst C+ → A-. No new features, same bare binary, no new deps. Read-only database access is now enforced in three real layers — engine-level DuckDB READ_ONLY, a structural SQL classifier that replaces the old bypassable leading-keyword prefix gate, and PostgreSQL/MySQL session read-only — and any data mutation now raises a human approval card (the model can no longer self-approve via confirm:true). Query cancel/timeout genuinely interrupts a running DuckDB query (duckdb_interrupt) and a row-cap prevents OOM. The AI tool registry is mode-aware (the read-only Ask segment and Data mode can't write files), Compose runs a host-enforced dry-run, edits are atomic via QSaveFile (a failed write can't destroy your original), and approve all is scoped per action kind. Truncated model output is now a distinct "Response cut off — Retry" state. Testing: the previously-phantom read-only-gate test (was git-excluded and never compiled) is now real and CI-run (143 assertions), plus new write-gate (44) and async-DB (30) suites; full ctest 70/70 (up from 67), every fix red-state proven, and a 5-agent adversarial fleet caught and closed a fix-introduced regression. |
| v0.1.114 | 2026-06-11 | Opening files is now bulletproof — the Windows "double-click does nothing" ghost-open class is dead. The single-instance handoff is greeting-gated (the running instance proves it's alive before the payload is sent, every byte confirmed drained; a hung instance yields a visible temporary window instead of silence — double opens structurally impossible). Non-ASCII paths open on Windows (true UTF-16 argv, was ANSI-codepage mangling). Sessions can't be silently lost: every session-passive window (--new, fallback, restore-skip) prompts Save/Discard/Cancel on close; session.json writes are atomic on Windows; login races can't produce two session writers. File → Close All no longer freezes the app (pre-existing infinite loop vs the keep-one-tab backfill); watcher prompts are strictly one-at-a-time (a mid-prompt file deletion could crash via a freed editor); word count is cached/debounced for multi-MB files; crashes leave an async-signal-safe flag. 13 deep-dive findings + 30 fleet-confirmed follow-ups — every serious one fixed, 7 low-priority items documented as deferred. 13 new test suites (67 total Full). |
| v0.1.113 | 2026-06-06 | Noter grows up — graded A (from C-). The v0.1.112 system-of-record wave shipped under this tag plus a hardening pass that closed 12 adversarial re-verify findings (3 critical + 9 high, several introduced by the v0.1.112 fixes themselves): reminders fire app-wide and follow renames, failed saves can't be silently discarded by checklist navigation, conflicts stay visible mid-navigation and keep real titles, Extract single-flights and applies to the launch note, checklist theme switches keep undo/redo intact, body search is linear (2 MB note: ~125 s hang → ~1.4 s). apply_diff writes inserted lines byte-exact; gated-write approval resets the retry guard and always logs. Full ctest 54/54, every finding pinned by a dedicated contract test; 7-dimension regrade: A. |
| v0.1.112 | 2026-06-06 | Noter — the system-of-record wave (audit grade C- → target A). A 19-agent usability audit found Noter great for live capture but untrustworthy around it; this release fixes every CRITICAL/HIGH finding. Reminders now actually fire — the engine is app-lifetime instead of dying with the Noter tab, and reminders missed while the app was closed arrive as one catch-up digest. Save failures are loud (red NOT-SAVED + banner + save-a-copy escape), external edits divert to a conflict copy instead of being clobbered, saves are fsync-durable with a ≤2 s crash-loss draft sidecar. Search reads note bodies (was titles-only) with snippets and match counts; one display title everywhere; headers survive reload. AI Extract can't hang the app (pre-flight + watchdog + Cancel) and its results persist idempotently into the note. Noter follows Dark / Monokai themes (was hardcoded light). Plus a 3-layer apply_diff hardening for the coding agent (tool-side escape-lint + degenerate-hunk rejection, model-agnostic recovery ladder, perseveration breaker). No new deps, same bare binary. Full ctest green (54 suites). |
| v0.1.111 | 2026-05-31 | The lovable AI agent wave — four assistant UX features that make agentic editing trustworthy (grade B- → B+/A-). Agent mode now shows an Approve / Reject write-confirmation gate before write_file / apply_diff ever touches disk, so autonomous edits never land silently. A context-transparency chip + popover reveals exactly what codebase context is sent to the model, with per-source exclude toggles to drop a file or folder before it leaves your machine. Every Composer Apply is now drift-protected and byte-exact — one "Undo apply" restores the file precisely and refuses if it changed underneath you. And the Ctrl+I inline edit renders a real, theme-aware Myers diff so you see exactly what changed before accepting. UX/behaviour only — no new deps, same bare binary, download sizes unchanged. 52/52 ctest pass. |
| v0.1.110 | 2026-05-31 | AI coding assistant — trust & clarity pass (driven by a deep usability audit, grade C+ → B-). The assistant header now shows your full posture at a glance — AI · CODING · COMPOSE (review-before-write, teal) vs AI · CODING · AGENT (writes hit disk live, red) — so you never guess whether the AI will edit your files. Coding now defaults to the safe Compose lane (propose-then-review), not autonomous Agent, so reopening never silently drops you into live disk writes. Applying a proposed edit now marks each file ✓ applied, drops a confirmation, and disables re-apply (no more "did it land?" or accidental double-write). Inline edit (Ctrl+I) now uses your selected model (not a fixed small default — fixes the no-hardcoded-model rule) and groups its change as one undo so a single Ctrl+Z reverts the whole AI edit. UX/state only — no new deps, same bare binary, download sizes unchanged. 51/51 ctest pass. |
| v0.1.109 | 2026-05-31 | The full build drops its "Full" name tag — the default build now self-identifies simply as "Notepatra". notepatra --version, the window title bar and the About dialog show Notepatra for the full (DuckDB-bundled) build instead of "Notepatra Full" — the full edition is the default, so it carries no qualifier. The bare build still says Notepatra Lite so you can tell it apart, and the cloud-free builds are Notepatra Local AI / Notepatra Local AI Lite. Naming only — the Lite vs Full downloads are unchanged (Full still bundles the DuckDB engine), as are the binary's features and download sizes. 51/51 ctest pass. |
| v0.1.108 | 2026-05-31 | Data Analyst mode writes sharper SQL — three filter-correctness idioms added to the built-in analyst prompt. Case-insensitive text search: "description contains X" now lower-cases both sides (LOWER(col) LIKE '%x%') instead of a case-sensitive match that silently skipped Capitalized rows and undercounted. Exact-code matching: when a question names a coded value ("LOINC 8480-6", "CVX 140") the analyst matches the bare stored code and stops, instead of bolting on a guessed SYSTEM=/CATEGORY=/redundant LIKE predicate that could drop every matching row. No more inventing a category/type literal from the question's English label ("obese", "active") without verifying it exists. Prompt-only — same bare Lite binary, no new dependencies, download sizes unchanged; the three idioms were validated against a multi-domain text-to-SQL evaluation (the production model's lone remaining synthetic-EHR defect was fixed, with no regressions). 51/51 ctest pass. |
| v0.1.107 | 2026-05-30 | Lite + Full downloads for all three platforms, with DuckDB v1.1.3 bundled in Full. Previously only Linux published a -full build; the macOS DMG / Windows MSI shipped without WebEngine despite the docs (the v0.1.106 Windows zip had zero WebEngine DLLs). Now every platform offers a Lite (bare) and a Full download — Full bundles DuckDB v1.1.3 everywhere (in-process Parquet / JSON / S3 / SQLite federation), plus the QtWebEngine inline Vega-Lite chart renderer on Linux/Windows (macOS Full is DuckDB-only; the native fenced ```chart QtCharts renderer works on every platform). The app now names its build flavor in --version / title bar / About (Notepatra Lite / Full, and Notepatra Local AI Lite / Full). Fixed: 13 double-mapped file extensions resolved only by hash order (.hh opened as Hack not C++) — deduped and locked by tests; Ctrl+Shift+K was bound to both Delete Line and Uncomment Line (Uncomment moved to Ctrl+Alt+U). 51/51 ctest pass. |
| v0.1.106 | 2026-05-29 | A reliability + security hardening release — every change hardens a code path that already shipped, no new features. Session restore could write unsaved content to the wrong file when a restored tab pointed at an unreadable/already-open file (now lands orphaned content in a fresh untitled tab). CredScrub now runs over the AI tool-result channel (read_file/git_diff bodies were forwarded verbatim to the backend); the read-file deny-list adds the dotenv family incl. app.env/database.env, secrets.json, and *.tfvars/*.tfstate; chart HTML export is XSS-escaped (both build paths); dry-run edits now queue an absolute path so they can't clobber $HOME; an interior-NUL heap over-read in the Rust core (Base64 decode) is fixed. Plus a large-file memory soft-gate. |
| v0.1.105 | 2026-05-29 | The per-symbol diagram colours from v0.1.104 now appear by default in the ER and System starter templates, not just the flow chart. Colour was never gated on diagram type — it has always worked on database cylinders and icon nodes — so this is purely a discoverability fix: the ER starter now colour-groups entities (green master / blue transactional) and the System starter groups by tier (teal edge / blue app / purple data), and Help states colour works on every node type and diagram. Colour stays opt-in. |
| v0.1.104 | 2026-05-29 | Diagrams gain optional per-symbol colours and chained arrows — both opt-in, both native, both staying monochrome by default. Add a #hex or named colour after a node's shape (node proc [Process] #1565c0, node start (Start) green) for auto-contrast coloured symbols on any palette/theme; connect a run of nodes on one line (a -> b -> c, label rides the last hop). Plus .npd parser hardening surfaced by an adversarial parse audit — node ids may contain colons/URLs (http://x -> y), node labels may contain ->, and an untrusted diagram title can no longer inject a live tag into exported HTML/SVG. |
| v0.1.103 | 2026-05-27 | Diagrams now render natively in the default binary — on every platform, including macOS Apple Silicon and the Windows installer — with no WebEngine, no Chromium, and no separate "Full" download. v0.1.102 drew diagrams inside an embedded Chromium (QtWebEngine + dagre.js), so the picture never appeared on the default lite binary, on macOS Apple Silicon, or anywhere WebEngine wasn't bundled. v0.1.103 replaces that whole pipeline with a native QPainter renderer (src/diagram/diagram_render.{h,cpp}): longest-path layering + barycenter crossing-reduction, border-anchored bezier connectors, ~55 hand-drawn icons (~150 aliases) for system/ER/flowchart, 5 palettes, scroll-zoom/drag-pan. Export to PNG/JPEG/PDF on every build, plus SVG/HTML where the Qt Svg module is present and WebP where the WebP image plugin is present — the export menu shows exactly what your build supports. No more Full vs Lite split for diagrams. |
| v0.1.102 | 2026-05-25 | Diagrams — flow / ER / system from a text DSL, with AI generation, a live canvas, and PNG/SVG/PDF/HTML export. New first-class diagram tool (Full flavor): the text-first .npd DSL is the source of truth and the canvas is a live projection. Create three ways — AI Generate (describe it → review the generated .npd → Insert), templates, or write .npd directly (plus Import Mermaid). 5 shapes, 12 icons, directed/labelled/bidirectional arrows, label-overflow→hover, 5 palettes, infinite pan/zoom. Toolbar button next to Noter + in-tool Help cheat-sheet + samples/diagram_showcase.npd + npd_render CLI. Also fixed: CMAKE_AUTORCC was never enabled, so .qrc resources (vega.qrc inline charts, icons.qrc, the diagram render layer) weren't bundling in Full builds — now corrected. 53/53 ctest pass. |
| v0.1.101 | 2026-05-25 | In-app updater finalizes even when a prior download is locked. On macOS the update downloaded + verified, then failed with "Could not finalize the download file." Updater::runUpdate() moved the verified ~/Downloads/<name>.part to <name> with if (exists) remove(); rename(); but ignored the remove result — and on macOS a .dmg the updater previously opened stays MOUNTED in Finder, which macOS won't let you delete, so QFile::rename then refused to overwrite it and every retry re-wedged. Fix: new uniqueDestPath() lands the file under a browser-style <name> (1).dmg when the destination can't be removed, plus a copy+remove fallback. Net improvement on all platforms. New test_updater cases cover the dedup for .dmg/.tar.gz/.msi/.deb/.AppImage (a version-dot edge case was caught + fixed pre-release). 47/47 ctest pass. |
| v0.1.100 | 2026-05-25 | 🎉 100th release — window opens centred on Windows. On a cold start / file-double-click the window could open with its title bar above the top of the screen, hiding the min/max/close buttons. Root cause: position was saved with QWidget::x()/y() (Qt FRAME coords, incl. the title bar) but restored with setGeometry() (CLIENT coords), so on Windows the title bar climbed up by its own height each launch until the controls left the screen; the ≥100 px on-screen clamp didn't catch a body-on-screen / title-bar-off-top window. Fix: new centeredWindowRect() helper — both restore sites (config + session) now centre on the saved size with a title-bar-height top margin instead of restoring the saved x/y. Maximized state + window size preserved; only on-screen position is dropped (always centred). Also confirmed (no change): the AI Interaction Log at %APPDATA%\Notepatra\ai-logs\ persists across upgrades and survives uninstall (installers never touch %APPDATA%). 47/47 ctest pass. |
| v0.1.99 | 2026-05-25 | Noter sidebar renders light on macOS. The Notes/Reminders/Trash tree drew on a dark background while the rest of the Noter panel stayed light — a stale stylesheet selector after the list→tree migration. sidebarStyle() still carried the old QListWidget#noterMeetingList rules and had none for the migrated QTreeWidget#noterSidebarTree; a Qt item-view paints its viewport from its own QPalette::Base, which the parent panel's QSS background doesn't reach, so the unstyled tree fell through to the dark system base on macOS (Linux/Windows default to a light base, so it never showed there). Fix: explicit QTreeWidget#noterSidebarTree light-background rules plus a QPalette::Base/Text pin in buildSidebar() as a belt-and-braces fallback; ::branch left native so disclosure arrows survive. Cosmetic-only — Noter worked throughout. 47/47 ctest pass. |
| v0.1.98 | 2026-05-25 | Windows in-app updater fixed. The v0.1.96 EOL-OpenSSL guard hard-disabled Check for Updates on any OpenSSL 1.0.x / 1.1.x runtime, popping an "in-app update check disabled" dialog for every Windows user. That was a misdiagnosis — "end-of-life" (no upstream security patches) ≠ "broken": the bundled OpenSSL 1.1.1w speaks the TLS api.github.com requires, and the real v0.1.96 launch-hang was the session-restore loop (fixed separately in v0.1.96). The gate is removed; the update check keeps its existing, sufficient safety net — async QNetworkAccessManager, an 8-second abort timer, a QSslSocket::supportsSsl() guard, and a TLS-aware "Open Releases Page" fallback. One-time: v0.1.96 / v0.1.97 Windows builds still carry the gate, so download v0.1.98 manually once; in-app updates work from then on. (Bundling OpenSSL 3 on Windows — the Qt 5.15.2 ABI targets OpenSSL 1.1 — remains queued as separate security hygiene.) 47/47 ctest pass. |
| v0.1.97 | 2026-05-25 | Noter reminders grow up + cloud-AI fixes. New central Reminders sidebar root (between Notes and Trash) lists every scheduled reminder grouped Overdue / Today / This week / Later — click to open the note, pencil to reschedule, ✕ to delete; overdue rows render red; painter-drawn amber clock icon. Extract now schedules real reminders (the "Remind" checkboxes were previously cosmetic) via new NotesTodos::addReminder(); resolves natural-language times ("10am tomorrow") to a concrete LOCAL wall-clock (was UTC, which shifted the hour); returns a plain-English summary shown in the dialog + saved above the action items; and flags already-scheduled actions on re-run (fuzzy match) so you don't pile up duplicates. Remind defaults ON only when a concrete time was extracted. Three cloud-AI fixes: double-/v1 URL (…/api/v1/v1/models → 404 "unreachable") normalized via openAiV1Base(); streaming 401 no longer spins ~60s silently — surfaces a prompt auth error; and cancelling the Extract result no longer crashes (the dialog deferred off the network signal so a trailing downloadProgress can't hit freed memory). AI Interaction Log surfaced as a first-class privacy feature (Features → AI Interaction Log…) in the in-app Help, README, and website. 47/47 ctest pass (new test_stream_error, test_notes_todos §13, test_notes_panel_widget §21–23, sweep-prompt local-time + summary). |
| v0.1.96 | 2026-05-24 | Same-day Windows hotfix for v0.1.95. Three root causes of a reproducible launch-hang permanently addressed. (1) Platform-conventional config dir — pre-fix Notepatra wrote to ~/.config/notepatra/ on every OS; Windows users' %APPDATA%\Notepatra\ appeared empty while session state sat in C:\Users\<u>\.config\notepatra\, invisible to IT tooling / backup tools / group policy. New Config::appConfigDir() returns %APPDATA%\Notepatra on Windows, ~/Library/Application Support/Notepatra on macOS, $XDG_CONFIG_HOME/notepatra on Linux (unchanged). One-time copy-migration from the legacy path on first launch; legacy cleanup queued for v0.1.97. All ~6 hard-coded call sites migrated (config.json, session.json, recovery/, ai-logs/interactions.db, db-connections.json, plugins/). (2) Crash-safe session restore — MainWindow::restoreSession() now writes a session.json.restoring marker before opening files and deletes it on success. On next launch, if the marker exists, the previous restore was interrupted (kill / hang) — session.json is moved aside to session.json.failed-<timestamp> (preserving the tab list) and a deferred dialog tells the user where to find it. No more "reopen the same bad file on every launch" loop. (3) EOL-OpenSSL update-check gate — runtime check on QSslSocket::sslLibraryVersionString() hard-skips the GitHub update check if the bundled OpenSSL is 1.0.x or 1.1.x (EOL since Sept 2023). The TLS handshake against modern endpoints can stall the UI thread on a broken stack; bypassing eliminates that path. Bundling OpenSSL 3.x in the Windows MSI queued for v0.1.97 (CI workflow change). All v0.1.95 tests carry forward green (~1325 total, 0 failing) plus new unit tests for appConfigDir() per-platform via env override. |
| v0.1.95 | 2026-05-24 | Windows crash hardening + Noter full redesign. Three Windows crash / hang classes fixed: Save / Save As / right-click-Save reliable crash (root cause: QFileDialog::setProxyModel + QIdentityProxyModel synthetic-column crash on QFileSystemModel's async QFileInfoGatherer; removed the proxy entirely, dropped the Date Created column); multi-PID on double-click launcher (root cause: listen-after-MainWindow-construction + 300ms probe + unconditional removeServer() orphaning the running instance; now bind pipe before constructor, two-stage 500+1500ms probe, conditional removeServer); invisible main window after monitor change (root cause: persisted coords from disconnected secondary monitor; new clampWindowToScreens intersect-tests against connected screens, recenters on primary if no overlap). Plus surfaced 3 silent-save-failure dialogs at File→Save / right-click Save / closeTab Save sites where the bool return was being ignored. Noter full redesign (the meeting-notes tab): rewrote src/notes.cpp from scratch into a two-pane sidebar+editor shape inspired by Apple Notes / Bear / Granola; deleted the 6-button insert bar, slash menu (src/notes_slashmenu.{cpp,h}), 5-button header row, edge-strip icons, and WebEngine path (src/notes_bridge.{cpp,h}, src/notes_editor.{cpp,h}); added markdown shortcuts (- [ ] + Space → ☐; F4 toggle), inline ✕ delete button on every meeting + every todo row, Trash + restore for meetings (Noter/Trash/.trashed-<ts>-<name>.html) AND todos (status='trashed'), inline-editable todo titles via QStackedWidget [QLabel, QLineEdit], calendar pickers in Add-todo dialog (Due + Remind, each with today / tomorrow / 1h-before-due / clear quick-picks), right-click context menus on todo rows with Set due / Set reminder / Mark done / Delete (soft) / Restore / Delete permanently, AI model selector in editor footer auto-populated from OllamaClient::listModels(), single ✨ Extract button bottom-right runs NoterSweepPrompt against Ollama and inserts an Action Items section on Apply. Tool brand-color palette unified — new src/tool_colors.{h,cpp} is the single source of truth for tab strips + feature-toolbar icons + Welcome cards (notepatraToolAccent(toolKey)). Palette overhauled to 12 visibly-distinct hues spread ~30° apart, resolving 5 collisions (AI / JSON both blue → royal blue / cyan; HTML / Project Search both orange → HTML hot pink; Noter / Git both red → Git lime; Brackets / Project Search same hue). Light QMenu styling forced per-instance on every Noter context menu — Qt's QSS doesn't cascade through widgets that set their own stylesheet so the rules must be on each menu. Fixes dark-on-dark right-click that the user reported. ~303 Noter tests (was 257) — test_notes_panel_widget.cpp (NEW 24-case integration test) + 14 new editable-row cases + 22 new trash-lifecycle cases. ~1325 total tests pass across the wider Notepatra suite. All v0.1.93 fixes carry forward unchanged. |
| v0.1.93 | 2026-05-19 | Project Search: intelligent flood protection, live phrase-relevance ranking, match=N% badges, cross-platform completion notifications. Replaces the v0.1.92 unbounded-emit model that OOM-killed the app on import os over $HOME with an informed search experience. Soft warning at 10k matches (amber progress bar + status note, no interruption). Hard checkpoint popup at 50k with Continue / Show me these / Cancel — clicking Show me these flips the UI phase to Idle immediately so the timer doesn't keep ticking while threads wind down; clicking Cancel re-enables the Search button instantly instead of waiting for the worker to unwind. Match all words default OFF — import os runs as exact-phrase by default; power users tick the box for any-order any-line AND semantics. match=N% self-describing badge on every result row (100% = full phrase on this line, 99% = all tokens scattered, lower = partial; column is fixed-width and visually consistent in every mode). Live phrase-relevance ranking on both axes — within-file std::stable_sort descending on per-line %, cross-file live re-positioning in the tree (take + insert at the correct slot) the moment a file's best-line % improves, so 100% files surface to the top during the scan instead of only at the end. Cross-platform desktop notification when a search ≥ 3s completes with the window unfocused — one code path (QSystemTrayIcon::showMessage) routes to libnotify/D-Bus on Linux, Toast on Windows, NSUserNotificationCenter on macOS, no visible tray icon added. Bug fixes: modal dialog double-%% ("Scanned X of Y (4%%)"), "⏳ stalled on:" wording replaced with neutral "· current:" when the scan is just slow on one big file, title 🔍 emoji-font fallback on Linux so the header looks the same as Win/Mac, "Show me these" no longer leaves the status saying "Searching…" for 47 seconds. 39/39 ctest pass (unchanged). All v0.1.92 fixes carry forward unchanged. |
| v0.1.92 | 2026-05-19 | SQL formatter deep-dive: 9 dialect bugs fixed in one pass. User-reported regression — [onelook-db-1] mangled to [ onelook - db - 1 ] — fixed via T-SQL bracket-identifier preservation; 9 other latent bugs found in the same audit also fixed. Bumped sqlparser-rs 0.52 → 0.55 (13 distinct breaking AST shape changes migrated: Statement::Update.from now UpdateTableFromKind enum, Insert.table now TableObject, OrderBy restructured with OrderByKind, OrderByExpr.options.{asc,nulls_first}, ObjectNamePart::Identifier, Case.conditions now Vec<CaseWhen>, SelectItem::QualifiedWildcard now SelectItemQualifiedWildcardKind enum, JoinOperator 5 new variants for unqualified forms, JoinConstraint::Using now Vec<ObjectName>). New Statement::Merge Writer arm: MERGE INTO ... USING ... ON ... WHEN MATCHED/NOT MATCHED THEN ... — was collapsing to one line via Display fallback. DISTINCT ON (cols) rendering: previously emitted bare DISTINCT, silently dropped the column list. FETCH FIRST N ROWS [PERCENT] [WITH TIES | ONLY] — q.fetch field was never read by Writer pre-v0.1.92. T-SQL GO batch separator pre-split: new split_tsql_go_batches() walks input with string/comment-state tracking, formats each batch independently, rejoins with GO. T-SQL PRINT pre-mask: sqlparser doesn't recognize PRINT; we mask it as a parseable placeholder, restore verbatim. apply_keyword_case() token-aware case helper replaces .to_lowercase() whole-string on Display-emitted ON CONFLICT / MERGE INSERT bodies — preserves identifiers like Email, Name, and MySQL function VALUES(a) from being mangled to lowercase in lowercase-keyword mode. 10 new regression tests + hardened bracket-id test: 34 → 44 in sql_fmt module; 39/39 ctest still green. Zero C++ changes; same FFI surface (npc_format_sql in src/rustbridge.cpp:213/222 binary-compatible with new librust_core.a). All v0.1.91 fixes carry forward unchanged. |
| v0.1.91 | 2026-05-19 | Notepad++-style per-line change-history strip + holistic path-separator sweep + status-bar change counter + Find/Replace UX upgrade. Margin 3 now paints a 4 px per-line strip: orange while edited, green after save (Notepad++ behaviour). Manual implementation via SCN_MODIFIED / SCN_SAVEPOINTREACHED / SCN_SAVEPOINTLEFT because Ubuntu 24.04's QScintilla 2.14.1 ships Scintilla compiled WITHOUT SCI_SETCHANGEHISTORY (the message round-trips as a no-op; confirmed via PyQt5). FullRectangle marker shape — never Background (whole-line paint bug from pre-v0.1.91 git-diff gutter that filled markdown drafts with green). Safety nets: m_loadingFile flag stops wholesale setText() from marking every line orange on open; SCN_MODIFIED.line recomputed from position via SCI_LINEFROMPOSITION (QScintilla 2.14.1 only fills line for fold events); onSavePointReached() does markerDeleteAll(23) belt-and-braces + full update() because some X11 compositors don't synthesise margin repaints from marker state alone. Status bar gains N modified · M saved counter between cursor-position and EOL fields — hidden when both zero. O(1) via parallel QSet<int> members + changeHistoryUpdated() signal pattern; no buffer walking. Holistic QDir::toNativeSeparators sweep — 16 new sites across 6 files (findreplace / fileexplorer / mainwindow / tabmanager / gitpanel / welcome) wrapped so Windows shows \ everywhere paths display; no-op on Linux/macOS. Find/Replace dialog upgrade: Notepad++-style italic bottom status bar (single source of truth for every tab; pre-v0.1.91 Replace tab results silently dropped because m_resultsOutput was nullptr); Find↔Replace string carry-forward (with non-clobber guard so retyping on the destination tab survives a re-switch); two-step wrap at cycle end (Find Next stops at last match with "Reached end — press again to wrap" notice; SECOND consecutive press actually wraps). Dialog min size 660×400 → 720×440 + setWordWrap(true) so the longest status message fits. 6 new test executables, 78 assertions: test_change_history (16) · test_change_history_mainwindow (12) · test_change_history_watcher (6) · test_change_history_stress (20) · test_changehistory_statusbar (14) · test_findreplace_carry_forward (20). 39/39 ctest pass (was 33/33). All v0.1.90 fixes carry forward unchanged. |
| v0.1.90 | 2026-05-16 | Save As detail view now shows a "Date Created" column. v0.1.88 shipped the dialog UX baseline (960×640 geometry, Detail view default, sort by Date Modified descending) but the user-requested Date Created column was deferred — the first attempt used a hand-rolled QSortFilterProxyModel whose index(row, extraCol) returned a createIndex() with no source mapping, crashing Qt's tree view on Ctrl+S. v0.1.90 ships the column via the supported QIdentityProxyModel pattern. New src/savedialogfsmodel.h/cpp wraps QFileDialog's internal QFileSystemModel with one extra column whose data() returns QFileInfo::birthTime() for ext4 (kernel ≥ 4.11), falls back to metadataChangeTime() (ctime) on filesystems that don't store birth time, and renders an em-dash when both are invalid — so the cell never crashes the row layout. mapToSource() maps the extra column to source column 0 so Qt's model APIs that traverse back to the file system can resolve. Wired into configureSaveDialogUx() for both saveFileAs() and the close-tab "Save before close?" prompt. Why a proxy and not a QFileSystemModel subclass: QFileDialog builds its own QFileSystemModel internally; the supported extension point is setProxyModel(). Subclassing the model and tv->setModel() would break path-bar / name-edit / selection wiring hard-coded to the d-pointer's model. 32/32 ctest pass. |
| v0.1.88 | 2026-05-15 | Save As file-type dropdown now actually drives the saved extension. v0.1.88 user-reported same day: dropdown populated with 72 entries but picking "Python" + typing foo still saved as foo (no extension). Root cause: QFileDialog::setDefaultSuffix was never wired so the selected filter didn't drive the extension. Fixed in src/mainwindow.cpp (both saveFileAs() and closeTab() unsaved-prompt) by wiring setDefaultSuffix from the preselected filter at init AND from the filterSelected signal so switching mid-dialog stays in sync. Post-Accept safety net via new applySaveAsFilterSuffix(path, filter) helper for platform dialogs that silently ignore setDefaultSuffix (some Linux GTK builds, certain macOS dialog states). New firstExtensionFromFilter("Python (*.py *.pyw *.pyx)") → "py" extractor handles bare-name filters (Dockerfile / CMakeLists.txt / .gitignore) and "All Files (*)" correctly. New test_save_as_filters regression — 16 contract assertions that drive the user-visible end-to-end flow: bare 'foo' + Python → foo.py · foo.py + Python → unchanged · foo.txt + Python → foo.txt.py (filter wins) · All Files → unchanged · bare Dockerfile name match → unchanged · etc. Codifies the v0.1.88 ship miss: proxy assertions ("function called", "list populated") instead of contract assertions ("the bytes on disk match the selected filter"). New memory rule: test the user-visible contract, NOT proxy properties. 32/32 ctest pass (was 31 in v0.1.88). |
| v0.1.88 | 2026-05-15 | Save As file-type dropdown + large-file load speed-up. Two user-reported pain points fixed: dead Save As dropdown vs Notepad++ (src/mainwindow.cpp:1655 was passing only "All Files (*)" so the bottom-of-dialog dropdown had one entry), and 100+ MB files slower than the "up to 2 GB" promise. New buildSaveAsFilters() helper in src/lexerutils.{h,cpp} returns ~72 language entries matching Notepad++ behaviour (Plain Text · Markdown · Python · JS/TS · C/C++ · Rust · Go · SQL · JSON · YAML · TOML · CSV · HTML · CSS · …); pre-selects the filter matching the current tab's language. Same fix in closeTab() "Save before close?" prompt which had no filter at all. Large-file load — 3 fixes that compound: (1) UTF-8 fast path in rust-core/src/file_io.rs — skip UTF_8.decode() when mmap bytes are already valid UTF-8 (was allocating a fresh 118 MB String even when not needed; saves 118 MB heap + 250 ms on 118 MB file). (2) EOL detection bounded to first 64 KB — was scanning 118 MB twice for ~300 ms. (3) Drop CString round-trip in Rust→C FFI — FileLoadResult.text now allocates a Box<[u8]> instead of a CString (skips O(N) NUL scan + extra 118 MB allocation + appended NUL byte). New npc_free_file_text(ptr, len) reclaims the boxed slice by length. Large-file UX gates: files > 50 MB also disable word wrap + indent guides + edge column. NOTE: shipped with the Save As filter-extension bug fixed in v0.1.88. 31/31 ctest + 130/130 cargo test pass. |
| v0.1.86 | 2026-05-15 | Factual-audit gate, BGR-bug sweep, download-size truth-up — no new features. Post-v0.1.85 audit caught two classes of inaccuracy that had been drifting across releases. Two more Scintilla BGR byte-order bugs: src/findreplace.cpp:770 Find→Mark All indicator passed raw 0x0000FF (intended blue, byte-swapped to red); src/merge_helper_widget.cpp:185 merge-conflict annotation text passed raw 0x00204050 (intended dark blue, byte-swapped to olive-brown). Both now BGR-packed correctly — Mark All renders Tailwind blue-500 #3B82F6, conflict text renders #204050. Same root cause as v0.1.85's editor.cpp fix. Six download-size claims on docs/index.html / docs/docs.html / README narrative were inflated by ~1.5 MB across every platform (3.6 / 28.7 / 42.5 / 33.7 / 38.4 MB) while actual artifact bytes were 3.45 / 26.86 / 40.56 / 32.18 / 36.68 MB. All reconciled to within ±0.05 MB of gh release view output. The README install table at lines 262-267 was already correct and used as the cross-check reference. bare-binary marketing claim was both stale AND false: bare binary is 9.71 MB (not 9), AND CI has no strip step so the shipped binary is unstripped (carries 2.65 MB of DWARF debug info; stripped it would be 7.06 MB). Live marketing now reads "under 10 MB (~9.7 MB on Linux x64)" without the misleading "stripped" word; forensic release-notes rows preserved unchanged. The miss: feedback_release_factual_audit.md had been in memory since v0.1.80 saying "run a fact-check pass every release because stale-text-check.sh doesn't catch download sizes" — and the operator (Claude) skipped it under release-time load. Same pattern as v0.1.83's memory→gate promotion. The new gate scripts/verify-download-sizes.sh calls gh release view "v$VERSION" --json assets, finds the closest "X.Y MB" claim across the three docs for each artifact, and asserts drift ≤ ±0.15 MB. Also downloads the Linux tarball and runs file ... | grep ", stripped$" to verify the "stripped" claim is honest. Wired into scripts/release-check.sh as a Phase 2 step. New post-mortem memory feedback_factual_audit_must_be_a_gate.md. CI strip step (drops binary to 7.06 MB) queued for a future release in project_next_release_ci_strip_step.md — separate scope so the CI change can be verified end-to-end. 31/31 ctest pass. |
| v0.1.85 | 2026-05-15 | Markdown Light palette overhaul, Claude-Code-blue selection, neon-orange match highlight, and a real Scintilla byte-order bug fix. The v0.1.84 Solarized MD palette landed yesterday and felt off in front of the user — too rainbow, with the classic yellow-H2-on-pale-paper legibility issue. After iterating through Solarized → jewel-tone purple/magenta/blue → final SSMS, MD Light now reuses the same six-hue palette users already know from .sql files: H1 magenta #FF00FF, H2 blue #0000FF, H3 maroon #7F0000, H4 navy #000080, H5 orange #FF8000, H6 green #008000. Inline code chip on slate-800 (#1E293B) text + slate-300 (#CBD5E1) paper for a denser, more present "code pill" feel. Light theme text selection bumped from VS Code's pale #ADD6FF wash to #5BC8FA — the bright cyan-sky Claude Code uses on its prompt focus rings. Real bug fix in src/editor.cpp:664-679: Scintilla's SCI_INDICSETFORE expects a Win32 COLORREF (BGR byte order), but the indicator setup passed QColor::rgb() & 0xFFFFFF (Qt RGB) — so the double-click word-match indicator had been rendering the wrong color since the feature was first added (#E8A848 clay-orange was painting as a light blue-purple). Now BGR-packed correctly; indicator 9 also bumped to neon orange #FF5500 (alpha 160 fill / 255 outline) so matches pop. Dark + Monokai MD palettes verified unchanged. |
| v0.1.84 | 2026-05-15 | Biggest syntax-highlighting refresh since v0.1.31. Every supported language got reserved-word coverage updated from primary vendor sources (postgresql.org, learn.microsoft.com, tc39.es, cppreference, dart.dev, …). Two brand-new lexers — Plain Text and CSV / TSV — replace previous monochrome fallbacks. Markdown / YAML / CSS palettes refreshed across Light + Dark + Monokai (Markdown H1–H6 contrast gradient, inline-code chip paper-tint, YAML unquoted-value tint, Markdown prose softening, CSS pseudo-class branch). New src/sql_keywords.h carries comprehensive SQL keyword union for six dialects including DuckDB (T-SQL / PG / MySQL / DuckDB / SQLite / Oracle) — MERGE, RETURNING, LATERAL, JSONB, OVER PARTITION BY, WITHIN GROUP, OPENROWSET, ASOF JOIN now all paint as keyword in every .sql tab. New src/lang_keywords.h carries primary-source keyword constants for ~50 languages wired via populateExtraKeywords() in lexerutils.cpp immediately after setLexer(). 11 concrete keyword bugs fixed across R (repeat+return), Groovy (yields→yield+volatile+this), Crystal (next+until), Dart (Record+augment), GDScript (@rpc+@tool+@warning_ignore+namespace+Rect2i+Vector4i), Solidity (blockhash+blobhash for EIP-4844), Mojo (ref), Julia (outer+public+Float16), F# (ValueOption+ValueTuple), Protobuf (WKT siblings), HCL (template directives). 74 sample files in samples/ (one rich exemplar per supported lexer, all synthetic — no PII). Test infrastructure hardened: three previously-stale "Not Run" tests (test_network_policy, test_chart_types, test_fontpack) wired into notepatra_all_tests meta-target. 31/31 ctest pass (was 28/31). Hard-cap paint() closure pattern codified as memory rule for all future custom Scintilla lexers. |
| v0.1.83 | 2026-05-14 | Stale-version-ref sweep + release-check gate. v0.1.82 shipped the security hardening sweep cleanly, but the website had eleven user-facing strings still pointing at v0.1.81 (hero badge, sticky download CTA aria-label + visible text, main download section label, both "Get Notepatra v…" buttons, JSON-LD FAQ "Latest v0.1.81 download sizes" + "as of v0.1.81", page body "As of v0.1.81: 226 file types" lead, lexer paragraph). This release: sweeps all eleven, swaps the latest release card from v0.1.82 → v0.1.83, AND wires a stale-version-ref scanner into scripts/stale-text-check.sh so release-check.sh fails when user-facing version strings drift. The check uses a __V__ placeholder substituted twice (once with current version literal, once with 0\.1\.[0-9]+ regex) and compares match counts on each enumerated phrase. Older versions in CHANGELOG / release-notes / past release-card descriptions are untouched. No C++ / Rust changes — same binary as v0.1.82, all security hardening (install-script hard-fail SHA, cosign verify, 38/38 actions SHA-pinned, AI Base URL validation, credscrub patterns) carries forward unchanged. 31/31 ctest pass. Memory rule promoted from "remember to sweep" → "the gate enforces sweep" (the canonical example of memory-to-gate promotion). |
| v0.1.82 | 2026-05-14 | Security hardening sweep + tab-numbering fix. Outcome of a deep audit across 6 attack surfaces. Install scripts hard-fail SHA: docs/install.sh + docs/install.ps1 previously soft-failed (silently skipped verification) if the SHA256SUMS fetch returned a network error — an MITM that blocked just the sums file would bypass verification. Both now refuse to install if sums are unreachable or the artifact is missing from them. Cosign verification automatically run by install scripts and the in-app auto-updater when cosign is on PATH, with the cert-identity pinned to the literal release workflow + tag (was a permissive regex matching any workflow in the repo). All 38 of 38 GitHub Actions SHA-pinned (was 0/38 — including dtolnay/rust-toolchain@stable which was on a branch ref the action owner could fast-forward at any time). New install-canary.yml workflow diffs the served notepatra.org/install.{sh,ps1} against repo daily; opens an issue on drift. AI Base URL validation in src/preferences.cpp rejects malformed URLs, plain-http public hosts (API key would travel in plaintext), and warn-confirms non-vendor hosts (closes a known "use this faster mirror" phishing pattern). Credential scrubber adds Cloudflare, DigitalOcean, npm, Twilio, Azure AccountKey=, GCP service-account, HTTP auth-header patterns. Tag protection ruleset on refs/tags/v*; Dependabot security updates enabled. release-check.sh gains cargo clippy -D warnings + cargo fmt --check + cargo audit --deny warnings as required gates. Tab-numbering fix in src/mainwindow.cpp: updateTabTitle() used to re-derive the name from index + 1 on every modification event (closing "new 1" made the next keystroke in the surviving "new 2" rename it to "new 1"); m_newCount reset on every launch so session restore brought back "new 5" but the next Ctrl+N created "new 2". Fixed by dynamic max-scan in newFile() + preserve-existing-name in updateTabTitle(). SECURITY.md reconciled with the live ruleset reality. 31/31 ctest pass. All v0.1.81 fixes carry forward unchanged. |
| v0.1.81 | 2026-05-14 | Polish + housekeeping. Linux in-app updater dialog now matches the downloaded file type; deep dependency refresh. src/updater.cpp::installReleaseInteractive() Linux branch was hard-coded to say "move the new AppImage into place" regardless of what was downloaded. We ship the .AppImage for Linux x86_64 only — on Linux ARM64 the picker falls back to the .tar.gz at priority 30, so ARM64 users were told to move an AppImage they didn't have. (The original symptom in issue #12 was reported against v0.1.17, which shipped no AppImage at all — same fallback path.) Now .appimage gets the original wording plus a chmod +x reminder; .tar.gz / .tgz / .tar.xz gets a copy-paste-ready tar xzf … && mv notepatra ~/.local/bin/ example; anything else gets a generic fallback. Closes issue #12 finding 2. (Subtle note: the dialog is rendered by the currently-running binary, so ARM64 users still see the old "AppImage" text on v0.1.80 → v0.1.81; the fix kicks in from v0.1.81 → v0.1.82. Linux x86_64 users get the AppImage path — correct text both before and after.) GitHub Actions matrix bumped past the Node 20 cliff: actions/checkout v4 → v6 (#5), actions/download-artifact v4 → v8 (#4), github/codeql-action v3 → v4 (#8). Same 51 signed artifacts, same cosign + SLSA guarantees. RustCrypto family to 0.11: md-5, sha1, sha2 all 0.10 → 0.11 (#6 / #7 / #11). The 0.10 → 0.11 release moved the digest output from GenericArray to Array (no longer impls LowerHex); compute_hash() updated to use a small hex_encode() helper. MD5/SHA-1/SHA-256/SHA-512 hex output is byte-identical to v0.1.80. sqlformat 0.3.5 → 0.5.0 (#9) with ..FormatOptions::default() for future-proofing. libc 0.2.183 → 0.2.184 (#10). Plus cargo fmt sweep (191 LoC, 5 files, pure whitespace) and four pre-existing cargo clippy -- -D warnings fixes (explicit_auto_deref, if_same_then_else, redundant_closure, two collapsible_match). 130/130 cargo tests + 31/31 ctest still green. All v0.1.80 fixes carry forward unchanged. |
| v0.1.80 | 2026-05-14 | Two paper-cut fixes: Windows .txt file icons + Search Clear button no longer hides the ✕. installers/windows.wxs — <ProgId> now sets Icon="NotepatraExe" IconIndex="0", generating the missing HKCR\Notepatra.Document\DefaultIcon\(Default) = "...\notepatra.exe,0" registry value. Pre-v0.1.80 the MSI registered the ProgId tree but without DefaultIcon; for .txt specifically Windows has a special-cased fallback to the cached txtfile/Notepad icon, so .txt files kept the old Notepad icon after Notepatra was set as default (every other extension flipped correctly). Now matches what the sideloaded installers/register-associations.bat already wrote. src/searchresults.cpp::clear() — clicking Clear in the Project-Search results header no longer hides the ✕ close button. Pre-v0.1.80 it hid both, collapsing two distinct user actions (Clear = wipe contents · ✕ = dismiss panel) into one state with no recovery path — empty panel pinned open with no way to dismiss. Only the Clear button hides itself now (nothing left to clear); the ✕ stays visible. All v0.1.79 focus-handoff fixes carry forward unchanged. |
| v0.1.79 | 2026-05-14 | Double-click-from-file-manager focus handoff fix — Linux X11 and Windows. Targeted patch on top of v0.1.78 — no editor, AI, or core changes. Notepatra is single-instance: when you double-click a file with it already running, the second process forwards the path over the local IPC socket and exits. Pre-v0.1.79, the new tab opened in the background while the file manager stayed focused, and on Linux a busy-cursor spinner ticked until the WM's 15 s timeout. Linux X11 fix: src/mainwindow.cpp::handleRemoteOpen now ports wmctrl's three-part activate_window sequence — _NET_ACTIVE_WINDOW (source=2 / pager) + xcb_map_window + xcb_configure_window(STACK_MODE_ABOVE) + round-trip fence (xcb_get_input_focus_reply) before disconnect. The xcb_configure_window ConfigureRequest path bypasses Cinnamon/Muffin's focus-stealing prevention which was demoting plain activate ClientMessages to _NET_WM_STATE_DEMANDS_ATTENTION. StartupNotify=false in the generated .desktop stops the infinite spinner. Spec-compliant remove: ID="<id>" startup-notify message sequence also wired in src/main.cpp::sendStartupNotifyComplete for other DEs that respect the protocol. Windows fix: second-instance process calls AllowSetForegroundWindow(ASFW_ANY) before exiting so the running instance's SetForegroundWindow() succeeds instead of just flashing the taskbar — Explorer hands foreground rights to the newly spawned process, not the running one, so we surrender them before exit. Belt-and-braces TOPMOST flip guarantees z-order. handleRemoteOpen signature extended to forward DESKTOP_STARTUP_ID (captured before QApplication's constructor strips it from the env) for _NET_STARTUP_ID property handoff. libxcb linked explicitly on Linux. All v0.1.78 encoding fixes carry forward unchanged. 31/31 tests pass. |
| v0.1.78 | 2026-05-13 | Encoding & file-open fixes — UTF-16 / UTF-32 BOM parity with Notepad++. Reordered BOM detection in rust-core/src/file_io.rs so UTF-16 LE text (50 % nulls by design — SQL Server Generate Scripts, sqlcmd -o, PowerShell Out-File, Java -Dfile.encoding=UTF-16) is no longer mis-flagged as binary. UTF-32 LE/BE detected before UTF-16 LE so t |