Skip to content

Sparrow-RMS/vite-plugin-react-ai-bridge

 
 

Repository files navigation

vite-plugin-react-ai-bridge

Custom Vite plugin that allows you to edit React components directly from your browser. Click an element, type a prompt, and update your code in real-time.

demo.mp4

✨ Why Custom Plugin?

  • Low Token Usage - You only pay for the context you want
  • Less Hellucination - AI models often hallucinate when overwhelmed by a massive context window.
  • Context Switching - You dont have to manually find the file and line number in your IDE to start and edit

🚀 Getting Started

  1. Install your favourite agent CLI. (Cursor , gemini , codex).
  2. Ensure that you have the neccasary vite and react plugin installed
npm install -D @vitejs/plugin-react
  1. Configure vite.config.ts.

    Part A: source metadata injection (Babel)

    This plugin extracts the original file path and line number for each component.

    // 1. Metadata Plugin: Injects data-source-file and data-source-line
     const metadataPlugin = react({
     babel: {
       plugins: [
         function (babel) {
           const { types: t } = babel;
           return {
             visitor: {
               JSXOpeningElement(path, state) {
                 if (path.node.loc) {
                   const { filename } = state.file.opts;
                   const { line, column } = path.node.loc.start;
    
                   const addAttr = (name, value) => {
                   if (!path.node.attributes.some(attr => attr.name?.name === name)) {
                     path.node.attributes.push(
                       t.jsxAttribute(t.jsxIdentifier(name), t.stringLiteral(String(value)))
                     );
                   }
                 };
    
                 addAttr("data-source-file", filename);
                 addAttr("data-source-line", line);
                 addAttr("data-source-column", column);
               }
             },
           },
         };
       },
     ],
     },
     });

    Part B: The CLI Bridge (Vite Server (Using Cursor CLI here))

    import { exec, spawn } from 'node:child_process';
    
    // 2. Bridge Plugin: Connects Browser to your AI CLI
    const cliBridgePlugin = {
      name: 'cli-bridge',
    configureServer(server) {
    server.middlewares.use((req, res, next) => {
     if (req.url === "/__ai-cli" && req.method === "POST") {
       let body = "";
       req.on("data", (chunk) => { body += chunk.toString(); });
       req.on("end", () => {
         try {
           const { prompt, file, line, elementType } = JSON.parse(body);
    
           // Normalize path for WSL(you dont need this if you are in Mac , linux)
           const normalizedPath = file.replace(/\\/g, "/");
           exec(`wsl wslpath -u "${normalizedPath}"`, (err, wslPath) => {
             if (err) return res.end(JSON.stringify({ status: "error" }));
    
             const unixPath = wslPath.trim();
             const safePrompt = prompt.replace(/'/g, "'\\''");
             // This can be different for different clis
             const commandArgs = ["bash", "-lc", `agent -p --force '${safePrompt} for ${elementType} @${unixPath} on line ${line}' --model auto`]; 
    
             // Execute command and stream output to your main terminal
             const child = spawn("wsl", commandArgs, { stdio: "inherit" });
             
             child.on("close", (code) => {
               res.statusCode = code === 0 ? 200 : 500;
               res.end(JSON.stringify({ status: code === 0 ? "success" : "error" }));
             });
           });
         } catch (e) {
           res.statusCode = 400;
           res.end("Invalid JSON");
         }
       });
     } else {
       next();
     }
    });
    },
    };
  2. Create a file named Inspector.tsx and paste this.

    // Inspector.tsx (Minimal Implementation)
     import React, { useState, useCallback } from "react";
    
     const InspectorBridge = () => {
     const [isActive, setIsActive] = useState(false);
     const [selectedElement, setSelectedElement] = useState(null);
    
     // 1. Capture the element's source metadata on click
     const handleClick = useCallback((e) => {
         if (!isActive) return;
         const target = e.target.closest("[data-source-file]");
         if (target) {
             setSelectedElement({
                 file: target.getAttribute("data-source-file"),
                 line: target.getAttribute("data-source-line"),
             });
         }
     }, [isActive]);
    
     // 2. Send the prompt and context to the Vite server
     const sendToAI = async (prompt) => {
         await fetch("/__ai-bridge", {
             method: "POST",
             body: JSON.stringify({ prompt, ...selectedElement, tool: 'cursor' }),
         });
     };
    
     return (
         <div onClickCapture={handleClick}>
             {/* [YOUR_UI_COMPONENTS_HERE] 
                 Render your toggle button and prompt input form here.
             */}
         </div>
     );
     };
  3. Import this file to main.tsx and make sure its available only during development mode.

    {import.meta.env.DEV && <Inspector />}

    Personally i would recommend avoid using gemini-cli as the startup time is very high. Refer this

🛠️ Troubleshooting

The AI CLI command isn't firing
  • Ensure you have WSL installed and configured.
  • Check if agent is in your Linux $PATH.
Missing data-source attributes
  • Make sure Part A of the config (Babel plugin) is active.
  • Restart your Vite dev server to clear the cache.

About

Directly edit React components from browser using your favourite AI CLI(gemini , cursor , codex etc).

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 86.2%
  • CSS 8.8%
  • JavaScript 3.1%
  • HTML 1.9%