What shipped,
and what is next.

13 releases since 26 August 2026.

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 release
Linelark

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

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.

  • A headless Claude Code beside the editor
  • Nothing changes without you seeing it
  • More than one terminal at a time

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.

  • File ▸ Compare with File on Disk
  • A plugin can say which project it acts on

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.

  • Windows, the way VS Code has them
  • Panels belong to the window, preferences belong to the app
  • The terminal panel splits

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.

  • Plugins can keep things, and follow you
  • Sign in to a plugin through your browser
  • Diffs can be merged, not only read
  • Remotes, links and settings from a panel
  • The catalog stops offering what it cannot run

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.

  • Upgrading from 1.0 no longer empties the editor
  • Unsaved tabs survive an upgrade

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 titlebar does the editing
  • Completion can offer itself
  • Diffs are drawn as two files
  • Plugin panels grew up

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.

  • Reviewed JavaScript plugins
  • Editing foundation
  • Files, formats, and search
  • Panes and continuity
  • Studio terminal
Studio 1.0

Studio 1.0.

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

What is being
built next.

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.

Being weighed up

  • Code folding

    (needs a per-language fold-region model on top of the tokenizer)

  • Macro recording and playback

    Macro recording and playback

Shipped

  • Undo back to the saved state clears the dirty marker

    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

  • Folder tree refresh

    button in the panel header, a *Refresh* item on every folder's context menu, and View ▸ Refresh Folder Tree

  • Auto-completion

    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

  • Overwrite (INS) mode

    per document, toggled from Edit ▸ Overwrite Mode or the INS/OVR status-bar indicator; a newline is never overwritten

  • Optional spell checking

    for prose file types (Normal Text, Markdown, LaTeX, NFO), off by default and never enabled in code

  • Recent files prune

    entries whose file has gone are dropped on launch and on use

  • Bookmarks

    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

  • Replace in Files

    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

  • Embedded lexers in markup

    <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

  • CSS brace tracking

    (fell out of the above) — the CSS lexer now follows { } across lines instead of guessing per line, so single-line rules colour correctly

  • Large-file highlighting

    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

  • Document map

    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

  • Column / rectangular selection and editing

    ⌥-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

  • Integrated terminal

    (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

  • Split terminals

    (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

  • More than one terminal at a time

    (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

  • Diff view

    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

  • Phase 1 — read-only MCP

    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.

  • Phase 2 — the dock

    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.

  • Phase 3 — agency

    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.

  • Phase 4 — triggers

    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.

  • Phase 5 — autostart

    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.