The sandboxed edition is on the App Store.
Linelark Notepad — the sandboxed edition — has been accepted by Apple and is now on the Mac App Store, free, with no account.
- The second edition is out

News
Every version is listed with what it brought and the number of changes it stands for. The latest is Studio 1.8.0, published 5 September 2026.
Download the current releaseLinelark Notepad — the sandboxed edition — has been accepted by Apple and is now on the Mac App Store, free, with no account.
Claude in the right dock — a headless `claude` per window that reads your open tabs as typed through a per-window MCP server, and changes them only after you have seen the change as a diff. Five phases: the read-only server, the dock, agency, triggers, and autostart, each safe only once the one before it existed. Everything that can start a turn without you is off until it is switched on.
Plugin API generation 8 — workspaceFolders() and repoSelectFolder(), so a plugin can say which project in a multi-root workspace its git actions are about — and File ▸ Compare with File on Disk, §5's diff view.
Build 10. The Store target's MARKETING_VERSION is deliberately untouched — a separate submission on its own train.
Studio 1.6.1.
Windows and terminal panes, which is the version this tree has been building towards: 1.5.0 published the sidebar's file operations, and everything since has been about a workspace that can be in more than one place at once.
Studio 1.5.0.
The plugin API reaches generation 7 in this build, and a plugin declaring 3 or above — which is most of them now — cannot load on 1.2.0 at all. Nothing in the catalog filters by generation, so the plugins waiting on this release are unpublishable until it is out.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1.0 shipped as com.linelark.notepad.studio and 1.1 as com.linelark.studio. UserDefaults.standard is keyed by that identifier, so the rename did not migrate the session — it stranded it. Upgrading looked like a factory reset: no tabs, no folder, default settings, with everything 1.0 had written intact but in a domain nothing reads any more.
The next untitled number was read off the session manifest rather than off what the restore produced, and the manifest names two kinds of buffer that are not on screen.
A minor rather than a patch: the plugin API gains a generation, which is a promise about what a plugin may depend on, not a fix.
The Developer ID string, the team id and the notary profile name were only discoverable by running `security find-identity` and guessing what the profile was called, and both documents used OWNER/TEAMID placeholders that cannot be copied and pasted.
- Introduced a new Xcode target "Linelark App Store" for the Mac App Store edition. - Updated build scripts and documentation to reflect the new target. - Configured entitlements and Info.plist specific to the App Store edition. - Excluded terminal and Claude Code integration from the App Store target. - Adjusted source files and settings to ensure proper target membership and compilation conditions. - Updated README and launch roadmap to clarify the separation between Studio and App Store editions.
Published from the same roadmap the work is planned in. Anything not listed here is either finished, or not settled enough to be worth promising.
(needs a per-language fold-region model on top of the tokenizer)
Macro recording and playback
the document keeps a fingerprint of the text as last loaded or saved; hashing only runs when the length matches, so it costs nothing on ordinary edits
button in the panel header, a *Refresh* item on every folder's context menu, and View ▸ Refresh Folder Tree
the language's keywords plus identifiers near the caret, via Edit ▸ Complete Word (⌃Space, and macOS's own ⌥Esc). Nothing is preselected, so typing always wins
per document, toggled from Edit ▸ Overwrite Mode or the INS/OVR status-bar indicator; a newline is never overwritten
for prose file types (Normal Text, Markdown, LaTeX, NFO), off by default and never enabled in code
entries whose file has gone are dropped on launch and on use
click the gutter margin to toggle, ⌘F2 / F2 / ⇧F2 to toggle and step through them (wrapping), plus *Remove Bookmarked Lines* and *Clear All Bookmarks*. Marks are held as character offsets and shift with edits above them
Search ▸ Replace in Files… or the find bar's In Files scope. Confirms with the exact match and file counts before writing, preserves each file's encoding and line endings, skips files open with unsaved changes, reloads clean tabs afterwards, and reports any file it could not write
<script> and <style> bodies are handed to the JavaScript and CSS lexers, with block-comment state carried across lines and the closing tag matched case-insensitively
(fell out of the above) — the CSS lexer now follows { } across lines instead of guessing per line, so single-line rules colour correctly
the highlighter now caches the lexer state each line starts in, so painting any region starts from the state it genuinely begins in instead of assuming "normal" 200 lines back. An edit only invalidates the cache from its own line down, and repaints that are not edits (scrolling, retheming) keep it
View ▸ Show Document Map. Draws one bar per screen row rather than per line (sampling the longest line in each slice), echoes the real token colours where they have been computed, marks the viewport, and click/drag scrolls the editor
⌥-drag selects the same columns on every line; typing and backspace apply to all of them as one undoable change (AppKit collapses multi-range edits to the first range, so this is done by hand). Plus the Column Editor (⌥⌘C): insert text or a decimal/hex/octal number sequence, with optional zero padding, down the column
(Terminal ▸ Show Terminal, ⌃`) — a real xterm, bound either to a tab (started in that file's folder, so switching tabs switches to a shell already rooted where the file lives) or to one of the workspace's folders, which the panel asks about when there is a real choice. A file terminal can be pinned to keep it running past its tab, and reopening the file takes the pinned shell back over; a project terminal is pinned by definition. Hiding the panel only hides it, and the panel goes to full height and back without losing the height it had. A login shell, so the PATH is the user's own even when the app is launched from Finder; full TUI programs run in it. While any shell is alive the open files are polled and clean tabs reload themselves, so edits a command makes show up in the editor
(Terminal ▸ Split Terminal, ⌃⇧5, or the header's ⧉) — the panel divides into up to three columns, as the editor divides into panes. A split is a second shell on the *same* binding, so both halves export the same file, pin together and go when the tab does; it starts in the directory the shell it came from is currently in. The keyboard follows the click, Focus Next Terminal steps round the columns, and a tab switch only ever changes the pane being worked in
(Terminal ▸ New Terminal, ⌃⇧N, or the header's ✚) — a new shell *in place of* the one on screen rather than beside it, which is what makes it different from a split. It asks the same question the first terminal is picked from, over the top of what is running, and the ✕ goes back. The question is put differently, though: the panel resolving what to show is asked *for* you and prefers doing something over asking, while this is asked *by* you and asks whenever there is more than one answer — so choosing "this file" once can no longer put a project terminal out of reach. Every open shell is then listed down the right-hand side, click to switch, drag its edge to resize, and it draws itself only once there are two to list. Ending the shell the panel was showing asks the same question rather than sliding another one into its place — which is what closing a terminal used to do, and once "this file" had been answered it did it by starting the terminal again
File ▸ Compare with File on Disk, which opens the buffer against what is on disk as a two-column diff tab. Shipped in 1.7.0; §7 phase 3 then reused it to show an agent's proposed edit before it lands, which is the same review surface. UnifiedDiff.patch was the missing half: the renderer read git diff output, and the newer side of this comparison has never been written anywhere for git to read. It is checked *against* git diff line for line over randomised inputs, which is the only independent opinion available about whether a diff is right
A local server over a Unix domain socket (0600, its path handed to each shell as $LINELARK_MCP_SOCKET beside $LINELARK_FILE), JSON-RPC, every call hopping to the main thread because NSTextStorage is not readable off it. Tools: open_tabs, active_buffer, selection, read_lines, tail_lines and find_in_buffer — that last one through FindEngine, so it searches the buffer *as typed* rather than the file on disk, which is the whole reason to ask the editor instead of running grep. Scoped to the window whose shell made the call and not to WorkspaceRegistry.current: the front window follows the user's clicks, so "the active tab" would otherwise drift away from the session that asked. Reads are capped and ranged, or a 40 MB buffer arrives in a context window whole. The installer writes the bridge, registers the server in ~/.claude.json and pre-approves these tools, the way it already writes the SessionStart hook. No UI at all.
A ClaudeConversation per window, a right-dock panel, an AgentSession wrapping the headless process, start/stop from the titlebar toolbar, and a sign-in step that opens the terminal panel with claude in it rather than failing behind the curtain. The conversation resumes across a relaunch, keyed by WindowScope the way tabs and frames already are — a panel that forgot everything on quit would be the only thing in the app that does. Read and talk; no write access to anything yet.
switch_tab, open_file, replace_lines and insert_lines, the last two going through EditorSession.replace(range:with:actionName:), so an agent's edit is one undoable edit that reaches every pane — the same rule a plugin's writes already follow. It landed with the §5 diff view, because that is the same review surface: an edit that can be undone still has to be *seen* first, so the card in the dock carries the patch the buffer would become rather than the name of the tool asking. The load-bearing half is the permission prompt: a headless agent asking to write hits Claude Code's own permission system, which has no TUI to ask in, so --permission-prompt-tool points at an MCP tool of ours and the approve/deny is drawn in the dock.
An explicit button first, then file events (save, open), then an interval. Each one off by default and switched on separately. Debounced, because a burst of saves is one event and not six turns; and gated on the busy state the stream already reports, so a trigger arriving mid-turn is queued or dropped rather than interleaved into a running one.
Last on purpose. It is only safe once stop, interrupt, the busy state, the per-trigger switches and a visible indication of what a session is spending all exist — an agent that starts itself before any of those is a background process the user cannot see, stop or account for. Two of those five did not exist yet, so the phase is three things rather than one.