npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

visual-ai-editor

v1.6.2

Published

AI-powered visual HTML editor with selection tools, design-system enforcement, and surgical source patching. Works with any OpenAI-compatible LLM provider, including local ones like Ollama.

Downloads

1,380

Readme

visual-ai-editor

AI-powered visual HTML editor.

Select any element on the page, describe what you want to change in natural language, and the AI rewrites it — following your design system.

npm version license

Demo · Install · Quick Start · Providers · Frameworks · Design System · Saving · API · Security


Demo

demo/ is a runnable page — a fictional analytics landing page with its own DESIGN.md. It exists so you can see the design-system enforcement do something real: the page uses a 17-color palette, and the demo doc documents it, so an off-palette request gets blocked by the server rather than by the model's good intentions.

git clone https://github.com/bruno-gs-dev/visual-ai-editor
cd visual-ai-editor/demo
npx visual-ai-editor start

First run writes a .env — paste an API key into AI_API_KEY= and run it again. (Already installed the package? The demo ships in the tarball too: cd node_modules/visual-ai-editor/demo.)

[ai-editor] DESIGN.md loaded (6784 characters, 17 palette colors).
[ai-editor] server at http://localhost:3000
[visual-ai-editor] Editor no ar: http://localhost:3000
[visual-ai-editor] Toolbar injetada automaticamente em qualquer .html servido.

The browser opens with the toolbar on the left. Three things worth trying, in order:

1. Edit an element. Pick Select, click one of the feature cards, type add a warning badge saying Beta, press Apply. The card re-renders in place using the .badge.highlight class from the demo's design system — the AI reused it because DESIGN.md was in its system prompt. Hit Save and the change lands in demo/index.html as a small diff, with the original copied to .ai-editor/history/ first.

2. Hit the palette guard. Select anything and ask for change the background to pink. Pink isn't in the demo's palette, so the server rejects the response and shows a warning with an Apply anyway button. That button reuses the HTML the model already returned — overriding costs zero extra tokens.

3. Select more than one thing. Area selection drags a rectangle over the four metric tiles; Pencil lassoes freehand around a group. With several elements selected, make these use the secondary button style rewrites all of them in one request.

Ctrl+Z undoes any of it instantly and without an API call.

The demo folder also gives the design commands something real to report on:

$ npx visual-ai-editor design:lint
[visual-ai-editor] Paleta detectada (17 cores): #6366f1, #0b0f19, #131a2a, ...
[visual-ai-editor] Verificando 1 arquivo(s)...

Nenhuma cor fora da paleta encontrada. ✓

$ npx visual-ai-editor design:check
[visual-ai-editor] DESIGN.md encontrado em: .../demo/DESIGN.md

  ✓ Visão Geral          ✓ Layout e Grid
  ✓ Princípios de Design ✓ Componentes
  ✓ Paleta de Cores      ✓ Acessibilidade
  ✓ Tipografia           ✓ Regras Específicas para IA
  ✓ Espaçamentos         ✓ Exemplos de Prompts
  ✓ Bordas, Raios e Sombras

Cobertura completa — todas as 11 seções recomendadas foram encontradas.

(Two columns here for space — the CLI prints one section per line.)

CLI output is currently Portuguese-only; the in-browser UI follows <html lang> (en / pt-BR). The demo page is lang="en", so its toolbar is in English.


Features

| Feature | Description | |---------|-------------| | 🎯 Visual selection | Click, drag-to-select area, or draw a freehand lasso around elements | | 🤖 AI-powered edits | Describe changes in natural language — any OpenAI-compatible provider works | | 🎨 Design system enforcement | AI follows your DESIGN.md — deterministic palette check catches what the model misses | | 💾 Surgical saves | Patches your source file in-place with 1-line diffs; auto-backup before every save | | 🤝 AI-agent handoff | React/Vue pages export a change manifest instead of overwriting rendered output | | ⌨️ Undo / Redo | Ctrl+Z / Ctrl+Y — zero tokens, instant | | 🌐 Framework-agnostic | Works with static HTML, React, Angular, Vue, or any framework | | 🌍 EN / pt-BR UI | Auto-detected from <html lang>, or set explicitly | | 📋 DESIGN.md viewer | View your design system reference in a modal inside the editor |


Install

npm install visual-ai-editor

That's it — express, dotenv, and markdown rendering are bundled. Nothing else to install.


Quick Start

1. Start the editor

npx visual-ai-editor start

First run creates a .env file in your project root.

2. Add your API key

Open .env and paste your key:

AI_API_KEY=gsk_your_key_here

Any OpenAI-compatible provider works. The fastest way to try it:

| Provider | How to get a key | Default endpoint | |----------|------------------|------------------| | Groq (free tier) | console.groq.com | Built-in | | OpenAI | platform.openai.com | https://api.openai.com/v1/chat/completions | | Ollama (local) | ollama.com | http://localhost:11434/v1/chat/completions | | LM Studio (local) | lmstudio.ai | http://localhost:1234/v1/chat/completions |

Local providers (Ollama, LM Studio) don't need an API key — the server detects localhost endpoints automatically.

3. Run again

npx visual-ai-editor start

The server boots, the browser opens, and the editor toolbar is auto-injected into every .html page. No script tags to add, no start scripts to write.

CLI flags

| Flag | Description | |------|-------------| | --port <n> | Port to listen on (default: 3000) | | --no-inject | Serve without auto-injecting the client | | --no-open | Don't open the browser automatically |

CLI commands

| Command | Description | |---------|-------------| | start (default) | Boot the editor in the current directory | | design:init | Write DESIGN.prompt.md — a guided prompt for creating your DESIGN.md | | design:check | Report which of the 11 recommended DESIGN.md sections exist | | design:lint | Find off-palette colors across the project's CSS/HTML/JS | | agents:init | Install or update AGENTS.md (normally done automatically on install) |

AGENTS.md, delivered on install

On npm install, the package writes an AGENTS.md to your project root — a guide that explains this tool to any AI coding agent (Claude Code, Cursor, …): the API contract, how DESIGN.md enforcement works, force mode, and troubleshooting. If you already have an AGENTS.md, only our own block is appended (and updated in place on later upgrades) — your existing content is never touched. If your npm setup blocks install scripts, run npx visual-ai-editor agents:init to get the same result.


AI Providers

Any OpenAI-compatible chat-completions API works. Configure it three ways, in order of precedence: the ai option to startServer(), then environment variables, then the built-in default (Groq, llama-3.3-70b-versatile).

Via .env — no code changes:

AI_ENDPOINT=https://api.openai.com/v1/chat/completions
AI_MODEL=gpt-4o-mini
AI_API_KEY=sk-...

Via code:

startServer({
  ai: {
    endpoint: 'https://api.openai.com/v1/chat/completions',
    model: 'gpt-4o-mini',
    apiKey: process.env.OPENAI_API_KEY
  }
});

Local models (Ollama, LM Studio)

Local providers have a shorthand, so you don't type the endpoint URL:

startServer({ ai: { provider: 'ollama', model: 'llama3.2' } });   // pull it first
startServer({ ai: { provider: 'lmstudio', model: 'your-loaded-model' } });

'ollama' resolves to http://localhost:11434/v1/chat/completions, 'lmstudio' to http://localhost:1234/v1/chat/completions. An explicit endpoint always wins over the preset. No API key is required — there's nothing to authenticate against on localhost, and the server auto-detects this from the endpoint's host for any localhost/127.0.0.1 URL, preset or not. Override it either direction with requiresApiKey: true | false.

A note on model capability. The plumbing (no-auth requests, error propagation, structured-JSON parsing) works regardless of model size. Response quality doesn't. A capable model — Groq's 70B, GPT-4o-mini, or a comparable local model your hardware can run — reliably follows the full instruction set (DESIGN.md compliance, force mode, the {html}/{warn} JSON contract). A 3B local model, in testing, sometimes returned truncated or malformed JSON once a project's DESIGN.md was in the system prompt. That's a model limit, not a bug to route around: if edits come back empty or malformed on a local model, try a larger one before assuming something is broken.

Legacy environment variables

GROQ_API_KEY and GROQ_MODEL are still honored as a fallback, so setups from earlier versions keep working — there's no need to rename anything.


Usage by Framework

HTML (static pages)

The zero-config CLI handles everything — just run npx visual-ai-editor start and the toolbar appears automatically.

If you prefer manual wiring, add this before </body>:

<script type="module">
  import { init } from '/__ai-editor/ai-editor.esm.js';
  init({ apiBase: '/api' });
</script>

Or via UMD (no module):

<script src="node_modules/visual-ai-editor/dist/ai-editor.js"></script>
<script>
  AIEditor.init({ apiBase: '/api' });
</script>

React

import { useEffect } from 'react';
import AIEditor from 'visual-ai-editor';
import 'visual-ai-editor/dist/ai-editor.css';

export function AIEditorProvider({ children }) {
  useEffect(() => {
    AIEditor.init({ apiBase: '/api' });
    return () => AIEditor.destroy();
  }, []);

  return <>{children}</>;
}

Wrap your app:

function App() {
  return (
    <AIEditorProvider>
      <YourApp />
    </AIEditorProvider>
  );
}

Angular

import { Component, OnInit, OnDestroy } from '@angular/core';
import AIEditor from 'visual-ai-editor';
import 'visual-ai-editor/dist/ai-editor.css';

@Component({
  selector: 'app-root',
  template: '<router-outlet></router-outlet>'
})
export class AppComponent implements OnInit, OnDestroy {
  ngOnInit() {
    AIEditor.init({ apiBase: '/api' });
  }

  ngOnDestroy() {
    AIEditor.destroy();
  }
}

Vue

<script setup>
import { onMounted, onUnmounted } from 'vue';
import AIEditor from 'visual-ai-editor';
import 'visual-ai-editor/dist/ai-editor.css';

onMounted(() => AIEditor.init({ apiBase: '/api' }));
onUnmounted(() => AIEditor.destroy());
</script>

<template>
  <router-view />
</template>

Design System

DESIGN.md keeps the AI on-brand. It documents your colors, typography, spacing, components, and rules — and the AI is instructed to follow it on every edit.

Create your DESIGN.md

npx visual-ai-editor design:init

This writes a DESIGN.prompt.md — a guided interview you feed to any AI agent (Claude Code, Cursor, etc.) to generate your DESIGN.md.

Check coverage

npx visual-ai-editor design:check

Reports which of the 11 recommended sections exist in your DESIGN.md.

Lint off-palette colors

npx visual-ai-editor design:lint

Scans your CSS/HTML/JS for colors that aren't in DESIGN.md's palette.

How enforcement works

  1. AI-side: DESIGN.md content is injected into the LLM's system prompt on every edit
  2. Server-side: Every response is checked against the palette — if a color isn't in DESIGN.md, the edit is rejected with a warning (the computed HTML is attached so "Apply anyway" costs zero extra AI calls)
  3. Force mode: Click "Apply anyway" to override — reuses the already-computed HTML, no second API call

Try it end-to-end in demo/ — see Demo above.


Saving Edits

Save does one of two very different things, depending on the page.

Static / server-rendered HTML — surgical patches

The client sends { before, after } pairs of exactly the HTML that changed to /api/save, which locates that text in your source file and replaces just it:

  • Formatting, comments and indentation everywhere else are untouched.
  • Git diffs stay small — one line changed, not the whole file.
  • A timestamped backup goes to .ai-editor/history/ before every save (capped at the 100 most recent per file; .ai-editor/ belongs in .gitignore).
  • If a patch's before text can't be located — you hand-edited the file since the last save, for example — that save falls back to writing the full page snapshot. The status bar tells you which mode was used, so a silent full-file overwrite never surprises you.
  • Multi-page projects: the browser's location.pathname is sent automatically as page, and the server resolves it to a file inside staticDir, rejecting any path that escapes it.

Framework pages (React / Vue) — agent handoff

Writing rendered DOM back over JSX or a template would corrupt it, so nothing is written to your source. Instead the edit is appended to .ai-editor/pending-changes.md via /api/handoff — one entry per edit, with the detected source location, your instruction, and the before/after HTML.

This means clicking Save on a React page does not change your .jsx. Open that manifest with an AI coding agent (Claude Code, Cursor, …), ask it to apply the listed changes to the real source, and delete each entry as it lands.

The source location comes from React's _debugSource fiber data or Vue 3's __file metadata when available — or from a data-ai-source="path/to/File.jsx:42" attribute you add yourself.

Event listeners after an edit

Applying an AI edit (and redo) replaces the element via el.replaceWith(newEl), which drops any listener attached directly to the old element with addEventListener. The element still looks right and simply stops responding — no console error.

Frameworks re-bind on re-render, so React/Vue/Angular apps don't need to care (and framework pages go through the handoff flow above anyway). Plain <script> wiring on a static page does. Re-run it in onAfterApply, scoped to the returned elements:

init({
  apiBase: '/api',
  onAfterApply: function (elements) {
    elements.forEach(function (el) {
      if (el.matches('.chip')) el.addEventListener('click', onChipClick);
    });
  }
});

onAfterUndo is the same hook for the undo path.


Keyboard Shortcuts

| Action | Shortcut | |--------|----------| | Select element | Click | | Select area | Drag | | Lasso select | Draw freehand | | Undo | Ctrl+Z | | Redo | Ctrl+Y |


API Reference

Client (visual-ai-editor)

import AIEditor from 'visual-ai-editor';

AIEditor.init(options?);
AIEditor.destroy();
AIEditor.setTool(tool);        // 'cursor' | 'area' | 'pencil'
AIEditor.selectElements(els);  // programmatically select DOM elements

init() options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | apiBase | string | '/api' | Backend API base URL | | apiToken | string | — | Bearer token for authenticated endpoints | | cssInject | boolean | true | Auto-inject CSS into <head> | | cssUrl | string | 'dist/ai-editor.css' | Custom CSS URL when injecting | | locale | 'en' \| 'pt-BR' | auto | UI language | | maxHtmlSize | number | 60000 | Reject selections larger than this (chars) | | onAfterApply | (elements) => void | — | Callback after AI edit replaces elements | | onAfterUndo | (elements) => void | — | Callback after undo restores elements |


Server (visual-ai-editor/server)

const { startServer } = require('visual-ai-editor/server');

startServer({
  port: 3000,
  envPath: require('path').join(__dirname, '.env'),
  inject: true  // auto-inject client into served HTML
});

startServer() options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | port | number | 3000 | Port to listen on | | envPath | string | — | Path to .env file | | inject | boolean | false | Auto-inject editor into served .html pages | | staticDir | string | cwd | Directory to serve as static files | | designMdPath | string | cwd/DESIGN.md | Design system reference path | | indexHtmlPath | string | cwd/index.html | Fallback save target | | apiToken | string | — | Require bearer token on write endpoints | | allowUnsafeProduction | boolean | false | Bypass the NODE_ENV=production safety check | | silent | boolean | false | Build the app without calling .listen() — returns { app, port } to mount yourself | | maxHtmlBytes | number | 200000 | Reject /api/edit selections larger than this (413) before calling the provider | | backup | boolean | true | Write timestamped backup before saves | | locale | 'en' \| 'pt-BR' | 'en' | Server message language | | ai | object | — | { endpoint, model, apiKey, jsonMode, temperature } |

Endpoints:

| Method | Endpoint | Description | |--------|----------|-------------| | POST | /api/edit | Send HTML + instruction → { html }, { warn }, or { warn, violations, html } on a palette conflict | | GET | /api/design | Returns { md, exists, palette }palette is the colors extracted from DESIGN.md | | POST | /api/save | Applies patches surgically to the source file, falling back to writing html in full | | POST | /api/handoff | Appends a change manifest to .ai-editor/pending-changes.md for framework pages |

CSS variables

The editor's own UI reads three variables from your page, each with a fallback — set them to match your product's look, or ignore them entirely:

:root {
  --font: 'Inter', sans-serif;   /* Font family for the toolbar and panel */
  --warning: #f4b400;            /* Color of the design-system warning state */
  --lg: 16px;                    /* Toolbar distance from the left edge */
}

Security

This is a development/staging tool by default:

  • The API is unauthenticated unless you set apiToken
  • The static server serves your project directory (.git, node_modules, .env* are blocked)
  • Save backups accumulate in .ai-editor/history/ — add it to .gitignore

For production, put this behind your own auth layer (reverse proxy, VPN) and set apiToken as a second layer.

Two behaviors worth knowing before you deploy anything:

The server refuses to start in production without a token. With NODE_ENV=production and no apiToken, startServer()/buildApp() throw an explanatory error rather than silently exposing an editor. If you've already solved authentication at another layer, pass allowUnsafeProduction: true to opt out deliberately.

The client toolbar has no visibility gate. If the init() call ships in your production bundle, every visitor sees and can use the editor UI — even when the backend correctly rejects their requests. Gate init() yourself: an environment check (process.env.NODE_ENV !== 'production'), a feature flag, or an admin-only route.


License

MIT