Open a project
How midcode opens a project folder, what it detects, how it starts your dev server, where the log is, and why a project can be view-only.
midcode opens a folder. It works out what runs the site in it, starts that dev server on a port of its own, and shows the running site at every breakpoint, side by side. Nothing is imported or converted: the canvas is your dev server.
Open a folder
Any of these opens a project:
Press ⌘O (File → “Open Folder…”) and choose the folder.
Click “Open folder” on the hub, or the + at the end of the tab strip. From the next release the + starts a new project instead: it goes to the hub with “New project” open, and “Open an existing folder…” in that dialog opens a folder.
Drop the folder on the hub.
Choose the project’s root: the folder with its package.json, or with its index.html when it’s plain HTML.
The hub lists the projects you’ve opened, each with a picture of the site and when you last opened it. The ⋯ on a card has “Open”, “Open in Finder”, “Open in code editor”, “Copy path” and “Remove from recents”. Removing one only takes it off the list. From the next release it reads “Remove from the list”, and the hub can be arranged your way: Organize the hub.
Tabs
Each project opens in its own tab, like a browser. Every tab has its own dev server, and it keeps running while another tab is in front. ⇧⌘H (File → “Back to Projects”) goes to the hub without closing anything. Closing a tab stops that project’s dev server, its terminals and its agent.
A project opens once: opening it again brings its tab forward.
Organize the hubNext release
The hub keeps every project you’ve opened, and you arrange it your way. The arrangement is midcode’s own list: no folder is moved, renamed or changed on your disk.
Groups. The + beside “Groups” in the sidebar makes a group and lets you type its name. Drag a project’s card onto a group to put it there, or use “Move to group…” in the card’s ⋯ menu (a right-click on the card opens the same menu). A project is in one group at a time. “Ungrouped” shows the ones in none, and a card dropped on it leaves its group.
Pins. The pin on a card, or “Pin to the top” in its menu, keeps the project first, under “Pinned”, in every view.
Order. The menu at the top right orders the cards by “Last opened”, by “Name” or in a “Custom order”. Drag a card beside another one to set your own: the order becomes “Custom order”, starting from what was on screen, so only the card you dragged moves. Pinned cards are ordered among themselves.
Search. “Search projects” filters the open view by name or by path.
A group’s row has its own ⋯ (and right-click) with “Rename” and “Delete group”. Double-click a group to rename it, and drag it to reorder the groups. Deleting a group keeps its projects on the hub, in no group.
Dragging a card out of midcode, onto a terminal or an editor, drops the project’s path.
All of it is one file in midcode’s own data folder, never in a project:
{
"groups": [{ "id": "531a1d5e", "name": "Clients" }],
"where": { "05393af05cce": "531a1d5e" },
"pinned": ["5968533421fe"],
"order": ["05393af05cce", "74cd0d0aa0b1"],
"sort": "manual"
}Projects are known there by an id made from their path. “Remove from the list” also takes a project out of its group, its pin and its place in the order. A project whose folder is missing (an unplugged disk) isn’t listed, and finds its group again when the folder is back.
What midcode detects
midcode reads the dependencies in package.json. The first one it finds, in this order, decides how the project runs:
In package.json | Opens as | On the canvas |
|---|---|---|
next | Next.js | Edited. |
@react-router/dev | React Router | Edited. |
@remix-run/dev | Remix | Edited. |
astro | Astro | Its React, Preact and Solid islands are edited. .astro files: next release. |
@sveltejs/kit | SvelteKit | Edited. |
nuxt | Nuxt | View-only. Edited in the next release. |
vite | Vite | Edited with react, preact, solid-js or svelte in the dependencies. With vue or Qwik: next release. Otherwise view-only. |
none of these, and a dev script | Your dev script | View-only. |
no package.json (or none of the above in it), and an index.html | Plain HTML | View-only. Edited in the next release. |
What works with what has the whole list, with how much of each stack midcode edits.
More stacks, and any folderNext release
In the released version, a folder that matches no row of the table above (no known framework, no dev script and no index.html) is refused: “midcode couldn’t tell what runs this folder.” From the next release, any folder opens:
Stacks midcode knows by their files run with their own command: Laravel, Django, Flask, FastAPI, Rails, Hugo, Jekyll, WordPress, plain PHP, Angular, a Shopify theme and more. The list and each command are in Any other stack.
Sites built with webpack or Rspack (Rsbuild among them) run with their own script, on midcode’s port where the tool takes
--port, and are edited: Create React App, Vue CLI, Gatsby, Docusaurus.Sites a server renders from templates are edited too, in a different way: Sites a server renders.
The top of a repository with one site inside (
apps/web,frontend/) opens that site. See Monorepos.When midcode can’t tell how a folder runs, the canvas asks. See Change how it runs.
Dependencies
If the project has a package.json and no node_modules, the canvas shows “Dependencies are missing” with one button: “Install with pnpm” (or npm, yarn, bun). The package manager is the one your lockfile names: pnpm-lock.yaml, bun.lockb or bun.lock, yarn.lock, package-lock.json. With no lockfile it’s npm.
The button runs <package manager> install in your login shell, in the project folder, and then starts the server. What it prints goes to the log.
How the dev server starts
midcode picks a free port, starting at 4310, so it never takes the port your own terminal uses. It runs the server with the Node your terminal has (it asks your login shell for its PATH, so nvm, fnm and Homebrew setups are found), or with its own Node when the Mac has none.
| Project | What midcode runs |
|---|---|
| Next.js | next dev --port <port>, with midcode’s hook preloaded |
| Vite, SvelteKit | vite dev --port <port>, with a config that loads yours and adds midcode’s plugin |
| React Router | react-router dev --port <port>, the same way |
| Remix | remix vite:dev --port <port>, the same way |
| Astro | astro dev --port <port>, the same way |
| Nuxt | nuxi dev --port <port>. Next release: with a layer that adds midcode’s plugin |
| Plain HTML | midcode’s own static server, which reloads the page when a file changes |
| Anything else | <package manager> run dev, and the address is read from what it prints |
The hook and the plugin are what tell the canvas where each element is written. They’re added from outside, on the command line: your next.config, vite.config and package.json are not changed. How midcode works follows that road end to end.
From the next release, a Vite project is also started with the options its own dev script gives Vite (vite --mode ssr keeps --mode ssr). The port, the host and the config stay midcode’s.
For that process midcode also sets PORT, sets BROWSER=none so no browser window opens, and turns off Next.js and Astro telemetry.
The server is ready when the site answers. The first line of the log says what was run and with which Node:
$ next dev --port 4310 (/Users/you/.nvm/versions/node/v22.11.0/bin/node)When midcode’s way doesn’t start
Some projects need something only their own script sets up: an env file, a second process, a toolchain that wraps Vite. For Vite, SvelteKit, React Router, Remix, Astro and Nuxt projects, if the server stops or doesn’t answer in 45 seconds, midcode stops it and runs your own dev script instead, with 2 minutes to come up.
The site then shows, but without midcode’s plugin there’s nothing to edit by, so the project is view-only for that session. A message says so: “midcode couldn’t start this project its own way, so it runs your dev script: the site shows, but editing on the canvas needs midcode’s plugin. The log says why.” Its “Send report” button opens the feedback form with the report attached.
Next.js has no fallback. It always runs with the hook.
The server log
The button at the right of the top bar, a dot beside a terminal icon, opens the dev server’s log. The dot is green while the server runs. While it starts, installs or fails, a word sits beside it: “Starting”, “Installing”, “Error”, “Stopped”.
The log panel has “Send report”, “Copy report”, “Restart” and a close button. An error opens it by itself. Troubleshooting says what a report contains.
One Next.js dev server per folder
Next.js allows one next dev per project folder. If yours is already running in a terminal, midcode’s stops and the canvas shows “A Next server is already running for this project”, with the address and process id of the other one.
“Stop it and use midcode” stops that process (midcode checks first that it is a Next.js server) and starts its own.
Or stop it yourself in your terminal, then click “Restart” in the log.
midcode needs its own server: the one in your terminal runs without the hook, so its pages don’t say where each element is written.
HTTPS and other hosts
A dev server that serves
httpswith a certificate it made for itself (Vite’s basic-ssl plugin, mkcert) works. midcode reads the address from the server’s “Local:” line,httporhttps, and accepts a self-made certificate forlocalhost,127.0.0.1and*.localhostonly.The released version only shows a site at
localhostor127.0.0.1. From the next release a site can live on another host (app.testunder Herd or Valet), which needs a certificate macOS trusts.A Laravel app with Vite is shown at the
APP_URLof its.env, as served by Herd, Valet orphp artisan serve, with Vite running beside it. IfAPP_URLis a plainhttpaddress on this Mac and nothing answers there, midcode startsphp artisan serveitself. In the released version a Laravel site is shown only when that address islocalhostor127.0.0.1, and it’s view-only: see Laravel.
Change how it runsNext release
When a server fails, the error card has a “Change how it runs” link. It opens the same form the canvas shows for a folder it can’t work out: “How does this site run?”
“Command”: what starts the site, for example
bin/rails server -p $PORT.$PORTis the free port midcode picked.“Address”: where it answers, for example
http://localhost:$PORT.
Give one or both. With an address and no command, midcode starts nothing and only shows a site that’s already running (under Herd, Docker, another terminal). “Start” saves it in the project:
{
"command": "bin/rails server -b 127.0.0.1 -p $PORT",
"url": "http://127.0.0.1:$PORT"
}What’s in that file comes before anything midcode would do by itself. “Reset” empties it and lets midcode work it out again.
Any other stack has the details.
Editable or view-only
A project is editable when midcode has a way to know where each element is written. When it doesn’t, the right panel says so, for example “Nuxt: preview and comments”, and you can still:
see every breakpoint, and reload them;
use the site at full size with Preview (P);
leave comments for your agent, each anchored to its element;
browse the page in Layers, by its DOM;
work on the code in Code and with your agent;
A project is view-only when its stack isn’t one midcode edits yet (the table above), when it’s running with its own dev script because midcode’s way didn’t start, or, from the next release, when a .midcode/server.json tells a Next.js, Vite, Astro, Nuxt, SvelteKit, React Router or Remix project to run its own way.
Limits
midcode starts the site and nothing else. If your site needs an API or a database running beside it, start those yourself.
With no Node on the Mac, midcode runs your dev server with its own, but it can’t install dependencies: that needs your package manager.
midcode picks the port for the servers it starts itself. A
devscript with a fixed port still fails if that port is taken: what it prints is in the log. From the next release, a script that is only a call to a tool that takes--portand doesn’t name one (rsbuild, rspack, webpack, vue-cli-service, docusaurus, gatsby, parcel, vitepress, vuepress, eleventy) is given midcode’s port.The routes midcode lists as pages come from each framework’s files. How they’re found is on each framework’s page, and in Pages and navigation.