Skip to content

singhpratech/notepatra

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

491 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Notepatra — click for notepatra.org

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

Website · Download · Features · The Story · Install · Plugins · AI · Contributing · Security · Changelog

Build CodeQL Release License Security

Total downloads GitHub stars Forks Open issues Last commit


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.116 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.

JSON Tools (inbuilt)

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

HTML Tools (inbuilt)

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

Bracket Tools (inbuilt)

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

SQL Formatter (inbuilt)

  • 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 entriesOllama (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

# Easiest path: Ollama
curl -fsSL https://ollama.com/install.sh | sh
ollama pull qwen2.5-coder:3b   # 2 GB, best for code on CPU-only / 16 GB RAM
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 waysAI 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.
  • ExportPNG / 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

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.116 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.

Or download manually — Latest release: v0.1.116

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.116.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.116_amd64.deb sudo apt install ./notepatra_0.1.116_amd64.deb — installs to /opt/notepatra/ + symlink at /usr/bin/notepatra, hicolor icons, .desktop registration. ARM64: replace amd64arm64.
🐧 Fedora / RHEL / CentOS Stream / Rocky / Alma (x64 + ARM64) notepatra-0.1.116-1.x86_64.rpm sudo dnf install ./notepatra-0.1.116-1.x86_64.rpm — same layout as the .deb. ARM64: replace x86_64aarch64. 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.116-x86_64.AppImage chmod +x Notepatra-0.1.116-x86_64.AppImage && sudo cp Notepatra-0.1.116-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:

OS Artefact Silent admin install
🪟 Windows notepatra-local-ai-0.1.116.msi msiexec /i notepatra-local-ai-0.1.116.msi /quiet — installs to C:\Program Files\Notepatra Local AI\, distinct UpgradeCode so SCCM treats it as its own product. Add/Remove Programs shows "Notepatra Local AI".
🐧 Debian/Ubuntu x64 notepatra-local-ai_0.1.116_amd64.deb sudo apt install ./notepatra-local-ai_0.1.116_amd64.deb
🐧 Debian/Ubuntu ARM64 notepatra-local-ai_0.1.116_arm64.deb sudo apt install ./notepatra-local-ai_0.1.116_arm64.deb

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.116_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.116 for the lite build and Notepatra v0.1.116 for the full build (DuckDB bundled), plus Notepatra Local AI Lite v0.1.116 / Notepatra Local AI v0.1.116 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.

# Linux / macOS — checksum
curl -sL -O https://github.com/singhpratech/notepatra/releases/latest/download/SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missing

# Anywhere — cosign verify (Sigstore)
# Replace `linux-x64` with `linux-arm64` if you downloaded the ARM64 build.
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

# Anywhere — SLSA build provenance
# Replace `linux-x64` with `linux-arm64` if needed.
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:

  1. 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).
  2. Stream-download it to ~/Downloads/*.part with a cancellable progress dialog.
  3. 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.
  4. Atomic-rename .part → final name once verified.
  5. Hand off to the OS installermsiexec /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 QScintilla from source (brew's version links Qt6)
./build.sh
Windows (MSVC)

Prerequisites

  1. Visual Studio 2022 with the "Desktop development with C++" workload
  2. Qt 5.15.2 for msvc2019_64 — install via Qt Online Installer or aqtinstall
  3. CMake ≥ 3.16 — winget install Kitware.CMake or cmake.org/download
  4. 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:

// myplugin.cpp
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) {
        // Your magic here — transform text, return malloc'd result
        // Return NULL to keep text unchanged
    }
}
# Linux
g++ -shared -fPIC -o myplugin.so myplugin.cpp

# macOS
clang++ -shared -o myplugin.dylib myplugin.cpp

# Windows
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.116. 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:

./build.sh --tests

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.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 restoreMainWindow::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 OFFimport 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 (yieldsyield+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 the shared FF FE prefix doesn't collide. No-BOM UTF-16 sniffed via even/odd null-column ratio (matches Notepad++/uchardet). Manual UTF-32 decoder added (encoding_rs intentionally doesn't ship one). src/editor.cpp save path now preserves UTF-16 / UTF-32 BOMs on round-trip — pre-v0.1.78 the BOM was silently stripped because QTextCodec("UTF-16LE") emits no BOM. Encoding menu lists 4 new BOM variants: UTF-16 LE BOM, UTF-16 BE BOM, UTF-32 LE BOM, UTF-32 BE BOM. 11 new rust-core unit tests + new rust_core_tests ctest target wires cargo test into release-check.sh. 31/31 tests pass.
v0.1.77 2026-05-11 Expanded chart catalogue (4 → 12), hover tooltips, modal viewer + PNG export, AI picks the chart for the data. 8 new chart types in src/chartrender.cpparea, horizontal-bar, stacked-bar, stacked-horizontal-bar, grouped-bar, donut, histogram, boxplot — all via QtCharts so they work in both lite (~9 MB) and full builds. Histogram auto-bins a numeric column; boxplot computes 5-quantile per group. Multi-series specs (grouped-bar, stacked-bar, stacked-horizontal-bar) take y as array of column names. Rewrote Data Analyst system prompt with explicit data-shape → chart-type mapping so the AI picks the right chart instead of defaulting to bar. New src/chart_modal.{h,cpp} — every inline chart now has a ⛶ expand button; click → 960×640 modal with chart at full size + Save as PNG (HiDPI-correct, defaults to ~/Pictures/) + Copy image (clipboard) + Close. Hover tooltips wired on every series — bars show category/value, line/scatter/area show x/y, pie/donut show label/value/percentage, boxplot shows full 5-quantile summary. New test_chart_types.cpp (26 sub-checks); updated test_ai_dataanalyst.cpp for the new 12-type catalogue. 30/30 ctest pass. All v0.1.75 features (font-pack downloader, MSI Component-GUID fix, AI dock fixes, network policy allowlist) carry forward.
v0.1.75 2026-05-11 Runtime font-pack downloader — 27 premium open-source fonts on demand. New Settings → Manage Fonts… dialog lists a curated catalogue of JetBrains Mono, Fira Code, Cascadia Code, Source Code Pro, IBM Plex Mono, Hack, Geist Mono, Inconsolata, Roboto Mono, Fira Mono, Noto Sans Mono, Space Mono, Anonymous Pro, Victor Mono, Comic Mono, Inter (variable), Roboto, Manrope, Source Serif 4 (variable), Merriweather — every entry SIL OFL 1.1 / Apache 2.0 / MIT, grouped by category (Code · Monospace / UI · Sans-serif / Serif · Prose / Display · Distinctive). Pick rows, click Install, dialog downloads each TTF / OTF from its pinned upstream HTTPS URL to ~/.local/share/notepatra/fonts/ and registers it with QFontDatabase immediately — no restart. Bare binary stays at ~9 MB because nothing is bundled; same on-demand pattern as the Charts Pack / DuckDB extensions. HTTPS-only manifest enforced by test_fontpack.cpp so network attackers can't swap bytes. Air-gapped friendly: drop TTFs into the font-pack directory manually; startup hook (NotepatraFontPack::loadInstalledFonts()) scans on every launch. New files: src/fontpack.{h,cpp} (~300 LoC manifest + Installer queue), src/fontpack_dialog.{h,cpp} (Qt5 dialog), test_fontpack.cpp (23 sub-checks). 29/29 ctest pass. All v0.1.74 fixes carry forward unchanged.
v0.1.74 2026-05-11 notepatra-local-ai MSI upgrade fix + website cleanup. Targeted patch on top of v0.1.73 — no editor, AI, or core changes. Fixes the Windows MSI upgrade flow for the notepatra-local-ai (cloud-free) flavor: when users had both regular and local-ai installed side-by-side, upgrading local-ai from v0.1.72/v0.1.73 dropped into the Repair/Remove maintenance UI instead of running MajorUpgrade. Root cause: three hardcoded Component GUIDs (MainExecutable / ApplicationShortcut / DesktopShortcut) were shared between both flavors' MSIs — Windows Installer reference-counted them as the same component, so the upgrade couldn't release v0.1.72/v0.1.73 components while the regular install's reference held them. Fixed by switching all three to Guid="*" (WiX auto-generates a deterministic GUID from each component's KeyPath, which differs by INSTALLFOLDER between flavors). Also added AllowSameVersionUpgrades="yes" to <MajorUpgrade> so re-running same-version MSIs runs install flow instead of maintenance UI. Website cleanup: docs/index.html no longer shows slim "horizontal line" rows for older releases — only the latest detail card remains, plus the single "See every release on GitHub" link. v0.1.0–v0.1.73 live exclusively on GitHub Releases. 82-line deletion. Deferred to v0.1.75: runtime font-pack downloader (Settings → Manage Fonts… with ~25 premium open-source fonts fetched on demand). All other features carry forward from v0.1.73.
v0.1.73 2026-05-11 AI dock blank-on-reopen fix (patch on v0.1.72). Two coordinated fixes for the close → reopen flow. The red ✕ close button in the panel header now routes through MainWindow::setAiDockVisible(false) (via a new closeDockRequested signal) instead of poking parentWidget()->setVisible(false) directly — pre-fix the × bypassed Config::aiDockVisible persistence, the toolbar button's checked state, and the splitter rebalance bookkeeping, so subsequent re-opens via the toolbar sometimes showed a 0-px-wide blank dock. Additionally rebalanceAiDockSplit() now detects "collapsed slot" (width ≤ 40 px) and re-applies the 60/40 split even when m_aiDockSizedOnce is true — pre-fix the bailout protected against clobbering mid-session user drags but also blocked the rescue path after a full hide → show cycle. Two new regression scenarios (test_ai_fullscreen_exit.cpp S16 + S17): hide-show round-trip width assertion + ✕ close → toolbar reopen round-trip. 28/28 ctest pass on the lite build, 53 sub-checks in test_ai_fullscreen_exit (up from 47 in v0.1.72). Installer matrix from v0.1.72 carries forward unchanged.
v0.1.72 2026-05-11 Enterprise-ready Linux installers + cloud-free notepatra-local-ai build. Notepatra now ships .deb for Debian/Ubuntu (x64 + ARM64), .rpm for Fedora/RHEL/CentOS/Rocky/Alma (x64 + ARM64), and a universal Notepatra-0.1.72-x86_64.AppImage for Arch / openSUSE Tumbleweed / Manjaro / any glibc 2.38+ distro. All three formats install to /opt/notepatra/ with symlink at /usr/bin/notepatra, hicolor icons across all sizes, full .desktop registration, and bundled QScintilla 2.14.1 so the same upstream binary runs across distro packaging differences. Admins push silently via apt install ./*.deb / dnf install ./*.rpm / Ansible-friendly. notepatra-local-ai cloud-free flavor (built via -DNOTEPATRA_NO_CLOUD=ON CMake flag, shipped as notepatra-local-ai_0.1.72_*_.deb) — the binary physically cannot reach api.openai.com / api.anthropic.com / api.mistral.ai / generativelanguage.googleapis.com / openrouter.ai / api.groq.com / api.together.xyz / api.deepseek.com / api.cohere.com / api.x.ai / any other public LLM endpoint. Every QNetworkAccessManager request goes through NotepatraNetworkPolicy::isPrivateNetworkHost() (49-case unit-tested allowlist of loopback / RFC1918 / CGNAT-100.64/10 / IPv6 ULA fc00::/7 / .local/.lan/.internal/.intranet/.corp/.home suffixes). The cloud-URL paste box is stripped from the AI panel; the Backend dropdown only shows Ollama + llama.cpp. apt Conflicts ensures notepatra and notepatra-local-ai can't coexist. notepatra --version self-identifies as Notepatra v0.1.72 (cloud-free / local-ai) so users can verify. Ollama, llama.cpp, LM Studio, Jan, vLLM, self-hosted Ollama on the LAN — all continue to work. For regulated industries (finance / healthcare / legal / gov), data-sovereignty regions (EU / India / China), air-gapped fleets. New files: src/network_policy.{h,cpp} (~95 LoC, 49-case unit test), installers/debian/build-deb.sh, installers/rpm/{notepatra.spec.in, build-rpm.sh}, installers/appimage/build-appimage.sh. 28/28 tests pass on both regular and cloud-free builds.
v0.1.71 2026-05-11 AI Interaction Log — audit every cloud + local LLM exchange. Tools → AI Interaction Log… opens a viewer onto ~/.config/notepatra/ai-logs/interactions.db (SQLite, WAL mode) that records every cloud (OpenRouter, OpenAI, Azure OpenAI, Ollama Cloud) + local (Ollama, llama.cpp, LM Studio / Jan / vLLM via OpenAI-compat) call: timestamp, session id, backend tag, model, mode (chat/coding/data), role (user/system/assistant/tool_call/tool_result), full content, tool name + args + result, prompt + eval tokens, elapsed ms, error string. Rows older than 7 days are pruned on every app start; DB file capped at 50 MB. Viewer dialog has backend/mode/model filters, row-click → full content panel, Export JSON (up to 10,000 events), Prune now button, and a Toggle "Log AI interactions" for in-place opt-out. Config::aiInteractionLogging default ON — privacy-as-transparency by default, flip off anywhere and the recorder becomes a no-op (DB file isn't even opened). Credential scrubber before every write masks Bearer …, OpenAI sk-…, Anthropic sk-ant-…, GitHub PATs (ghp_/ghs_/gho_/ghu_/ghr_…), AWS AKIA…, Google AIza…, and -----BEGIN ... PRIVATE KEY----- PEM blocks. No network egress — purely local SQLite. New OllamaClient::setMode("chat"|"coding"|"data") lets the panel tag each turn for filter purposes. Records at every responseStats emit site (Ollama /api/generate + /api/chat done frame, OpenAI-compat [DONE] frame, OpenAI-compat finish_reason frame); each resolves the backend tag from m_baseUrl (openai.azure.comazure-openai, api.openai.comopenai, ollama.comollama-cloud, openrouter.aiopenrouter, else openai-compat). New files: src/ai_interaction_log.{h,cpp} (~280 LoC), src/ai_log_dialog.{h,cpp} (~250 LoC). 27/27 tests pass.
v0.1.70 2026-05-11 AI cleanup + Notepad++-style session persistence + Data Mode actually shows what's attached. Closing the app no longer prompts — every open buffer (file-backed, untitled, modified, pristine) is serialised to ~/.config/notepatra/session/session.json with full unsaved content + cursor + modified flag and silently restored on relaunch. Individual tab close still shows Save / Discard / Cancel. Legacy recovery_*.txt files now only run as a fallback when session.json is absent. Removed hard-coded model capability gates — the "isn't strong enough for Data mode" / "doesn't support tool calls" error bubbles + amber dropdown coloring are gone. Substring allowlists rotted with every release (Gemma 4 26B got rejected as "too small" despite native tool calling). Context gates (no folder / no DB connection) stay. Data Mode now shows what's attached — green chip below Manage Connections reads ✓ Attached: <conn name> (N tables) — visible to the model via query_sql; multiple connections render ·-joined; broken ones go red. Schema injected into the Data system prompt — saved connections + first ~30 tables embedded into every Data-mode send with hard "these are the ONLY data sources; do NOT speculate about the workspace" preamble. New DbConnections::listTables() per-driver helper (sqlite_master / INFORMATION_SCHEMA / sys.tables) drives both. Chat bubbles get breathing room — padding 14/16 → 22/24, gap 14 → 26 px, line-height 1.55 → 1.7. QTextDocument::setDocumentMargin(0) + removed +8 px slop kill trailing whitespace under short replies. Viewport-resize event filter re-fits bubble heights on panel resize. QODBC connection string composition fix — SQL Server now builds DRIVER=…;SERVER=host,port;DATABASE=…;UID=…;PWD=…; via setDatabaseName() instead of the per-field setters that don't compose. Conversation-history threading fix in ollama.cpp — uses /api/chat with spliced history when prior messages exist. SVG icons replacing emoji codepoints (bot, git-branch, database, image, file, lock, unlock, lightbulb, wrench, x, check, folder, alert-triangle). Website / About claim updated: "local-first, cloud-optional" — Notepatra supports 6 cloud backends, the "Zero cloud" claim was misleading.
v0.1.69 2026-05-10 Critical fix — AI Coding / Data mode no longer locks tool buttons. Coding Mode and Data Mode auto-fullscreen the AI dock by emitting fullscreenToggled(true) directly (the expand button is hidden in those modes). Pre-v0.1.69, clicking any tool button (Project Search, Terminal, REST Client, JSON / HTML / Bracket Tools, SQL Formatter, Compare, Welcome) while in those modes appeared to do nothing — the tab was added but m_tabs stayed hidden behind the splitter squashed to 100% AI dock. Root cause: AIPanel::forceExitFullscreen() only un-checked the expand button; in Coding / Data the button was never checked, so the function was a no-op. Fix: emit fullscreenToggled(false) directly when the button isn't checked. MainWindow's restore handler is idempotent. test_ai_fullscreen_exit.cpp extended from 14 to 23 sub-checks across 6 scenarios — two new scenarios (S5: Coding + Project Search, S6: Data + Terminal) drive the Coding / Data path under offscreen Qt and assert m_tabs->isVisible() after the tool action triggers. They FAIL on pre-v0.1.69 code and PASS on the fix. Runs in CI on every commit — the v0.1.67 / v0.1.68 silent-failure class is now wired-out. 27/27 ctest pass on the lite build.
v0.1.68 2026-05-10 AI dock auto-exits fullscreen on editor tab switches too — closes the v0.1.67 gap. v0.1.67 wired exitAiFullscreenIfActive() into 14 tool-tab open sites (Project Search, Terminal, Git, REST Client, SQL Formatter, JSON/HTML/Bracket Tools, Compare, Welcome) but editor-tab switching (Ctrl+Tab, clicking a tab in the tab bar, double-clicking a Project Search result, File > Open) still left the newly-focused tab hidden behind the fullscreen AI dock — the focus shift was real but invisible, so the editor felt frozen. v0.1.68 instruments the QTabWidget::currentChanged signal directly so every tab-focus change collapses the dock. Gated by a one-shot m_skipAiAutoExitOnNextTabChange flag that newFile() (Ctrl+N) sets right before setCurrentIndex(idx) — preserves the v0.1.61 background-tab UX rule (Ctrl+N while AI is fullscreen creates the new editor in the background so you don't lose AI flow). openFile() deliberately does NOT set the flag, so user-initiated file opens correctly collapse the dock. The 14 explicit calls from v0.1.67 remain in place as belt-and-braces no-ops. 26/26 deterministic tests pass on the lite build.
v0.1.67 2026-05-10 AI Assistant: three independent per-mode conversations. The Chat / Coding / Data toggles each now keep their own conversation history. Pre-v0.1.67 a single m_messages vector was shared across all three modes — flipping from Coding into Data mode left the previous bubbles on screen but ran the next model turn under the data-analyst system prompt, producing erratic first replies that referenced the wrong mode's tools. v0.1.67 replaces m_messages (32 references) with three vectors (m_chatMessages, m_codingMessages, m_dataMessages) routed through a new activeMessages() helper. On-disk JSON format bumps version: 1version: 2 to carry all three vectors; old files migrate into the chat vector with coding/data empty ({version: 1, messages: [...]} and bare top-level arrays both supported). clearChat() now clears only the active vector, not all three. New saveChatHistoryNow() force-saves at every key transition (user submit, stream end, mode switch, Reset) — the 2-second debounce stays as a backstop for streaming-chunk flurries. New test_ai_chat_history.cpp regression suite (61 sub-checks) covers cross-mode partitioning, the migration paths, save/reload round-trip, and clearChat() semantics. 26/26 deterministic tests pass on the lite build.
v0.1.66 2026-05-10 Manage Connections UX + local SQL Server harness + website cleanup. Database connection manager gets a Preset dropdown with seven ready-to-use templates: SQL Server (localhost ODBC), SQL Server Express (named instance + Windows Auth), Azure SQL Database (TLS-required), PostgreSQL (localhost), MySQL/MariaDB (localhost), SQLite (file), DuckDB (file or :memory:). Per-driver smart defaults: onDriverChanged() auto-populates the default port (1433 / 5432 / 3306) when still 0, sets per-driver placeholders on every field, and renders a new driverHint label — amber + per-OS install commands when the Qt SQL plugin is missing (covers Microsoft msodbcsql18 setup for Linux / macOS / Windows), green usage tip when present. Local SQL Server Docker harness: new docker/sql-server-local.yml runs mcr.microsoft.com/mssql/server:2022-latest on 127.0.0.1:1433, and bash scripts/sql-server-local-setup.sh brings it up + seeds NotepatraTest sample tables + registers a connection automatically. --teardown / --wipe flags. Comprehensive "How to connect" docs: per-database walkthroughs (SQL Server local + remote + Windows Auth + Azure SQL TLS; PostgreSQL with sslmode + Unix-socket; MySQL/MariaDB with SSL_CA; SQLite file picker; DuckDB multi-mode Database field), driver-availability matrix per OS, troubleshooting for the five most common connection failures. Website cleanup: removed obsolete v0.1.23 → v0.1.24 Windows MuiCache FAQ; collapsed ~60 historical version cards (~2500 lines) into a single "See every release on GitHub →" link. docs/index.html dropped from 4787 to 2201 lines (54% smaller). Version history housekeeping: v0.1.63 + v0.1.64 GitHub Releases backfilled with notes-only entries ("rolled into v0.1.65") so the Releases page shows a continuous v0.1.62 → v0.1.66 ladder. 25/25 tests pass.
v0.1.65 2026-05-10 Hotfix: QJsonDocument(QJsonValue) crash in WebEngine code path. The vega_chart_renderer.cpp WebEngine path used a QJsonDocument constructor that doesn't exist in Qt5 (only QJsonObject / QJsonArray constructors are valid). The bug was invisible on dev machines without libqt5webengine5-dev installed (the broken line lives behind #ifdef NOTEPATRA_WITH_WEBENGINE) but crashed every CI runner that had WebEngine — Ubuntu 24.04 + Windows install-qt-action. Net result: v0.1.63 GitHub Release silently never published (only the macOS-arm64 artifact compiled, under brew qt@5's patchset); v0.1.64 inherited it and would have produced a -full flavor build failure. Fix: wrap the spec string in a 1-element QJsonArray, serialize, and slice off the surrounding [ ] to get a valid JS string literal. Plus #include <QJsonArray> at the top. All v0.1.64 behavior (lite default, Charts Pack required card, plugin_loader scaffolding, install-handler) ships unchanged.
v0.1.64 2026-05-10 Lite mode by default — bare binary stays small, charts move to an on-demand pack. v0.1.63 bundled QtWebEngine into every release (~95 MB extra on macOS/Windows). v0.1.64 flips NOTEPATRA_WITH_WEBENGINE from ON to OFF and the default Linux x64 binary drops to 9.2 MB with zero WebEngine deps (verified via ldd). Same speed, same features, minus the inline Vega-Lite renderer. "Charts Pack required" card replaces the old rebuild-hint stub. When generate_chart fires on a lite binary, instead of the old "rebuild with -DNOTEPATRA_WITH_WEBENGINE=ON" stub label users now see a proper theme-aware card with title "📊 Chart rendering requires the Charts Pack", description from plugin_loader::packDescription(), size + download metadata, and two buttons: [Install charts pack] (opens the GitHub Releases page for the current tag with platform-specific download instructions) + [View JSON instead] (appends the Vega-Lite spec as a fenced code block under the card). New signals: installRequested(), viewJsonRequested(QJsonObject). New accessor: isLiteStub(). src/plugin_loader.{h,cpp} scaffolding with NotepatraPlugins::isInstalled(name), installPath(name), pluginDir(), packDescription, approximateDownloadSize, manualInstallDocUrl. Uses QStandardPaths::AppDataLocation (Linux → ~/.local/share/notepatra/plugins/; macOS → ~/Library/Application Support/notepatra/plugins/; Windows → %APPDATA%/notepatra/plugins/). Registered packs: charts (95 MB), pdf (12 MB, v0.1.66 candidate). Linux full-flavor CI artifacts: Linux x64 + ARM jobs each run a second build pass with WebEngine ON, producing notepatra-linux-x64-full.tar.gz + notepatra-linux-arm64-full.tar.gz alongside the lite defaults. SHA-256, cosign signing (keyless OIDC), and SLSA build provenance all extended. macOS + Windows full-flavor artifacts ship in v0.1.66 (need careful macdeployqt / windeployqt second-pass work plus testing across the existing OpenSSL / QScintilla / signing pipeline). Tests: test_vega_chart.cpp extended with two new subcases — lite-mode button → signal wiring (find both QPushButtons, click each, assert signals fire exactly once with the round-tripped spec) and plugin_loader sanity. All 25 tests pass on the lite build.
v0.1.63 2026-05-10 Data Analyst charts — Vega-Lite renderer scaffold. New generate_chart AI tool emits a Vega-Lite spec; Notepatra renders it inline in the chat via a QWebEngineView-backed VegaChartRenderer. Picked Vega-Lite (over Plotly / Chart.js / Qt Charts / QCustomPlot) because the grammar-of-graphics spec format is the easiest for an LLM to emit correctly first try — mark + encoding + data is ~220 chars for a bar chart with a public $schema for validation. Coverage: bar (V/H/stacked), line, area, pie/donut, scatter, histogram (via bin), heatmap (via mark: "rect"), box-plot. Optional QtWebEngine — new NOTEPATRA_WITH_WEBENGINE=ON CMake option defaults ON when Qt5::WebEngineWidgets is found; auto-downgrades to OFF with a warning otherwise. When OFF, VegaChartRenderer becomes a stub QLabel showing the rebuild hint, so the slim ~9 MB build still compiles + runs. AI tool registration: generate_chart(spec, title?, id?)spec is the Vega-Lite JSON object, validated for mark + encoding keys (relaxed for composite specs using layer / hconcat / vconcat / repeat / facet). 100 KB spec size cap. Inline rendering: tool result triggers a styled QFrame#chartCard (1px border, 8px padding) hosting the renderer + optional title + hidden error label connected to renderError. Inserted into the chat content layout above the stretch. CI installs WebEngine on every runner: .github/workflows/build.yml updated — Linux x64 + ARM add qtwebengine5-dev + libqt5webenginewidgets5 to apt; Windows install-qt-action modules 'qtcharts''qtcharts qtwebengine'; macOS brew qt@5 already includes it. Tests: new test_vega_chart with 5 subcases (construction, setSpec, registry contains generate_chart, valid/rejected specs, composite specs); existing test_ai_tools hardcoded tools.size() == 12 relaxed to >= 12. Pptx export, "explain this chart" modal, and Poppler-Qt5 PDF→PNG rendering for vision models are deferred to v0.1.66+.
v0.1.62 2026-05-10 VS Code-parity Git in Coding mode. The "real" Source Control work the v0.1.61 release notes promised, coordinated by two parallel implementation agents in isolated worktrees. Per-hunk stage / revert from the editor gutter — click any green / red / blue line marker → new GutterHunkPopup anchors at that line showing the hunk's before-vs-after content (embedded DiffView) plus Stage hunk / Revert hunk / Copy diff buttons. Patch synthesizer with safety rails: new src/git_hunk_apply.{h,cpp} emits a unified diff + pipes to git apply --cached --whitespace=nowarn via QProcess. Inline safety mirrors ai_tools.cpp::resolveSafePath (workspace-anchor + canonical-path + credentials deny-list *.pem/*.key/~/.ssh///etc/passwd etc. + 5000-line hunk cap). After successful apply, GitGutter::updateGitGutter() re-reads the file and refreshes line markers atomically. CompareWidget per-hunk Stage / Revert buttonssetGitContext(repoRoot, filePath) enables a scrollable hunk strip docked between the toolbar and file headers; one row per contiguous diff-row run formatted as Hunk N/M · rows a–b · [Stage hunk] [Revert hunk] [Jump →]. Marker-based merge resolution helper — new src/merge_helper.{h,cpp} scans column-0 <<<<<<< / ======= / >>>>>>> markers (nested text-inside-content treated as text); src/merge_helper_widget.{h,cpp} renders Take ours / Take theirs / Take both / Jump → per region + QScintilla annotation labels. GitPanel — files with UU status now show a Resolve button instead of +/-. Full 3-way LOCAL/BASE/REMOTE merge editor remains v0.2 scope. Editor wiring: src/editor.{h,cpp} enables margin-3 sensitivity and routes marginClickedGitGutter::hunksForFile() / hunkIndexForLine() → popup at the screen position computed via SCI_POINTXFROMPOSITION / SCI_POINTYFROMPOSITION. 23/23 deterministic regression tests pass; stale-text audit returns 21/21 surfaces matching canonicals.
v0.1.61 2026-05-10 UX overhaul: smarter gating, vision drops, coding redesign. Thirteen distinct improvements + three bonus chrome consolidations, coordinated by five parallel agents (four research, two implementation in isolated worktrees). Coding area redesigned: the top QTabWidget split between Chat and Composer is GONE — replaced by a single unified conversation surface with a 3-segment toggle (Chat / Compose / Agent) at the BOTTOM of the panel, matching the iOS/Slack keyboard-accessory mental model that Continue.dev, Copilot Chat, and Cursor 3.0 all converged on. ChatModeSegment enum replaces m_chatTabs->currentWidget() checks; EditPlanList reparented to the chat content layout and surfaced inline when the agent emits dry_run write_file / apply_diff. Vision drag-and-drop: drop image (PNG/JPG/WEBP/GIF/BMP) or document (PDF/DOCX/PPTX) onto the AI dock; if the active model isn't vision-capable, a styled error bubble lists alternatives — local qwen2.5vl:7b / gemma3:4b, cloud claude-sonnet-4-6 / gpt-5 / gemini-2.5-flash. Detection: Ollama via /api/show capability cache (conservative-false on empty cache to avoid silent-drop trap); cloud via May-2026 prefix allowlist. Smart input gating: updateInputAvailability() now fires on QEvent::EnabledChange via an event filter, eliminating the v0.1.59 race where the dropdown enabled silently after currentTextChanged and the input stayed disabled until manual wiggle. Placeholders rewritten with concrete next-action commands per state (ollama serve, ollama pull qwen2.5-coder:7b, click ⚙). Context guards: Coding mode without a workspace → pointer to File → Open Folder; Data mode without a saved DB connection → pointer to Manage Connections. Runtime capability response: Coding mode + non-tool-capable model → refusal card listing alternatives; Data mode + insufficient model → recommend qwen2.5-coder:14b local or cloud Claude / GPT / Gemini / DeepSeek. File explorer Coding-only: explorer sidebar visible only when Coding mode is active; new HiddenPathProxy : QSortFilterProxyModel adds right-click "Hide "X"" with Config::explorerHiddenPaths persistence and "Show hidden (N)" undo. Copy buttons on user bubbles: both widget streaming path and HTML transcript path expose ⧉ copy below each user message. Bonus chrome consolidations: Coding AND Data auto-fullscreen (v0.1.57 did Coding only); ⛶ expand button hidden in those modes (only ✕ remains); 🔒 Share file checkbox visible only in Coding. Standalone Git toolbar shortcut dropped — Plugins → Git Integration menu still works; full VS Code-parity Source Control in Coding mode lands in v0.1.62 with the agent-A roadmap (90% of infrastructure already exists). 23/23 deterministic regression tests pass; stale-text audit returns 21/21 surfaces matching canonicals.
v0.1.60 2026-05-10 Stale-text drift killed — About dialog matches reality + wired audit. A 24-hour follow-up to v0.1.59. The in-app About dialog still claimed "100+ file types · 48 language lexers · Local AI via Ollama / llama.cpp / OpenAI-compatible" — frozen since v0.1.31; the version number auto-flowed via NOTEPATRA_VERSION at compile time, but the feature counts in the body string did not. The GitHub repo description (rendered on github.com and in search snippets) was even further out of sync at "~4 MB bare binary, 100+ file types". v0.1.60 fixes both: the App About body now reads 226 file extensions · 92 language lexers and 6 AI backends — Ollama / llama.cpp / OpenRouter / Ollama Cloud / OpenAI / Azure OpenAI; the GitHub repo description (set via gh repo edit) carries the same numbers. Wired audit so this can't drift again: new scripts/stale-text-check.sh has canonical counts at the top (LEXER_COUNT=92 / FILE_EXT_COUNT=226 / BACKEND_COUNT=6) and 21 grep-asserts across every surface — App About body, README intro + releases-table row, docs/index.html stat cards + meta description + version-card, docs/docs.html attribution lines, CHANGELOG, release_notes/, and the live GitHub repo description queried via gh repo view. Wired into scripts/release-check.sh as a sub-step under ── stale-text audit (feature counts) ── — every release preflight now runs it automatically. Misc cleanups: .github/ISSUE_TEMPLATE/bug_report.yml version placeholder bumped from v0.1.8 to v0.1.60; SECURITY.md verification examples bumped from v0.1.0 to v0.1.60 so copy-paste commands work without first noticing the URL is for the very first release; AGENTS.md test-build target replaced an outdated five-test list with the notepatra_all_tests meta-target; Language-menu code comment in src/mainwindow.cpp fixed from "78 languages" to "92 lexers / 226 file extensions". 21/21 deterministic regression tests pass; stale-text audit returns 21/21 surfaces matching canonicals. Bare binary unchanged — pure copy + script changes, no new deps.
v0.1.59 2026-05-10 Input gating + author-link refresh. Two small UX fixes. The chat input + Send button now disable across all three modes (Chat / Coding / Data) whenever the model dropdown is in a placeholder/error state ((detecting…), (Ollama offline), (no models installed), (API key required)) — until now the field stayed editable and Send-time was the first signal that no model was usable. State-specific placeholders explain why (e.g. Ollama is offline — start it ('ollama serve') and click ↻ Refresh.). Implementation is one currentTextChanged connect + a parenthesis-convention helper, so all 9 dropdown-disable call sites are covered without touching them. Send button gains a :disabled rule (45% label opacity) so the dock visibly tells the user the action is unavailable. The existing sendPrompt() guard stays as a backstop. Plus: the "Prateek Singh" credit on the in-app About dialog and on both website footers (docs/index.html, docs/docs.html) now links to the author's blog at https://theaivibe.org/about. Bare binary unchanged at 7.73 MB stripped. 21/21 deterministic tests pass.
v0.1.58 2026-05-09 Composer wiring complete — Slices B/C/D end-to-end. v0.1.57 shipped the Composer surface but the Composer tab body was a literal placeholder reading "coming in Slice B/C/D". v0.1.58 closes the loop: EditPlanList is now the Composer body, dry_run write_file/apply_diff results route into Edit Plan rows, Apply All / Apply Selected writes files atomically (.tmp + rename) and fires fileWrittenByAgent so the editor reloads. Workspace root is synced so per-row paths render relative (src/foo.cpp) instead of absolute. composerMode=true is now passed to AiSystemPrompt::build whenever the user is on the Composer tab in CodingStrict intent — the existing composerModeLayer instructs the model to ALWAYS pass dry_run: true, so file writes never bypass the Edit Plan review step. Bare binary unchanged at 8.93 MB stripped. 21/21 deterministic tests pass.
v0.1.57 2026-05-09 Coding-mode revamp (Composer + Edit Plan + per-hunk apply), @file mention picker, Ctrl+I inline edit, agentic git tools, multi-cursor editing, fullscreen AI dock, mode-aware model coloring, Windows mojibake fix, crash hardening. Major UX release for the AI dock. Coding mode revamp (Slices A→F): separate Composer tab in the AI dock alongside Chat, with a multi-file Edit Plan list — each entry shows path + diff preview, per-hunk Accept / Reject checkboxes, and an action bar at the bottom (Apply All / Apply Selected / Reject All). Coding mode now auto-expands the AI dock to fullscreen the moment you flip the switch (and restores the previous width when you switch back to Chat / Data). @file mention picker — type @ in the chat input and a workspace-files popup surfaces; selected files are prepended to the prompt as context. Ctrl+I inline edit: select code in the editor → modal asks "what should this become?" → AI returns a replacement with a side-by-side diff → Apply replaces the selection in place. Agentic git tools (read-only): git_status, git_diff, git_log, git_branch_list, git_show so the model can answer "what's my git status?" / "what changed since yesterday?" by calling these autonomously — no shell-out. Mode-aware model coloring: the model dropdown renders non-tool-capable models amber in Coding mode and non-Data-capable models amber in Data mode, with hover tooltips explaining why; tool-capable models stay accented in green. Multi-cursor editing: Alt+drag for column-select, Ctrl+click adds a cursor, type once and the same edit lands at every selection. Windows mojibake compile fix: added /utf-8 MSVC flag so emoji and non-ASCII (en-dash, ellipsis) render correctly on Windows MSVC builds — pre-fix they showed as ðŸ", …, · garbage in the chat / banners. Banner wrap fixes: Data Mode capability banner moved to its own row + heightForWidth(true) so panels resize without clipping at narrow dock widths. llama.cpp install detection: distinguishes "installed but not running" from "not installed" so the status hint is actionable. Crash hardening: null-pointer guards, network timeouts, and structured error surfacing across aipanel.cpp / ollama.cpp / ai_tools.cpp — never-crash AI even when the backend disappears mid-stream. Binary size: still ~9 MB Linux x64 stripped (additions are pure UI / wiring, no new heavy deps).
v0.1.55 2026-05-09 DuckDB engine, Azure OpenAI, Ollama Cloud, 78 language lexers, privacy toggle, credential scrubber. Major release. Cloud AI: 4 cloud backends (OpenRouter, OpenAI, Ollama Cloud, Azure OpenAI) each with their own per-provider key slot (no cross-provider bleed). New AI Settings dialog with Test/Save/Forget per provider. Searchable model dropdown — type xai/grok, claude, gpt, kimi, gemini to surface the right group. Ollama /api/show capability probe — new tool-trained models work without a Notepatra release. OpenRouter reasoning parameter routing — Think checkbox now actually works for Claude / o-series / Gemini. DuckDB native engine for Data mode: libduckdb-1.1.3 linked dynamically (RAII C wrapper, structurally leak-proof, streaming row callback, schema introspection, httpfs/S3/Parquet/CSV/JSON view registration). New DUCKDB driver in DB Connections panel. Database Tree dialog (Browse Schemas...): connection → schemas → tables → columns, lazy-loaded, right-click "Send schema to AI" / "Sample 10 rows" / "Copy SELECT *". 78 languages (47 → 78): 32 new dedicated lexers — Dart, Solidity, Zig, Vala, Hack, Julia, R, Protobuf, F#, HCL, Thrift, GraphQL, GDScript, Nim, Cython, Mojo, Crystal, Elixir, Scala, Groovy, Apex, Jinja, Liquid, Twig, Dockerfile, Fish, Nushell, TOML, DotEnv, Gitignore, JSON5, BibTeX. Each keyword table verified against the official spec by parallel research agents. Comment toggling (Ctrl+Q / Ctrl+Shift+Q) wired for every new language. Privacy: credential scrubber catches 14 vendor patterns (OpenRouter / Anthropic / OpenAI / GitHub / GitLab / AWS / Slack / Stripe / SendGrid / Google / JWT / PEM / generic key=value) before any text leaves the machine. New 🔒 Share file with AI toggle (default OFF, Coding mode only) — Chat / Data modes never see file content. Welcome page emoji fallback chain — Linux tofu boxes (□) for icons replaced with real glyphs. Multi-file analyst context: .notepatra/data-analyst/ directory loader (instructions / data-dictionary / business-rules / KPIs / sample-queries). Tests: 21/21 pass — added test_credscrub (14 vendor patterns) + test_duckdb (streaming + RAII + schema). Binary size: ~9 MB Linux x64 stripped (basically flat from v0.1.54 — DuckDB is a separate .so users install once).
v0.1.54 2026-05-08 AI backend dropdown trimmed to 4 + wiring fixes + Search-icon polish. Removed Custom (no URL field in panel chrome — was a no-op), LM Studio, and Jan (both are just GUI wrappers around llama.cpp; Ollama covers easy local and llama.cpp covers power-user; curated 12-GGUF catalog already in the model dropdown). Dropdown is now exactly Ollama / llama.cpp (GGUF) / OpenRouter (cloud) / OpenAI. Ollama re-detect bug fixed: switching back from cloud to Ollama now resets the base URL properly (pre-fix, m_ollama stayed pointing at openrouter.ai and the /api/tags probe failed silently → "(Ollama offline)"). modelsError handler dispatched by backend so llama.cpp / OpenRouter / OpenAI catalogs stay visible even when the live probe fails. Data Analyst banner uses family names (Claude / GPT / Gemini / Qwen-Coder / Llama) instead of version pins (Claude Sonnet 4.5, GPT-5, …) which would go stale every couple of months. Banner colours theme-aware — readable on Light, Dark, Monokai. Project Search toolbar icon lens position adjusted (centre 0.40 → 0.44, radius 0.28 → 0.25) so the magnifying-glass stroke at the top-left no longer anti-alias-merges with the rounded square's corner at 150 % DPI. New OllamaClient::modelsListedRich(QJsonArray) signal for upcoming v0.1.55 live model picker. Rust 119 / 119, C++ 19 / 19, build clean.
v0.1.53 2026-05-08 Curated model lists + Data Analyst welcome card. The model dropdown now shows curated catalogs per backend instead of just whatever the live /v1/models returned: 12 GGUF picks for llama.cpp (Qwen2.5-Coder 1.5B/7B/14B, Llama 3.2 3B / 3.1 8B, Phi-4, Gemma 2, Mistral 7B, DeepSeek-Coder-V2-Lite, StarCoder2 — each with HuggingFace download URL in tooltip), 13 cross-provider picks for OpenRouter (Claude Sonnet/Opus/Haiku 4.5, GPT-5 / 5-mini / 4o / o1-mini, Gemini 2.5 Pro / Flash, DeepSeek R1, Llama 3.3 70B, Qwen2.5-Coder 32B, Mistral Large), 6 picks for OpenAI direct. Plus when the user toggles into Data Analyst mode on a fresh chat, a styled orange welcome card now appears at the top of the AI chat with title, one-paragraph explainer, three clickable example prompt chips, connection count + Manage Connections button, model-capability indicator (✓/⚠), and a Hide button (sticky via Config::aiHideDataWelcome). Capability banner reworked to mention a local fix (ollama pull qwen2.5-coder:14b) instead of cloud-only — pre-fix, local-Ollama users thought paying for cloud was the only way out. Rust 119 / 119, C++ 19 / 19, build clean.
v0.1.52 2026-05-08 Toolbar icon HiDPI rendering + Project Search button visibility. v0.1.50's AA_UseHighDpiPixmaps was right but makeFeatureIcon() still rasterized to a fixed 32×32 QPixmap — at 150% Qt bilinear-scaled the result to ~48 device px → pixelation. Now paints into 32 × dpr backing store with setDevicePixelRatio(dpr) + painter.scale(dpr, dpr) so every drawXxxFeatureGlyph() produces sub-pixel-precise output at native density. Toolbar Search / AI / Terminal / Compare / JSON / HTML / SQL / Brackets / REST / Git icons stay sharp on Windows 125% / 150% / 175% and Retina. Buttons are NOT affected — v0.1.49's per-button min-width and v0.1.50's HighDpiScaleFactorRoundingPolicy::PassThrough are preserved. Plus Project Search Cancel + Clear history button labels switched from textPrimary/#AAA (nearly invisible on Light theme) to a bold orange (#E67E22) visible on every theme. Rust 119 / 119, C++ 19 / 19, build clean.
v0.1.50 2026-05-08 HiDPI / fractional-zoom fix — Windows 125% / 150% / 175% finally render correctly. User reported Bracket / HTML / SQL Tools button labels were still getting cut on Windows even after v0.1.49's per-button min-width fix. Root cause: the app had zero Qt HiDPI attributes set — Qt 5.15's default Round policy was scaling 150% to 200%, making every button 33% wider than its layout. Three Qt attributes set BEFORE QApplication construction: AA_EnableHighDpiScaling (opt into logical-pixel coords), HighDpiScaleFactorRoundingPolicy::PassThrough (use OS scale factor exactly), AA_UseHighDpiPixmaps (@2x bitmap variants). Fixes button truncation everywhere on Windows + Linux distros with fractional fontconfig DPI. macOS already handled. Rust 119 / 119, C++ 19 / 19 — build clean.
v0.1.49 2026-05-08 Windows button-truncation fixes + SQL Compact formatter. Multi-word button labels (Format (2 spaces), Generate Docs, Add Comments, Write Tests, Insert at Cursor, Replace Selection, …) were truncated with on Windows because Segoe UI is ~20% wider than Linux DejaVu Sans Mono and the buttons had only fixed height, no min width. Every affected button across FormatterPanel::addButton, AI quick-action rows, and the SQL panel now sets minimumWidth = fontMetrics.horizontalAdvance(label) + 22..28 so labels never clip. Plus a new Compact button in SQL Tools — emits a one-line-where-possible rendering of the same parsed AST: short queries stay on one line, long queries break only at major clause boundaries (SELECT / FROM / WHERE / GROUP BY / ORDER BY / RETURNING). Same dialect coverage as Format (T-SQL, PostgreSQL, MySQL, SQLite, Oracle, ANSI). New Rust format_sql_compact + FFI npc_format_sql_compact + C++ wrapper. Rust 119 / 119 (4 new compact-mode tests), C++ 19 / 19.
v0.1.48 2026-05-08 AI panel UX overhaul + JSON / HTML / Bracket / SQL hardening + git inline diff. AI panel: 3-way segmented selector `[Chat
v0.1.47 2026-05-08 Icon refresh. User reported the Notepatra taskbar icon read visibly smaller than other apps'. The artwork was correct but the leaf+circuit graphic only filled ~60% of the canvas, leaving lots of dark padding around it. Re-rendered all icons with the leaf scaled up ~22% so it now fills ~85% of the canvas — reads larger at every taskbar / dock / file-manager size. All standard PNG sizes regenerated from the new 1024px master (16/24/32/48/64/128/256/512/1024); Windows .ico rebuilt with the 7 standard embedded sizes; macOS .icns regenerated.
v0.1.46 2026-05-08 Hotfix on top of v0.1.45. Two regressions: (1) v0.1.45 added beginSession() to doFindAllCurrent (the Find tab → Find All in Current button) but left a stale sr->clear() line right above it, so every search wiped the panel's history before adding the new session — stacking was effectively dead in the most-common Find path. Removed the stale clear(). (2) The new ✕ close button on the bottom-docked Search Results panel was visible even on an empty panel (before any search had been run), looking like a stray floating UI element. Fixed: panel starts with the ✕ hidden; the first beginSession call shows it; explicit clear() hides it again. After v0.1.46, all three Find paths behave consistently — each search creates a new session at the top, previous sessions collapse, capped at 10.
v0.1.45 2026-05-08 Hotfix on top of v0.1.44. v0.1.44 added the close button + stacked history to ProjectSearch (the Ctrl+Shift+G tab) but Find in Files (Ctrl+Shift+F) populates a different widget — the bottom-docked SearchResultsPanel — which still had no close button and wiped its history on every search. v0.1.45 ports both features to that panel: red ✕ close button (#E81123, theme-independent), each Find All becomes a top-level session row (🔎 Search "needle" — N hits in M files · HH:MM:SS), prior sessions auto-collapse, capped at 10. Plus explicit Comment / Uncomment menu items, Notepad++-style — the v0.1.44 menu only had Toggle Line/Block; v0.1.45 adds dedicated Comment Line (Ctrl+K), Uncomment Line (Ctrl+Shift+K), Comment Block, Uncomment Block actions in both the right-click context menu and the Edit → Comment/Uncomment submenu. New uncommentLine only strips when the marker is the FIRST non-whitespace token on the line, so inline -- in SQL or # in strings stays intact.
v0.1.44 2026-05-07 Project Search UX + language-aware comment toggling. Three Notepad++-style upgrades: (1) Red ✕ close button on the Project Search panel header (theme-independent #E81123, white-on-red on hover) — pre-v0.1.44 there was no way to dismiss the panel except right-clicking the tab. (2) Stackable, collapsible search history — pressing Search no longer wipes prior results; each search becomes a top-level session row (🔎 "pwd" Aa W — 5 hits in 1 file · 12:34:07 · /path) with files as children + matches as grandchildren. Earlier sessions auto-collapse; capped at 10; Clear History button wipes all. (3) Right-click Toggle Line Comment + Toggle Block Comment, language-aware via the new public static Editor::commentSyntaxFor(lang) → {line, blockOpen, blockClose} — Python #, C-family // line + /* */ block, SQL -- line + /* */ block, HTML/Markdown <!-- -->, PowerShell # + <# #>, Lua -- + --[[ ]], etc. Pre-v0.1.44 the editor prepended # regardless of file type, silently turning Markdown lines into headings. New keybindings Ctrl+Q (line) and Ctrl+Shift+Q (block) match Notepad++. New toggleBlockComment() does atomic wrap-or-strip. Plain Text / unrecognised extensions disable the menu items with (no syntax for X) so the user sees why nothing happens. +25 test asserts in test_options_actually_work covering 12 languages.
v0.1.43 2026-04-30 Data Analyst Mode for the AI assistant. New Data toggle in the AI panel header (mutually exclusive with Coding Mode). When on: attached CSVs get a structured schema-aware preview instead of a raw text dump (delimiter sniff, type inference for Integer / Real / Boolean / Date, head + tail rows, capped at 4 KB); a Manage Connections… dialog lets you save SQLite / PostgreSQL / MySQL / SQL Server connections; two new agentic tools csv_query and query_sql let the model run real SQL (SELECT-only by default — mutations require explicit confirmation); chart specs in fenced ```chart blocks render as interactive QChartView widgets inline (line / bar / pie / scatter); .notepatra/data-analyst.md is auto-prepended as project context; capability banner warns when the active model is below the recommended bar. New modules: csvanalyst.{h,cpp} · dbconnections.{h,cpp} · chartrender.{h,cpp}. Build adds Qt5 Sql + Charts. New test_ai_dataanalyst (~50 assertions). 18/18 ctest suites green. Connection passwords are obscured at rest (XOR + base64), NOT encrypted — documented honestly.
v0.1.42 2026-04-30 The "make every option actually work" release. Audit revealed the Preferences dialog's General / Editing / Margins / Tab Settings / Auto-Completion / New Document tabs were entirely stub UI — checkboxes / radios / spinboxes constructed with hardcoded values, never read from Config, never written back. There wasn't even an OK/Apply button. v0.1.42 fixes every dead control across the entire app. Preferences dialog rewritten — 25+ controls now wired to Config with OK/Apply/Cancel; new font picker; new Hide-toolbar / Tabs-closable / Show-bookmark / Default-EOL options. Encoding menu actually re-decodes / converts — new Reinterpret bytes as and Convert to submenus (UTF-8 / UTF-8 BOM / UTF-16 LE / UTF-16 BE / Windows-1252 / ISO-8859-1) that re-read bytes via QTextCodec and write the right bytes on save. View menu checkmarks sync — Show All Characters / Show Whitespace / Show End of Line / Show Indent Guide / Word Wrap reflect actual editor state and propagate to all open tabs. Edit → EOL Conversion + Settings → Tab Settings + Zoom — all persist now. New Editor::applyConfig() is the single source of truth. New test_options_actually_work (49 assertions) — programmatically toggles every Preferences control and verifies the editor + Config actually changes. 18/18 ctest suites green (was 17). Plus Compare polish: Diff only is now default + prominent, dark-theme labels fixed, button text no longer truncates, AI dock free manual resize on Windows / macOS, modern font defaults broadened.
v0.1.41 2026-04-30 The "Diff only" toggle release. New checkbox in the Compare toolbar — tick Diff only and matching lines disappear from both panes, leaving only Added / Deleted / Changed rows. Original line numbers preserved in the gutter so each diff still anchors to its real file location. Default OFF — full-files view unchanged from v0.1.40. Single-file change in src/compare.cpp (filters the row vector before rendering — no code-path duplication). Test 7 (NEW, 4 assertions) in test_compare_widget. 17/17 regression suites green; the change is purely additive.
v0.1.40 2026-04-29 The "stop screwing up my JSON" release. User reported v0.1.39's AI dock chat would add fields and restructure when asked to fix broken JSON — Tools → JSON Tools → AI Fix had strict minimal-change rules, but the chat-mode "fix my json" path didn't. Six fixes: (1) AI-chat fix-intent detectionfix my json / repair this html / the sql is broken etc. swap the system prompt to a strict minimal-change patcher; does NOT trigger on explain my json / what is json. New module src/ai_intent.{h,cpp}. (2) Three new quick-action buttons — Fix JSON / Fix HTML / Fix SQL — route directly to the strict-patcher prompt. (3) apply_diff three-tier match — strict → strip read_file's N\t line-number prefix → .trimmed() comparison; emits result.warnings on the relaxed tiers so the agent self-corrects. True conflicts still refused. (4) read_file with_line_numbers parameter — default true (back-compat); pass false for raw content with no prefix, recommended when feeding lines into apply_diff old_lines. (5) Tool-call JSON parse-error surfacing — both Ollama and OpenAI-compat paths now use QJsonParseError and emit a structured error_kind: malformed_args tool result back to the model with a 240-char raw-args preview, instead of silently passing empty args. (6) Tool-mode system prompt updated to teach the model the new param + forbid copying the line-number prefix into apply_diff. 17/17 regression suites green (was 16; new test_ai_intent with 49 assertions, +20 in test_ai_tools).
v0.1.39 2026-04-27 Coding Mode write tools + persistent chat history + visible close button. Three new agentic tools close the gap reported by user ("create me a Python file" should actually create a .py file): write_file (modes: overwrite/create/append; auto-mkpath inside the workspace; refuses traversal + deny-listed paths; auto-opens or silently reloads the file in the editor), search (literal or regex; glob filter; case-sensitivity; capped at 200 matches; same heavy-dir filter as the file explorer), apply_diff (atomic two-phase apply: validates ALL hunks against the live file first → if any drifted return error_kind:conflict and DON'T touch the file; otherwise apply in reverse-line-order so earlier hunks' indices stay stable; .tmp + std::rename so a kill-9 mid-write leaves either old or new, never half-baked). Plus persistent chat history — per-workspace JSON at ~/.config/notepatra/chat-history/<sha1>.json, debounced 2s saves, 1 MB cap with oldest-message rolloff, atomic write, deleted on Reset. Plus a UI fix: the AI dock close button was tone-on-tone-invisible against the chrome on every theme — now uses U+00D7 × in Windows-canonical close-button red #E81123 (transparent at rest, red bg + white X on hover), theme-independent. New resolveSafeWritePath helper for write-side path safety: target may not exist yet, but the lowest existing ancestor must canonicalize inside the workspace; deny-list applies to the candidate target. 134 tool tests (was 80; +54 covering every mode/edge case/refusal). 16/16 regression suites green. Total assertions across the suite ≈ 2050.
v0.1.38 2026-04-26 AI Assistant crash fix + custom-chat file-leak fix. Bug 1: clicking Coding Mode while a model was streaming caused a use-after-free crash. renderTranscript() called aiClearChat() which deleteLater()'d the streaming card + its child m_streamingStats QLabel — but the 250 ms timer kept firing on the dangling pointer. Fix: stop the timer + nullify m_streamingStats BEFORE clearing the layout. Bug 2: typing "hi" in the AI chat (Coding Mode OFF, no selection) appended the entire open file to the prompt because m_context falls back to whole-file when there's no selection, and the "custom" action unconditionally inlined m_context. Fix: new m_contextIsSelection flag — "custom" action only inlines context when it's a real user selection; quick-actions (Explain/Refactor/etc.) still inline the whole file because they need code; workspace-context block still attaches via shouldAttachWorkspace for project-level questions; Coding Mode's read_file tool still works. 16/16 regression tests pass.
v0.1.37 2026-04-26 Comprehensive lexer palette coverage — 1650 styles × 3 themes, 0 gaps. New test_lexer_coverage audit walked all 28 lexers (22 QScintilla + 6 Notepatra-local) on Light / Dark / Monokai. Pre-v0.1.37: 359 style gaps falling through to default text. Post-v0.1.37: 0 gaps. ~30 new style-kind matchers added to npp_palette.cpp: escape sequences (\n/\xNN), task markers (TODO/FIXME), here-docs, scalars, POD, labels, sections, keys, references/anchors, document delimiters, list items, block quotes, strikeouts, horizontal rules, SGML/CDATA, HTML fragments, CSS @-rules + !important + ID selectors + hash colors, diff +/-/changed lines + position markers, parameter expansion (${var}), module names, IDL UUIDs, code blocks, and more. Monokai operator fix — was #F8F8F2 (same as default text → invisible). Now #FD971F (Monokai amber). YAML brand — keys (style 2 "Identifier" — which IS the key in YAML), references/anchors, document delimiters all themed (was unstyled). Properties/TOML/INI/.env brand — sections, keys, values distinct (was unstyled for TOML-specific kinds). Diff brand+/-/@@ lines red/green/cyan. test_lexer_coverage runs on every CI build — any future refactor that introduces unthemed styles fails CI. 16/16 regression tests pass. Total assertions across the suite ≈ 1900 (was ~280).
v0.1.36 2026-04-26 Multi-word phrase search + Compare ignore-spaces by default. Project Search: multi-word literals like import os (with space) already worked via substring (QString::indexOf) and the rust aho-corasick fast path — but the placeholder text ("Search for a string, word, or regex pattern…") sounded single-token, so users assumed they had to use regex \s or wholeWord mode. New placeholder: "Search any text — words, phrases like "import os", or regex patterns…". Plus query auto-trim so " import os " works equivalently. Two new test cases lock the multi-word behaviour in: phrase narrowing vs single-word baseline, exclusion of non-contiguous matches (from os import path doesn't match "import os"), trim invariant. Compare: Ignore spaces checkbox now defaults to ON — most common compare-tab use case is "did this code change?" where re-indentation shouldn't show up as diffs. Users who want byte-exact compares (YAML / Python indentation) untick. test_projectsearch: 26 → 31 assertions, test_compare_widget Test 1 inverted to match new default. 15/15 regression tests pass.
v0.1.35 2026-04-26 Coding Mode is now AGENTIC — the AI reads files and lists directories on its own. Toggle Coding Mode + ask about your code → the AI calls read_file(path, offset?, limit?) and list_dir(path) to walk your workspace before responding. Every read shows up as an inline 🔧 read_file (...) → 247 lines card. Multi-turn round-tripping. Works with EVERY backend: Ollama (local) via /api/chat with tools array · llama.cpp llama-server (with --jinja) · OpenAI-compat (OpenRouter, OpenAI direct, LM Studio, vLLM, Anthropic via OpenRouter, Gemini via OpenRouter, Jan, etc.). Three-layer path security: workspace-anchor canonicalization (rejects ../../etc/passwd), hardcoded deny-list (refuses ~/.ssh/, *.pem, *.key, id_rsa*, `/etc/passwd
v0.1.34 2026-04-26 White-fold-margin Dark-theme fix across all formatter panels. Bug fix: SQL Formatter, JSON Tools, HTML Tools, and Bracket Tools all showed a stark white vertical strip between line numbers and editor content on Dark theme (reported via user's Windows SQL Formatter screenshot). Root cause: setFolding(BoxedTreeFoldStyle, 2) creates a fold-marker margin but does NOT theme it — setFoldMarginColors() is a separate call that all four panels were missing. QScintilla defaults the fold margin to white. Fixed in src/sqlfmtpanel.cpp (which was also missing setMarginsBackgroundColor / setMarginsForegroundColor entirely) and src/fmtpanel.cpp (in both constructor and onThemeChanged reapply path). Structural fold-margin pairing test added to test_palette.cpp — reads the relevant .cpp files at test time and verifies any file calling setFolding() with a real fold style ALSO calls setFoldMarginColors(). Future editor panels that forget to theme the fold margin fail CI before users see the bug. test_palette: 60/60 checks (was 56; +4 fold-margin pairing). 14/14 regression tests pass.
v0.1.33 2026-04-26 Linux emoji rendering fix + comprehensive palette test coverage. Bug fix: AI Assistant responses on Linux were rendering emoji as tofu (Hello! 👋Hello! □). Linux text fonts (Inter, Noto Sans, DejaVu, etc.) don't ship emoji glyphs and Notepatra's CSS family chains in src/fonts.h didn't list any emoji font as fallback. Fix: appended Apple Color EmojiSegoe UI EmojiNoto Color EmojiTwemoji MozillaTwitter Color EmojiSymbola to both UI and code CSS chains. Windows already worked because Segoe UI auto-pairs with Segoe UI Emoji at the OS level. Test coverage expanded from 38 → 56 colour checks. New assertions cover PowerShell ISE signature ($variable #FF4500, cmdlet #0000FF, alias #0080FF), Python brand (class/function #795E26 amber, built-in #267F99 teal), SQL SSMS magenta #FF00FF, JS teal types #267F99, plus 6 emoji-fallback chain assertions. Two more bugs caught by the new tests and fixed in same release: (1) Python built-ins fell through to default text because QsciLexerPython style 14 description "Highlighted identifier" hit the identifier matcher first; (2) SQL user-defined keyword slot 19 fell through to default text because "User defined 1" doesn't contain "keyword". Both fixed by adding a d.contains("highlighted") || d.contains("user defined") branch BEFORE the identifier matcher in npp_palette.cpp. Regressions in palette / fonts now die at CI, not user screenshots. 14/14 regression tests pass.
v0.1.32 2026-04-26 PowerShell highlighting hotfix + per-language brand palettes (Python, SQL, JSON, JS/TS, C/C++, Bash). Bug fix: PowerShell New-Object, Get-Item, Where-Object etc. were rendering as default white text on Windows. Root cause: LexerPowerShell::keywords() had its keyword sets misordered against Scintilla's SCI_SETKEYWORDS slots — full Verb-Noun cmdlet names landed in idx 4 (User1) instead of idx 1 (Cmdlets), and the User1 description fell through to default text. Sets reordered: set 1 → Keywords, set 2 → Cmdlets (full Verb-Noun + verbs), set 3 → Aliases, set 5 → User1 (.NET type names). PowerShell ISE canonical palette: $variable paints OrangeRed #FF4500 (the ISE signature, also used in Microsoft's official "PowerShell ISE" theme bundled with the VS Code PowerShell extension); cmdlets pure blue #0000FF light / #9CDCFE dark; aliases lighter cyan #0080FF. Per-language brand palettes restored after v0.1.31 over-pruned them: Python uses VS Code Dark+ canonical (blue keywords + teal built-ins + amber function names); SQL uses SSMS signature (blue keywords + magenta #FF00FF system functions/types — the SSMS-instantly-recognisable look); JSON uses VS Code Light+/Dark+ (property keys #0451A5 JSON-blue light / #9CDCFE dark, true/false/null bright keyword-blue); JS/TS/CoffeeScript + C/C++/C# use VS Code Dark+ teal types #4EC9B0/#267F99. Multi-editor research drove all colour choices: Notepad++ master + VS Code Dark+/Light+ defaults + PowerShell ISE + SSMS docs + PyCharm Darcula + Sublime Mariana, hex-verified across 11 languages. New d.contains("property") palette matcher so JSON/CSS/YAML keys all pick up per-language brand colours automatically. 14/14 regression tests pass.
v0.1.31 2026-04-26 Notepad++ canonical 9-hue palette overhaul + persistent stream stats + privacy hotfix. v0.1.30 user complaint "keywords + actual syntax are all just shades of blue" fixed by three changes: identifiers paint as default text (was #001080/#9CDCFE blue), operators paint navy/olive bold (was plain text), secondary keywords (types) paint vivid violet #8000FF (was #267F99 teal). Light-theme palette: keywords #0000FF bold · types #8000FF violet · comments #008000 italic · numbers #FF8000 · strings #808080 · operators #000080 bold · preprocessor #804000 · classes #7F0000 maroon · identifiers default text. Dark theme uses Zenburn-derived hues from N++ DarkModeDefault.xml: warm sand keywords · sage types · rose strings · olive operators · peach preprocessor · cyan numbers · sandy-yellow classes. Hex codes verified directly against notepad-plus-plus/notepad-plus-plus master stylers.model.xml + DarkModeDefault.xml. Pruned 14 redundant per-language overrides; kept brand accents (Rust amber, Go cyan, Swift Xcode-pink, Kotlin Darcula-orange, Java IntelliJ navy, Ruby red, etc.). Stream stats persist after the response completesendAssistantBubble seeds final counts onto the ChatMessage so aiAddAssistantCard keeps ⏱ N tok · X tok/s · Y s visible on every bubble forever, with responseStats overwriting with canonical Ollama numbers moments later. Privacy hotfix: removed three website screenshots (tour.gif, editor-dark.png, ai-assistant.png) that leaked filesystem paths in Project Search Folder field + Recent Files panel; replaced with two clean re-captures. About dialog "100+ languages" → "100+ file types · 48 language lexers" (actual count audited). 14/14 regression tests pass.
v0.1.30 2026-04-26 Live streaming token-rate on AI assistant bubble during generation. v0.1.26 added per-response stats (1234 tok · 123.4 tok/s · 2.3 s) but only after the response completed. v0.1.30 makes those stats stream live — small ⏱ 145 tok · 23.4 tok/s · 6.3 s label sits between the card header and streaming body, refreshes every 250 ms while the model produces output. Three display modes (warming up / no-tok-rate / full triple) handle the transition gracefully. When the stream ends, the live label vanishes and the bubble re-renders with the canonical eval_count / prompt_eval_count from Ollama's done frame baked into the header (so chat history retains the final stats permanently). Pre-existing bug fixed: hitting Stop during generation now also ends the assistant bubble cleanly (OllamaClient::cancel() disconnects silently with no signal — was leaving the card frozen with the timer ticking). Works with every model + every backend (Ollama / llama.cpp / OpenAI-compatible). 14/14 regression tests pass.
v0.1.29 2026-04-26 CI hotfix unblocking the v0.1.27 + v0.1.28 release pipeline. v0.1.27 and v0.1.28 both compiled cleanly on every platform but failed the regression suite at test_palette because two assertions were hardcoded against v0.1.26's palette colours: C++ secondary keywords (style 16, SCE_C_WORD2) was asserting #800080 purple but v0.1.27 changed it to #267F99 teal (VS Code default for type names); JSON keyword (style 11) was asserting generic #0000FF but v0.1.27 changed it to #0451A5 JSON-key blue (VS Code default JSON theme). Both intentional per-language tunings — the test assertions just hadn't caught up. Updated test_palette.cpp to expect the new values. v0.1.29 bundles all v0.1.27 + v0.1.28 changes — comprehensive lexer overhaul (PowerShell / Rust / Go / Swift / TypeScript / Kotlin with comprehensive keyword sets from official docs) + 23 per-language palette accents + 5 new style-kind matchers (variable / cmdlet / alias / here-string / identifier) + dark-theme readability fixes for Lua / Perl / D / Ruby. Users on v0.1.20+ jump straight from v0.1.26 → v0.1.29 via the in-app updater (skipping the never-published v0.1.27 and v0.1.28). 14/14 regression tests pass.
v0.1.28 2026-04-26 Dark-theme palette readability hotfix. Four languages had dark-only keyword colours that disappeared on #1E1E1E background: Lua navy #000080 → Lua-bright #4FC1FF on dark; Perl camel-blue #39457E → VS Code blue #569CD6; D brick-red #B03A2E → coral #FF6E6E; Ruby brand-red #CC342D → softer salmon #FF7B72. Light theme keeps each language's official brand colour. All 23 per-language palette branches in src/npp_palette.cpp audited and verified to use proper 3-way monokai/dark/light differentiation. 100% of languages now have proper dark + light compatibility.
v0.1.27 2026-04-26 Comprehensive lexer + palette overhaul covering all 44+ languages. Per-language accent palettes for 23 languages — Rust (rust-amber #DEA584), Go (Go-cyan #00ADD8), Swift (Xcode pink + Swift-orange attributes), Kotlin (Darcula orange #CC7832 + gold #FFC66D types), Java (IntelliJ navy / Darcula), TypeScript / JavaScript / C / C++ / C# / SQL (VS Code defaults), HTML / PHP / CSS / XML (tag-orange / attribute-blue), JSON / YAML (key-blue), Python (blue + teal), Ruby (#CC342D ruby-red), Perl (camel-blue), Lua (navy), Bash / Batch / CoffeeScript / D / Markdown / Pascal / CMake / Makefile (per-language tuned). Five new style-kind matchers (variable, cmdlet, alias, here-string, identifier) — fixes the v0.1.26 PowerShell complaint where $variables, cmdlets, and aliases all rendered as plain text. Comprehensive keyword sets re-verified against official language docs for the 6 new lexers — PowerShell gets 5 sets (~470 keywords from Microsoft Approved Verbs / about_Reserved_Words), Rust gets all strict+reserved+2024-edition keywords + 65 std-lib types from rust-lang.org, Go gets exactly 25 reserved + Go 1.21 predeclared from go.dev/ref/spec, Swift gets 75 keywords across all categories + 80 std-lib types + SwiftUI wrappers from docs.swift.org, TypeScript gets ES+TS keywords + 70 utility types from microsoft/TypeScript scanner.ts, Kotlin gets hard+soft+modifier keywords + coroutine types from kotlinlang.org.
v0.1.26 2026-04-25 Six new lexers + terminal 256-colour/truecolour + AI tokens & timing. Six custom lexer subclasses (PowerShell wrapping SCLEX_POWERSHELL, Rust/Go/Swift on QsciLexerCPP, TypeScript on QsciLexerJavaScript, Kotlin on QsciLexerJava) — .ps1 / .rs / .go / .swift / .ts / .kt files now get proper syntax highlighting (were wrongly rendered as Batch / C++ / JS / Java before). 16 additional language extensions added (Dart, Zig, Nim, Elixir, Erlang, Crystal, Haskell, OCaml, F#, Clojure, Julia, Elm, Scala, Groovy, etc.). Terminal ansiToHtml() rewritten with full SGR support: 256-colour palette (38;5;N), truecolour (38;2;R;G;B), italic (3), faint (2), strikethrough (9), cancel codes (22/23/24/29) — bat / eza / fzf / delta / gh now render correctly. AI bubbles show per-response stats: 1234 tok · 123.4 tok/s · 2.3 s. Windows: Think checkbox stays visible (greyed) when Coding Mode on, mystery circle in input bar suppressed. 113 new test assertions across test_lexers_v0125 + test_terminal_ansi.
v0.1.25 2026-04-25 AI Assistant prompt-engineering overhaul. Tool-calling models (Qwen3 / Qwen3.5 / Hermes-3 / Llama 3.1+ / Mistral Large / Command R / GLM-4 / GPT-OSS) used to respond to casual chat input like hi with hallucinated {"command":...,"output":...} JSON instead of greeting back. New src/ai_systemprompt.{h,cpp} layered builder: identity + anti-tool-call + mode-specific (Chat / Explain / Transform / CodingStrict) + language hint. Workspace-context block now gated by intent + heuristic — skipped for casual chat / selection-focused work / Coding Mode, attached for project-level questions. Header rephrased from # Workspace context to [Project info] (less agent-frame-shaped). Coding Mode users see byte-identical behaviour.
v0.1.24 2026-04-25 Windows mojibake cleanup. resources/notepatra.rc em-dash + © rewritten to ASCII so rc.exe (which defaults to cp1252 without a BOM) doesn't ship Notepatra â€" native code editor into the "Open with" menu. docs/install.ps1 now sets [Console]::OutputEncoding = UTF8 at the top + falls back to ASCII box-banner so irm | iex doesn't render as â/∠garbage on PowerShell 5.1.
v0.1.23 2026-04-25 Critical dark-theme fix. Editor body painted white on theme = "System" + dark OS — Config::theme stays as the literal preference, but lexer paint compared against "Dark" directly so "System" fell through to LIGHT. New npResolvedThemeName() helper resolves "System" → OS preference; every lexer-paint site now uses it. Hardcoded setPaper("#FFFFFF") in Editor::applyLexer() removed.
v0.1.22 2026-04-25 Windows truncation pass. AI close button mojibake (✕) → QStyle::SP_TitleBarCloseButton (OS-native X icon). All tool-panel headers fixedHeight(20-24)minimumHeight(26-28) so Windows fonts have room. Find/Replace buttons widened. JSON / HTML / Bracket / SQL Formatter "Copy Output" right margin 8→16. AI panel error label setWordWrap(true). SQL Model dropdown 150→200 + AdjustToContents.
v0.1.21 2026-04-25 Windows UI polish. Explicit setSpacing(8-12) on every option row across SQL Formatter, Find/Replace, Compare toolbar, JSON/HTML/Bracket panels — Windows Vista/11 styles default to 0 px (Linux Fusion gives ~6 px) so widgets butted together. AI close button widened, padding zeroed.
v0.1.20 2026-04-25 Windows single-instance + updater closes app before install. Double-click a file with Notepatra open now opens a tab via QLocalServer IPC instead of spawning a clone. In-app updater closes Notepatra before handing off to msiexec so it can replace .exe cleanly.
v0.1.19 2026-04-24 Project Search Windows hang fix + clean Notepatra wordmark. OneDrive/cloud-placeholder skip via GetFileAttributesW(), per-file 30-second watchdog, live "⏳ stalled on: " diagnostic. Em-dashes / dashes removed from MSI / NSIS / license labels. SQL/C-family comment colour calibrated.
v0.1.18 2026-04-24 SQL Claude-style AST pretty-printer + Git panel rewrite + holistic theme propagation. format_sql now an AST walker on sqlparser v0.52 with 11 dialects. Git panel becomes a real workflow tool (staged/unstaged trees, branch chip, commit box, sync row, history+stash menu). Every panel gets onThemeChanged() slot — runtime theme switch no longer leaves dark-on-dark melt.
v0.1.17 2026-04-21 Safe in-app updater. Picks the right asset for your OS, streams to .part with cancellable progress, downloads + verifies SHA256SUMS, atomic rename, hand-off to OS installer. Never touches the running binary.
v0.1.16 2026-04-19 Workspace context for AI ("what does my whole project look like?"), JSON-LD SoftwareApplication on the homepage, expanded language detection.
v0.1.15 2026-04-18 New panel: REST Client — Postman-style request runner inside Notepatra. Multi-backend AI: Ollama / llama.cpp / OpenAI-compatible.
v0.1.14 2026-04-15 Coding Mode 3-column layout (file tree • editor • AI dock). AI Assistant attachments (PDF/DOCX/PPTX/XLSX/images).
v0.1.13 2026-04-13 Voice input refinements, AI streaming UX, panels theme audit pass.
v0.1.12 2026-04-12 macOS dylib install_name rewriting + QtPrintSupport force-copy + ad-hoc re-sign — bundles v0.1.11's hotfix in a clean release.
v0.1.11 2026-04-11 Hotfix for v0.1.10 macOS DMG. Rewrite libqscintilla2 /opt/homebrew/* dyld references to @rpath, bundle QtPrintSupport.framework, re-sign — fixes "Library not loaded" crash on launch.
v0.1.10 2026-04-11 Fix macOS installer (DMG + Tahoe Gatekeeper), add curl | sh uninstaller, Claude.ai-themed website with comprehensive uninstall docs.
v0.1.9 2026-04-11 ~3200-line refactor, unified FormatterPanel base, full Anthropic-style docs site, Linux ARM64 build, voice input in AI Assistant.
v0.1.8 2026-04-09 AI Fix actually works for Qwen3 / DeepSeek-R1 / any thinking model — passes think:false to /api/generate + defensively strips <think> tags + trims prose preambles. JSON Tools shows "Show Diff" button after AI Fix to open a side-by-side compare of original vs fixed. AI Assistant rewritten as a proper chat-bubble UI (right-aligned blue user bubbles, left-aligned gray assistant bubbles, clear chat button, show-thinking toggle). Tested end-to-end on Linux GUI via xdotool against real local Ollama.
v0.1.7 2026-04-09 Plugin panels overhaul: JSON / HTML / Bracket Tools format buttons now actually do something visible (BIG status banner). JSON Tools white-on-white text bug fixed. AI Fix (Ollama) reports progress + completion clearly. Default font 11pt → 10pt, less bold = lighter feel. SQL Formatter dialect dropdown (T-SQL / PL/SQL / MySQL / PostgreSQL / SQLite). Compare picker lists unsaved tabs. Windows MSVC C2666 fix in SCI_SETKEYWORDS call.
v0.1.5 2026-04-09 NSIS Windows installer (notepatra-setup-0.1.5.exe) — registers in Settings → Apps → Installed apps, generates uninstall.exe, Start Menu shortcuts, optional PATH integration. Live 3-platform download counter on website footer. notepatra --version no longer hard-coded to v0.1.0 (now driven by CMake project()). scripts/bump_version.sh for one-command release bumps.
v0.1.3 2026-04-09 Preprocessor color polish (#include/#define now brown bold). New test_palette and test_ollama test suites.
v0.1.2 2026-04-09 Notepad++ default palette across all lexers (Windows keyword highlighting works). Dynamic Ollama model detection (/api/tags). Ctrl+B brace swivel.
v0.1.1 2026-04-06 First shipping release on all 3 platforms. Windows lexer fix (Riverbank QScintilla source). Embedded Windows + macOS icons. RFC 9116 security.txt. SLSA + cosign.
v0.1.0 2026-04-06 First public release. Linux x64, macOS Apple Silicon, Windows x64. 100+ file types, 44 lexers.

v0.1.4 and v0.1.6 were tagged but never published a GitHub Release — both had CI failures (NSIS macro bug + Windows MSVC C2666). Their content shipped in v0.1.5 and v0.1.7 respectively.

See the full CHANGELOG for every change in every release, or browse the version history on notepatra.org.


Acknowledgments

Notepatra is an original product. This section names the open-source libraries and runtimes it depends on — the people maintaining them deserve credit.

  • Qt — the cross-platform C++ GUI framework.
  • Scintilla / QScintilla (Neil Hodgson, Riverbank Computing) — the text-editing control.
  • Rust + Cargo — the language and toolchain for the Rust core (search, diff, formatters, hashing, encoding).
  • aho-corasick (BurntSushi) — the literal-search engine behind Project Search.
  • regex, sqlformat, sqlparser — regex and SQL dialect handling.
  • Ollama, llama.cpp, OpenRouter, OpenAI, Azure OpenAI — AI inference backends Notepatra speaks to over standard HTTP APIs.
  • Sigstore / Cosign / SLSA — keyless signing and build provenance for every release.
  • Claude (Anthropic) — co-authored the implementation inside Claude Code.

Thanks also to the ~200 transitive crates in rust-core/Cargo.lock and the broader Qt / QScintilla ecosystem maintainers.


Envisioned by Prateek Singh. Built with Claude.

"I didn't build Notepatra because the world needed another text editor.
I built it because every developer — on Linux, Windows, or Mac — deserves one that's fast, free, and smart."

© 2026 Prateek Singh. All rights reserved.
Source code licensed under GNU General Public License v3.0.


License, warranty, and liability

Notepatra is licensed under the GNU General Public License v3.0. The full legal text lives in the LICENSE file at the root of this repo. The short version, in plain English:

You are free to:

  • Run Notepatra for any purpose, commercial or personal, on any number of machines.
  • Read, study, and modify the source code.
  • Redistribute the source or your modified versions, provided you also distribute them under GPL-3.0 and provide the source.

You must:

  • Keep the copyright notice intact when redistributing.
  • License any derivative work under GPL-3.0 (the "copyleft" requirement).
  • Make the source code available to anyone you distribute a binary to.

You may not:

  • Distribute Notepatra (or anything based on it) under a proprietary license.
  • Strip out or sue over the no-warranty / no-liability clauses below.

Disclaimer of warranty (GPL §15, plain English)

Notepatra is provided "AS IS", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. The entire risk as to the quality and performance of the program is with you. Should the program prove defective, you assume the cost of all necessary servicing, repair, or correction.

In other words: I built this in good faith, CI runs focused smoke and regression tests on pushes and pull requests, and every release is checksummed and signed — but I am one person on the internet shipping a free editor. Verify your downloads, back up your work, and don't blame me if something breaks.

Limitation of liability (GPL §16, plain English)

In no event will Prateek Singh, contributors, or anyone else who modifies or conveys Notepatra be liable to you for any damages — general, special, incidental, or consequential — arising from the use or inability to use the program, including but not limited to loss of data, data being rendered inaccurate, losses sustained by you or third parties, or a failure of the program to operate with any other program — even if I have been advised of the possibility of such damages.

In other words: if Notepatra eats your file, crashes during a deadline, or fails to highlight your YAML — that's on you. The whole point of the GPL is that you can read the source, fix it, and ship the fix back to everyone. There is no SLA, no support contract, no money changing hands.

What this means in practice

Scenario Who is responsible
You download the wrong file because of a typo and it bricks your shell You — verify SHA-256 first (SECURITY.md)
You install a third-party plugin that exfiltrates your code You — read plugin source before loading
Notepatra crashes and your unsaved file is gone You — but crash recovery saves every 10s, check ~/.config/notepatra/recovery/
Notepatra has an actual security bug Report it via private vulnerability disclosure, I'll fix it
You modified Notepatra and it broke your customer's production You — that's literally why GPL ships with §15

Third-party components and their licenses

Notepatra links against several open-source libraries. None of them ask for money. Their licenses live in the source trees they ship from:

Component License Purpose
Qt 5.15 (LGPL-3) LGPL-3.0 UI framework
QScintilla 2.14 (GPL-3) GPL-3.0 Editor widget + lexers (Notepatra inherits GPL-3 from this)
Scintilla / Lexilla (HPND) HPND Editing engine bundled with QScintilla
Rust crates (mostly MIT/Apache-2.0) MIT/Apache-2.0 dual Memory-safe core: file I/O, search, JSON/HTML/SQL fixers, diff
Ollama (when used for AI) MIT Optional local AI runtime — runs separately, you install it yourself

The combination is GPL-3.0 because QScintilla is GPL-3.0 and the GPL is contagious. If you don't like that, the alternative would be to pay Riverbank Computing for a commercial QScintilla license — Notepatra doesn't.

About

Native C++/Rust code editor for the AI era — Linux/macOS/Windows, under-12 MB bare binary, 238 file extensions / 82 language lexers, Rust-powered search, 6 AI backends (Ollama / llama.cpp / OpenRouter / Ollama Cloud / OpenAI / Azure OpenAI). Free forever, GPL-3.0.

Topics

Resources

License

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors