Build with AI
Hand this page to your coding agent and it installs Paintless in your project.
Paintless is installed by the same kind of agent it feeds. This page is written for one: give your coding agent the URL and it has the detection rules, the exact packages for your stack, the snippets, and a way to check its own work.
Give this to your agent
Set up Paintless in this project by following
https://kyechan99.github.io/paintless/build-with-ai.md
Follow it exactly. Stop and ask me only where the page says to ask.Anything that can read a URL and edit files works — Claude Code, Cursor, Copilot,
aider, or your own. The .md suffix serves this page as plain Markdown; drop it
for the HTML version. Every docs page answers to both, and
llms.txt indexes all of them.
Working in a chat instead? Easy to use at the top of this page copies the raw Markdown link, or opens the page straight in ChatGPT or Claude.
Everything below is addressed to the agent.
What you are installing
Three parts, in the order they matter:
| Part | What it does | Where it goes |
|---|---|---|
| Mapper | Annotates rendered elements with data-paintless-source="file:line:column" during dev | A build plugin in the app's bundler config |
| Widget | The floating button, element picker and inspect panel | Mounted once in the app, dev only |
| Dev host | npx paintless dev — a local WebSocket server that runs an agent against the working tree and commits on approval | A terminal, not a file |
Definition of done: the human runs their dev server and npx paintless dev,
opens the app, clicks the Paintless button, and the picker labels a hovered
element with its own file:line. Anything short of that is a partial install —
say so rather than reporting success.
1. Read the project before you touch it
Establish these from the repository, not from assumption:
- Package manager — from the lockfile (
pnpm-lock.yaml,package-lock.json,yarn.lock,bun.lock). - Framework —
react,vue,svelte, or none, frompackage.jsondependencies. - Bundler —
vite,webpack, ornext, from dependencies and the config files present. - TypeScript — whether a
tsconfig.jsongoverns the app. - Monorepo — if there are workspaces, find the one package that renders in a browser. The mapper and widget go there; the dev host still runs at the repository root (see the rules below).
- The app's root component and dev entry — where a
<Paintless />would mount, and which command starts the dev server.
If nothing in this repository runs in a browser dev server, stop and tell the human: Paintless has nothing to attach to.
If Paintless is already installed, repair or upgrade what is there. Do not add a second mapper, a second widget mount, or a second config.
2. Fast path: the setup command
npm create paintless@latest -- --yesIt detects the stack you just confirmed, installs the matching packages, writes
paintless.config.ts, and prints the snippets for the bundler config and the app.
--yes accepts the detected defaults (dev mode, Claude Code agent) with no
prompts, which is what you want — you cannot answer interactive prompts.
pnpm create paintless@latest --yes/yarn create paintless --yesfor the other managers.--dir <path>to target one workspace in a monorepo.--no-installto write the config and print the steps without installing.
It does not edit the bundler config or the app source. Take the snippets it printed and apply them yourself — steps 3.2 and 3.3 are the same edits, written out. If the command cannot run at all (no network, blocked registry, a stack it does not detect), do step 3 by hand.
3. Manual path
3.1 Install the packages
| Stack | Install |
|---|---|
| React + Vite | -D @paintless/mapper-vite and @paintless/react |
| Vue + Vite | -D @paintless/mapper-vue and @paintless/vue |
| Svelte + Vite | -D @paintless/mapper-vite and @paintless/svelte |
| Next.js | -D @paintless/mapper-next and @paintless/react |
| React or Vue + webpack | -D @paintless/mapper-webpack and the matching binding |
| No bundler / plain HTML | @paintless/widget by script tag — no file:line, the fallback below still applies |
Add -D paintless too if the human wants paintless as a project script rather
than npx paintless dev each time.
3.2 Wire the mapper into the build
The mapper must run before the framework plugin — it annotates the source the framework plugin then compiles.
Vite (React or Svelte):
// vite.config.ts
import { paintlessMapper } from '@paintless/mapper-vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [paintlessMapper(), react()],
})Vite (Vue SFC):
import { paintlessVueMapper } from '@paintless/mapper-vue'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [paintlessVueMapper(), vue()],
})Next.js:
// next.config.mjs
import { withPaintless } from '@paintless/mapper-next'
export default withPaintless({
// the project's existing config, unchanged
})webpack:
module: {
rules: [
{
test: /\.[jt]sx$/,
exclude: /node_modules/,
enforce: 'pre',
use: ['@paintless/mapper-webpack'],
},
],
}Mappers annotate the dev server only by default. Leave it that way: { build: true }
publishes your source layout to anyone who opens devtools on the deployed site,
and it is only worth it in prod mode, which the human has to choose deliberately.
3.3 Mount the widget, dev only
React:
import { Paintless } from '@paintless/react'
export function App() {
return (
<>
{/* the app */}
{import.meta.env.DEV && <Paintless />}
</>
)
}Use process.env.NODE_ENV !== 'production' where there is no import.meta.env
(Next.js, webpack). Vue is <Paintless /> from @paintless/vue, Svelte is
use:paintless from @paintless/svelte, and a plain page can load
@paintless/widget's IIFE build with a script tag. The gate is the point: mount
nothing in the production bundle unless the human is setting up prod mode.
3.4 The config file, only if you need it
paintless dev runs with no config: the default agent is Claude Code against the
project root. Write paintless.config.ts when the human wants a different agent:
import { command, defineConfig } from 'paintless'
export default defineConfig({
agents: { default: command('aider --message {prompt} --yes') },
})Destinations and routes belong to prod mode. Do not add them here — point the human at Self-Hosting instead.
4. Verify
- Start the app's dev server the way the project already does.
- Start the dev host in a separate long-running process:
npx paintless dev. It does not exit; do not wait on it and do not run it in a blocking step. - Confirm it prints
[paintless] dev host listening on ws://127.0.0.1:7327. - Load the app and confirm the floating Paintless button is mounted.
- Open the picker and hover an element. The label must read a real path such as
<button> · src/App.tsx:24. "no source mapper on this page" means step 3.2 did not take effect — fix it before reporting done.
If you can drive a browser, do steps 4 and 5 yourself. If you cannot, say so explicitly and give the human the two commands and what they should see.
5. Report back, then stop
Tell the human:
- every file you changed, and the one-line reason for each;
- the two commands that start the loop;
- that the first agent run needs an authenticated Claude Code install or
ANTHROPIC_API_KEYin the environment; - what you deliberately did not do — production mode, a self-hosted server, delivery destinations, and anything the rules below stopped you from touching.
Rules
- Never write a secret into a tracked file. API keys and tokens go in
.env, which must be gitignored. If a step seems to need one, ask instead. - Do not commit unless the human asked you to. Leave the working tree reviewable.
- Do not point the dev host above the repository.
--rootis the boundary of everything the agent can edit; the default (the current directory) is correct for a single app, and the repository root is correct for a monorepo. - Do not bind the dev host to a public interface. The socket is
unauthenticated.
--hostexists, and changing it exposes the repository to that network. - Do not change what the app already does. Every edit here is additive: a plugin, a mounted component, a config file.
- Do not turn on
build: true, prod mode, or a destination on your own judgment. Those publish source paths or send data outward and are the human's call.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Picker says "no source mapper on this page" | Mapper missing, after the framework plugin, or you are looking at a production build | Re-check 3.2; the mapper goes first, and dev-serve only annotates the dev server |
| Widget cannot reach a dev host | paintless dev not running, or a port change on one side only | Start it; if --port changed, pass the same URL to the widget's host option |
| Connection refused from a phone or another machine | The dev host only accepts localhost origins | paintless dev --allow-origin http://192.168.1.20:5173 |
Next.js with Turbopack shows no file:line | Turbopack builds are not annotated yet | Nothing to fix — requests still resolve through the fallback below |
| Elements resolve to the wrong file in a monorepo | Dev host rooted inside one package | Run it at the repository root so the agent can edit shared packages |
Source mapping is an accelerator, never a requirement: without annotations a request still carries the selector path, component trail, element HTML and computed styles, and the agent finds the code by searching the repository.
Where to go next
- Getting Started — the same setup, for a human.
- Configuration — agents, destinations, routes.
- Widget & Bindings — every mounting option, theming,
trigger: 'manual'. - Source Mapping — what each bundler can annotate.
- Security — what the dev host and the agent are allowed to touch.
- Self-Hosting — production requests, approvals and PRs.
