vite-plugin-ai-annotator 1.14.16 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -92,81 +92,54 @@ export default defineNuxtConfig({
92
92
 
93
93
  **That's it!** Nuxt handles the rest automatically.
94
94
 
95
- #### Configure MCP (Vite and Nuxt)
96
-
97
- **Option A: Auto Setup (Recommended)**
98
-
99
- Enable automatic MCP configuration in your Vite config:
95
+ #### Step 3: Start your dev server
100
96
 
101
- ```typescript
102
- annotator({
103
- autoSetupMcp: true,
104
- })
97
+ ```bash
98
+ bun dev
105
99
  ```
106
100
 
107
- For Nuxt, configure in `nuxt.config.ts`:
101
+ The annotator toolbar will automatically appear in your application. The plugin auto-writes a skill file (`.claude/skills/ai-annotator/SKILL.md`) so Claude Code knows how to read your feedback over the REST API on every restart.
108
102
 
109
- ```typescript
110
- export default defineNuxtConfig({
111
- modules: ['vite-plugin-ai-annotator/nuxt'],
112
- aiAnnotator: {
113
- autoSetupMcp: true,
114
- }
115
- })
116
- ```
103
+ ## Usage
117
104
 
118
- This automatically creates/updates `.mcp.json`, `.cursor/mcp.json`, and `.vscode/mcp.json` based on your project.
105
+ ### With the channel plugin (push, recommended)
119
106
 
120
- **Option B: Manual Setup**
107
+ The Vite plugin prints these install commands on dev-server start when the channel plugin isn't enabled — copy/paste once:
121
108
 
122
109
  ```bash
123
- claude mcp add annotator -- npx vite-plugin-ai-annotator mcp -s http://localhost:7318
110
+ /plugin marketplace add nguyenvanduocit/claude-annotator-plugin
111
+ /plugin install claude-annotator-plugin@claude-annotator-plugin
112
+ # Restart with the channel flag (Claude Code v2.1.80+; research preview)
113
+ claude --dangerously-load-development-channels plugin:claude-annotator-plugin@claude-annotator-plugin
124
114
  ```
125
115
 
126
- #### Step 3: Start your dev server
127
-
128
- ```bash
129
- bun dev
130
- ```
116
+ After that the hint stays silent (the Vite plugin reads `~/.claude/settings.json` to detect it; it never writes to that file). Set `autoSetupChannelPlugin: false` if you want to silence the hint without installing.
131
117
 
132
- The annotator toolbar will automatically appear in your application.
118
+ 1. Click the **inspect** button on the toolbar to enter feedback mode
119
+ 2. Click any element(s) you want to give feedback on, type a comment
120
+ 3. Click **send** — Claude Code's session immediately receives a `<channel source="ai-annotator">` event with your `session_id`, `page_url`, and `count`
121
+ 4. Claude fetches the details from the REST API, edits the source files, and pushes a toast back to your toolbar with progress
133
122
 
134
- ## Usage
123
+ ### Without the channel plugin (pull)
135
124
 
136
- 1. Click the **inspect button** on the toolbar to enter feedback mode
137
- 2. Click on any element(s) you want to provide feedback on
138
- 3. Ask Claude Code to modify them - it will use `annotator_get_feedback` to get the selected feedback with their source locations
139
- 4. Claude modifies the source code directly
125
+ Same first three steps. Then ask Claude Code to apply your feedback — it follows the auto-installed skill, calls `GET /api/sessions/<id>/feedback`, edits the files, and `DELETE`s the feedback when done.
140
126
 
141
- Example prompt: *"Make the selected button larger and change its color to blue"*
127
+ Example prompt: *"Apply the feedback I just left in the browser."*
142
128
 
143
129
  ## Configuration
144
130
 
145
131
  ```typescript
146
132
  annotator({
147
- port: 7318, // Server port (default: 7318)
148
- autoSetupMcp: true, // Auto-configure MCP files (default: false)
149
- autoSetupSkills: true, // Auto-write AI tool skill files (default: true)
150
- verbose: false, // Enable detailed logging (default: false)
133
+ port: 7318, // Server port (default: 7318)
134
+ autoSetupSkills: true, // Auto-write AI tool skill files (default: true)
135
+ autoSetupChannelPlugin: true, // Print one-time install hint for the Claude Code
136
+ // channel plugin if it isn't already enabled in
137
+ // ~/.claude/settings.json (default: true; read-only)
138
+ injectSourceLoc: true, // Inject data-source-loc attrs into HTML (default: true)
139
+ verbose: false, // Enable detailed logging (default: false)
151
140
  })
152
141
  ```
153
142
 
154
- ### Auto MCP Setup
155
-
156
- When `autoSetupMcp: true`, the plugin automatically:
157
-
158
- 1. **Detects your package manager** from lockfile:
159
- - `bun.lockb` / `bun.lock` → uses `bunx`
160
- - `pnpm-lock.yaml` → uses `pnpm dlx`
161
- - Otherwise → uses `npx`
162
-
163
- 2. **Creates/updates MCP config files**:
164
- - `.mcp.json` - Claude Code, Cline, Roo Code
165
- - `.cursor/mcp.json` - Cursor (only if `.cursor/` exists)
166
- - `.vscode/mcp.json` - VS Code (only if `.vscode/` exists)
167
-
168
- 3. **Preserves existing config** - merges with other MCP servers, doesn't overwrite
169
-
170
143
  ### Auto AI Skills Setup
171
144
 
172
145
  When `autoSetupSkills: true` (default), the plugin writes skill/instruction files on every dev server start with the correct server address baked in. This means AI tools automatically know how to call the REST API:
@@ -6238,14 +6238,14 @@
6238
6238
  while (currentElement && currentElement !== document.body) {
6239
6239
  let selector = currentElement.tagName.toLowerCase();
6240
6240
  if (currentElement.id && this.isUniqueId(currentElement.id)) {
6241
- selector += `#${currentElement.id}`;
6241
+ selector += `#${CSS.escape(currentElement.id)}`;
6242
6242
  parts2.unshift(selector);
6243
6243
  break;
6244
6244
  }
6245
6245
  if (currentElement.className && typeof currentElement.className === "string") {
6246
- const classes = currentElement.className.split(" ").filter((c4) => c4 && !c4.includes("css-") && !c4.includes("emotion-")).slice(0, 2);
6246
+ const classes = currentElement.className.split(/\s+/).filter((c4) => c4 && !c4.includes("css-") && !c4.includes("emotion-")).slice(0, 2);
6247
6247
  if (classes.length > 0) {
6248
- selector += "." + classes.join(".");
6248
+ selector += "." + classes.map((c4) => CSS.escape(c4)).join(".");
6249
6249
  }
6250
6250
  }
6251
6251
  const siblings = Array.from(currentElement.parentElement?.children || []).filter((sibling) => sibling.tagName === currentElement.tagName);
@@ -6344,7 +6344,7 @@
6344
6344
  const primary = this.generateElementSelector(element);
6345
6345
  const fallbacks = [];
6346
6346
  if (element.id && this.isUniqueId(element.id)) {
6347
- fallbacks.push(`#${element.id}`);
6347
+ fallbacks.push(`#${CSS.escape(element.id)}`);
6348
6348
  }
6349
6349
  if (element.getAttribute("data-testid")) {
6350
6350
  fallbacks.push(`[data-testid="${element.getAttribute("data-testid")}"]`);
@@ -7665,6 +7665,11 @@
7665
7665
  this.socket.on("connect_error", (error) => {
7666
7666
  this.log("Connection error:", error.message);
7667
7667
  });
7668
+ this.socket.on("channel:notify", (data) => {
7669
+ if (typeof data?.message !== "string") return;
7670
+ const prefix = data.status === "done" ? "\u2713 " : data.status === "error" ? "\u2717 " : data.status === "progress" ? "\u2026 " : "";
7671
+ this.showToast(prefix + data.message);
7672
+ });
7668
7673
  }
7669
7674
  wrapRpcHandler(name, handler) {
7670
7675
  return (async (...args) => {
@@ -8062,13 +8067,17 @@
8062
8067
  return;
8063
8068
  }
8064
8069
  const text = `I have selected ${elements.length} feedback item(s) in the browser (session: ${this.sessionId}). Fetch them via GET ${this.wsEndpoint}/api/sessions/${this.sessionId}/feedback and modify the code accordingly.`;
8070
+ if (this.socket?.connected) {
8071
+ this.socket.emit("feedback:submitted", { count: elements.length });
8072
+ }
8065
8073
  try {
8066
8074
  await navigator.clipboard.writeText(text);
8067
- this.showToast(`Copied ${elements.length} element(s)`);
8075
+ this.showToast(`Sent ${elements.length} element(s) to Claude`);
8068
8076
  this.exitInspectingMode();
8069
8077
  } catch (error) {
8070
- this.showToast("Failed to copy");
8071
- this.log("Failed to copy:", error);
8078
+ this.showToast(`Sent ${elements.length} element(s) to Claude`);
8079
+ this.log("Clipboard copy failed (channel push still sent):", error);
8080
+ this.exitInspectingMode();
8072
8081
  }
8073
8082
  }
8074
8083
  toggleInspect() {
@@ -8169,13 +8178,16 @@
8169
8178
  return;
8170
8179
  }
8171
8180
  const text = `I have feedback in the browser (session: ${this.sessionId}). Fetch them via GET ${this.wsEndpoint}/api/sessions/${this.sessionId}/feedback`;
8181
+ if (this.socket?.connected) {
8182
+ this.socket.emit("feedback:submitted", { count: 0 });
8183
+ }
8172
8184
  try {
8173
8185
  await navigator.clipboard.writeText(text);
8174
- this.showToast("Copied!");
8186
+ this.showToast("Sent to Claude");
8175
8187
  this.log("Copied to clipboard:", text);
8176
8188
  } catch (error) {
8177
- this.showToast("Failed to copy");
8178
- this.log("Failed to copy:", error);
8189
+ this.showToast("Sent to Claude");
8190
+ this.log("Clipboard copy failed (channel push still sent):", error);
8179
8191
  }
8180
8192
  }
8181
8193
  renderErrorIcon() {