<p>Most editor features live inside a single file. Syntax highlighting, completion, go-to-definition — they're about the file under your cursor. But the bugs that actually eat your afternoon almost never live in one file. They live in the space between two files that pretend to be one.</p><p>A controller sends props; a Vue component hopes they're the right shape. A Livewire class declares <code>$name</code>; a Blade view binds to it with <code>wire:model="name"</code> — and nothing keeps them in sync. A route runs beautifully in dev and fires forty queries you never see. That in-between space is invisible to almost every tool.</p><p>e 0.6.4 is about making it visible. Here's the how and the why, with examples.</p><h2>Why Inertia was the big gap</h2><p>e already understood both halves of an Inertia app: PHP and Laravel on one side, Vue/Svelte/TypeScript on the other, plus the live database schema. But the bridge between them — which is Inertia — was invisible. The architecture map, for instance, was built around Blade views, so an Inertia project showed you half an app: route → controller → …nothing.</p><p>That's a table-stakes miss for a Laravel editor. So the first thing was resolution.</p><h3>It just resolves now</h3><p><code>Inertia::render('Users/Index')</code> now behaves exactly like <code>view('users.index')</code> always has:</p><pre><code class="language-php">return Inertia::render('Users/Index', [
    //                   └── ⌘-click jumps to resources/js/Pages/Users/Index.vue
    'users' =&gt; User::paginate(),
]);
</code></pre><p>Completion suggests your real page components, and the architecture map (⌘⌥M) now runs the whole way: route → controller → page component. Half an app became a whole one.</p><h3>The killer: the props contract (⌘⌥C)</h3><p>Here's the problem nobody had solved cleanly. The controller sends <code>['users' =&gt; User::paginate(), 'filters' =&gt; …]</code>, and the page component just... trusts it. People bolt on <code>spatie/typescript-transformer</code>, Wayfinder, or hand-rolled typegen scripts to paper over it.</p><p>e can do it with zero package setup, because it already knows your Eloquent models and the live schema. Open a page component and press ⌘⌥C:</p><pre><code class="language-text">Props Contract — Users/Index
controller: app/Http/Controllers/UserController.php

users: User[]           ✓
filters: unknown        ⚠ sent but unused
role                    ⚠ used but never sent
</code></pre><p>It found the controller, inferred that <code>User::paginate()</code> is a <code>User[]</code>, noticed the component never touches <code>filters</code>, and — the other direction — noticed the component reads <code>props.role</code> that the controller never sends. That second one is the bug that ships to production and blows up on someone's laptop.</p><p>And Generate TypeScript writes real interfaces, expanding each model from the actual database columns:</p><pre><code class="language-ts">export interface UsersIndexProps {
  users: User[];
  filters: unknown;
}

export interface User {
  id: number;
  email: string;
  bio?: string;      // nullable column → optional
}
</code></pre><p>No transformer, no schema file to maintain. It's the one thing no editor or LSP does today, because it needs PHP, TypeScript, and the database understood at the same time — which is exactly the combination e sits on.</p><h3>The natural extensions</h3><p>Once the bridge exists, the rest falls out of it.</p><p><strong>Ziggy on the JS side.</strong> <code>route('users.show')</code> in your Vue/Svelte files gets completion, hover, and go-to-definition — from the same route table the PHP side uses:</p><pre><code class="language-js">router.visit(route('users.show', user.id))
//                  └── hover shows GET /users/{user} → UserController@show
</code></pre><p><strong>Shared props.</strong> <code>HandleInertiaRequests::share()</code> is parsed, so the "global" props that were invisible to tooling finally complete:</p><pre><code class="language-js">$page.props.auth.user   // ← completes, because e read share()
</code></pre><p><strong>Inertia-aware replay.</strong> Replaying a route used to show raw HTML. Now it shows the Inertia payload as an explorable tree, component name on top:</p><pre><code class="language-text">⚛ Users/Index
props
  users  [15 items]
    [0]
      id: 1
      email: "ada@example.com"
</code></pre><p><strong>The form contract.</strong> <code>useForm({ name: '', email: '' })</code> checked against the matching FormRequest's rules — following <code>form.post(route('users.store'))</code> all the way to <code>StoreUserRequest::rules()</code>:</p><pre><code class="language-text">form → users.store  (StoreUserRequest)
  phone   ⚠ not validated
  terms   ⚠ validated but not in the form
</code></pre><p>Forms are 80% of CRUD, and this catches the exact mismatches that silently drop fields.</p><h2>While we were at it: Livewire and runtime</h2><p>Two more in-between spaces got filled.</p><p>Livewire treats the class and the Blade view as the one thing they pretend to be. <code>wire:model</code> completes from the class's public properties, ⌘⌥J flips between the two files, and the crown jewel — renaming a property with F2 updates both the class and every <code>wire:</code> reference in the view:</p><pre><code class="language-text">public $email;          →  public $address;   (in the class)
&lt;input wire:model="email"&gt;  →  wire:model="address"   (in the view)
</code></pre><p>One rename, both files, and it leaves unrelated strings alone.</p><p>Runtime insight (⌘⌥I) is a Telescope-style panel that captures every request against your dev app via Clockwork while you work:</p><pre><code class="language-text">POST /orders   201   84ms   Q:23 ⚠×19   C:2/1   ✉1   ⚡3   ✨
</code></pre><p>That <code>Q:23 ⚠×19</code> is an N+1 caught in the act — one query ran nineteen times. ✨ hands the whole request to your agent. No Telescope, no Debugbar, no install. The editor is the debugger.</p><h2>How it's built, still the same way</h2><p>None of this is magic; it's composition. The props contract reuses the model→table logic from Eloquent completion and the schema plumbing already in the database layer. The Ziggy features reuse the exact route table the PHP side loads. The form contract is the props-contract machinery, pointed at forms. Every feature is two things e already had, wired together — and every one shipped compiling, unit-tested (17 new tests here), and signed &amp; notarized by Apple in CI so the .dmg just opens.</p><h2>What I hope it feels like</h2><p>Less flipping between an editor, a schema tool, a TypeScript generator, and a production incident — and more the editor already understands how the two halves fit together. The truth about a VILT app is spread across PHP, TypeScript, and a database. It should be one keystroke away, not one outage away.</p><p>The space between your files is where the interesting bugs live. 0.6.4 moves in.</p><p>☕ Grab it on the releases page. If the props contract flags something it shouldn't, tell me — reconciling three languages at once is exactly the conversation worth having.</p>