<p>There's a particular kind of afternoon where you're deep in a Laravel project, three terminals open, an AI assistant in a fourth window, your database client in a fifth — and you realize you're spending more energy arranging tools than writing code. That afternoon is where e started.</p><p>I didn't set out to build "the next VS Code." I set out to build the editor I actually wanted: fast, native, quiet, and unusually good at the specific stack I live in every day. This is the story of why, and how — with examples you can try.</p><h2>Why build another editor?</h2><p>Three itches, honestly.</p><ol><li><p><strong>Native speed, without the ceremony.</strong> Electron editors are wonderful and I owe them years of good work. But I wanted something that opens instantly, sips battery, and feels like it's part of the machine. So e is pure Rust, GPU-accelerated, drawing its own UI. Cold start is measured in a blink, not a spinner.</p></li><li><p><strong>PHP and Laravel as first-class citizens, not plugins-on-top.</strong> Most editors treat Laravel as "PHP, plus some extensions you configure." I wanted the framework baked in — routes, views, config, translations, Blade components — understood natively.</p></li><li><p><strong>AI that actually cooperates with the editor.</strong> Not a chat box bolted onto the side. An agent that can see your cursor, reuse your language server, query your database through your existing connection, and — crucially — propose edits you review, rather than silently rewriting files.</p></li></ol><p>None of those are impossible in existing editors. I just wanted them to be the center of gravity, not the periphery.</p><h2>How it's built: grow it, don't scaffold it</h2><p>The guiding principle was incremental honesty. Every feature had to compile, pass a test, and survive a real editing session before the next one started. No giant "framework" commit that does nothing. The project is a small Rust workspace:</p><pre><code class="language-text">e-core   → buffers, syntax (tree-sitter), git, diffing, the undo tree
e-lsp    → Language Server Protocol client
e-term   → PTY-backed terminal
e-db     → MySQL / Postgres / SQLite / ClickHouse drivers
e-app    → the UI, wiring it all together
</code></pre><p>Reactive UI, so state flows one way and the screen follows. Here's the flavour — a settings toggle that applies live and persists:</p><pre><code class="language-rust">toggle_row(
    "Inlay hints",
    "Inline type &amp; parameter hints",
    move || s.settings.get().inlay_hints,
    move |v| {
        s.settings.update(|st| st.inlay_hints = v);
        config::set_bool("inlay_hints", v);
    },
);
</code></pre><p>That's the whole pattern: read a signal, write a signal, save to disk. The screen re-renders itself.</p><h2>Example 1: Laravel that the editor actually understands</h2><p>Open a Blade file and type a route helper. e doesn't guess — it ran <code>php artisan route:list</code> for you and knows your routes:</p><pre><code class="language-php">return redirect()-&gt;route('invoices.show', $invoice);
//                        └── completes real route names,
//                            hover shows the URI + controller,
//                            ⌘-click jumps to the controller method
</code></pre><p>And here's the one that still makes me smile. Type <code>$user-&gt;</code> on a model instance:</p><pre><code class="language-php">$user = User::find($id);
$user-&gt;    // ← suggests created_at, email, remember_token…
           //   real columns, read from your live database schema
</code></pre><p>That last suggestion isn't from a static stub file. e connected to your database (from <code>.env</code>), read the actual <code>users</code> table, and merged those columns in next to your language server's methods. It's the kind of thing you don't notice until you switch to an editor that can't do it.</p><h2>Example 2: a branching undo tree</h2><p>Every editor has undo. Almost none let you get back a branch you abandoned. In e, undo is a tree: undo a few steps, type something new, and the old path isn't thrown away — it becomes a sibling you can return to.</p><pre><code class="language-text">● now  (+40)
├─ ⑂  "the refactor I kept"   (2m ago)
└─ ⑂  "the experiment I undid" (5m ago)   ← still here, one click away
</code></pre><p>Press ⌘⌥U, click any node, and the buffer time-travels there. The whole tree is saved per file, so it survives closing the app. I've recovered "lost" work with this more times than I'd like to admit.</p><h2>Example 3: search by meaning, locally</h2><p>Sometimes you don't know the function name — you know what it does. Press ⌘⌥K and describe it:</p><pre><code class="language-text">│ where is the invoice email sent
</code></pre><p>e ranks locations by meaning. If you have a local embedding model running it uses real semantic search; if not, it falls back to a fast lexical index. Either way, nothing leaves your machine. Privacy isn't a setting here; it's the architecture.</p><h2>Example 4: an AI agent that asks before it writes</h2><p>The agent gets a local socket to cooperate with the editor. When it wants to change a file, it doesn't just write it — it proposes it, and you review hunk by hunk:</p><pre><code class="language-diff">- $user-&gt;notify(new InvoiceMail($invoice));
+ Mail::to($user)-&gt;queue(new InvoiceMail($invoice));   [ accept ] [ reject ]
</code></pre><p>Accept the changes you like, reject the rest. There's an autonomous test loop too (⌘⇧T): run the suite, let the agent iterate on failures — proposing edits you approve — until it goes green. It's collaboration with a seatbelt.</p><h2>The unglamorous part: shipping</h2><p>A tool you can't install nicely isn't finished. So the last stretch was all the boring, important stuff: a universal <code>.dmg</code> (Apple Silicon and Intel), signed with a Developer ID and notarized by Apple so it opens without scary warnings. That whole pipeline now runs itself in CI on every tag:</p><pre><code class="language-bash">xcrun notarytool submit e-x.y.z-universal.dmg --keychain-profile e-notary --wait
# → status: Accepted
xcrun stapler staple e-x.y.z-universal.dmg
# → The staple and validate action worked!
</code></pre><p>The app-specific password lives only in the keychain and in encrypted CI secrets — never in the repo. Details matter, especially the invisible ones.</p><h2>What I've learned</h2><ul><li><p><strong>Small commits are a feature.</strong> Every step compiling and tested meant I was never more than one <code>git revert</code> from a working editor.</p></li><li><p><strong>Reuse beats cleverness.</strong> The command palette's fuzzy ranking is the same function as the file finder's. One good idea, two places.</p></li><li><p><strong>Constraints are a gift.</strong> "Native, local, private, fast" ruled out a lot of options — and made every remaining decision easier.</p></li></ul><p>e isn't trying to be everything. It's trying to be right for a particular way of working: PHP and Laravel, a terminal at hand, an AI agent that helps without taking the wheel, and a UI that gets out of the way.</p><p>The editor for the rest of us. Come build with it. ☕</p>