# midcode > midcode is a macOS app that turns a website built in code into a visual editor. You open the project folder that's on your Mac, midcode runs it and shows it live at every breakpoint on a canvas, and every edit you make (text, styles, images, layout, new sections) is written into your own source files as a minimal diff. It is the mid between code and no-code. Key facts: - Platform: macOS 13 or later, Apple silicon and Intel. Current version: 1.1.2. - Price: one payment of $199 (launch price $119 for the first buyers), no subscription, every update included, on up to 3 Macs. Free trial: 3 days, no card. - Runs locally. No account is needed, and nothing is uploaded: midcode edits the files on your Mac. - Nothing is installed in the project and no config is changed. Comments and layer names live in a .midcode folder. - It never reformats or reprints a file: only the range that was edited changes, and every edit can be undone. - AI agents (Claude Code, Codex, Gemini CLI, OpenCode, Cursor) run beside the canvas with the user's own account. midcode sells no model usage. - Made by Akila Studio (https://akilastudio.com). - Download: https://midcode.app/download — Homebrew: `brew install --cask agusdellaquila/midcode/midcode` --- # midcode docs > What midcode is, how it edits the code you already have, and where each thing is explained. - Page: https://midcode.app/docs - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt midcode is a Mac app that opens a site you built in code and lets you edit it by looking at it. Click an element and change its text, its size or its color, move it, add a section. Every change is written into your project's own files, as the code you would have typed. There is no export and no format of its own. Your project stays a normal repository: you keep working on it in your editor or with an AI agent, and midcode shows whatever is there. ## How it works, in four steps 1. **Open your project's folder.** midcode starts the project's dev server and shows the running site on a canvas, at every breakpoint side by side. 2. **Click something.** The right panel shows where that element is written, as `file:line`, and everything you can change about it. 3. **Change it.** midcode edits only the characters that change. Nothing else in the file is touched, so `git diff` shows exactly your edit. 4. **Undo it or publish it.** Every edit is one step of `⌘Z`. Publish lists what changed, commits the files you choose and pushes them. This is what changing a section's padding from the panel leaves in your code: ```diff title="app/page.tsx" -
+
``` [How midcode works](https://midcode.app/docs/start/how-it-works.md) follows one click all the way from the canvas to the file. ## What it works with The version you can download today edits React (Next.js, Vite, React Router, Remix, and Preact or Solid with Vite) and Svelte. The next release adds Vue, Nuxt, Astro, Angular, plain HTML, Laravel, WordPress, Django, Rails, Hugo, Jekyll and Shopify themes, among others. How much it can do depends on the stack: a Next.js project gets everything, and a site that a server renders is edited where its templates write it. [What works with what](https://midcode.app/docs/start/supported-stacks.md) has the whole table, with what's released and what's next, and each stack has its own page under Frameworks. Styles are written as classes: Tailwind's own in a Tailwind 4 project, and midcode's `mid:` utilities [everywhere else](https://midcode.app/docs/styling/without-tailwind.md), compiled to plain CSS with nothing installed in your project. ## Where to start - [Install and license](https://midcode.app/docs/start/install.md): the download, the three-day trial and where the key goes. - [Open a project](https://midcode.app/docs/start/open-a-project.md): what midcode needs from a folder, and what to do when a project doesn't start. - [The canvas and breakpoints](https://midcode.app/docs/editor/canvas.md): the place where you'll spend your time. - [What midcode adds to your project](https://midcode.app/docs/start/project-files.md): every file it may write, and what each one is for. ## For AI agents midcode runs your own agent beside the canvas ([Your agent in midcode](https://midcode.app/docs/agents/overview.md)), and these docs are written to be read by one too: - Every page has a **Copy for AI** button. It copies the page as Markdown, ready to paste into a chat. - Every page is also served as Markdown at its own address with `.md` at the end: `midcode.app/docs/styling/tailwind.md`. - [`/llms.txt`](https://midcode.app/llms.txt) lists every page, and [`/llms-full.txt`](https://midcode.app/llms-full.txt) is all of them in one file. - [The midcode skill](https://midcode.app/docs/agents/skill.md) puts all of this inside your agent, so it knows how to write code that stays editable on the canvas. ## What midcode is not - It isn't a hosting service. Your site is deployed the way it already is; [Publish](https://midcode.app/docs/publish/publish.md) is a commit and a push. - It isn't a site builder with its own runtime. What it inserts is plain markup in your files, and its interactive components are written into your project as source you own. - It doesn't sell AI usage. An agent runs with your own account. - It doesn't need an account, and it doesn't upload your code. It edits the files on your Mac. [Privacy and security](https://midcode.app/docs/reference/privacy.md) lists everything that does go over the network. --- # Install and license > Install midcode on a Mac with Homebrew or the disk image, how it updates itself, how the 3-day trial and the license key work, and what's in Settings. - Page: https://midcode.app/docs/start/install - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt midcode is a Mac app. It runs on macOS 13 or later, on Apple silicon and Intel. It's free for 3 days. After that it's one payment, with every update included, and a license key that works on up to 3 Macs. ## What you need - A Mac with macOS 13 or later. - A project: a folder with a site built in code. [Open a project](https://midcode.app/docs/start/open-a-project.md) says what midcode recognizes, and [What works with what](https://midcode.app/docs/start/supported-stacks.md) how much of each it edits. - Git, if you want to [publish](https://midcode.app/docs/publish/publish.md) from midcode. Everything else works without it. Node.js is optional for running a project. midcode starts your dev server with the Node your terminal uses, and with its own when the Mac has none. A few things run `npm` or `npx` and do need Node installed: installing a project's dependencies from midcode, the Claude Code and Codex chats in the [Agent tab](https://midcode.app/docs/agents/overview.md), and, from the next release, [New project](https://midcode.app/docs/start/new-project.md). ## Install with Homebrew ```bash brew install --cask agusdellaquila/midcode/midcode ``` The cask lives in midcode's own tap and is updated with every release. ## Install from the disk image 1. [Download midcode](https://midcode.app/download). That link gives the Apple silicon build; on an Intel Mac use `midcode.app/download?arch=x64`. Every release, with both files, is at `github.com/agusdellaquila/midcode/releases`. 2. Open the `.dmg` and drag midcode to Applications. 3. Open it from Applications. If you open midcode straight from the disk image, or from Downloads, the first thing it shows is "Move midcode to Applications". Click "Move to Applications": midcode moves itself there, sends an older copy to the Trash and opens again from Applications. "Not now" skips the move, but midcode only updates itself from Applications. The app is signed and notarized. ## Updates midcode updates itself. It looks for a new version a few seconds after it opens and every four hours after that, downloads it in the background and checks its signature. A banner under the tabs says where it is: | Banner | What to do | | --- | --- | | "midcode … is available. Downloading… …%" | Nothing. Keep working. | | "midcode … is ready. Restart to update: your work is saved." | Click "Restart now". If you close the banner instead, the update is installed the next time you quit. | | "Updated to midcode …." | "What's new" opens the [changelog](https://midcode.app/changelog). | Updates come from the same public releases on GitHub as the download. There's no midcode server in between. The version you have is at the bottom of Settings. ## The free trial The first time midcode opens it shows "Welcome to midcode", with three ways forward: - Type your email and click "Start 3-day free trial". - "Buy midcode". - "I have a license key". The trial is the whole app for 3 days, counted from the moment you start it. The email is where news of a new version goes, and you can unsubscribe. The trial starts with no connection too: the address is sent the next time midcode opens with one. While the trial runs, a badge at the top of the window says how long is left: "Trial · 2 days left", then hours, then minutes. Click it to buy, or to paste a key. When the trial ends, midcode shows "Your free trial has ended" and waits for a purchase or a key. Nothing happens to your projects: every edit you made is already in their files. Reinstalling midcode doesn't start a new trial on the same Mac. ## Buy and activate "Buy midcode" opens the checkout at `midcode.app/buy`. It's one payment: the app, every update, and a license key that works on up to 3 Macs. The launch price is $119. The regular price is $199. After you pay, the key is on the page you land on, and in your email. To activate it: 1. Open midcode. 2. Click "I have a license key" on the welcome screen. If the trial is still running, click the trial badge at the top instead. 3. Paste the key and click "Activate". > [!NOTE] > Settings shows your license but has no field for a key. A key is pasted on the welcome screen or in the trial badge. Activating needs a connection: the key is checked with the license server of Polar, the service that sells midcode. From then on: - midcode checks the key again about once a day, in the background. - Offline, the last good answer stands for 30 days. Past that, the license waits until midcode reaches the server again. - A fourth Mac is refused: "This key is already active on 3 Macs. Free one from the Polar customer portal (the link is in your order email), then try again." ### Move the license to another Mac 1. On the Mac you're leaving, press `⌘,`. 2. In the "License" row, click "Deactivate" and confirm. That frees one of the 3 Macs on your key. Paste the key again whenever you want that Mac back. ## Settings `⌘,` opens Settings from anywhere. On the hub there's also a gear at the top right. | Setting | What it does | | --- | --- | | Language | The language of midcode itself: English or Español. | | Code editor | Where "Open in your code editor" takes you: Automatic (the first of Cursor, Visual Studio Code, Windsurf or Zed that's installed), one of those four, or "Default app for the file". midcode has [its own code view](https://midcode.app/docs/editor/code.md) too. | | Nudge amount | How many px `⇧` plus an arrow key moves an element, or adds to a size. A plain arrow is 1. From 1 to 1000; it starts at 10. | | License | During the trial: when it ends, and "Buy midcode". Once activated: the key, shortened, and "Deactivate". | | Connections | [GitHub](https://midcode.app/docs/publish/github.md) and [Vercel](https://midcode.app/docs/publish/vercel.md). Optional: midcode works without them. | The version is at the bottom of the sheet. Settings are kept in midcode's own folder, `~/Library/Application Support/midcode/settings.json`, never in a project. ## Limits - macOS only. There is no Windows or Linux build. - The trial can't be extended or paused: it's 3 days from the moment it starts. - A key activates on 3 Macs at a time. Free one with "Deactivate", or from the Polar customer portal linked in your order email. - Activating a key needs a connection. Working afterwards doesn't, for up to 30 days at a stretch. If something goes wrong on the way, see [Troubleshooting](https://midcode.app/docs/reference/troubleshooting.md). What midcode sends to the license server, and what it keeps on your Mac, is in [Privacy and security](https://midcode.app/docs/reference/privacy.md). --- # 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. - Page: https://midcode.app/docs/start/open-a-project - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt 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](https://midcode.app/docs/start/new-project.md) 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](#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 hub (next 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: ```json title="~/Library/Application Support/midcode/hub.json" { "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](https://midcode.app/docs/frameworks/nextjs.md) | Edited. | | `@react-router/dev` | [React Router](https://midcode.app/docs/frameworks/react-router.md) | Edited. | | `@remix-run/dev` | [Remix](https://midcode.app/docs/frameworks/react-router.md) | Edited. | | `astro` | [Astro](https://midcode.app/docs/frameworks/astro.md) | Its React, Preact and Solid islands are edited. `.astro` files: next release. | | `@sveltejs/kit` | [SvelteKit](https://midcode.app/docs/frameworks/svelte.md) | Edited. | | `nuxt` | [Nuxt](https://midcode.app/docs/frameworks/nuxt.md) | View-only. Edited in the next release. | | `vite` | [Vite](https://midcode.app/docs/frameworks/react-vite.md) | 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](https://midcode.app/docs/frameworks/html.md) | View-only. Edited in the next release. | [What works with what](https://midcode.app/docs/start/supported-stacks.md) has the whole list, with how much of each stack midcode edits. ### More stacks, and any folder (next 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](https://midcode.app/docs/frameworks/custom-server.md). - 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](https://midcode.app/docs/frameworks/webpack.md). - Sites a server renders from templates are edited too, in a different way: [Sites a server renders](https://midcode.app/docs/frameworks/server-templates.md). - The top of a repository with one site inside (`apps/web`, `frontend/`) opens that site. See [Monorepos](https://midcode.app/docs/frameworks/monorepos.md). - When midcode can't tell how a folder runs, the canvas asks. See [Change how it runs](#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 ` 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 `, with midcode's hook preloaded | | Vite, SvelteKit | `vite dev --port `, with a config that loads yours and adds midcode's plugin | | React Router | `react-router dev --port `, the same way | | Remix | `remix vite:dev --port `, the same way | | Astro | `astro dev --port `, the same way | | Nuxt | `nuxi dev --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 | ` 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](https://midcode.app/docs/start/how-it-works.md) 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: ```text $ 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](https://midcode.app/docs/reference/troubleshooting.md) 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 `https` with a certificate it made for itself (Vite's basic-ssl plugin, mkcert) works. midcode reads the address from the server's "Local:" line, `http` or `https`, and accepts a self-made certificate for `localhost`, `127.0.0.1` and `*.localhost` only. - The released version only shows a site at `localhost` or `127.0.0.1`. From the next release a site can live on another host (`app.test` under Herd or Valet), which needs a certificate macOS trusts. - A Laravel app with Vite is shown at the `APP_URL` of its `.env`, as served by Herd, Valet or `php artisan serve`, with Vite running beside it. If `APP_URL` is a plain `http` address on this Mac and nothing answers there, midcode starts `php artisan serve` itself. In the released version a Laravel site is shown only when that address is `localhost` or `127.0.0.1`, and it's view-only: see [Laravel](https://midcode.app/docs/frameworks/laravel.md). ## Change how it runs (next 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`. `$PORT` is 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: ```json title=".midcode/server.json" { "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. > [!NOTE] > In a project midcode already knows how to start (Next.js, Vite, Astro, Nuxt, SvelteKit, React Router, Remix), a `server.json` makes it run the project's own way, without midcode's hook or plugin: the site shows, with no marks, and it's view-only. To get midcode's own start back, delete the file and click "Restart" in the log. [Any other stack](https://midcode.app/docs/frameworks/custom-server.md) 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](https://midcode.app/docs/editor/canvas.md) (`P`); - leave [comments](https://midcode.app/docs/editor/comments.md) for your agent, each anchored to its element; - browse the page in [Layers](https://midcode.app/docs/editor/layers.md), by its DOM; - work on the code in [Code](https://midcode.app/docs/editor/code.md) and with [your agent](https://midcode.app/docs/agents/overview.md); - [publish](https://midcode.app/docs/publish/publish.md). 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 `dev` script 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 `--port` and 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](https://midcode.app/docs/editor/pages.md). --- # New project > Start a site from midcode's hub, with the starters it offers, the exact command each one runs, what has to be on your Mac, and what you get. - Page: https://midcode.app/docs/start/new-project - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt - Status: This ships with the next release of midcode. The version you can download today (1.1.2) doesn't have it yet. "New project" makes a project from scratch and opens it on the canvas. midcode doesn't have templates of its own: it runs the tool each framework's community uses (`create-next-app`, `create-vite`, `sv`, `ng`, `composer create-project`…) with every question already answered, in a folder you choose. Use it when there's no project yet. If you already have one, [open its folder](https://midcode.app/docs/start/open-a-project.md). ## Make a project 1. On the hub, click "New project". With no projects yet, the same dialog is behind "Start a new project". From inside a project, the + at the end of the tab strip or `⌘N` (File → "New Project…") goes to the hub with the dialog open; your tab stays open behind it. 2. Under "Built with", choose the technology: React, Vue, Svelte, Angular, Solid, Astro, HTML, PHP, Python or Shopify. 3. Under "Start with", choose the starter. The first one is marked "Recommended". 4. Type a "Name". It becomes the folder's name, in lowercase with dashes: "My Site" makes `my-site`. 5. "Where" is the folder it's made in. Click it to choose another. midcode remembers the last one; the first time it offers `~/Developer` or `~/Documents`. 6. If more than one package manager is installed, choose it under "Package manager" (pnpm, npm, bun or yarn). 7. Click "Create project". While it runs, the line at the bottom shows what the tool prints. "Cancel" stops it. The Node.js starters take about a minute. When it's done, midcode says "my-site is ready" and opens the project in a tab. Making a project needs a license or a running trial, like any edit. ## Technologies and starters | Built with | Start with | Needs on your Mac | | --- | --- | --- | | React | Next.js, Vite, React Router, Preact | Node.js | | Vue | Nuxt, Vite | Node.js | | Svelte | SvelteKit | Node.js | | Angular | Angular | Node.js | | Solid | Vite | Node.js | | Astro | Astro | Node.js | | HTML | Plain HTML | Nothing | | HTML | Eleventy | Node.js | | HTML | Hugo | Hugo | | PHP | Laravel, Laravel + React, Laravel + Vue, Laravel + Livewire | PHP, Composer and Node.js | | Python | Django, Flask | Python 3 | | Shopify | Dawn, Skeleton, "A theme from your store" | The Shopify CLI, signed in | "Node.js" means `node`, `npx` and at least one package manager on your shell's `PATH`. When something is missing, the dialog says what and links to where to get it: "This needs Node.js on your Mac", "This needs Hugo on your Mac" (`brew install hugo`), "This needs PHP and Composer on your Mac" (Laravel Herd installs both), "This needs Python on your Mac". Install it and come back. For Shopify, the dialog shows the card that installs the CLI or signs you in: see [Shopify themes](https://midcode.app/docs/shopify/themes.md). ## What each starter runs Commands run in your login shell, with `CI=1` so nothing stops to ask: the first in the "Where" folder, the rest inside the new project. `` is the folder name and `` your package manager. ### React Next.js: ```bash npx --yes create-next-app@latest --typescript --tailwind --eslint --app --no-src-dir --import-alias "@/*" --turbopack --use- --yes ``` Vite: ```bash npx --yes create-vite@latest --template react-ts install add -D tailwindcss @tailwindcss/vite ``` React Router: ```bash npx --yes create-react-router@latest --yes --no-git-init --install --package-manager ``` Preact is the Vite recipe with `--template preact-ts`. With npm, the third line is `npm install -D tailwindcss @tailwindcss/vite`. After a Vite template, midcode adds the Tailwind plugin to `vite.config.ts`, writes `@import "tailwindcss";` as the stylesheet, and replaces the template's demo with one plain page. This is what it adds to the config: ```diff title="vite.config.ts" +import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ - plugins: [react()], + plugins: [react(), tailwindcss()], }) ``` ### Vue Nuxt: ```bash npx --yes nuxi@latest init --template minimal --packageManager --no-gitInit --no-modules add -D tailwindcss @tailwindcss/vite ``` midcode then writes `assets/css/main.css`, `app.vue` and a `nuxt.config.ts` that loads the stylesheet and the Tailwind plugin. Vite is the same recipe as React's, with `--template vue-ts`. ### Svelte, Angular, Solid, Astro SvelteKit: ```bash npx --yes sv@latest create --template minimal --types ts --add tailwindcss="plugins:none" --install ``` Angular: ```bash npx --yes @angular/cli@latest new --defaults --style=tailwind --ssr=false --skip-git --skip-tests --ai-config=none --package-manager ``` Astro: ```bash npx --yes create-astro@latest --template minimal --no-install --no-git --yes install npx --yes astro add tailwind --yes ``` Solid is the Vite recipe with `--template solid-ts`. In each of these the first page (`src/routes/+page.svelte`, `src/app/app.html`, `src/pages/index.astro`, `src/App.tsx`) is replaced with the plain starting page. ### HTML Plain HTML runs nothing. midcode writes three files: `index.html`, `style.css` and a `.gitignore`. Eleventy is written by midcode too: a `package.json` with `@11ty/eleventy` and the scripts `dev` (`eleventy --serve`) and `build`, an `eleventy.config.js` that copies `public/` to the site's root, a layout in `_includes/base.njk`, `index.njk` and `public/style.css`. Then ` install`. Hugo: ```bash hugo new site ``` midcode adds the layouts (`layouts/_default/baseof.html`, `single.html`, `list.html`, and `layouts/index.html`) and `static/style.css`. ### PHP Laravel: ```bash composer create-project laravel/laravel --no-interaction --prefer-dist install ``` The three kits are the same command with Laravel's own starter kits: `laravel/react-starter-kit`, `laravel/vue-starter-kit` and `laravel/livewire-starter-kit`. ### Python Django: ```bash python3 -m venv .venv .venv/bin/python -m pip install --quiet --disable-pip-version-check django .venv/bin/django-admin startproject config . ``` midcode then adds `templates/base.html`, `templates/home.html` and `static/style.css` at the top of the project, points `config/settings.py` at them (`DIRS` and `STATICFILES_DIRS`), routes `/` to the home template in `config/urls.py`, and writes `requirements.txt`. Flask: ```bash python3 -m venv .venv .venv/bin/python -m pip install --quiet --disable-pip-version-check flask ``` midcode writes `app.py` with one route, the same two templates, `static/style.css` and `requirements.txt`. Both get a virtual environment of their own in `.venv`. It's what midcode runs the project with afterwards: see [Django, Flask and FastAPI](https://midcode.app/docs/frameworks/python.md). ### Shopify Skeleton: ```bash shopify theme init --path ``` Dawn: ```bash shopify theme init --path --clone-url https://github.com/Shopify/dawn.git ``` A theme from your store: ```bash shopify theme pull --store --theme --path / ``` For Dawn and Skeleton, "Store to work on it with" is optional ("Decide later"). For a theme from your store, choose the store and then the theme; the button reads "Download and open". The store is saved in `.midcode/shopify.json`. ## What you get - **Tailwind 4**, set up by the tool or by midcode, in every React, Vue, Svelte, Angular, Solid, Astro and Laravel starter. It's what midcode writes styles with: see [Tailwind CSS](https://midcode.app/docs/styling/tailwind.md). - **A plain stylesheet** (`style.css`) in Plain HTML, Eleventy, Hugo, Django and Flask. There, style edits use midcode's own utilities: see [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md). - **One starting page** where the tool's own is a demo to delete: a heading, a paragraph and a button, ready to click. - **TypeScript** where the tool offers it. - **A git repository**. If the tool didn't make one, midcode runs `git init`. There's no commit and no remote: those are yours to make. A Shopify theme is left as the Shopify CLI makes it. - **A `.gitignore`** in the projects midcode writes itself (Plain HTML, Eleventy, Hugo, Django, Flask). If the tool fails, or you cancel, the half-made folder is deleted and the dialog shows the tool's last lines. While the tools run, midcode sets `NEXT_TELEMETRY_DISABLED`, `ASTRO_TELEMETRY_DISABLED`, `NUXT_TELEMETRY_DISABLED`, `NG_CLI_ANALYTICS=false` and `DO_NOT_TRACK=1`. ## Limits - There are no options beyond the ones in the dialog. For JavaScript instead of TypeScript, a `src/` folder, or another template, run the tool yourself and open the folder. - The name is letters, digits and dashes. A folder with that name that isn't empty is refused: "There's already a folder named … there." - A tool that runs longer than 15 minutes is stopped. - Qwik isn't offered: the `create-vite` Qwik template doesn't install today. A Qwik project made by hand opens and is edited like Solid. - Tried by creating and opening each one from midcode: Next.js 16, Vite + React, Vite + Vue (with pnpm), React Router, Astro, SvelteKit, Nuxt, Plain HTML, Angular 22, Vite + Solid and Vite + Preact. All of them came up editable on the canvas. - Eleventy, Hugo, Laravel, Laravel + React, Django and Flask were created and opened from midcode too. - Laravel + Vue and Laravel + Livewire were made with the same command run by hand, not from the dialog. --- # How midcode works > The road of one click in midcode, from the canvas to the line of code it changes, through the dev server, the marks on each element, the edit and undo. - Page: https://midcode.app/docs/start/how-it-works - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt midcode keeps no copy of your site and has no format of its own. The canvas is your dev server, running. An edit is a change to one of your files, a few characters wide. This page follows one click from the canvas to the line it changes, so you know what midcode does to your project and what it never does. The example is a Next.js project. Other stacks take the same road with a different first step, linked along the way. ## 1. Your dev server, with one thing added When you [open a project](https://midcode.app/docs/start/open-a-project.md), midcode starts its dev server on a free port. For Next.js that is your own `next`, from your `node_modules`, with one file preloaded: ```bash node --require /next-hook.cjs node_modules/next/dist/bin/next dev --port 4310 --hostname localhost ``` The hook wraps the function Next.js loads its config with. On the config it gets back, in memory, it adds one rule: files ending in `.tsx` and `.jsx` go through midcode's loader first, under Turbopack or webpack, whichever your project uses. It also turns off the Next.js badge that would sit over the canvas. Nothing is written to disk for this. Your `next.config` isn't edited, and nothing is installed in the project. If a future Next.js changes shape and the hook doesn't fit, it steps aside: the server runs as it always would, without selection. Projects that run on Vite (Vite + React, React Router, Remix, SvelteKit, Astro) get the same thing through a config file kept in midcode's own folder: it loads your config and adds midcode's plugin. See [React with Vite](https://midcode.app/docs/frameworks/react-vite.md), [Svelte and SvelteKit](https://midcode.app/docs/frameworks/svelte.md) and the other pages under Frameworks. ## 2. Every element learns where it's written The loader runs on each of your JSX files as the dev server compiles them. It adds one attribute to every element: the file, line and column where that element is written. ```tsx title="app/page.tsx, as you wrote it"

Built to outlast

``` ```tsx title="What the dev server compiles"

Built to outlast

``` There are three marks: | Mark | On | Says | | --- | --- | --- | | `data-mc="file:line:col"` | Every HTML element in your JSX | Where that element is written. | | `data-mci="Name\|file:line:col"` | Every component instance | Which component it is, and where the instance is written. | | `data-mcu` | The element a component returns | Where that component is used, outermost first. | The last one is why selecting a `
` drawn by `` can lead to the `` written in the page, and why Layers can name components. What matters about the marks: - They exist only in what the dev server compiles. The file on disk is untouched, and the rest of its text stays exactly where it was, so line numbers stay true. - They exist only while midcode runs the server. Your own `next dev`, your production build and your deployed site never have them. - Files in `node_modules` are skipped. An element drawn by a library has no mark, and the panel says so: "A library draws this element: it isn't in your project's files, so there's nothing to edit here." - Wrappers that render nothing of their own are skipped too: fragments, `Suspense`, `StrictMode`, context providers. A site a server renders from templates (Django, Rails, Hugo, plain PHP) has no build step to hook into. From the next release, midcode finds each element in the project's templates after the page loads: see [Sites a server renders](https://midcode.app/docs/frameworks/server-templates.md). Where pages are built from Markdown (Hugo, Jekyll, Eleventy), it finds headings, paragraphs and list items in the `.md` files by their words: see [Hugo, Jekyll and Eleventy](https://midcode.app/docs/frameworks/static-generators.md). ## 3. The bridge inside each frame Each breakpoint on the canvas is a frame that loads your dev server at that width. When a frame finishes loading, midcode runs a script inside it: the bridge. The bridge draws the hover and selection outlines, in a layer of its own that your CSS can't reach, and it listens for clicks, double-clicks and drags. A site may refuse to be shown inside a frame (`X-Frame-Options`, or `frame-ancestors` in its Content Security Policy). midcode lifts that for your dev server's address only. When you click, the bridge finds the nearest element with a mark and tells the editor what it is: its marks, which copy it is when one line renders many (a `.map()`), and a CSS selector as a last resort. The "Code" section of the right panel then shows where it lives: - "Element": the file and line of the element itself. - "Instance": the component instance that drew it, if any. - "Used at": where its component is used. - "Inside": the nearest ancestors written in other files. Click any of them to copy `file:line:col`. That reference is what you hand to an agent: see [Your agent in midcode](https://midcode.app/docs/agents/overview.md). ## 4. The edit goes to the file Say you double-click the heading and type. The window sends the element's reference, the old text and the new one to midcode's main process, the only part of the app that reads and writes your files. It opens `app/page.tsx`, parses it, finds the element that starts at line 13, column 7, and checks that its text is what the canvas was showing. Then it replaces only the characters that change: ```diff title="app/page.tsx"
-

Built to outlast

+

Built to endure

``` A style works the same way. The [style panel](https://midcode.app/docs/editor/styles.md) works out which classes to take out and which to add, and the new one takes the old one's place: ```diff title="app/page.tsx" -
+
``` midcode never reprints a file. It doesn't format, reorder or touch a line it wasn't asked about, so `git diff` shows exactly the edit. Edits to one project run one at a time, in order. If the file moved on while you were looking (you or your agent saved it and the page hasn't reloaded), the element isn't where the mark says, and the edit is refused rather than guessed: "Couldn't find the element at app/page.tsx:13 (did the file change?)" or "The text in the code doesn't match the preview. Wait for it to reload and try again." ### When the text isn't where it shows Sites often keep their words in data: ```tsx title="app/page.tsx" {features.map((f) => (

{f.title}

))} ``` The `

` knows where it's written, but there's no text there to change. So midcode looks for the exact text as a string in the project's files: modules, JSON, Markdown, CSS. One match is the answer. With several, it keeps the ones in files the element's file imports. If it still can't tell, it names the places instead of picking one: "That text appears in 3 places (…). Open the right one in your editor." Text a program computes has nothing to rewrite: "This text isn't written there: it comes from a variable or prop." [Text, images and video](https://midcode.app/docs/editor/text-and-media.md) has the full rules, and [Code that midcode can edit](https://midcode.app/docs/agents/editable-code.md) says how to write code that stays editable. ## 5. The page updates itself midcode doesn't redraw anything. The file changed on disk, your dev server noticed, and its hot reload brought the change to every frame. What you see on the canvas is always your real site, built by your real toolchain. ## 6. Undo, and the list of changes Every write is kept as a before and an after. - `⌘Z` puts the file back as it was. `⇧⌘Z` does the edit again. - One edit is one step, whatever it took: a class changed on five selected elements, or a component inserted along with the file midcode created for it and its import. Undoing an edit that created a file deletes that file. - An undo only happens if the file is exactly as the edit left it. If you, your editor or your agent changed it since, midcode leaves it alone: "The file changed outside midcode, so that edit can't be undone". - A file midcode created that something else now imports stays too. - What your agent changes from the chat in the Agent tab goes into the same history, one step per change it makes. - The history is kept in memory: up to 200 steps per project, until you quit midcode. After that, your undo is git. Each edit also adds a line, in words, to the list in [Publish](https://midcode.app/docs/publish/publish.md): Text "Built to outlast" → "Built to endure". Undo takes the line out again. Publishing turns the files you choose into one commit and pushes it. Some things midcode saves aren't code edits and have no undo: comments, layer names, breakpoints and page order (files in `.midcode/`), a page made or removed from the page menu (a removed page's file is in the Trash), rows of a database, and a Shopify store. Each page says so where it applies. ## What this means for your project - Your project never depends on midcode. There's no runtime, no wrapper component and no build plugin left behind. - What midcode adds is plain files you can read: [What midcode adds to your project](https://midcode.app/docs/start/project-files.md) lists every one. - You can edit the same files in your editor or with an agent while the project is open. midcode shows whatever is there. ## Limits - Only elements written in your project's files can be selected by their code. What a library draws can't. - In Next.js and Vite projects the marks go on `.tsx` and `.jsx` files. JSX written in a plain `.js` file isn't marked. - A mark is a line and a column. Between a save and the reload that follows, marks can point at the old position; edits made in that moment are refused, not misplaced. - Undo history doesn't survive quitting midcode. - How much can be edited depends on the stack: see [What works with what](https://midcode.app/docs/start/supported-stacks.md). --- # What midcode adds to your project > Every file midcode may write in your project besides your own code, from the .midcode folder to midcode.css, with their formats and what to commit. - Page: https://midcode.app/docs/start/project-files - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt Almost everything midcode does is an edit to a file you already have. This page is about the rest: the files midcode creates. All of them are plain text or plain assets that you can read, commit, ignore or delete, and that your agent can read without midcode. On its own, midcode never edits your `package.json`, your lockfile, your framework's config or your `.gitignore`. The only install it runs is your own package manager's, when you click "Install with …" on a project whose dependencies are missing. The marks that link the canvas to your code exist only in the running dev server: see [How midcode works](https://midcode.app/docs/start/how-it-works.md). ## At a glance | File | Appears when | Commit it? | | --- | --- | --- | | `.midcode/comments.json`, `.midcode/attachments/` | You leave a comment | If the comments are for people or agents working from the repo | | `.midcode/layers.json` | You rename a layer | Yes, to keep the names | | `.midcode/breakpoints.json` | You add, edit or remove a breakpoint, or change the primary | Yes | | `.midcode/pages.json` | You reorder pages, or remove one | Yes, to keep the order | | `.midcode/theme.css` | First style edit in a project without Tailwind 4 | Yes | | `.midcode/translations.json` | You translate a site that has one language | Yes, until the languages are wired up | | `.midcode/server.json` (next release) | You tell midcode how the site runs | If your team runs it the same way | | `.midcode/shopify.json` (next release) | You choose the store for a theme | Either | | `.midcode/queries/*.sql` (next release) | You save a SQL query | Yes, to share them | | `.midcode/README.md` | With the first comment, or with `midcode.css` | Either | | `midcode.css` | First style edit in a project without Tailwind 4 | Yes: the site needs it | | `app/midcode-scratch/page.tsx` | You put something on the canvas outside the breakpoints (Next.js, App Router) | No. midcode never publishes it | | `components/midcode/*` | You insert an interactive component | Yes: your pages import them | | Files in `public/` | You add or replace an image, a video, audio or a sticker, or set a favicon or social image outside Next.js (next release) | Yes | Files under `.midcode/` are listed in [Publish](https://midcode.app/docs/publish/publish.md) but not ticked: tick the ones you want in the commit. Tick `.midcode/theme.css` if `midcode.css` is built anywhere but your Mac, because [the midcode package](https://midcode.app/docs/styling/package.md) reads it. ## The .midcode folder ### comments.json and attachments/ [Comments](https://midcode.app/docs/editor/comments.md) you leave on the canvas. The file is a list, one object per comment: ```json title=".midcode/comments.json" [ { "id": "3f9a1c2e", "createdAt": 1759688400000, "resolvedAt": null, "page": "/", "breakpoint": "desktop", "viewportWidth": 1440, "anchor": { "ref": { "mc": "app/page.tsx:13:7", "mci": null, "i": 0, "sel": "body > main > section > h1", "mcu": null }, "tag": "h1", "component": null, "text": "Built to outlast", "context": ["app/layout.tsx:18:9"], "fx": 0.42, "fy": 0.5 }, "body": "Make this fit on one line", "attachments": [] } ] ``` - `anchor.ref.mc` is where the element is written, as `file:line:col`. `mci` is the component instance that drew it (`Name|file:line:col`), and `mcu` where its component is used. - `i` tells apart the copies one line renders (a `.map()`), and `sel` is a CSS selector for elements with no place in the code. - `context` lists where the nearest ancestors from other files are written. `fx` and `fy` are where on the element the pin sits, from 0 to 1. - `resolvedAt` is `null` while the comment is open. A file attached to a comment is copied to `.midcode/attachments/-` and listed in `attachments` with its `path`, `type` and `size`. Deleting the comment deletes its attachments. ### layers.json The names you give [layers](https://midcode.app/docs/editor/layers.md), or that Apple Intelligence gives them. ```json title=".midcode/layers.json" { "version": 1, "names": [ { "file": "app/page.tsx", "tag": "section", "classes": "px-6 py-24", "text": "", "loc": "app/page.tsx:12:5", "name": "Hero" } ] } ``` A name is found again by `loc` first. Lines move as you edit, so the file, tag and classes are kept as a second way to find the same element: an element in the same file with the same tag and the same classes takes the name. [Layers](https://midcode.app/docs/editor/layers.md) has the details. ### breakpoints.json The project's [breakpoints](https://midcode.app/docs/editor/canvas.md). Until you change them there is no file, and midcode uses three defaults. Written out, they are: ```json title=".midcode/breakpoints.json" { "version": 1, "list": [ { "id": "desktop", "label": "Desktop", "width": 1440, "height": 900, "screen": "lg" }, { "id": "tablet", "label": "Tablet", "width": 768, "height": 1024, "screen": "md" }, { "id": "phone", "label": "Phone", "width": 390, "height": 844, "screen": "" } ], "primary": "desktop" } ``` `screen` is the Tailwind screen each breakpoint's overrides are written at. The smallest writes with no prefix. `height` is the device height that viewport units (`vh`) mean on the canvas. ### pages.json The order you gave the pages in the [page list](https://midcode.app/docs/editor/pages.md), as routes. The home page is always first, so it isn't listed. ```json title=".midcode/pages.json" { "version": 1, "order": ["/work", "/about", "/contact"] } ``` ### theme.css In a project without Tailwind 4, the colors, fonts and text styles you make under "Styles" in the Assets tab. It starts like this: ```css title=".midcode/theme.css" /* * This project's theme in midcode: the colors, fonts, sizes and text styles made in the Design panel. * midcode builds midcode.css from this file and from the mid: classes in the code. * * --color-brand: #ff5b2e; → mid:bg-brand, mid:text-brand * --font-display: "Inter"; → mid:font-display */ @theme { } ``` "Design panel" in that comment is the "Styles" list of the Assets tab. [Theme and design tokens](https://midcode.app/docs/styling/theme.md) says what each control writes. In a Tailwind 4 project there is no `theme.css`: "Styles" edits the `@theme` in your own stylesheet. ### translations.json Only for a site written in one language. Translations you make in the [Translations view](https://midcode.app/docs/data/languages.md) are kept by the site's own text until the languages are wired into the code: ```json title=".midcode/translations.json" { "source": "en", "languages": ["es"], "texts": { "Built to outlast": { "es": "Hecho para durar" } } } ``` A site that already has languages keeps its translations where it always did, and midcode edits them there. ### server.json (next release) How the site runs, when you told midcode: a command, an address, or both. `$PORT` is the free port midcode picks. ```json title=".midcode/server.json" { "command": "bin/rails server -b 127.0.0.1 -p $PORT", "url": "http://127.0.0.1:$PORT" } ``` See [Any other stack](https://midcode.app/docs/frameworks/custom-server.md). Before you commit it, check that the command has nothing that's only true on your Mac. Don't add it to a project midcode starts by itself (Next.js, Vite, Astro, Nuxt, SvelteKit, React Router, Remix): with it, the site runs the project's own way, without marks, and is view-only. ### shopify.json (next release) The store a [Shopify theme](https://midcode.app/docs/shopify/themes.md) is worked on with. No key or password is in it. ```json title=".midcode/shopify.json" { "store": "your-store.myshopify.com" } ``` ### queries/ (next release) Each query saved in the [SQL editor](https://midcode.app/docs/data/sql.md) is one file, `.midcode/queries/.sql`, with the SQL as you typed it. ### README.md A short note for whoever opens the folder, written with the first comment: ```md title=".midcode/README.md" # .midcode Written by midcode (the visual editor). Safe to commit or to ignore. - `comments.json`: comments left on the site's preview. Each one is anchored to an element: `anchor.ref.mc` is where that element is written (`file:line:col`) and `anchor.ref.mci` is the component instance that rendered it, if any. - `attachments/`: files attached to those comments, referenced by path. ``` In a project without Tailwind 4, midcode adds a "Styles" section that tells an agent how `mid:` classes work. ## midcode.css and its import In a project without Tailwind 4, midcode writes styles as its own utilities (`mid:p-6`) and keeps their CSS in `midcode.css`. On your first style edit it creates the file and imports it where your app's stylesheets go: | Project | Where `midcode.css` goes | Imported in | | --- | --- | --- | | Next.js | Next to the root layout | `app/layout.tsx` (or `pages/_app.tsx`) | | React Router, Remix | Next to `root.tsx` | `app/root.tsx` | | Vite | Next to the module `index.html` loads | That module, for example `src/main.tsx` | | SvelteKit | `src/routes/` | `src/routes/+layout.svelte`, created if there is none | | Astro | `src/` | The frontmatter of every layout in `src/layouts` (of every page, when there are none) | ```diff title="app/layout.tsx" import type { Metadata } from 'next' import './globals.css' +import './midcode.css' ``` The import is one undoable edit. The stylesheet itself is rewritten whenever the `mid:` classes in your code change, so it's outside undo, and it shows in Publish as one entry: "Styles written by midcode (midcode.css)". Don't edit it by hand. In the next release the same happens for plain HTML (a `` in every page), Nuxt (`app.vue`), Shopify themes (`assets/midcode.css`, with its tag in `layout/theme.liquid`) and sites a server renders (a `` in each layout). [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md) covers all of them. ## The canvas page In a Next.js App Router project, what you draw or drop [outside the breakpoints](https://midcode.app/docs/editor/free-canvas.md) is code too. It lives in one page: ```tsx title="app/midcode-scratch/page.tsx" import { notFound } from 'next/navigation' // What floats on midcode's canvas, outside the site's breakpoints. It only exists in development // (a 404 on the live site), and midcode never publishes it. export default function MidcodeCanvas() { if (process.env.NODE_ENV === 'production') notFound() return (
) } ``` The page is in `src/app/` when your project has that folder, and it's `page.jsx` in a project with no `tsconfig.json`. Each floating element carries its id and its place on the canvas. The page is left out of Publish, out of the list of changes and out of the page list. It isn't in `.gitignore`, on purpose: Tailwind doesn't scan ignored files, and the classes of what floats would never be generated. ## Components and media - **Interactive components.** Inserting a carousel, a slideshow, a ticker, tabs, a cookie banner, a locale switcher or a shader gradient writes its source once to `components/midcode/` (`src/components/midcode/` when the project has a `src/` folder, `app/components/midcode/` in React Router and Remix). It's `.tsx` with a `tsconfig.json`, `.jsx` without. From then on the file is yours: midcode never writes over it. See [Insert](https://midcode.app/docs/editor/insert.md). - **Images, video and audio.** A file you drop, paste or pick is copied to `public/images`, `public/videos` or `public/audio`. A replacement goes next to the file it replaces when that one is in `public/`; in Next.js, an image your code imports is replaced in place. Names are lowercased, a different file with the same name gets `-1`, `-2`, and the same file is never copied twice. SvelteKit uses `static/` instead of `public/`. See [Text, images and video](https://midcode.app/docs/editor/text-and-media.md). - **Stickers** are saved as `public/stickers/-.svg`. ## Files you ask for These are created because a feature's job is to create them. Each feature's page has the details. | Feature | File | | --- | --- | | A new [page](https://midcode.app/docs/editor/pages.md) (Next.js) | `app//page.tsx` | | A new page in another framework (next release) | The file that framework expects: `src/pages/about.astro`, `src/routes/about/+page.svelte`, `about.html`, `content/about.md`. In React Router and Laravel, also a line in `app/routes.ts` or `routes/web.php`. [Pages and navigation](https://midcode.app/docs/editor/pages.md) has the list | | Favicon and social image in [Site settings](https://midcode.app/docs/editor/site-settings.md) (Next.js) | `app/icon.` or `favicon.ico`, `opengraph-image.` | | Favicon and social image outside Next.js (next release) | `favicon.` and `og-image.`, copied to the folder the site serves as it is (`public/` in most projects, `static/` in SvelteKit and Hugo, the site's root in plain HTML) | | Metadata for a client page (Next.js) | A `layout.tsx` next to it | | The rest of [Site settings](https://midcode.app/docs/editor/site-settings.md) outside Next.js (next release) | No new file: the title, description, language and indexing are tags in the `` your site already writes (`index.html`, `src/app.html`, a layout) | | "New collection" in the [CMS](https://midcode.app/docs/data/cms.md) | `content/.ts`, or `src/content/.ts` | | "Create a database" (next release) | `data/database.db` | | A [table change](https://midcode.app/docs/data/schema.md) (next release) | A `.sql` file in the project's migrations folder. With Prisma: an edit to `prisma/schema.prisma` and a folder in `prisma/migrations` | | A value in [Environment variables](https://midcode.app/docs/data/env.md) (next release) | A line in `.env.local` or `.env` | A migration file is the change in words, then its SQL: ```sql title="data/migrations/20261005183000_drop_posts_subtitle.sql" -- Drop the column “subtitle” of the table “posts” alter table "posts" drop column "subtitle"; ``` ## What stays out of your project midcode keeps its own things in `~/Library/Application Support/midcode`, never in your repo: the list of changes waiting to be published, your recent projects and their thumbnails (from the next release, also how you grouped, pinned and ordered them on the hub), settings, the config wrappers it starts dev servers with, and anything secret. [Privacy and security](https://midcode.app/docs/reference/privacy.md) lists what's there. ## If you delete them - `.midcode/`: comments, layer names and the page order are gone. Breakpoints go back to the three defaults; classes already written keep the prefixes they have. Without `theme.css`, the tokens and text styles you made stop producing CSS. - `midcode.css`: styles made in midcode stop showing. It's built again with your next style edit, or with [the midcode package](https://midcode.app/docs/styling/package.md). - `app/midcode-scratch/`: what floated on the canvas is gone. Nothing else changes. - `components/midcode/`: the pages that import those components stop compiling. To stop using midcode, keep `midcode.css` (or the package) if you used `mid:` classes, and delete `.midcode/` and `app/midcode-scratch/` if you like. Nothing else of the app is in your project. --- # What works with what > Every framework and stack midcode opens, how much of each you can edit on the canvas, and which parts ship with the next release. - Page: https://midcode.app/docs/start/supported-stacks - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt midcode opens any folder that holds a website. What changes from one stack to another is how much of the site it can link back to your code, and that decides how much you can edit by clicking. There are three levels. - **Marked while it runs.** midcode starts your dev server with a plugin of its own that tells every element where it's written. Everything on the canvas can be edited. This is React, Svelte, Vue, Astro and plain HTML. Laravel's Blade views and Shopify themes are marked as they're served too, each in its own way. - **Found after it loads.** The site is rendered by a server midcode can't plug into (PHP, Django, Rails, a static site generator, Angular's build). midcode reads the loaded page and finds each element in your templates. What it finds is edited like any other; what it can't tell apart is left alone. See [Sites a server renders](https://midcode.app/docs/frameworks/server-templates.md). - **View and comment.** The site runs and shows at every breakpoint. You can check it, play it and [leave comments](https://midcode.app/docs/editor/comments.md) for your agent, but not edit. This is what you get when midcode knows nothing about the stack, or when the project runs with its own command. "Next" in the tables means it ships with the next release. In the version you can download today, those stacks open to view and comment when the folder has a `dev` script in its `package.json` or an `index.html`, and aren't opened otherwise, unless the table says otherwise. ## React | Stack | Editing | Pages | Release | | --- | --- | --- | --- | | [Next.js](https://midcode.app/docs/frameworks/nextjs.md) | Everything, in `.tsx` and `.jsx` files | Read from `app/` and `pages/`. Add and remove | Released | | [React with Vite](https://midcode.app/docs/frameworks/react-vite.md) | Everything, in `.tsx` and `.jsx` files. Also Preact and Solid; Qwik is next | Type a path. Qwik City: the folders of `src/routes`, add and remove (next) | Released | | [React Router and Remix](https://midcode.app/docs/frameworks/react-router.md) | Everything, in `.tsx` and `.jsx` files | Read from `app/routes.ts` or the route files. Add and remove is next | Released | | [TanStack Start](https://midcode.app/docs/frameworks/tanstack-start.md) | Everything, in `.tsx` and `.jsx` files | Read from the route files. Add and remove | Next (today: opens as React with Vite, with no pages listed) | | [Create React App, Gatsby, Docusaurus, Rsbuild](https://midcode.app/docs/frameworks/webpack.md) | The project's own JSX, in `.js` files too | Gatsby: `src/pages`. The others: type a path | Next | "Everything" is text, styles, attributes, images, inserting, moving, duplicating and deleting elements, components with their props, variants and states, and [variables](https://midcode.app/docs/editor/variables.md). ## Svelte, Vue, Astro, Angular | Stack | Editing | Pages | Release | | --- | --- | --- | --- | | [Svelte and SvelteKit](https://midcode.app/docs/frameworks/svelte.md) | Text, styles, attributes, structure, component props, variants and states | SvelteKit: `src/routes`. Add and remove is next | Released | | [Vue with Vite](https://midcode.app/docs/frameworks/vue.md) | Text, styles, attributes, structure, component props, variants and states | Type a path | Next | | [Nuxt](https://midcode.app/docs/frameworks/nuxt.md) | Text, styles, attributes, structure, component props, variants and states | The files of `pages/`. Add and remove, once it has a `pages/` folder | Next | | [Astro](https://midcode.app/docs/frameworks/astro.md) | `.astro` files: text, styles, attributes, structure, component props, variants and states. Islands as their own framework | `src/pages`. Add and remove | Next (today: its React, Preact and Solid islands only) | | [Vue CLI](https://midcode.app/docs/frameworks/webpack.md) | The project's own `.vue` files | Type a path | Next | | [Angular](https://midcode.app/docs/frameworks/angular.md) | Text, styles, attributes and structure in component templates, found after the page loads | The routes in `*.routes.ts` or a routing module | Next | "Structure" is inserting, moving, duplicating and deleting elements. From the next release it also covers several selected elements at once (removed, wrapped in a stack or moved together) and `⌥`-drag, which leaves a copy. In these files a value the language computes (`{{ title }}`, `{title}`) shows on the canvas and is changed in the code. ## HTML and sites a server renders | Stack | Editing | Pages | Release | | --- | --- | --- | --- | | [Plain HTML](https://midcode.app/docs/frameworks/html.md) | Text, styles, attributes, structure | Every `.html` file. Add and remove, at the top of the site | Next | | [Laravel](https://midcode.app/docs/frameworks/laravel.md) | Blade views; Inertia pages as React or Vue; Livewire views | The GET routes of `routes/web.php`. Add and remove | Next (today: only with Vite, shown at a `localhost` address, not edited) | | [WordPress](https://midcode.app/docs/frameworks/wordpress.md) | A classic theme's templates. A block theme has little to edit | Type a path | Next | | [PHP and Twig](https://midcode.app/docs/frameworks/php.md) | What's written in the templates | Type a path | Next | | [Django, Flask and FastAPI](https://midcode.app/docs/frameworks/python.md) | What's written in the templates | Django: `urls.py`. Flask: the `@app.route` lines. FastAPI: type a path | Next | | [Ruby on Rails](https://midcode.app/docs/frameworks/rails.md) | What's written in the views | Read from `config/routes.rb` | Next | | [Hugo, Jekyll and Eleventy](https://midcode.app/docs/frameworks/static-generators.md) | Templates. In Markdown content, the words of headings, paragraphs and list items | The content files. Add and remove | Next | | [Shopify themes](https://midcode.app/docs/shopify/themes.md) | Liquid, section settings, the store behind the theme | The theme's templates, with the store's products and collections | Next | In these stacks, what the server fills in (a post's body from the database, a product's title, the output of a helper) is on the page but not in a template: it can't be edited from the canvas. A stack that isn't in any table still opens. [Any other stack](https://midcode.app/docs/frameworks/custom-server.md) lists what midcode recognises by its files, and how to tell it the command that runs your site. A site inside a bigger repository is found too: see [Monorepos](https://midcode.app/docs/frameworks/monorepos.md). ## Features by kind of project | | Next.js | Other React | Svelte, Vue, Astro | HTML and server templates | | --- | --- | --- | --- | --- | | Text, styles, attributes, media | Yes | Yes | Yes | Yes | | Insert, move, duplicate, delete | Yes | Yes | Yes | Yes | | [Insert](https://midcode.app/docs/editor/insert.md)'s interactive components | Yes | Yes | No: they're React | No | | Component props in the panel | Yes | Yes | Yes | No | | [Variants, states](https://midcode.app/docs/editor/components.md), [variables](https://midcode.app/docs/editor/variables.md) | Yes | Yes | Variants and states (Vue and Astro: next). Variables (next) | No | | [Free canvas](https://midcode.app/docs/editor/free-canvas.md) | Yes (Pages Router: next) | Vite + React, Preact or Solid (next) | Plain elements, not in Nuxt (next) | Plain elements in a folder of HTML (next) | | [Add and remove pages](https://midcode.app/docs/editor/pages.md) | Yes (new pages in the App Router) | React Router, Remix, TanStack Router, Qwik City (next) | SvelteKit, Nuxt, Astro (next) | HTML, Hugo, Jekyll, Eleventy, Laravel (next) | | [Site settings](https://midcode.app/docs/editor/site-settings.md) | Yes (App Router) | With an `index.html` (Vite, Create React App), and Docusaurus (next) | SvelteKit, Vue, Nuxt, Astro, Angular (next) | In the page's or the layout's ``; not WordPress or Shopify themes (next) | | [CMS](https://midcode.app/docs/data/cms.md), [Languages](https://midcode.app/docs/data/languages.md) | Yes | Yes | Lists in `.ts`, JSON and Markdown files | JSON and Markdown files | | [Database](https://midcode.app/docs/data/database.md) (next) | Yes | Yes | Yes | Yes | | [Agent](https://midcode.app/docs/agents/overview.md), [Code](https://midcode.app/docs/editor/code.md), [Comments](https://midcode.app/docs/editor/comments.md), [Publish](https://midcode.app/docs/publish/publish.md) | Yes | Yes | Yes | Yes | ## Styles How styles are written depends on one thing: whether the project uses Tailwind 4. | Project | The panel writes | More | | --- | --- | --- | | Tailwind 4 | Tailwind's own classes, swapping only the one that changes | [Tailwind CSS](https://midcode.app/docs/styling/tailwind.md) | | Anything else: plain CSS, CSS modules, Bootstrap, Tailwind 3 | midcode's `mid:` utilities, compiled to a `midcode.css` in your project | [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md) | ## What midcode doesn't open - A site that isn't code on your Mac: a Webflow, Framer, Wix or Squarespace site, or a WordPress site you only reach through its admin. - Native apps (React Native, Swift, Flutter). midcode shows web pages. - A Next.js or Vite project that writes JSX in `.js` files opens, but its elements aren't marked: rename the files to `.jsx`. - Parcel and Nuxt 2 projects open to view and comment only. midcode itself runs on macOS only. ## If you're not sure Open the folder. Nothing is added to a project by opening it, and the right panel says what you got: a file and a line when you click an element, or "preview and comments" when the site has no marks. If the project doesn't start, [Troubleshooting](https://midcode.app/docs/reference/troubleshooting.md) has the usual reasons. --- # The canvas and breakpoints > The canvas shows your running site at every breakpoint, side by side. How to move around it, how a breakpoint becomes a Tailwind screen, and what an override writes. - Page: https://midcode.app/docs/editor/canvas - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt The canvas is the middle of the window: your site, running, once per breakpoint. Each frame is a real page of your dev server at that width, so what you see is what your code does. You pan and zoom over all of them like a design file. A change made in one frame is written for that breakpoint. That's the whole model: one breakpoint is the rule, the others take overrides, and both end up as ordinary responsive classes in your code. ## What a frame is Each frame loads the page that's open (see [Pages and navigation](https://midcode.app/docs/editor/pages.md)) from your dev server, at its breakpoint's width. On the canvas the page is in design mode: - A click selects an element. It doesn't follow links, press buttons or submit forms. To use the site, open the [Preview](#preview). - The frame is as tall as the whole page, so every section is on the canvas at once and the page itself never scrolls. Scrolling moves the canvas. - Viewport units (`vh`, `svh`, `dvh`, `lvh`) mean the breakpoint's device height, not the frame's: a `min-h-screen` hero stays one screen tall. The device height is 900 px for a width of 1024 or more, 1024 px from 600, and 844 px below that. - Motion rests while you design. Looping animations and videos are paused, and one-shot animations are shown finished, so a title that fades in is visible instead of stuck at zero opacity. - Embeds (a YouTube player, a map, an audio player) can be hovered and selected like any other element. Over the bottom of the canvas floats the toolbar: "Insert" (the +, see [Insert](https://midcode.app/docs/editor/insert.md)), "Inspect" (`V`), "Comment" (`C`, see [Comments](https://midcode.app/docs/editor/comments.md)) and "Preview" (`P`). Next.js App Router projects also have "Frame: draw one on the canvas" (`F`), for [the free canvas](https://midcode.app/docs/editor/free-canvas.md). ## Move around | To | Do this | | --- | --- | | Pan | Scroll with two fingers or the wheel, anywhere, also over a frame. | | Pan by dragging | Hold `Space` and drag. Or drag the empty canvas with the middle mouse button. | | Zoom | Pinch, or hold `⌘` and scroll. It zooms on the pointer. | | Zoom in, zoom out | `⌘=` and `⌘-` | | Zoom to fit every frame | `⌘1`, `⇧1`, or click the percentage in the top bar. | | Actual size (100%) | `⌘0` | | Fit the first frame | `⇧2` | The zoom goes from 5% to 400%. Right-click the empty canvas for the same commands as a menu: "Preview", "Zoom to fit", "Actual size", "Paste", "Show rulers" / "Hide rulers", "Reload breakpoints" and "Deselect". Double-click a breakpoint's row in [Layers](https://midcode.app/docs/editor/layers.md) to bring that frame into view. ## Breakpoints A project starts with three, until you change them: | Breakpoint | Width | Tailwind screen | | --- | --- | --- | | Desktop | 1440 | `lg` | | Tablet | 768 | `md` | | Phone | 390 | none (no prefix) | They sit on the canvas from the widest to the narrowest. Above each frame is its bar: - The play button opens that breakpoint in the [Preview](#preview). So does a double-click on the bar. - The name and width open "Edit breakpoint". - "Primary" marks the primary breakpoint. - A third number, like `× 5230`, is the page's height when it's taller than the device. - The + on the first bar is "New breakpoint". Right-click a bar for all of it: "Preview Desktop", "Edit breakpoint…", "Make primary", "New breakpoint…" and "Remove breakpoint". ### Add, edit and remove 1. Click the + on the first bar, or right-click any bar and choose "New breakpoint…". 2. Type a name and a width in px (240 to 3840). The name is optional: without one, the breakpoint is called by its width. 3. Click "Add". A new frame appears at that width, in its place by size. Two breakpoints can't have the same width. "Edit breakpoint…" changes the name or the width. A new width also gives the frame the device height that goes with it, and a new screen if the old one no longer fits. "Remove breakpoint" takes the frame away; the last breakpoint can't be removed. Removing the primary makes the widest one primary. > [!NOTE] > Adding, editing or removing a breakpoint changes `.midcode/breakpoints.json` and nothing else. Classes already in your code are not rewritten. ### The primary breakpoint and overrides One breakpoint is the primary: Desktop, until you choose another with "Make primary". The rule is the one Framer uses: - A change made in the primary frame applies to every breakpoint that doesn't say otherwise. - A change made in any other frame is an override for that breakpoint. It also carries on to the breakpoints further from the primary that were showing the same value: override a size on Tablet and Phone follows, until Phone gets a value of its own. Which breakpoint you're editing is the frame you selected the element in. The right panel says so in its "Breakpoint" row, where you can also switch, and picking a layer under a breakpoint in [Layers](https://midcode.app/docs/editor/layers.md) selects it there. In a breakpoint that isn't the primary, the panel reads "Changes here override Desktop. Overridden properties show in blue." A property with an override has a blue label. Hover it to see what it overrides, and right-click it for "Remove override": the breakpoint follows its neighbour toward the primary again. ### How a breakpoint becomes a Tailwind screen Overrides are written as Tailwind's responsive prefixes, mobile first. So each breakpoint needs a screen, and midcode picks it: - The narrowest breakpoint writes with no prefix. - Every other breakpoint gets a screen that starts above the next narrower breakpoint's width and no later than its own. That keeps each frame in its own range, so an override never leaks into its neighbour. - A named screen is used when one fits (`sm` 640, `md` 768, `lg` 1024, `xl` 1280, `2xl` 1536). If none fits, midcode writes an exact one in rem: a breakpoint at 1000 px gets `min-[62.5rem]`. Screens are worked out again whenever the list changes. Add a "Laptop" at 1200 to the defaults and it takes `lg`, which moves Desktop to `xl`: | Breakpoint | Width | Screen before | Screen after | | --- | --- | --- | --- | | Desktop | 1440 | `lg` | `xl` | | Laptop | 1200 | | `lg` | | Tablet | 768 | `md` | `md` | | Phone | 390 | none | none | In a project without Tailwind 4 the same prefixes go after midcode's own: `mid:md:text-4xl`. See [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md). ## What midcode writes The value at each breakpoint becomes one class at that breakpoint's screen, from the narrowest up. Neighbours with the same value share a class. Here is one heading through four edits of its font size, with the default breakpoints and Desktop as primary: | You do | The heading's classes after | | --- | --- | | Set 5xl in Desktop | `font-semibold text-5xl` | | Set 4xl in Tablet | `font-semibold text-4xl lg:text-5xl` | | Set 3xl in Phone | `font-semibold text-3xl lg:text-5xl md:text-4xl` | | "Remove override" in Tablet | `font-semibold text-3xl md:text-5xl` | The second row shows the carry-on: the Tablet override reached Phone too, so the unprefixed class is the Tablet value and Desktop keeps its own behind `lg:`. As a diff: ```diff title="src/app/page.tsx" -

Design in code

+

Design in code

``` A class that changes is swapped where it stands, and new ones go at the end of the list. The order of classes in the attribute doesn't change what the browser shows. When a narrower breakpoint has a value and a wider one has none, the wider one gets the value that means "nothing". Padding given to a stack on Phone only: ```diff title="src/components/Card.tsx" -
+
``` Every style edit goes through this, from the [style panel](https://midcode.app/docs/editor/styles.md) to a resize handle. [Tailwind CSS](https://midcode.app/docs/styling/tailwind.md) has the rest: arbitrary values, your theme's tokens, and classes built with `cn()`. ### .midcode/breakpoints.json Your breakpoints are saved in the project the first time you change them. Until then the file doesn't exist and the defaults apply. This is the file after adding the Laptop breakpoint above: ```json title=".midcode/breakpoints.json" { "version": 1, "list": [ { "id": "desktop", "label": "Desktop", "width": 1440, "height": 900, "screen": "xl" }, { "id": "laptop-1200", "label": "Laptop", "width": 1200, "height": 900, "screen": "lg" }, { "id": "tablet", "label": "Tablet", "width": 768, "height": 1024, "screen": "md" }, { "id": "phone", "label": "Phone", "width": 390, "height": 844, "screen": "" } ], "primary": "desktop" } ``` - `height` is the device height that viewport units mean in that frame. - `screen` is the prefix its overrides are written with. Write your own (`"screen": "xl"`) and midcode keeps it as long as it fits the rule above. One that doesn't fit is ignored, and replaced the next time midcode saves the file. - `primary` is the `id` of the primary breakpoint. Commit the file so everyone who opens the project gets the same frames. Delete it to go back to the defaults. [What midcode adds to your project](https://midcode.app/docs/start/project-files.md) lists every file of this kind. ## Preview The canvas never runs the site. The Preview does: one breakpoint, full size, scrolling and taking clicks like a browser, with its animations, menus and videos. 1. Press `P`, or click "Preview" in the toolbar. That opens the primary breakpoint. The play button on a breakpoint's bar opens that one. 2. Use the site. 3. Press `Esc`, or click "Back", to return to the canvas. `P` closes it too until you click into the site: from then on the site has the keyboard and `P` is its key. The bar over the preview has "Reload", "Hide UI (full screen)", a menu to switch breakpoint, and the width and height, which you can type. Drag either side of the frame to try any width: the breakpoint then reads "Custom". With the interface hidden, "Show UI" in the corner (or `Esc`) brings it back. A frame too big for the window is scaled down, and the scale is shown under it. Nothing is edited in the Preview. Opened from a component, it shows that component alone: see [Components](https://midcode.app/docs/editor/components.md). ## Reload Your dev server's hot reload brings each edit into every frame without loading the page again. When a frame gets stuck anyway, click "Reload breakpoints" in the top bar (the circular arrow), or right-click the empty canvas and choose it. Every frame loads the page again. ## The top bar - Left: the project's name, its branch, and the page that's open. See [Pages and navigation](https://midcode.app/docs/editor/pages.md) and [Publish](https://midcode.app/docs/publish/publish.md). - Middle: "Design" and "Code", two ways into the same project. "Code" swaps the canvas for your files, an editor and a terminal: see [Code view](https://midcode.app/docs/editor/code.md). - Right: the dev server's log ([Open a project](https://midcode.app/docs/start/open-a-project.md)), the site's languages ([Languages](https://midcode.app/docs/data/languages.md)), "CMS" ([CMS](https://midcode.app/docs/data/cms.md)), "Site settings" in the projects that have them ([Site settings](https://midcode.app/docs/editor/site-settings.md) says which), "Reload breakpoints", the zoom, and "Publish". "Reload breakpoints" and the zoom belong to the canvas. They're hidden in the Code view. ### CMS, Database and Store in the middle (next release) In the next release the middle of the bar lists every place of a project: "Design", "Code", "CMS", "Database" and, in a Shopify theme, "Store". [CMS](https://midcode.app/docs/data/cms.md), [Database](https://midcode.app/docs/data/database.md) and [Store](https://midcode.app/docs/shopify/store.md) open over the canvas, which stays loaded underneath, and "Reload breakpoints" and the zoom show only while the canvas itself is on screen. In version 1.1.2, CMS is a button on the right of the bar and the other two don't exist. ## Limits - A breakpoint is a width. There are no height breakpoints, and midcode doesn't write screens that end at a width (`max-md:`). A class in your code with a prefix it doesn't know is left where it is: the panel doesn't read it and never rewrites it. - A frame grows to 40,000 px. A page taller than that is cut off on the canvas. - The canvas shows one page at a time, the same in every frame. - Text styles and link styles from your theme can't be overridden per breakpoint: they're always written for every breakpoint. --- # Select, move and resize > Select elements on the canvas, move, copy and resize them by hand, and line them up with snapping, rulers and guides. With the code each gesture writes. - Page: https://midcode.app/docs/editor/select-move-resize - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt Everything you do to an element on the canvas with the pointer is an edit of the file it's written in: a drag moves its lines, a handle changes a class, a nudge changes two. This page covers those gestures with the "Inspect" tool (`V`), which is the tool you're in unless you picked another. ## Select Move the pointer over a frame and the element under it gets an outline. Click to select it. The selection shows a label with its tag (or its component's name), and its parent is outlined with a dashed line. The right panel now shows the element. Its "Code" section says where it's written: - "Element": the file and line of the element itself. Click it to copy `file:line:col`; the icon beside it is "Open code". - "Instance": where the component that renders it is used, when it belongs to one. - "Inside": the nearest ancestors written in other files. What you can select is what your code writes. An element drawn by a library (a chart's internals, the markup of a UI kit) isn't in your files, so a click on it selects the nearest element around it that is. When nothing around it is, the panel says so: "A library draws this element: it isn't in your project's files, so there's nothing to edit here." You get the same message for JSX written in a plain `.js` file in a Next.js or Vite project: midcode marks `.tsx` and `.jsx` files there, so rename the file to `.jsx`. An SVG selects as a whole. An element rendered by a loop is one line of code shown several times. The panel says "Item 3 of a list: editing the code changes all of them." | To | Do this | | --- | --- | | Select the parent | `⌘` + click inside the selection: one level up each time. Or `⌘↑`. | | Select from the tree | Click a layer in [Layers](https://midcode.app/docs/editor/layers.md). | | Deselect | `Esc`, or click the empty canvas. | | Edit a text | Double-click it. See [Text, images and video](https://midcode.app/docs/editor/text-and-media.md). | | Open a component | Double-click one of its instances. See [Components](https://midcode.app/docs/editor/components.md). | The same element is outlined in every frame. The frame you clicked in is the breakpoint you're editing: see [Breakpoints](https://midcode.app/docs/editor/canvas.md). ### Select several Hold `⇧` and click another element, on the canvas or in Layers, to add it to the selection. `⇧` + click on one that's already in takes it out. `⌘A` adds every sibling of the selection. The others are outlined without handles, and they're let go when you select something else. While several are selected: - On a page, the style panel writes to all of them. It's one edit, so one `⌘Z` undoes it everywhere. - Dragging one moves them all, if they sit next to each other in the same container. - `⌫` deletes them, `⌘C` / `⌘X` / `⌘D` copy, cut and duplicate them, and `⇧A` wraps them in a stack. - A right-click on any of them opens a menu for all: "Copy", "Cut", "Duplicate", "Delete", "Wrap in a stack" and "Copy references for your agent". To be moved, wrapped, duplicated or deleted together, they have to be written in the same file. Otherwise midcode says "These elements are written in different files." ## Move ### Drag to another place Press on an element and drag. It fades, and a blue line shows where it would land: before or after the element under the pointer, or between the children of the container under it. Over an empty container, the container is outlined instead and the element goes inside. Release to move it. `Esc` cancels. Pressing inside the selection drags the selection. Pressing anywhere else drags (and selects) what's under the pointer. You can also drag a row in [Layers](https://midcode.app/docs/editor/layers.md), which is easier when the target is small or covered. What moves is the element's code: its lines are cut and written at the new place, with the JSX comment right above it if it has one. ```diff title="src/components/Nav.tsx"
    -
  • Docs
  • Pricing
  • +
  • Docs
``` A component's root moves where the component is used: drag a whole `` section and the line that moves is the instance in the page's file. ### Moves that are refused - **To another file.** An element moves within the file it's written in: "For now elements move within the same file (page.tsx)." The exception is [the free canvas](https://midcode.app/docs/editor/free-canvas.md), where things go in and out of pages with their imports. - **Out of a list.** An item written inside a `.map()` uses values that exist only there, so it stays in its loop and nothing else goes into it: "This element uses values from where it's written (a list, a loop). Move it in the code." - **Into a component instance.** `` has no place in this file to put children in: "Can't put elements inside a component instance." Open the component to edit its inside. - **Into itself.** ### Positioned elements An element positioned with left and top ("Position" → "Type" → "Absolute" or "Fixed" in the right panel, and no right, bottom or inset class) isn't in the flow, so dragging it doesn't reorder anything. It moves freely, a label shows its new left and top, and on release the two classes are written: ```diff -New +New ``` Hold `⇧` to move along one axis. It [snaps](#snapping) to its container and its siblings. ### Arrow keys With a positioned element selected, the arrow keys move it 1 px. With `⇧` they move it by the nudge amount, 10 px unless you changed "Nudge amount" in Settings (`⌘,`). The element moves at once, and the classes are written when the keys rest, so a run of presses is one edit: ```diff -New +New ``` A value that's a multiple of half your spacing unit (2 px, with Tailwind's default of 4) is written as a scale number: `left-30` is 120 px. Anything else is written exactly, in brackets. Arrow keys do nothing to an element in the flow: there, order is the position. They also move what floats on [the free canvas](https://midcode.app/docs/editor/free-canvas.md). ## Copy | To | Do this | | --- | --- | | Duplicate in place | `⌘D`. The copy is written right after the element. | | Copy by dragging | Hold `⌥` while you drag. The element stays where it is and a copy lands where you release. | | Copy and paste | `⌘C`, select another element, `⌘V`. The copy goes after the selection ("Paste after"). | | Cut | `⌘X`: a copy, then a delete. | | Copy only the styles | `⌥⌘C` on one element, `⌥⌘V` on another. | In JSX files a copy made by dragging can land in another file, unlike a move: it takes along the imports it uses. A paste writes the copied code as it is, so pasting an element that uses a component into a file that doesn't import it is refused: midcode says pasting there needs ``, which the file doesn't import. `⌘C` on one element does two things. midcode keeps the element's code for `⌘V`, and the system clipboard gets the element's reference, ready to paste to an agent: ```text

"Design in code" written at src/components/Hero.tsx:8:7 inside src/app/page.tsx:9:5 page / · Desktop 1440px ``` `⇧⌘C` ("Copy Reference" in the Element menu) copies the reference alone. With several elements selected, `⌘C` puts their code on the system clipboard instead. "Paste styles" replaces the target's whole class list with the copied one. It's meant for two elements of the same kind. In a Next.js App Router project, `⌘V` with nothing selected puts the copy on the free canvas, in the middle of what's in view. In other projects, select the element the copy should go after first. `⌘V` checks the system clipboard first: with an image or files on it, it pastes those. See [Text, images and video](https://midcode.app/docs/editor/text-and-media.md). ## Wrap in a stack Select one or more siblings and press `⇧A`. A new `
` is written where the first one was, with all of them inside in their order. It runs the way their parent lays them out, with the parent's gap, so nothing moves on screen: a row inside a row, a column anywhere else. The new stack is selected. ```diff title="src/components/Pricing.tsx"

Pricing

-

One payment, every update.

- Buy +
+

One payment, every update.

+ Buy +
``` The elements have to sit next to each other in the same container: "Select elements that sit next to each other, in the same container." ## Resize Handles show on the sides whose size is "Fixed": - Width fixed: handles on the left and right. - Height fixed: handles on the top and bottom. - Both: the corners too. An element with no handles takes its size from its content or its container ("Fit", "Fill", "Relative"). To size it by hand, set "Width" or "Height" to "Fixed" in the "Size" section of the right panel first. [The style panel](https://midcode.app/docs/editor/styles.md) explains the modes. Drag a handle. The element takes the size as you drag and a label shows it in px. Hold `⇧` on a corner to keep the proportions. On release the size is written: ```diff -The team +The team ``` In the primary breakpoint that's the size everywhere. In another frame it's an override for that breakpoint. The same image, resized to 280 px in the Phone frame instead: ```diff -The team +The team ``` ## Snapping A positioned element being moved lines up with what's around it: - its edges and its center with the edges and centers of its siblings and of the container it's positioned in; - the same distance from a neighbour as its neighbours keep between themselves. It snaps within 6 px on screen. A line shows what it lined up with, and a number shows the distance to the neighbour on each side. | Key | While dragging | | --- | --- | | `⌘` or `⌃` | No snapping: it moves freely. Press it once the drag has started. | | `⇧` | Moves along one axis only. | Elements floating on [the free canvas](https://midcode.app/docs/editor/free-canvas.md) snap the same way, to each other, to the breakpoints' frames and to guides, also while you resize them. ## Rulers and guides Rulers run along the top and left of the canvas, in canvas px. `R` hides and shows them (`⇧R` does too). 1. Drag down from the top ruler for a horizontal guide, or right from the left ruler for a vertical one. 2. Drag a guide to move it. It snaps to the edges and centers of the breakpoints and of what floats on the canvas. 3. To remove one: drag it back onto its ruler, or click it and press `⌫`, or right-click it and choose "Remove guide". "Remove all guides" is in that menu and in the canvas's. What floats on the free canvas snaps to guides. Elements inside a page don't. Guides are yours: they're kept per project on this Mac, in midcode's own settings, and never written into the project. With the rulers hidden, guides are hidden too and nothing snaps to them. Rulers aren't shown while you edit a component. ## Right-click menus Right-click an element, on the canvas or in Layers. It's selected, and the menu offers: | Item | Key | What it does | | --- | --- | --- | | "Edit component" | | On an instance: opens its component. See [Components](https://midcode.app/docs/editor/components.md). | | "Copy reference for your agent" | `⇧⌘C` | The text shown under [Copy](#copy). | | "Comment" | | Starts a comment on the element. See [Comments](https://midcode.app/docs/editor/comments.md). | | "Copy", "Cut", "Paste after" | `⌘C` `⌘X` `⌘V` | | | "Copy styles", "Paste styles" | `⌥⌘C` `⌥⌘V` | | | "Duplicate" | `⌘D` | | | "Wrap in a stack" | `⇧A` | | | "Hide" / "Show" | `⌘;` | Adds or removes the `hidden` class, for every breakpoint. | | "Delete" | `⌫` | Removes the element's code. | | "Rename", "Auto rename" | `⌘R` `⌥⌘R` | Names the layer. See [Layers](https://midcode.app/docs/editor/layers.md). | | "Select parent" | `⌘↑` | | | "Open code" | | Opens the file at the element's line. See [Code view](https://midcode.app/docs/editor/code.md). | The same commands are in the "Edit" and "Element" menus of the menu bar. A right-click on the empty canvas, on a breakpoint's bar, on a guide or on a blue label in the panel opens that thing's own menu: see [The canvas and breakpoints](https://midcode.app/docs/editor/canvas.md). Each command that changes your code is one edit: `⌘Z` undoes it and `⇧⌘Z` redoes it. [How midcode works](https://midcode.app/docs/start/how-it-works.md) covers the history. ## Padding, gap and radius by hand (next release) With the pointer over the selection, three kinds of handles appear inside it: - a blue bar in each side's padding; - a pink bar in each space between its children, when it's a stack or a grid; - a dot in from each corner, for the radius. Drag one. The element shows the value as you drag, with the number in px by the pointer, and the class is written on release. Hovering a padding or a gap bar tints the areas it controls. | Handle | Plain drag | With `⌥` | With `⌥⇧` | | --- | --- | --- | --- | | Padding | That side: `pt-10` | That side and the opposite one: `py-10` | All four sides: `px-10 py-10` | | Gap | `gap-6` | | | | Radius | Every corner: `rounded-2xl` | That corner only: `rounded-tl-2xl` | | Starting from a card with `gap-4`, `rounded-lg` and `p-6`, and dragging the gap to 24 px, the top padding to 40 px and a corner to 16 px: ```diff -
+
``` They're written like the panel writes them: for the breakpoint of the frame you're in, as a scale number when the value allows it, and with your theme's radius names when one matches. A radius of 0 is `rounded-none`. When a pair or all sides are set together, the single-side classes they replace are removed. A handle is left out when there's nothing for it to show: - No padding bars on images, videos, inputs and other elements that hold no content, or on an element with no children and no padding. - No gap bars when the space between children isn't the gap's (`justify-between` and the like), when the element sets its two gaps apart (`gap-x-4`), or when it's rotated. - No radius dots on an element with nothing a radius would show on: no fill, border, shadow or clipping. - None at all when the element is small on screen (under about 40 px, 48 for the radius), with several elements selected, or in a project midcode can't write styles in. ## Limits - Moving, wrapping and deleting several elements at once work in JSX files. In Svelte, Vue, Astro and HTML files and in server templates they arrive in the next release; in version 1.1.2 midcode says so instead. Duplicating several at once stays JSX only: in other files the next release answers "These elements are written in different files." even when they share one. - `⌥` + drag copies in JSX files. In other files it arrives in the next release, for a copy that lands beside or inside an element of the same file. - From the next release, headings, paragraphs and list items that come from a Markdown file (Hugo, Jekyll, Eleventy) can be selected, edited as text and deleted. They can't be moved, copied, wrapped or styled: "This is written in Markdown: its words can be changed here, not its classes or attributes. Style it from the template around it." - `⌥` + drag doesn't copy an element positioned with left and top. Duplicate it with `⌘D` and move the copy. - Snapping and arrow keys apply to positioned elements and to the free canvas. An element in the flow is placed by its order and its container's layout. - Resize handles change `w-*` and `h-*`. Minimum and maximum sizes are set in [the style panel](https://midcode.app/docs/editor/styles.md). - In a project that midcode can only show (see [Open a project](https://midcode.app/docs/start/open-a-project.md)), you can select elements and comment on them, but nothing here writes. --- # Layers > The Layers tab lists the page's elements under each breakpoint, then what floats on the canvas. Select, reorder and rename layers, and see where names are kept. - Page: https://midcode.app/docs/editor/layers - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt Layers is the page as a tree: every element your code writes, nested the way it's nested in the page. It's the middle tab of the left panel, next to "Agent" and "Assets". Use it to select what's hard to click on the canvas, to move elements by dragging rows, and to give layers names that mean something. ## How the list is grouped On a page, the list has two parts: 1. **Each breakpoint**, widest first, with a device icon, its name, and its width (or "Primary" on the primary one). The primary breakpoint starts open and the others closed. Under each one are the page's layers. 2. **What floats on the canvas**, after the last breakpoint, in Next.js App Router projects. Each floating element is a top-level row with its own layers inside. See [The free canvas](https://midcode.app/docs/editor/free-canvas.md). The page's layers are the same tree under every breakpoint, because it's the same code. What changes is where a click lands: pick a layer under "Phone" and it's selected in the Phone frame, so what you change is written for that breakpoint. See [Breakpoints](https://midcode.app/docs/editor/canvas.md). Click a breakpoint's row to mark its frame; double-click it to bring that frame into view. While you edit a component, the list is the component's own layers, with no breakpoints over them. See [Components](https://midcode.app/docs/editor/components.md). ## What a row shows A layer is an element written in your project's files. An element a library draws isn't listed; what your code puts inside it is, one level up. | Icon | Layer | | --- | --- | | Component mark, in purple | The root of a component instance. The row shows the component's name. React and Svelte components; from the next release, Vue and Astro components too. | | Rows, columns or a grid | A container, by how it lays out its children: a vertical stack, a horizontal stack, or a grid. | | Heading | `h1` to `h6`, with the tag in a small badge. | | Image | `img`, `picture`, `video` | | Pen | `svg` | | Link | `a` | | Text | A paragraph, a span, a button or a list item with text of its own. | | Square | Anything else. | The name is, in this order: the name you gave it, the component's name, the element's own text (the first words), an image's alt text or file name, or the tag. A layer hidden with "Hide" (`⌘;`) is dimmed. An element rendered by a loop has one row per item it renders; they are all the same line of code. The list and the canvas follow each other. Hover a row and the element is outlined in the frames. Hover an element and its row lights up. Select on the canvas and the tree opens down to that layer and scrolls to it. ## Select from Layers | To | Do this | | --- | --- | | Select a layer | Click its row. | | Add to the selection, or take out of it | `⇧` + click. | | Open or close a layer | Click the arrow at its left. | | Open the element's menu | Right-click the row. The layer is selected first. | | Rename | Double-click the row. | What you can do with several layers selected is in [Select, move and resize](https://midcode.app/docs/editor/select-move-resize.md). ## Drag to reorder Press on a row and drag it over the list. A blue line shows where the layer would go, indented to the depth it would have: - Over the top part of a row: before that layer. - Over the bottom part: after it. If that layer is an open container, it becomes the container's first child. - Over the middle of a container: inside it, as its last child. The container's row is outlined. Release to move it. `Esc` cancels. A layer can't go inside itself. Containers are `div`, `section`, `main`, `article`, `header`, `footer`, `nav`, `aside`, `form`, `figure`, `ul`, `ol`, `li`, `fieldset`, `details` and `dialog`. A component instance isn't one: open the component to change what's inside it. A drag in Layers is the same edit as a drag on the canvas: the element's code moves, with the comment above it. ```diff title="src/app/page.tsx"
- +
``` The same rules apply: within one file, never out of a `.map()`, never into an instance. They're listed in [Select, move and resize](https://midcode.app/docs/editor/select-move-resize.md). The floating part of the list is the exception to the first rule: a floating layer can be dragged into the page, and a page's layer into a floating container. Beside a floating element, at the top level, only another floating element can go. ## Rename a layer 1. Double-click a row. Or select the layer and press `⌘R`, or right-click it and choose "Rename". 2. Type a name. 3. Press `Enter`. `Esc` leaves it as it was. Clear the name and press `Enter` to go back to the default. A name is a label for you and for your agent. It changes nothing in the element's code. The right panel's title and the reference you copy for an agent (`⌘C`, or `⇧⌘C` for the reference alone) use it too. ### Name layers with Apple Intelligence The colored mark at the top right of the list is "Name layers". It names the layers a tag says nothing about: containers and media that have no name yet, up to 120 at a time. "Auto rename" (`⌥⌘R`, or the element's menu) names one layer. The model runs on your Mac. Nothing is sent anywhere and it costs nothing. What it's given for each layer is a description: its tag, a few of its classes, its text, what's inside it and the heading of the section it's in. It needs macOS 26 or later on Apple silicon, with Apple Intelligence turned on in System Settings. Without it the button is disabled and says why. ## What midcode writes Names are kept in the project, in `.midcode/layers.json`, so they travel with the repository and an agent working there can read them. ```json title=".midcode/layers.json" { "version": 1, "names": [ { "file": "src/app/page.tsx", "tag": "section", "classes": "mx-auto max-w-6xl px-6 py-24", "text": "", "loc": "src/app/page.tsx:14:7", "name": "Pricing" }, { "file": "src/components/Hero.tsx", "tag": "h1", "classes": "text-5xl font-semibold tracking-tight", "text": "Design in code", "loc": "src/components/Hero.tsx:8:7", "name": "Hero title" } ] } ``` | Field | What it is | | --- | --- | | `file` | The file the element is written in, from the project's root. | | `tag` | Its tag. | | `classes` | Its class attribute when it was named (the first 160 characters). | | `text` | Its own text, if it has any (the first 48 characters). | | `loc` | Where it was when it was named: `file:line:col`. | | `name` | The name. | ### How a name is found again Code moves: add a line above an element and its `loc` is off by one. So a name isn't tied to the line alone. For each layer, midcode looks at the names saved for the same file and tag, and takes the best match: - same `loc`: the name is its own, whatever its classes are now; - otherwise, same `classes`: it's the same element, on another line. Matching text alone isn't enough. So a name holds while one of the two is still true. Once both have changed (lines were added above the element and its classes were edited), the entry no longer matches and the layer shows its default name. The entry isn't updated as the code changes: rename the layer again to save where it is now. An entry that matches nothing stays in the file and does no harm. Delete the file to remove every name. Commit it if you want the names shared; nothing breaks if you don't. ## Limits - Layers lists what's on the page now. A menu that isn't open or a section behind a condition isn't in the tree until it renders. - The tree stops after about 4,000 layers on one page. - In a project midcode can show but not edit (see [Open a project](https://midcode.app/docs/start/open-a-project.md)), Layers lists the page's elements as the browser has them. You can select them and comment on them. Names need elements that map to code, so renaming isn't available there. - A name belongs to one place in the code. An element drawn by a loop has one name for all its items. - Changing an element's tag (a `div` into a `section`) drops its name: names are matched within one file and one tag. --- # Text, images and video > Edit a text where it shows and midcode changes it where it's written, even in a data file. Replace, paste and drop images and videos, and set alt text. - Page: https://midcode.app/docs/editor/text-and-media - From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt Text and media are the two things on a page that are content rather than layout. midcode edits both where they show, on the canvas, and writes the change where the content lives in your project: in the markup, in a data file, or as a file in `public/`. ## Edit a text 1. Double-click a text on the canvas. It becomes editable in place, with its words selected. 2. Type. 3. Press `Enter`, or click somewhere else, to save. `Esc` cancels. midcode says where it went: "Saved to src/app/page.tsx". Your dev server reloads the text from the file, so what you see afterward is the code. You can also select the element and edit the "Text" field in the right panel. It writes the same way. The text is plain: no bold, no links, no line breaks. A line break typed with `⇧Enter` is saved as a space. ### Which elements can be edited in place An element whose content is only text. `

Design in code

` is. A paragraph with an element inside is not one text: ```tsx

Free for 3 days, then one payment.

``` Double-click "one payment" and you edit the ``. The words around it belong to the paragraph, mixed with an element, and can't be edited in place: use [Open code](https://midcode.app/docs/editor/code.md). Double-clicking something that isn't a text does something else: on a component's instance, it opens the [component](https://midcode.app/docs/editor/components.md). ## Where the text is written midcode looks in three places, in order, and edits the first that has the text. ### In the element The usual case: the words are between the element's tags. ```diff title="src/app/page.tsx" -

Design in code

+

Design, in your code

``` Only the text changes. Indentation and line breaks around it are kept. A text written as a string in braces counts too: `{'Design in code'}`. If your new text has a character JSX would read as code (`{`, `}`, `<`, `>`), it's written as a string: `{"Save 20% when x > 3"}`. If the text you're replacing wrote its apostrophes as `'`, the new one does too. ### Where the component is used A component that renders `{children}` has no words of its own. They're written where the component is used, and that's the line midcode edits: ```diff title="src/app/page.tsx" - + ``` This works when the element on the page knows which use of the component drew it, which it does when the component passes its remaining props on to its element (``). A component that takes `children` and passes nothing else on leaves midcode with no way to tell which use wrote the words. It says the text comes from a prop, and you edit it in the code. ### In your data Sites built with an agent often keep their copy apart from the markup: an array of features in `content/home.ts`, rendered with `.map()`. The element says `{feature.title}`, and the words are somewhere else. ```tsx title="src/components/Features.tsx" import { features } from '@/content/home' export function Features() { return (
    {features.map((feature) => (
  • {feature.title}

    {feature.body}

  • ))}
) } ``` When the element has no literal text, midcode searches the project for the exact text as a string and edits it there: ```diff title="src/content/home.ts" export const features = [ - { title: 'Edits are code', body: 'Every change is written into your files.' }, + { title: 'Every edit is code', body: 'Every change is written into your files.' }, { title: 'No lock-in', body: 'Your project stays a normal repository.' }, ] ``` The message then names the data file and the line: "Saved to src/content/home.ts:2". What's searched: `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.json`, `.md`, `.mdx` and `.svelte` files, outside `node_modules`, build folders, `public/` and folders whose name starts with a dot. In code and JSON the whole string has to be the text, between its quotes. In Markdown the text can be anywhere in the file. No other kind of file is read: a text kept in a YAML or TOML file, or in a `.vue`, `.astro`, `.html` or `.php` file other than the element's own, isn't found. That covers arrays and objects in modules, JSON files, message catalogs like `messages/en.json`, and Markdown content. If the text appears once, that's the one. If it appears several times, midcode narrows it down: first to the files the element's file imports, then to the exported values it imports by name. If it still can't tell, it doesn't guess: > That text appears in 3 places (src/content/home.ts:2, src/content/pricing.ts:8, messages/en.json:14). Open the right one in your editor. For content kept as lists, the [CMS](https://midcode.app/docs/data/cms.md) shows the same data as a table. For a site in several languages, [Languages](https://midcode.app/docs/data/languages.md) edits each language's text side by side. ### What can't be edited on the canvas - **Text put together by code.** `{count} items`, a formatted date, a template string with a value in it. There's no string in your files that equals what's on screen. midcode says: "This text isn't written there: it comes from a variable or prop. Open it in your editor." - **Text that isn't in the project.** Posts from a headless CMS, rows from a database, anything fetched while the site runs. - **Text in mixed content**, as above. - **Text the dev server hasn't caught up with.** If you edit twice quickly, midcode may say "The text in the code doesn't match the preview. Wait for it to reload and try again." It checks that the code still says what the canvas showed before it writes. In a component's own canvas, a text passed in where the component is used is the page's, not the component's: "This text is passed in where the component is used (page.tsx): edit it there." To make a component's text settable per use, see [Variables](https://midcode.app/docs/editor/variables.md). ## Replace an image or a video Select an image, a video, or an element with a background image. The right panel shows it under "Image", "Video" or "Background image", with a preview and its path. 1. Click "Replace…" and choose a file. Or drop a file from Finder onto the preview in the panel ("Drop to replace"). 2. midcode copies the file into your project and points the element at it. Select an element that isn't media itself but has some inside (a hero with a background video) and the panel shows "Media" with "Contains an image", "Contains a video" or "Contains a background image". Click "Select" to go to it. ### What midcode writes The file is copied into the folder your site serves as it is: `public/`, or `static/` in SvelteKit. It goes next to the file it replaces when that one is there too, otherwise into `images/`. Then the reference is rewritten: ```diff title="src/components/Hero.tsx" -The studio at night +The studio at night ``` - The copy's name is the file's name in lowercase, with anything that isn't a letter, a digit, an underscore, a dot or a hyphen turned into a hyphen. `Hero Winter.JPG` becomes `hero-winter.jpg`. - If a different file with that name is already there, the copy gets a number: `hero-winter-1.jpg`. If the very same file is there, it's reused and nothing is copied. - The old file is left where it is. - Both parts are in [Publish](https://midcode.app/docs/publish/publish.md): the edit, and "Add public/images/hero-winter.jpg". The reference is found like a text is. First the element's own `src`, then the `src` given where its component is used (``), then the exact path as a string in the same files a text is searched in: an array of slides, a JSON file. A background image is found by its path too, including inside `url()`, and for it the search also reads `.css` files: ```diff -
+
``` ### Images imported in the code In Next.js, an image can be imported instead of referenced by path: ```tsx title="src/components/About.tsx" import portrait from './portrait.jpg' Ana, the founder ``` There's no path in the code to rewrite, so midcode replaces the file itself: your new image is copied over `portrait.jpg`. Two rules keep that safe: - The new file must have the same extension. "This image is imported as a .jpg file: pick a .jpg to replace it." - If two files in the project have that name, midcode won't choose between them. "Several files in the project are called portrait.jpg. Replace it in your editor." > [!WARNING] > Replacing an imported image overwrites the file. It shows in Publish, but `⌘Z` doesn't bring the old image back. Recover it with git. ## Alt text With an image selected, "Alt text" is under its preview. Type a description and press `Enter`. An image with no alt text shows "Missing" beside the field: screen readers and search engines read it. ```diff - +The team at the studio in Lisbon ``` An existing `alt` is changed in place. A new one is added right after the tag's name. ## Paste and drop ### Paste Copy an image anywhere (a browser, a screenshot, a design tool) or copy files in Finder, then press `⌘V` in midcode. | It goes | When | | --- | --- | | After the selection | An element in a page is selected. | | On the free canvas | Nothing is selected, or something floating is, in a Next.js App Router project. It lands in the middle of what's in view. See [The free canvas](https://midcode.app/docs/editor/free-canvas.md). | | At the end of the page | Nothing is selected, in any other project. If midcode can't tell which file the page is, it asks you to select an element first. | A copied image with no file behind it is saved as `pasted-image.png`. ### Drop Drag images, SVGs, videos or audio from Finder onto the canvas. Over a frame, a blue line shows where each would land, as when you [move an element](https://midcode.app/docs/editor/select-move-resize.md). Over the empty canvas, they float there in a Next.js App Router project, and go to the end of the page in any other. Accepted: `png`, `jpg`, `jpeg`, `gif`, `webp`, `avif`, `svg`, `mp4`, `webm`, `mov`, `mp3`, `wav`, `ogg`, `m4a`, `aac` and `flac`. Several files at once land one after the other. ### What a paste or a drop writes Each file is copied into `public/images/`, `public/videos/` or `public/audio/` (in SvelteKit, under `static/`), with the naming rules above, and an element is written for it: ```tsx Team photo