# notepatra-mcp

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/singhpratech/notepatra  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/notepatra-mcp

## Description
Notepatra editor MCP server — 35 tools; every write human-approved in the editor; local-only.

## Claude Desktop Quick Installation
Heuristic fallback — verify the package name and runner against the repository README before running it. Uses `npx` (confidence: low):

```json
"mcpServers": {
  "notepatra-mcp": {
    "command": "npx",
    "args": ["-y","notepatra-mcp"]
  }
}
```

## Documentation & README

<p align="center">
  <a href="https://notepatra.org"><img src="https://raw.githubusercontent.com/singhpratech/notepatra/HEAD/resources/notepatra-256.png" alt="Notepatra — click for notepatra.org" width="128" height="128"></a>
  <h1 align="center"><a href="https://notepatra.org" style="text-decoration:none;color:inherit;">Notepatra</a></h1>
  <p align="center"><em>The first code editor built for the AI era.</em></p>
  <p align="center">
    <strong>C++ + Rust</strong> · <strong>~13 MB bare native executable</strong> · <strong>Zero Electron</strong> · <strong>238 file types</strong> · <strong>82 language lexers</strong> · <strong>Local AI formatters</strong> · <strong>MCP server</strong>
  </p>
  <p align="center">
    <strong>MCP:</strong> <a href="https://notepatra.org/mcp.html"><code>notepatra-mcp</code></a> now ships a prebuilt signed Windows sidecar and a one-click Claude Desktop bundle — connect Claude Desktop, Claude Code, OpenAI Codex, and any MCP client to the running editor. 49 tools (Git, read-only SQL, saved-connection queries, charts, Noter notes); every write human-approved in-window; nothing leaves your machine.
  </p>
  <p align="center">
    <a href="https://notepatra.org">Website</a> ·
    <a href="https://github.com/singhpratech/notepatra/releases/latest">Download</a> ·
    <a href="#ai-assistants-mcp--new-in-v01118">MCP</a> ·
    <a href="#features">Features</a> ·
    <a href="#the-story">The Story</a> ·
    <a href="#install">Install</a> ·
    <a href="#plugins">Plugins</a> ·
    <a href="#ai-powered">AI</a> ·
    <a href="https://github.com/singhpratech/notepatra/blob/HEAD/CONTRIBUTING.md">Contributing</a> ·
    <a href="https://github.com/singhpratech/notepatra/blob/HEAD/SECURITY.md">Security</a> ·
    <a href="https://github.com/singhpratech/notepatra/blob/HEAD/CHANGELOG.md">Changelog</a>
  </p>
  <p align="center">
    <a href="https://github.com/singhpratech/notepatra/actions/workflows/build.yml"><img src="https://github.com/singhpratech/notepatra/actions/workflows/build.yml/badge.svg" alt="Build"></a>
    <a href="https://github.com/singhpratech/notepatra/actions/workflows/codeql.yml"><img src="https://github.com/singhpratech/notepatra/actions/workflows/codeql.yml/badge.svg" alt="CodeQL"></a>
    <a href="https://github.com/singhpratech/notepatra/releases/latest"><img src="https://img.shields.io/github/v/release/singhpratech/notepatra?color=39FF14&label=release" alt="Release"></a>
    <a href="https://github.com/singhpratech/notepatra/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-GPL--3.0-blue" alt="License"></a>
    <a href="https://github.com/singhpratech/notepatra/blob/HEAD/SECURITY.md"><img src="https://img.shields.io/badge/security-disclosure%20policy-39FF14" alt="Security"></a>
  </p>
  <p align="center">
    <a href="https://github.com/singhpratech/notepatra/releases"><img src="https://img.shields.io/github/downloads/singhpratech/notepatra/total?color=39FF14&label=total%20downloads" alt="Total downloads"></a>
    <a href="https://github.com/singhpratech/notepatra/stargazers"><img src="https://img.shields.io/github/stars/singhpratech/notepatra?style=flat&color=39FF14" alt="GitHub stars"></a>
    <a href="https://github.com/singhpratech/notepatra/network/members"><img src="https://img.shields.io/github/forks/singhpratech/notepatra?style=flat" alt="Forks"></a>
    <a href="https://github.com/singhpratech/notepatra/issues"><img src="https://img.shields.io/github/issues/singhpratech/notepatra?color=blue" alt="Open issues"></a>
    <a href="https://github.com/singhpratech/notepatra/commits/main"><img src="https://img.shields.io/github/last-commit/singhpratech/notepatra" alt="Last commit"></a>
  </p>
</p>

---

## 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 — ~13 MB bare (~13.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.129 downloads: 4.7 MB Linux x64 (tarball, Qt from the system), 28.0 MB on macOS (DMG with bundled Qt), 32.8–43.9 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](https://github.com/pnedev/comparePlus) by Pavel Nedev** — credit where credit is due.

#### Git Integration (inbuilt)
- **Staged / Unstaged trees** — porcelain v2 parser, inline `+` / `−` buttons per row to stage/unstage
- **Branch chip with ahead/behind** — shows `main ↑3 ↓1` when diverged
- **Commit box (Ctrl+Enter)** — line-count indicator, blocks commit when nothing's staged
- **Sync row** — one-click Pull / Push / Fetch with live ahead/behind refresh
- **Collapsible history + stash menu** — recent commits expandable; stash / pop / list / drop
- **Git gutter** — green/yellow/red markers in editor margin for changed lines

### AI Powered — local-first, cloud-optional

Backend dropdown ships **6 entries** — **Ollama** (local default · `localhost:11434`), **llama.cpp (GGUF)** (local · `localhost:8080`), **OpenRouter** (cloud · 100+ models), **Ollama Cloud** (cloud · gpt-oss:120b / qwen3-coder:480b / deepseek-v3.1:671b), **OpenAI** (cloud · GPT-4o / GPT-5 / o-series), **Azure OpenAI** (enterprise). Per-provider key slots — no cross-provider bleed. The `llama.cpp` entry also accepts a user-configured base URL via Settings → Preferences → AI, so you can route it to any OpenAI-compatible HTTP server you have installed separately. Local backends keep code on your machine; cloud backends are opt-in. No telemetry. No subscription. No mandatory API key.

#### AI Assistant — side-dock (`Ctrl+Shift+A`)
The AI chat lives in a **persistent right-side dock**, not an editor tab. One conversation, preserved across tab switches. Tick **Coding Mode** to open the 3-column coding layout (file tree · editor · AI chat) 3-pane layout.

**Workspace awareness.** Every prompt carries:
- the selection (or full file if no selection is active)
- the full text of the current file
- excerpts of every other open editor tab
- a flat listing of all files under the workspace root (`.git`, `node_modules`, `target`, `dist`, etc. filtered out)

So the model can reason about files you haven't opened yet — "import from utils.py" works even when utils.py isn't in a tab. Budget-capped so small local models (3B, 4K–8K context) don't overflow.

**One-click actions** (hidden by default, click "▸ Quick actions" to reveal):
Explain · Find Bugs · Refactor · Write Tests · Add Comments · Generate Docs · Optimize · Translate. Or type a custom prompt. Responses stream in, each has a **Copy** link, and the last response can be inserted at the cursor or replace the selection with one click.

**Speech-to-text** — optional mic button, uses local `arecord` + `whisper` CLI when installed. No audio ever leaves the machine.

#### AI Setup
```bash
# 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.

#### 🔐 Password Generator — passwords, passphrases and SSH keys, with an honest entropy figure (`Tools > Password Generator — Passwords / Passphrases / SSH keys`)

A generator built into the binary, opened from the **Password** button on the Built-in Tools toolbar (third, immediately after **AI**) — press it again while the tab is focused and the tab closes. A **left rail** switches between three pages: **Password**, **Passphrase** and **SSH key** (`Alt+1` / `Alt+2` / `Alt+3`). It ships in **every build, including the default Lite binary** and works fully offline: no network, no download, no backend, no AI.
- **Password** — random characters, 4–256 (default 20) from a-z / A-Z / 0-9 / a 27-character symbol set, plus a free-text field for any extra characters you want, with optional *exclude look-alikes* (drops `0 O 1 l I`) and *at least one from each set* (on by default). The symbol set deliberately omits the quotes, backslash, backtick, pipe and space — the characters that break a password pasted into a shell command, a YAML file or a database connection string.
- **Passphrase** — 3–24 words (default 6) from a **2,048-word list compiled into the binary** (no dictionary file is read at runtime), so each word is worth exactly **11 bits** and six words is 66 bits; pick a hyphen / period / underscore / space / no separator, optionally capitalise each word or append two digits. Both text modes generate **1 to 100** independent values at once, with Generate · Copy · Insert into editor · Open in new tab.
- **SSH key** — a real OpenSSH key pair, generated locally: **Ed25519** (recommended), **ECDSA P-256 / P-384**, **RSA 3072 / 4096**, and **RSA 2048** marked *legacy*. An optional passphrase encrypts the private key with `aes256-ctr` + `bcrypt-pbkdf`, the same construction `ssh-keygen` uses. The **comment field is empty by default, on purpose**: `ssh-keygen` defaults it to `user@host`, which copies your username and machine name into every `authorized_keys` file the key is ever pasted into. The public line and its **SHA256 fingerprint** are shown immediately; the private key stays hidden until you tick **Show private key**. **Save private key…** creates the file, sets it to `0600`, *then* writes it, refuses to overwrite an existing file, and writes the `.pub` companion (`0644`) beside it. Generation runs **off the GUI thread**, so the window stays responsive while RSA-4096 takes its 1.6–5.9 s. Interoperability is checked against the system `ssh-keygen` in the test suite — `-y` reproduces the public line, `-lf` the fingerprint, and `-y -P` opens the encrypted key and rejects the wrong passphrase. The pure-Rust crypto (RustCrypto `ssh-key`) costs about **0.6 MB** of binary.
- **The bits number is a count, not a score** — it is the base-2 logarithm of how many distinct values your current settings can produce, not a heuristic guess at how complicated the output *looks*, which is what most meters show. Tick *at least one from each set* and the figure **drops slightly**: it is recomputed by inclusion–exclusion over the selected sets, because the guarantee rules out every password that misses a set, so fewer passwords exist. Most generators advertise the unconstrained number while enforcing the constraint, which overstates the result. Default settings read **129 bits**.
- **Real randomness, and nothing kept** — every draw comes from the **OS random source** (`getrandom` on Linux, the platform equivalent elsewhere) through the Rust core. Qt's `QRandomGenerator::system()` is now only the fallback, not the source — it was the source before v0.1.129, and on x86-64 that meant the CPU's `RDRAND` instruction first, which is a different provenance than most readers assume. SSH keys come from the same OS source. Draws use rejection sampling so no character is favoured just because the alphabet size doesn't divide `2^32`, and the set guarantee is enforced by **drawing again until it holds**, not by placing one character per set and shuffling — placement would make some strings likelier than others and turn the bits figure into a lie. The panel is a plain widget rather than an editor, and session save/restore, the AI-context sweep and the MCP read verbs are all gated on the tab being an editor, so a generated value never lands in `session.json`, never reaches an AI backend, and is not readable by a connected MCP client. **Copy** takes the value back off the clipboard after 30 seconds — but only if the clipboard still holds it, so it never clobbers something you copied since. The 30-second wipe arms for the **private key only**, never for the public line. Nothing is written to disk unless you choose **Save private key…**.

#### 👁 AI Interaction Log — see exactly what your AI did (`Features → AI Interaction Log…`)

**Total transparency, on by default.** Every request **and** response to **any** AI backend — local (Ollama / llama.cpp) or cloud (OpenRouter / OpenAI / Azure / Ollama Cloud), including Noter's Extract — is written to a **local SQLite log you can open and read**: timestamp, backend, model, mode, full prompt + full response, token counts, latency. **It never leaves your machine** (zero network egress), entries auto-prune after 7 days, and a built-in **credential scrubber** masks API keys / bearer tokens / PEM private keys before anything is stored. The viewer has backend/model/mode filters, **Export JSON**, **Prune now**, and a one-click opt-out toggle. You are never guessing what got sent to a model — you can audit every byte. *Privacy as transparency, not as a promise.*

#### 📝 Noter — meeting notes that turn into reminders (`Ctrl+Alt+N`)

A local-first, two-pane meeting workspace (notes list · editor) — no accounts, no bots; notes live under `~/Documents/Notepatra/Noter/`.
- **Write fast** — a top toolbar drops in *Action Items / What I plan / To-dos* headers and checkbox bullets; check a line to strike it through.
- **Extract** (`Ctrl+Alt+E`) runs your AI backend over the note and returns a **summary** plus **action items / decisions / questions / risks**. A task that mentions a time ("ship the build 10am tomorrow") comes back with that date/time pre-filled.
- **Reminders** — set one on a note (right-click) or schedule action items straight from Extract; they all collect in a central **Reminders** list grouped *Overdue / Today / This week / Later*, with desktop notifications at the due time while Notepatra is running (missed ones arrive as one summary at next launch). Click to open the note, pencil to reschedule, ✕ to delete. Re-running Extract flags what's already scheduled so you never pile up duplicates.

#### 📐 Diagram tool — flow / ER / system diagrams from text (`.npd`)

A first-class diagramming surface that **renders in the default binary on every platform** (incl. macOS Apple Silicon and the Windows installer — native Qt renderer, no WebEngine), opened from the toolbar next to Noter. The tiny `.npd` text DSL is the **source of truth**; the canvas is a live projection of it (so undo/redo and version control just work).
- **Create three ways** — **AI Generate** describes it in plain English and a local model writes the `.npd`, shown in a **review pane** before it touches the canvas (undo/redo after); or start from a Flow / ER / System **template**; or write `.npd` directly. **Import Mermaid** converts an existing flowchart, now mapping `-.->` to a dashed edge and `subgraph … end` to a `group`.
- **Why not just Mermaid?** — Mermaid needs a browser or JS runtime; `.npd` renders natively, offline, in the Lite binary, with AI Generate, node-pinned `note`s, `legend`s, built-in icons, theme-following palettes, and an `export_diagram` MCP tool. Mermaid still wins on diagram *types* (sequence, class, Gantt) and on GitHub rendering it inline — the full comparison is in the [docs](https://notepatra.org/docs.html#diagram-vs-mermaid).
- **Drawn flat, like an engineering diagram** — no gradients, no drop shadows: a 1.5 px border, the app's own font, and a tighter grid. Two **light** palettes ship — `paper` and `slate` — alongside the older dark ones (`clay` / `ocean` / `forest` / `mono` / `default`), and the new default `palette auto` follows the app theme: light theme → `paper`, dark theme → `default`.
- **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, **dashed edges** (`a -.-> b`, `a <.-> b`) for the async / optional / best-effort hop, label-overflow→hover, infinite pan/zoom canvas. A per-node colour renders as a **tint** on a light palette (soft wash, full-strength border, normal dark text) and as a solid fill with auto-contrast text on a dark one.
- **Say what a picture can't** — `direction LR` lays the diagram out left-to-right (`TB` is the default), `group "Edge tier" : cdn api` draws a labelled container behind those nodes, `note api "Rate-limited to 100 rps"` pins a small card beside one, and `legend dashed "async"` / `legend #cc785c "hot path"` build a legend box (which appears only if you write at least one `legend` line). **`group`, `note`, `legend`, `direction` and `layout` are now reserved words** — an existing file that uses one as a node id needs the id renamed.
- **Export** — **PNG / JPEG / PDF** on every build, plus **SVG / HTML** where the Qt Svg module is present and **WebP** where the Qt WebP image plugin is present (the menu shows exactly what your build supports). A **Help** button has the full cheat-sheet; `samples/diagram_showcase.npd` shows every element; the `npd_render` CLI renders any `.npd` to an image headless.

### More Features

| Feature | Shortcut |
|---|---|
| **Built-in Terminal** | `Ctrl+`` — opens as a tab, runs real commands |
| **REST Client** | `Ctrl+Shift+R` — send HTTP requests, see responses with pretty JSON |
| **Hex Editor** | View > Hex Editor — color-coded hex dump of any binary file |
| **Markdown Converter** | Features > Markdown — convert selection to table, list, code block, bold, link, heading, or strip HTML to markdown |
| **File Explorer** | `Ctrl+Shift+E` — tree view sidebar |
| **Function List** | View > Function List — lists all functions/classes, double-click to navigate |
| **Preferences** | Settings > Preferences — 6 tabs of configuration |
| **Password Generator** | Tools > Password Generator — Passwords / Passphrases / SSH keys — random characters, a 2,048-word passphrase, or an OpenSSH key pair (Ed25519 / ECDSA / RSA); the bits readout is the exact number of values the settings can produce |

### Keyboard Shortcuts

| Category | Shortcut | Action |
|---|---|---|
| **File** | `Ctrl+N` | New |
| | `Ctrl+O` | Open |
| | `Ctrl+S` | Save |
| | `Ctrl+W` | Close tab |
| **Edit** | `Ctrl+D` | Duplicate line |
| | `Ctrl+Shift+K` | Delete line |
| | `Ctrl+/` | Toggle comment |
| | `Ctrl+Shift+U` | UPPERCASE |
| | `Ctrl+U` | lowercase |
| **Search** | `Ctrl+F` | Find |
| | `Ctrl+H` | Replace |
| | `Ctrl+Shift+G` | Project Search (folder-wide names + contents) |
| | `F3` / `Shift+F3` | Find Next / Previous |
| | `Ctrl+G` | Go to line |
| | `Ctrl+B` | Go to matching brace |
| | `Ctrl+F2` / `F2` | Toggle / Next bookmark |
| **View** | `F11` | Full screen |
| | `Ctrl+=` / `Ctrl+-` | Zoom in / out |
| | `Alt+0` | Fold all |
| **Macro** | `Ctrl+Shift+M` | Start recording |
| | `Ctrl+Shift+T` | Stop recording |
| | `Ctrl+Shift+P` | Playback |
| **Features** | `Ctrl+`` | Terminal |
| | `Ctrl+Shift+A` | AI Assistant |
| | `Ctrl+Shift+E` | File Explorer |
| **Tabs** | `Ctrl+Tab` | Next tab |
| | Middle-click | Close tab |
| | Double-click empty | New tab |

### AI assistants (MCP) — new in v0.1.118

From v0.1.118, Notepatra ships **`notepatra-mcp`** — a stdio JSON-RPC 2.0 [Model Context Protocol](https://modelcontextprotocol.io) server that connects external AI assistants (Claude Desktop, Claude Code, OpenAI Codex, the OpenAI Agents SDK, and any spec-compliant MCP client) to the running editor over a local socket. Nothing leaves your machine: stdio to the client, local socket to the editor, no network connections.

**49 tools in three tiers**:

| Tier | Tools | Gate |
|---|---|---|
| **Read** (24) | tabs, selection, status, recent files, in-tab + project search, Noter notes, reminders, read-only Git (status / diff / log / show / branch), `.npd` validation, read-only SQL (`run_sql`), language list (`list_languages`), capability probe (`get_capabilities`), `.npd` source read (`get_diagram_source`), saved connections (`list_connections` / `run_query` / `list_tables`) | None — observation only |
| **Act** (13) | open file, new tab, go to line, set language, compare tabs, format JSON/SQL/HTML, open note, create diagram, open Noter, open Data Analyst (`open_data_analyst`), render chart (`render_chart`) | None — visible, non-destructive |
| **Write** (11) | insert text, replace selection, find-and-replace, save, create note, append note, set reminder, export diagram, set diagram source, export query results (`export_query_results`), export chart (`export_chart`) | **Approve/Deny card inside the editor** — 120 s auto-deny, FIFO one card at a time, no headless bypass |

`find_in_tab` and `search_project` also take an optional `regex` flag. `run_sql` is **SELECT-only** (rejected by the SQL classifier otherwise) and, on the Full/DuckDB edition, runs in an engine sandbox — the target file is materialized into an in-memory table, then DuckDB's external filesystem access is disabled (`enable_external_access=false`) before the untrusted query runs, so it cannot read host files. Since v0.1.119 the sidecar also supports Windows over a named pipe.

Hook it up in one line each (from v0.1.118):

```sh
# Claude Code (Anthropic)
claude mcp add notepatra -- notepatra-mcp --socket
```

```toml
# Codex CLI (OpenAI) — ~/.codex/config.toml
[mcp_servers.notepatra]
command = "notepatra-mcp"
args = ["--socket"]
```

Full tool reference, Claude Desktop / Agents SDK snippets, security model, and honest limitations (editor must be running; prebuilt Windows sidecar zip + one-click .mcpb bundle ship from the next release — until then Windows uses cargo install notepatra-mcp; cloud-only connector surfaces can't reach a desktop editor): [docs/mcp.html](https://github.com/singhpratech/notepatra/blob/HEAD/docs/mcp.html) / [notepatra.org/mcp.html](https://notepatra.org/mcp.html).

---

## Architecture

```
┌──────────────────────────────────────────────┐
│            C++ Layer (Qt5 + QScintilla)       │
│   UI · Menus · Tabs · Dialogs · Editor       │
│   Terminal · AI Panel · Compare · Plugins     │
├──────────────────────────────────────────────┤
│                C FFI boundary                 │
├──────────────────────────────────────────────┤
│            Rust Core Library                  │
│   File I/O (mmap) · Search (Aho-Corasick)    │
│   Diff (Myers) · JSON/HTML/SQL Formatters    │
│   Bracket Fixer · Hash · Base64 · Encoding   │
└──────────────────────────────────────────────┘
```

**Why this hybrid?**
- **C++** because Qt and QScintilla are C++ — zero friction for UI
- **Rust** because file I/O, text processing, and parsing must never crash — Rust's ownership system guarantees memory safety
- **Result**: the speed of C++, the safety of Rust. The bare executable is **~13 MB** on every platform (~13.4 MB Linux x64, similar on macOS / Windows). Latest v0.1.129 download sizes: **4.7 MB** Linux x64 tar.gz · **4.4 MB** Linux ARM64 tar.gz · **28.0 MB** macOS DMG (with bundled Qt) · **43.9 MB** Windows MSI · **33.0 MB** Windows NSIS · **37.8 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:**
```bash
curl -fsSL https://notepatra.org/install.sh | sh
```

**Windows (PowerShell):**
```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.129](https://github.com/singhpratech/notepatra/releases/latest)

| Platform | Download | Size | What's inside |
|---|---|---|---|
| 🐧 **Linux x64** | [`.tar.gz`](https://github.com/singhpratech/notepatra/releases/latest) | **4.7 MB** | Bare `notepatra` binary. Qt5 from your distro. |
| 🐧 **Linux ARM64** | [`.tar.gz`](https://github.com/singhpratech/notepatra/releases/latest) | **4.4 MB** | Bare `notepatra` binary for `aarch64` / ARM64 Linux. |
| 🍎 **macOS Apple Silicon** (M1–M4) | [`.dmg`](https://github.com/singhpratech/notepatra/releases/latest) | **28.0 MB** | `Notepatra.app` with Qt frameworks bundled. Drag to Applications. |
| 🪟 **Windows x64 (MSI)** | [`.msi`](https://github.com/singhpratech/notepatra/releases/latest) | **43.9 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`](https://github.com/singhpratech/notepatra/releases/latest) | **33.0 MB** | NSIS installer. Registers in Settings → Apps → Installed apps. Uninstall via Control Panel works. |
| 🪟 **Windows x64 (portable)** | [`.zip`](https://github.com/singhpratech/notepatra/releases/latest) | **37.8 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 (~13 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 **~13 MB** on each platform (~13.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.7 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`](https://github.com/singhpratech/notepatra/releases/latest) | `msiexec /i notepatra-0.1.129.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`](https://github.com/singhpratech/notepatra/releases/latest) | 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.129_amd64.deb`](https://github.com/singhpratech/notepatra/releases/latest) | `sudo apt install ./notepatra_0.1.129_amd64.deb` — installs to `/opt/notepatra/` + symlink at `/usr/bin/notepatra`, hicolor icons, `.desktop` registration. ARM64: replace `amd64` → `arm64`. |
| 🐧 **Fedora / RHEL / CentOS Stream / Rocky / Alma** (x64 + ARM64) | [`notepatra-0.1.129-1.x86_64.rpm`](https://github.com/singhpratech/notepatra/releases/latest) | `sudo dnf install ./notepatra-0.1.129-1.x86_64.rpm` — same layout as the .deb. ARM64: replace `x86_64` → `aarch64`. Bundles QScintilla 2.14.1 alongside the binary because Fedora ships an incompatible packaging. |
| 🐧 **Arch / openSUSE Tumbleweed / Manjaro / EndeavourOS / other glibc 2.38+** | [`Notepatra-0.1.129-x86_64.AppImage`](https://github.com/singhpratech/notepatra/releases/latest) | `chmod +x Notepatra-0.1.129-x86_64.AppImage && sudo cp Notepatra-0.1.129-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](#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.129.msi`](https://github.com/singhpratech/notepatra/releases/latest) | `msiexec /i notepatra-local-ai-0.1.129.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.129_amd64.deb`](https://github.com/singhpratech/notepatra/releases/latest) | `sudo apt install ./notepatra-local-ai_0.1.129_amd64.deb` |
| 🐧 **Debian/Ubuntu ARM64** | [`notepatra-local-ai_0.1.129_arm64.deb`](https://github.com/singhpratech/notepatra/releases/latest) | `sudo apt install ./notepatra-local-ai_0.1.129_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.129_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.129` for the lite build and `Notepatra v0.1.129` for the full build (DuckDB bundled), plus `Notepatra Local AI Lite v0.1.129` / `Notepatra Local AI v0.1.129` 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.

```bash
# 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](https://github.com/singhpratech/notepatra/blob/HEAD/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 installer** — `msiexec /i` on Windows, `open <dmg>` on macOS (Finder drag to Applications), `xdg-open` on the Downloads folder on Linux so you replace the binary yourself.

**Safety contract — the updater will never leave you with a broken install:**

| Failure | What happens |
|---|---|
| No internet | Error dialog, zero disk writes |
| Download cancelled | `.part` deleted, nothing else touched |
| Power / crash mid-download | `.part` orphan in `~/Downloads`, current binary untouched |
| SHA-256 mismatch | `.part` deleted, critical dialog shown, current binary untouched |
| No `SHA256SUMS` in release | Refuses to auto-install, opens release page for manual verify |
| No matching platform asset | Refuses to auto-install, opens release page |
| OS installer cancelled or fails | Installer's own rollback — current binary untouched |

The Notepatra process **never rewrites or replaces the running binary.** Only the OS installer you explicitly clicked through may do that, and those installers all have their own transactional rollback (MSI `MajorUpgrade`, DMG copy-on-drag, user-driven file-manager swap on Linux).

**Check manually:** `Help → Check for Updates` or `?` menu. The check is also visible on first launch (silent if up to date).

### Windows: refresh "Open with" entry after upgrading from v0.1.23 → v0.1.24

If you upgraded from v0.1.23 or earlier and your right-click → **Open with** menu still shows `Notepatra â€" native code editor` (mojibaked text) and/or a red ❌ overlay on the icon, that's **Windows shell-cache lag, not a Notepatra bug**. Windows' MuiCache permanently caches the `FileDescription` string the first time it reads an executable's `VERSIONINFO`, and never re-reads it on upgrade. The new v0.1.24 binary embeds clean ASCII; Windows is just showing the cached old string.

**One-time fix** — open PowerShell (no admin needed, all changes are HKCU-scoped) and paste this whole block. Tested and confirmed working on Windows 11:

```powershell
# 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

<details>
<summary>Linux (Ubuntu/Mint/Debian)</summary>

```bash
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
```
</details>

<details>
<summary>macOS</summary>

```bash
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
```
</details>

<details>
<summary>Windows (MSVC)</summary>

**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](https://www.qt.io/download-qt-installer) or [aqtinstall](https://github.com/miurahr/aqtinstall)
3. **CMake** ≥ 3.16 — `winget install Kitware.CMake` or [cmake.org/download](https://cmake.org/download)
4. **Rust** stable — [rustup.rs](https://rustup.rs)

**Build QScintilla via the CMake wrapper** *(once)*
```powershell
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**
```powershell
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**
```powershell
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.
</details>

---

## 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:

```cpp
// 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
    }
}
```

```bash
# 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 / 43.9 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.120. Linux is just the binary (Qt is system-installed). macOS and Windows include bundled Qt. The bare `notepatra` executable inside is ~13 MB on every platform (Linux 13.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 — **79 test suites** on the Full build (76 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:

```bash
./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](https://keepachangelog.com/) and [Semantic Versioning](https://semver.org/). 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.129**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.129) | 2026-08-22 | **SSH keys behind a rail, and diagrams that look drawn by an engineer.** The Password Generator's two radio buttons become a **left rail** — **Password** / **Passphrase** / **SSH key** (`Alt+1` / `Alt+2` / `Alt+3`), and `Tools > Password Generator — Passwords / Passphrases / SSH keys`. The new **SSH key** page generates a real OpenSSH pair: **Ed25519** (recommended), **ECDSA P-256 / P-384**, **RSA 3072 / 4096**, **RSA 2048** (legacy); an optional passphrase encrypts the private key with `aes256-ctr` + `bcrypt-pbkdf`, the same construction `ssh-keygen` uses. **The comment field is empty by default on purpose** — `ssh-keygen` defaults it to `user@host`, which copies your username and machine name into every `authorized_keys` the key is pasted into. The public line and its **SHA256 fingerprint** show immediately; the private key stays hidden until **Show private key**; **Save private key…** creates the file, sets `0600`, *then* writes, refuses to overwrite, and drops the `.pub` beside it. Generation runs **off the GUI thread** (RSA-4096 measured 1.6–5.9 s) and the 30-second clipboard wipe arms for the **private key only**. Keys come from the **OS random source** (`getrandom`) via the Rust core — and **every password draw now uses that source too**, with Qt's generator demoted to a fallback. Interop is asserted against the system `ssh-keygen` (`-y`, `-lf`, `-y -P`). Binary grows ~**0.6 MB** for the pure-Rust crypto (RustCrypto `ssh-key`). **Diagrams (`.npd`) are now flat** — no gradients, no shadows, 1.5 px borders, the app font — with two light palettes (`paper`, `slate`) and a new default **`palette auto`** that follows the app theme, so an existing file with no `palette` line now renders **light** on a light theme. New grammar: `direction LR|TB`, dashed edges `a -.-> b`, `group "Label" : a b c`, `note <id> "text"`, and `legend dashed "…"` / `legend #hex "…"`; per-node colours render as tints on light palettes; Mermaid import maps `-.->` and `subgraph`. `group`, `note`, `legend`, `direction` and `layout` are now **reserved words**. Suites: `test_passwordgen` 67 · `test_passwordgen_panel` 99 · `test_npd_parser` 117 · `test_mermaid_import` 44 · `test_diagram_view` 19 · cargo 175 — full offscreen ctest **79/79** green. |
| [**v0.1.128**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.128) | 2026-08-20 | **A password generator whose entropy figure is a count, not a compliment.** New built-in **Password Generator** — the **Password** button on the Built-in Tools toolbar (third, right after **AI**), or `Tools > Password Generator — Random / Passphrase` — producing either **random characters** (4–256, default 20; a-z / A-Z / 0-9 / a 27-character symbol set that omits the quotes, backslash, backtick, pipe and space so the result survives a shell command, a YAML file or a connection string; free-text extra characters; optional look-alike exclusion) or a **passphrase** (3–24 words from a **2,048-word list compiled into the binary**, so each word is worth exactly **11 bits** and six words is 66 bits), **1 to 100** at a time. **The bits readout is the base-2 logarithm of how many values the current settings can produce** — not a heuristic score of how complicated the output looks — so ticking *at least one from each set* makes the number **drop**, recomputed by inclusion–exclusion because the guarantee rules out every password that misses a set; most generators advertise the unconstrained figure while enforcing the constraint. Draws use `QRandomGenerator::system()` (the OS CSPRNG) with rejection sampling, and the set guarantee is met by **re-drawing** rather than placing-and-shuffling, which would skew the distribution and make the bits figure a lie. The panel is a plain widget, not an editor, so a generated value never reaches `session.json`, an AI backend, or a connected MCP client · **Copy** takes the value back off the clipboard after 30 seconds, but only if it still holds it · nothing is written to disk. Ships in **every build including Lite**, no new dependency, fully offline · two new suites (`test_passwordgen`, `test_passwordgen_panel`) take the Full build to **78/78** ctest green. No MCP verbs, lexers or file extensions changed. |
| [**v0.1.127**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.127) | 2026-08-07 | **Five doors refused a private key. The sixth handed it over in chunks.** Retesting v0.1.126 found `select_range` + `get_selection` returning credential-file contents that `open_file`, `read_tab`, `find_in_tab`, `search_project` and `resources/read` all correctly refused — `get_selection` had been exempted on the reasoning that a selection is human-made, but `select_range` lets the *client* choose the range, which makes that reasoning false. `apply_edit` was a quieter form of the same thing: reading the buffer as a match **oracle**, so "no match" versus a card naming the match reports a file's contents one probe at a time. **The fix is structural.** This is the third release with the same shape — v0.1.125 unified two hand-maintained copies of the deny *list*, v0.1.126 added the check to four verbs, and the retest found a fifth and sixth. Every fix was correct and none converged, because what kept going stale was the **enumeration**. Buffer text now leaves the editor through exactly one accessor that refuses before returning, `select_range` will not even *stage* a selection over a credential file, and a test lints the bridge source and fails the build on any direct buffer read that bypasses it. **Also fixed:** `read_tab`'s marker claimed `[truncated at 5 MB]` whatever cap actually applied (now `[truncated: showing N of M characters]`, and the v0.1.126 claim that it "always reports truncated and total_chars" is corrected — true of the wire, not the tool result) · `select_range` advertised `tab_id` in its schema and rejected it with `-32602`, so a test now walks the whole tool list asserting every advertised selector is accepted · `list_open_tabs` emitted `id` where verbs take `tab_id`, both ship for one release. |
| [**v0.1.126**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.126) | 2026-08-07 | **v0.1.125 taught `search_project` to skip credential files — it never told `open_file` or `read_tab`, so an agent that asked for `~/.ssh/id_rsa` by name still got the whole key back.** A deep retest confirmed all four v0.1.125 Windows fixes on real hardware and found nine more defects underneath. `open_file`, `read_tab`, `find_in_tab` and both legs of `search_project` now check the same deny-list — v0.1.125 had guarded only the filesystem walk, so the same file sitting in an open tab was read straight out of the buffer. **`format_json` invented data and reported success:** it called the JSON panel's auto-*fixer*, so `[1,2` came back as `[1,2]` with `isError:false` — a truncated config file made syntactically valid and semantically invented. The MCP path validates strictly now; the panel keeps its fixer. **Fixed:** a search with zero matches was reported as "No workspace folder is open", which is a different fact about the world · `workspace_searched`/`scope` were sent by the bridge and dropped by the Rust struct, so no client ever saw the fields v0.1.125 promised · without `--socket` every response was fabricated with no marker, so a dropped flag produced an assistant describing three files that do not exist · `find_in_tab` rejected a `title` that `read_tab` accepted · an invalid pagination cursor was ignored and the full list returned. **Changed:** tabs now have a stable `id` — `tab_index` is positional and re-points at a different document when a tab closes, and out-of-range was the *lucky* case · `read_tab` takes `max_bytes` and always reports `truncated` and `total_chars`. |
| [**v0.1.125**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.125) | 2026-08-07 | **A one-word MCP search could return the contents of your SSH private key — fixed, along with three other defects found by testing the MCP server on Windows.** No new editor features; every change is a fix. The credential deny-list existed in **two** hand-maintained copies that had drifted in *both* directions (`ai_tools.cpp` had `*.tfvars`/`*.tfstate`/`.pypirc`/the `.env` family; `git_hunk_apply.cpp` had `*.jks` and an unanchored `id_rsa`; neither was a superset) and `search_project` consulted **neither** — so `read_file` refused `~/.ssh/id_rsa` while a one-word search returned its lines. `src/path_denylist.h` is now the single list, built as the union, with all three callers routed through it. **`search_project` also walked the entire home directory:** `FileExplorer`'s constructor set `m_rootPath = QDir::homePath()` as a *display placeholder*, and four guards read it as "the workspace" — including the AI CSV sandbox and a security guard that had therefore never once closed. `workspaceRoot()` now stays empty until a folder is opened, and a workspace-less search reports `workspace_searched` and `scope` so a partial search cannot pass for a complete one. **Fixed:** `goto_line` past EOF reported `{"line": 99999, "ok": true}` from the top of the document, so an assistant aiming at the end of a file wrote at the beginning behind a correct-looking approval card · `insert_text` with `col` but no `line` silently dropped `col` and wrote at the cursor · the sidecar hung on every unrecognised argument (`--version` printed nothing and blocked on stdin; `--sokcet` silently started the **mock** server so the client received fabricated tabs) · the AI workspace root flapped on every Ctrl+Tab, swapping chat history and cancelling pending write approvals · **Windows binaries reported version 0.1.0** to winget, SCCM, Intune and File Properties for 124 releases — the `.rc` is generated from CMake now, with a CI step reading `VersionInfo` back off the shipped `.exe`. Every new test red-state verified by restoring the pre-fix code, not by negating a condition. |
| [**v0.1.124**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.124) | 2026-07-28 | **Invisible characters are now visible — 527 of them, where Notepad++ draws 113 — and two Find/Replace bugs that were destroying text are fixed.** A file containing a zero-width space used to look exactly like a file without one: symbol support was only whitespace dots and EOL markers, so every zero-width, bidirectional and exotic-space codepoint was undrawable. **View → Show Symbol** is rebuilt to Notepad++'s eight-item structure in its order (Show Space and Tab · Show End of Line · Show Non-Printing Characters · Show Control Characters & Unicode EOL · Show Every Other Invisible Character · Show All Characters · Show Indent Guide · Show Wrap Symbol). The non-printing (49) and control/Unicode-EOL (64) tables are transcribed from Notepad++'s source byte-for-byte, abbreviations included (`ZWNBSP` for U+FEFF, `OSPM` for U+1680, `SGCI` for U+0099). **"Show Every Other Invisible Character"** adds the 414 codepoints Notepad++'s fixed tables miss — variation selectors, the TAG block U+E0020–E007F, the Hangul fillers, the combining grapheme joiner. Blobs can show codepoints (`U+200B`) instead of abbreviations, and every Show Symbol setting now persists across restarts and applies to all tabs. Defaults match Notepad++ — everything off except the control-character display, so non-printing characters are **off out of the box**. **Fixed:** Replace All silently deleted any replacement containing `$` (handed to the regex engine as a substitution template, so `$100` parsed as capture group 100 and expanded to nothing) · Replace reported replacements it had not made and overwrote unrelated selections · Find in Files' default `*.*` filter skipped extensionless files like `Makefile` and `.gitignore` · Mark All highlighted the wrong spans (offsets computed into a lowercased copy, and lowercasing is not length-preserving in UTF-8) · indent guides rendered as a white-on-black barber pole in Dark and Monokai. Three new suites (75 total Full), each red-state verified. |
| [**v0.1.123**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.123) | 2026-07-23 | **The "Save As" dialog is now the native OS file dialog — the Windows Explorer tree everyone knows.** Save As was the only dialog still forced to Qt's own plain "Look in:" list (no folder tree); it now uses the native Windows / macOS / Linux dialog, matching Open, Open Folder, and Save a Copy As, which were already native. The 72-language file-type dropdown is preserved and extensions stay correct (bare name on a Python tab → `.py`; a typed extension is always honoured). It went non-native back in v0.1.88.1 because native dialogs dropped the "which type did you pick" signal and could save the wrong extension — now sidestepped by driving the default extension from the current tab's language instead of the dialog filter. Verified in the running app. |
| [**v0.1.122**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.122) | 2026-07-23 | **Save & Open dialogs remember your last folder — on every platform.** Saving a brand-new file, "Save a Copy As", "Open", and "Open Folder as Workspace" previously reopened at your home folder every time; they now start in the directory you last used and persist it across restarts. "Save As" still prefers the current file's own folder when it has one — the remembered directory is only the fallback for untitled buffers and secondary dialogs, so nothing regresses. Self-healing: a remembered folder that was deleted or lived on an unplugged removable drive falls back to home instead of failing. Covered by a new red-state-verified regression test (72 suites) and confirmed on the compiled Linux binary. |
| [**v0.1.121**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.121) | 2026-07-21 | **MCP design hardening — 6 fixes from live-driving all 48 verbs against a running editor (48 → 49 tools; Read 24 / Act 14 / Write 11).** `list_open_tabs` now reports an **`editable`** flag on every tab, and the read / insert / compare / save verbs gate on it — so the read-only **Welcome tab (index 0)**, previously a silent trap, returns a specific error instead of a no-op. **`save_tab` gains an optional Save-As `path`** so an AI can persist a `new_tab`'s content to disk — still Write-tier and approval-gated, path validated before the card, destination shown on it. New **`select_range`** verb (Act) finally makes `replace_selection` usable by an AI. `git_*` now falls back to the editor's **startup working directory**, so Git works when Notepatra was launched from inside a repo with no workspace open. Generic errors ("could not insert", "could not open compare view", "needs Save As") are replaced with **specific, actionable** ones. **Charts render on Qt 5.15 WebEngine** — the chart bundle now polyfills `Object.hasOwn` and `structuredClone` before the Vega scripts, which Qt 5.15's WebEngine (Chromium ~87) lacks; this had been breaking all chart rendering there (`render_chart` drew nothing, `export_chart` surfaced `e.isString`). Approval gate unchanged: every write needs an in-editor click, 120 s auto-deny, no headless bypass. |
| [**v0.1.120**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.120) | 2026-07-20 | **MCP full control — 48 tools, a remote gateway, and the sidecar finally works on Windows and macOS.** `notepatra-mcp` grows from 35 to 48 tools (Read 24 / Act 13 / Write 11), making every sub-app AI-drivable: **Diagram** (`create_diagram`, `get_diagram_source`, `set_diagram_source`), **Data-analyst** (`list_connections`, `run_query`, `list_tables`, `open_data_analyst`, `export_query_results` — reaches your saved PostgreSQL / MySQL / SQL Server / SQLite / DuckDB connections, SELECT-only and row-capped), **Charts** (`render_chart`, `export_chart`), **Noter** (`open_noter`), and **discovery** (`list_languages`, `get_capabilities`). **Two defects that made the sidecar unusable outside Linux are fixed:** the Windows named-pipe transport deadlocked on every verb — the pipe was opened without `FILE_FLAG_OVERLAPPED` and a parked reader thread blocked every write forever, so no tool call ever completed (only the first request *after* the greeting hung, which is why it looked healthy) — and on macOS the sidecar could not locate a running editor at all, because it guessed `$TMPDIR` while Qt binds under `NSTemporaryDirectory()`; the editor now publishes its real bound endpoint. New opt-in **loopback-only remote gateway** (`serve` / `pair` / `connect`, 8-digit HMAC pairing, fail-closed scopes) — the default build stays **crypto-free**, asserted in every CI job, and a remote write still raises the same local Approve card. Windows gains a prebuilt **cosign-signed sidecar zip** and a one-click **`.mcpb`** Claude Desktop bundle. `.mcpb` also fixed to stop handing Intel Macs an arm64 binary and ARM64 Linux an x64 one. Approval gate unchanged: every write needs an in-editor click, 120 s auto-deny, no headless bypass. Full offscreen ctest 71/71, sidecar 83 tests (117 with `--features remote`). |
| [**v0.1.119**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.119) | 2026-07-18 | **MCP depth — 35 tools.** The `notepatra-mcp` sidecar grows from 22 to **35 tools in three tiers** — **Read 18 / Act 9 / Write 8**. New read tools: `list_reminders`, read-only Git (`git_status` / `git_diff` / `git_log` / `git_show` / `git_branch`), `validate_npd`, and **`run_sql`** (SELECT-only). New act tool: `open_note`. New **human-approved** write verbs: `create_note`, `append_note`, `set_reminder`, `export_diagram`. `find_in_tab` / `search_project` gained an optional `regex` flag. **Windows named-pipe transport is now supported** (Linux/macOS/Windows all connect via `--socket`; prebuilt Windows binaries not yet in the signed bundle — build from source / `cargo install`). **`run_sql` security:** SELECT-only via the SQL classifier and, on the Full/DuckDB edition, an engine sandbox — the file is materialized in-memory, then `enable_external_access=false` is set before the untrusted query runs, so it cannot read host files; an adversarial security pass hardened it before ship. Bare Lite binary unchanged at 13.4 MB (the new tools live in the sidecar). Full offscreen ctest 71/71, Lite 68/68, 60 sidecar cargo tests, live end-to-end across all 35 tools. |
| [**v0.1.118**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.118) | 2026-07-17 | **MCP support — your editor, readable by your AI.** New standalone **`notepatra-mcp`** sidecar: a spec-compliant stdio JSON-RPC 2.0 [Model Context Protocol](https://modelcontextprotocol.io) server exposing **22 tools in three tiers** — read (10), act (8), and write (4) — plus open tabs / Noter notes as MCP resources and 3 ready-made prompts. Works with **Claude Desktop, Claude Code, OpenAI Codex CLI, the OpenAI Agents SDK**, and any spec-compliant stdio MCP client. **Every write is human-gated:** an Approve/Deny card inside the editor window, 120 s auto-deny, FIFO one-at-a-time, no headless bypass — the gate lives in the editor process, so no MCP client can write without a human click. Local socket + stdio only, no network connections; Linux/macOS first (Windows named-pipe transport in a future release). Prebuilt `notepatra-mcp` binaries ship as cosign-signed release artifacts; new docs page [notepatra.org/mcp.html](https://notepatra.org/mcp.html). Bare-binary size claim corrected 12.4 → 13.4 MB. Full ctest 68/68 + 47 sidecar cargo tests + live E2E 17/17. |
| [**v0.1.117**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.117) | 2026-07-17 | **The honesty release — a 32-agent adversarial audit of every component, every confirmed defect fixed, ~4,100 LOC of dead code removed. No new deps, same bare binary (slightly smaller).** **Find & Replace tells the truth:** Find in Files finally shows its results (they were written to a never-shown widget), whole-word is honored by Count / Replace All / Find in Files, line numbers are right in non-ASCII files, and dead controls are hidden until they work. **Noter export unlocked:** notes export as PDF / Markdown from the right-click menu (the exporter was fully built and tested — it just had no UI entry point); resizable pop-out with visible pin state; Dark/Monokai-correct dialogs. **UTF-32 files with emoji no longer corrupt on save** (byte-level round-trip regression-tested). Terminal gained a real Stop button; tab colors survive reorder; image attach is no longer gated by a hardcoded model list. **~150 Rust-core unit tests promoted into CI + release gates**; git-panel test resurrected; full ctest **70/70**. New for AI assistants: [llms.txt](https://notepatra.org/llms.txt) + crawler-friendly robots.txt + CITATION.cff. |
| [**v0.1.116**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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 `open`ed 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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.96) | 2026-05-24 | **Same-day Windows hotfix for v0.1.95.** Three root causes of a reproducible launch-hang permanently addressed. (1) **Platform-conventional config dir** — pre-fix Notepatra wrote to `~/.config/notepatra/` on every OS; Windows users' `%APPDATA%\Notepatra\` appeared empty while session state sat in `C:\Users\<u>\.config\notepatra\`, invisible to IT tooling / backup tools / group policy. New `Config::appConfigDir()` returns `%APPDATA%\Notepatra` on Windows, `~/Library/Application Support/Notepatra` on macOS, `$XDG_CONFIG_HOME/notepatra` on Linux (unchanged). One-time copy-migration from the legacy path on first launch; legacy cleanup queued for v0.1.97. All ~6 hard-coded call sites migrated (config.json, session.json, recovery/, ai-logs/interactions.db, db-connections.json, plugins/). (2) **Crash-safe session restore** — `MainWindow::restoreSession()` now writes a `session.json.restoring` marker before opening files and deletes it on success. On next launch, if the marker exists, the previous restore was interrupted (kill / hang) — session.json is moved aside to `session.json.failed-<timestamp>` (preserving the tab list) and a deferred dialog tells the user where to find it. No more "reopen the same bad file on every launch" loop. (3) **EOL-OpenSSL update-check gate** — runtime check on `QSslSocket::sslLibraryVersionString()` hard-skips the GitHub update check if the bundled OpenSSL is 1.0.x or 1.1.x (EOL since Sept 2023). The TLS handshake against modern endpoints can stall the UI thread on a broken stack; bypassing eliminates that path. Bundling OpenSSL 3.x in the Windows MSI queued for v0.1.97 (CI workflow change). All v0.1.95 tests carry forward green (~1325 total, 0 failing) plus new unit tests for `appConfigDir()` per-platform via env override. |
| [**v0.1.95**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.93) | 2026-05-19 | **Project Search: intelligent flood protection, live phrase-relevance ranking, `match=N%` badges, cross-platform completion notifications.** Replaces the v0.1.92 unbounded-emit model that OOM-killed the app on `import os` over `$HOME` with an informed search experience. **Soft warning at 10k matches** (amber progress bar + status note, no interruption). **Hard checkpoint popup at 50k** with Continue / Show me these / Cancel — clicking *Show me these* flips the UI phase to Idle immediately so the timer doesn't keep ticking while threads wind down; clicking *Cancel* re-enables the Search button instantly instead of waiting for the worker to unwind. **Match all words default OFF** — `import os` runs as exact-phrase by default; power users tick the box for any-order any-line AND semantics. **`match=N%` self-describing badge on every result row** (100% = full phrase on this line, 99% = all tokens scattered, lower = partial; column is fixed-width and visually consistent in every mode). **Live phrase-relevance ranking on both axes** — within-file `std::stable_sort` descending on per-line %, cross-file *live re-positioning* in the tree (take + insert at the correct slot) the moment a file's best-line % improves, so 100% files surface to the top *during* the scan instead of only at the end. **Cross-platform desktop notification** when a search ≥ 3s completes with the window unfocused — one code path (`QSystemTrayIcon::showMessage`) routes to libnotify/D-Bus on Linux, Toast on Windows, NSUserNotificationCenter on macOS, no visible tray icon added. **Bug fixes**: modal dialog double-`%%` ("Scanned X of Y (4%%)"), "⏳ stalled on:" wording replaced with neutral "· current:" when the scan is just slow on one big file, title 🔍 emoji-font fallback on Linux so the header looks the same as Win/Mac, "Show me these" no longer leaves the status saying "Searching…" for 47 seconds. **39/39 ctest pass** (unchanged). All v0.1.92 fixes carry forward unchanged. |
| [**v0.1.92**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.84) | 2026-05-15 | **Biggest syntax-highlighting refresh since v0.1.31.** Every supported language got reserved-word coverage updated from primary vendor sources (postgresql.org, learn.microsoft.com, tc39.es, cppreference, dart.dev, …). Two brand-new lexers — **Plain Text** and **CSV / TSV** — replace previous monochrome fallbacks. Markdown / YAML / CSS palettes refreshed across Light + Dark + Monokai (Markdown H1–H6 contrast gradient, inline-code chip paper-tint, YAML unquoted-value tint, Markdown prose softening, CSS pseudo-class branch). New `src/sql_keywords.h` carries comprehensive SQL keyword union for **six dialects including DuckDB** (T-SQL / PG / MySQL / DuckDB / SQLite / Oracle) — MERGE, RETURNING, LATERAL, JSONB, OVER PARTITION BY, WITHIN GROUP, OPENROWSET, ASOF JOIN now all paint as keyword in every `.sql` tab. New `src/lang_keywords.h` carries primary-source keyword constants for ~50 languages wired via `populateExtraKeywords()` in `lexerutils.cpp` immediately after `setLexer()`. **11 concrete keyword bugs fixed** across R (`repeat`+`return`), Groovy (`yields`→`yield`+`volatile`+`this`), Crystal (`next`+`until`), Dart (`Record`+`augment`), GDScript (`@rpc`+`@tool`+`@warning_ignore`+`namespace`+`Rect2i`+`Vector4i`), Solidity (`blockhash`+`blobhash` for EIP-4844), Mojo (`ref`), Julia (`outer`+`public`+`Float16`), F# (`ValueOption`+`ValueTuple`), Protobuf (WKT siblings), HCL (template directives). **74 sample files** in `samples/` (one rich exemplar per supported lexer, all synthetic — no PII). **Test infrastructure hardened**: three previously-stale "Not Run" tests (`test_network_policy`, `test_chart_types`, `test_fontpack`) wired into `notepatra_all_tests` meta-target. **31/31 ctest pass** (was 28/31). Hard-cap `paint()` closure pattern codified as memory rule for all future custom Scintilla lexers. |
| [**v0.1.83**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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.cpp` — `area`, `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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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.com` → `azure-openai`, `api.openai.com` → `openai`, `ollama.com` → `ollama-cloud`, `openrouter.ai` → `openrouter`, 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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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: 1` → `version: 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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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 `QPushButton`s, 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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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 buttons** — `setGitContext(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 `marginClicked` → `GitGutter::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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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 | Coding | Data]` replaces cluttered single-row checkboxes; default 50% dock width; fixed two dead-code `connect()` calls (Data toggle + Manage Connections were nullptr-wired). HTML / Bracket Tools brought to JSON's strict numbered-rules AI prompt framework ("preserve all content / no new fields"); full output cleanup pipeline parity (`<think>` strip, ``` fence strip, prefix strip, empty-response fallback, recordFix + Show Diff + logAction). Rust core hardening: 50 MB hard cap on JSON / HTML / SQL / Bracket; JSON regex via `OnceLock`; bracket `O(n²) insert(0)` → linear; bracket separate single/double quote tracking (fixes `"don't"` mis-toggle); bracket consecutive-backslash escape counting (fixes `"a\\"`); bracket word-boundary keyword pairs (`redo` no longer matches `do`). **SQL Formatter (real bugs found):** T-SQL `TOP N` and PostgreSQL `ON CONFLICT` were being silently dropped — fixed; UPDATE / DELETE / INSERT now properly pretty-printed instead of falling back to compact Display; CASE/WHEN expansion when long; long IN list wrap; GROUP BY column-per-line. REST client: 30 s transfer timeout; `errorOccurred` handler with semantic messages (DNS / refused / SSL / timeout); HTML-escape on non-JSON bodies. **Git panel: VS Code-style inline diff view** — click a file in Changes, panel splits, +/- lines render with color. SQL Formatter no longer freezes UI for 3 s (cached probe instead of blocking `OllamaClient::isAvailable()`). Removed orphan "Search results will appear here…" widget. Linux emoji-fallback fonts so 🔎 / 🔍 stop rendering as tofu. **Tests: Rust 115 / 115 (was 33; 82 net new), C++ 19 / 19** — including 12 real-world PostgreSQL patterns (UPSERT, recursive CTE, window functions, JSON ops, ARRAY_AGG, multi-condition JOIN ON, dollar-quoted strings, LATERAL). |
| [**v0.1.47**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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 detection** — `fix 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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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|shadow`, `~/.gnupg/`, `~/.aws/`, `~/.netrc`, `~/.npmrc`, `~/.docker/config.json`, etc. — catches symlinks-to-secrets), structured errors. **25-call hard cap** per user turn (prevents runaway loops). Temperature pinned to 0.1 for tool requests. **Anti-tool-call layer made conditional** — swapped for a tool-mode preamble when tools attached so the model doesn't see contradictory guidance. New `test_ai_tools` with **68 assertions** covering deny-list, workspace anchor, traversal blocks, pagination, binary detection, junk filtering, registry shape, model allowlist. Implementation driven by 3 parallel research agents: Ollama/llama.cpp/OpenAI-compat wire formats + established AI coding assistants safe-tool patterns + Notepatra integration mapping. 15/15 regression tests pass. v0.1.36+ deferred: `search` (ripgrep), `write_file`/`apply_diff`. |
| [**v0.1.34**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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 Emoji` → `Segoe UI Emoji` → `Noto Color Emoji` → `Twemoji Mozilla` → `Twitter Color Emoji` → `Symbola` 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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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 completes** — `endAssistantBubble` 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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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: <path>" diagnostic. Em-dashes / dashes removed from MSI / NSIS / license labels. SQL/C-family comment colour calibrated. |
| [**v0.1.18**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.13) | 2026-04-13 | Voice input refinements, AI streaming UX, panels theme audit pass. |
| [**v0.1.12**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/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**](https://github.com/singhpratech/notepatra/releases/tag/v0.1.0) | 2026-04-06 | First public release. Linux x64, macOS Apple Silicon, Windows x64. 100+ file types, 44 lexers. |

<sub>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.</sub>

See the full [CHANGELOG](https://github.com/singhpratech/notepatra/blob/HEAD/CHANGELOG.md) for every change in every release, or browse the [version history on notepatra.org](https://notepatra.org#versions).

---

## 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](https://www.qt.io/)** — the cross-platform C++ GUI framework.
- **[Scintilla](https://www.scintilla.org/) / [QScintilla](https://riverbankcomputing.com/software/qscintilla/)** (Neil Hodgson, Riverbank Computing) — the text-editing control.
- **[Rust](https://www.rust-lang.org/) + [Cargo](https://doc.rust-lang.org/cargo/)** — the language and toolchain for the Rust core (search, diff, formatters, hashing, encoding).
- **[aho-corasick](https://crates.io/crates/aho-corasick)** (BurntSushi) — the literal-search engine behind Project Search.
- **[regex](https://crates.io/crates/regex)**, **[sqlformat](https://crates.io/crates/sqlformat)**, **[sqlparser](https://crates.io/crates/sqlparser)** — regex and SQL dialect handling.
- **[Ollama](https://ollama.com/), [llama.cpp](https://github.com/ggerganov/llama.cpp), [OpenRouter](https://openrouter.ai/), [OpenAI](https://openai.com), [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)** — AI inference backends Notepatra speaks to over standard HTTP APIs.
- **[Sigstore](https://www.sigstore.dev/) / [Cosign](https://www.sigstore.dev/) / [SLSA](https://slsa.dev/)** — keyless signing and build provenance for every release.
- **[Claude](https://claude.ai/)** (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.

---

<p align="center">
  <strong>Envisioned by Prateek Singh. Built with Claude.</strong>
</p>
<p align="center">
  <em>"I didn't build Notepatra because the world needed another text editor.<br>I built it because every developer — on Linux, Windows, or Mac — deserves one that's fast, free, and smart."</em>
</p>

<p align="center">
  © 2026 Prateek Singh. All rights reserved.<br>
  Source code licensed under <a href="https://github.com/singhpratech/notepatra/blob/HEAD/LICENSE">GNU General Public License v3.0</a>.
</p>

---

## License, warranty, and liability

Notepatra is licensed under the **[GNU General Public License v3.0](https://github.com/singhpratech/notepatra/blob/HEAD/LICENSE)**. The full legal text lives in the [`LICENSE`](https://github.com/singhpratech/notepatra/blob/HEAD/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](https://github.com/singhpratech/notepatra/blob/HEAD/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](https://github.com/singhpratech/notepatra/security/advisories/new), 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.

