Back to the catalog

io.github.singhpratech/notepatra-mcp

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

Open source Open in the app JSON README (API)

About

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

Details

Kind
MCP servers
Topic
No topic detected
Publisher
singhpratech
Origin
official
Category
ferramentas
Transport
local
Version
0.1.119
Stars
6
Forks
1
Last push
2026-08-22T20:10:13Z
Repository state
ativo
Language
C++
License
GPL-3.0
Added
2026-08-29 04:01:24
Updated
2026-08-29 04:01:24
Origin id
io.github.singhpratech/notepatra-mcp

README

<p align="center">
  <a href="https://notepatra.org"><img src="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="CONTRIBUTING.md">Contributing</a> ·
    <a href="SECURITY.md">Security</a> ·
    <a href="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="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](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](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 

More