Alexis Rondeau

Newsfeed

Repository · Wednesday, June 17, 2026 · 16:25

kern

The text editor I've always wanted: The beefy love-child of Emacs, iA Writer, and Obsidian

2 stars · C

A minimalist text editor — a cross between Emacs and iA Writer. Emacs-style keybindings and editing model, with a clean, distraction-free iA Writer look: the iA Writer Quattro/Mono typefaces, live markdown styling, and a full-window writing surface.

A macOS app: a thin SwiftUI shell launches the editor, which is written in C with SDL2 + OpenGL for rendering and stb_truetype for font rasterization. There’s no UI toolkit — the editor draws everything immediately through a small 12-function renderer interface (renderer.h) over a ~30-line geometry/color header (gfx.h). SDL2 is statically linked from a vendored library, so the .app is self-contained (no Homebrew dependency) and runs with Hardened Runtime + App Sandbox.

Building

open application/Kern/Kern.xcodeproj

Then Build & Run (⌘R). Produces a self-contained Kern.app with the fonts bundled as resources. Apple Silicon only.

Usage

On launch it opens today’s daily note (YYYY-MM-DD.md, seeded with a date heading). The buffer auto-saves every few seconds while modified. Files live in the app’s sandbox container:

~/Library/Containers/com.rondeau.Kern/Data/Documents/

Open/save paths resolve relative to that folder (reach it in Finder via Go → Go to Folder).

Keybindings (Emacs-style)

Meta (M-) is either Alt or the Esc prefix — e.g. M-d = Alt-D, or Esc then D.

Files

KeysAction
C-x C-fFind/open file (inline completion — Tab to accept)
C-x C-sSave
C-x C-wWrite file (save as)
C-x bSwitch buffer (recent files — Tab to list/cycle)
C-x C-cQuit

Movement

KeysAction
C-f / C-bForward / backward char
C-n / C-pNext / previous line (by visual row)
C-a / C-eStart / end of line
M-f / M-bForward / backward word
C-v / M-vPage down / up
M-< / M->Beginning / end of buffer
C-lRecenter (center → top → bottom)
C-x tTypewriter mode (pin active line at golden ratio)
C-x ySyntax highlight (parts of speech, by brightness)
C-x sStyle check (strike cuttable fillers/redundancies)
C-x lSymbols on/off (draw -> -- != (c) quotes lambda forall as → — ≠ © “ ” λ ∀; on by default)
C-x pPage borders on/off (hide/show the page rails, gutters & note margins)
C-x gGraph view (interactive map of wikilinks, same-day & opened-after relations; click a node to open it, drag the background to pan)
M-gGo to line

Editing

KeysAction
C-dDelete char forward
C-kKill to end of line
M-d / M-⌫Kill word forward / backward
C-tTranspose characters
C-oOpen line
Tab / Shift-TabIndent / outdent list item
M-u / M-l / M-cUpcase / downcase / capitalize word
C-/Undo

Selection & clipboard (cut/copy/yank sync with the macOS clipboard)

KeysAction
C-SpaceSet mark
Backspace / DeleteDelete the selected region
C-w / M-wCut / copy region
⌘CCopy region
C-y / ⌘VYank (paste)
C-x hSelect whole buffer
C-x C-xExchange point & mark
** * == ++ `With a region marked, wrap it: bold / italic / highlight / underline / code
Cmd-Shift-HToggle <mark>highlight</mark> around the caret’s sentence
Cmd-Shift-UToggle ++underline++ around the caret’s sentence

Search & view

KeysAction
C-s / C-rIncremental search fwd / back
⌘= / ⌘-Increase / decrease font size
EscCancel / clear mark

Markdown headings, bold, italic, highlight (==), and underline (++text++) are styled live as you type. Each document also grows a read-only Context section at the bottom listing backlinks and notes created the same day.

Notes & wikilinks

KeysAction
` Wikilink autocomplete (↑/↓ to pick, Tab/Enter to accept)
Cmd-EnterFollow the [[wikilink</span> under the cursor
Cmd-Shift-← / Cmd-Shift-→Back / forward through note history
Cmd-Shift-NExtract the selected region into a new linked note
Cmd-Shift-MWrite a margin note (footnote) at the caret
Cmd-Shift-TOpen (or create) today’s daily note
C-x nDownload your X home feed into a time-stamped news note
C-x mDownload ALL your X bookmarks into a time-stamped note

Publishing to X (Twitter)

When an X account is connected, a paper-plane button appears in the top-right of the title bar (next to the help and folder buttons). Click it to post the note you’re viewing — or the marked region, if one is active — straight to your X timeline. The result shows up both as a macOS notification (Posted to X ✓ / a failure banner) and in the editor’s status bar. The button is hidden until you link an account.

The connection also works the other way: C-x n downloads your home feed into a time-stamped Twitter-Home-Newsfeed-YYYY-MM-DD-HHMM.md note — one ## author — date at time entry per post, with the text as a > blockquote (rendered in italics), the byline, and the URL below it, so you can mark up a post and Cmd-Shift-N it into a note of its own. Image-only posts (just a media link) and one-liners are filtered out.

C-x m does the same for your bookmarks: every bookmarked post (paginated through the whole list, nothing filtered — you marked them for a reason) lands in a Twitter-Bookmarks-YYYY-MM-DD-HHMM.md note in the same format. Requires the bookmark.read scope — accounts connected before this feature must disconnect and reconnect once in Settings → X. (Reading the timeline uses X’s timelines/reverse_chronological endpoint, which needs an API tier that includes read access.)

One-time setup

  1. Create an X app at console.x.com (pay-per-use; ~$0.015/post). In User authentication settings:

    • App permissions: Read and write
    • Type of App: Native App (public client — PKCE, no secret)
    • Callback URI: <http://127.0.0.1:8123/callback>
  2. Add your Client ID locally. Copy the OAuth 2.0 Client ID from the portal into a gitignored config file:

    cp application/Kern/Config/Secrets.example.xcconfig \
       application/Kern/Config/Secrets.xcconfig
    # then paste your id into X_CLIENT_ID
    

    It’s injected at build time via Info.plist, so it never enters the repo. A clone without this file still builds; the X feature just reports “not configured”.

  3. Connect. Build & run, open Settings (⌘,) → X, click Connect X Account, and approve in your browser. Tokens are stored in the macOS Keychain and auto-refreshed, so you only do this once. (The panel reads state when opened — use Refresh if it looks stale.)

Behavior

  • ≤ 280 characters → a normal post.
  • > 280 characters → sent as one long-form post (requires X Premium). Note that X’s API support for >280 is inconsistent even for Premium; if it’s rejected you’ll see X: Your Tweet text is too long and nothing is posted.

How it works. OAuth 2.0 with PKCE via a short-lived loopback redirect listener (RFC 8252), all on a background thread — editor_main parks the main thread in the SDL event loop, so the Swift networking layer must stay off the main actor and report through the C status bar.

Layout

application/Kern/
  Kern.xcodeproj    the app project (open this)
  Info.plist        app metadata; injects X_CLIENT_ID from the xcconfig
  Config/           Base.xcconfig + Secrets.example.xcconfig (Secrets.xcconfig
                    is local-only / gitignored)
  Kern/
    App/            SwiftUI shell + X publishing, bridging header, asset catalog
    Editor/         the C editor: textview, buffer, editing, navigation,
                    undo, md_render, commands, recent, clipboard, clock,
                    gfx, editor_types
    Platform/       SDL/GL renderer + macOS window chrome
    ThirdParty/     stb_truetype
    Resources/      iA Writer fonts + OFL.txt
  Vendor/SDL2/      vendored SDL2 static lib + headers

License

MIT — see LICENSE. Includes SDL2 (zlib) and stb_truetype (public domain), each under their own licenses. Typeset in the iA Writer Quattro & Mono typefaces (© Information Architects), used under the SIL Open Font License 1.1 — see OFL.txt.