vite-plugin-ai-annotator 1.14.3 → 1.14.5

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
@@ -28,6 +28,7 @@ Works with all Vite-supported frameworks:
28
28
  - 🅰️ **Angular** - Recognizes components and directives
29
29
  - 🟠 **Svelte** - Identifies components and stores
30
30
  - 📄 **Vanilla JS** - Works with plain HTML/CSS/JS
31
+ - 🟦 **Nuxt.js** - Full support via Nuxt module (Nuxt 3+)
31
32
 
32
33
  ## Installation
33
34
 
@@ -44,13 +45,15 @@ Then ask Claude: *"Set up ai-annotator for my project"* - it handles the rest!
44
45
 
45
46
  ### Option 2: Manual Setup
46
47
 
47
- #### Step 1: Install the package
48
+ #### For Vite Projects
49
+
50
+ ##### Step 1: Install the package
48
51
 
49
52
  ```bash
50
53
  bun add -d vite-plugin-ai-annotator
51
54
  ```
52
55
 
53
- #### Step 2: Add to your Vite config
56
+ ##### Step 2: Add to your Vite config
54
57
 
55
58
  ```typescript
56
59
  import { defineConfig } from 'vite';
@@ -63,7 +66,33 @@ export default defineConfig({
63
66
  });
64
67
  ```
65
68
 
66
- #### Step 3: Configure MCP
69
+ #### For Nuxt.js Projects (Nuxt 3+)
70
+
71
+ ##### Step 1: Install the package
72
+
73
+ ```bash
74
+ bun add -d vite-plugin-ai-annotator
75
+ ```
76
+
77
+ ##### Step 2: Add to your `nuxt.config.ts`
78
+
79
+ ```typescript
80
+ export default defineNuxtConfig({
81
+ modules: [
82
+ 'vite-plugin-ai-annotator/nuxt'
83
+ ],
84
+ // Optional: Configure the annotator
85
+ aiAnnotator: {
86
+ port: 7318,
87
+ autoSetupMcp: true,
88
+ verbose: false,
89
+ }
90
+ })
91
+ ```
92
+
93
+ **That's it!** Nuxt handles the rest automatically.
94
+
95
+ #### Configure MCP (Vite and Nuxt)
67
96
 
68
97
  **Option A: Auto Setup (Recommended)**
69
98
 
@@ -75,6 +104,17 @@ annotator({
75
104
  })
76
105
  ```
77
106
 
107
+ For Nuxt, configure in `nuxt.config.ts`:
108
+
109
+ ```typescript
110
+ export default defineNuxtConfig({
111
+ modules: ['vite-plugin-ai-annotator/nuxt'],
112
+ aiAnnotator: {
113
+ autoSetupMcp: true,
114
+ }
115
+ })
116
+ ```
117
+
78
118
  This automatically creates/updates `.mcp.json`, `.cursor/mcp.json`, and `.vscode/mcp.json` based on your project.
79
119
 
80
120
  **Option B: Manual Setup**
@@ -83,7 +123,7 @@ This automatically creates/updates `.mcp.json`, `.cursor/mcp.json`, and `.vscode
83
123
  claude mcp add annotator -- npx vite-plugin-ai-annotator mcp -s http://localhost:7318
84
124
  ```
85
125
 
86
- #### Step 4: Start your dev server
126
+ #### Step 3: Start your dev server
87
127
 
88
128
  ```bash
89
129
  bun dev
@@ -22,6 +22,7 @@ export declare class AnnotatorToolbar extends LitElement {
22
22
  private tooltipCleanup;
23
23
  private toastTimeout;
24
24
  private clickListenerTimeout;
25
+ private skipNextClickOutside;
25
26
  private socket;
26
27
  private rpc;
27
28
  private selectionManager;
@@ -6389,10 +6389,15 @@
6389
6389
  // src/annotator/constants.ts
6390
6390
  var Z_INDEX = {
6391
6391
  INSPECTION_OVERLAY: 999995,
6392
+ // Mouse inspection hover highlight
6392
6393
  HIGHLIGHT_OVERLAY: 999996,
6394
+ // Selected element marching ants border
6393
6395
  HOVER_OVERLAY: 999997,
6396
+ // Element hover during inspection mode
6394
6397
  BADGE: 999998,
6398
+ // Selection badge with index number
6395
6399
  TOOLBAR: 999999
6400
+ // Bottom-right toolbar UI
6396
6401
  };
6397
6402
  var CONSOLE_LIMITS = {
6398
6403
  MAX_ARG_LENGTH: 1e4,
@@ -7216,14 +7221,17 @@
7216
7221
  let vnode = null;
7217
7222
  if (!codeLocation) {
7218
7223
  const ctxVNode = el.__vnode?.ctx?.vnode;
7219
- if (ctxVNode?.el === el) {
7220
- codeLocation = ctxVNode?.props?.__v_inspector;
7224
+ if (ctxVNode && ctxVNode.el === element) {
7225
+ codeLocation = ctxVNode.props?.__v_inspector;
7221
7226
  }
7222
7227
  }
7223
7228
  if (!codeLocation) {
7224
- codeLocation = el.__vueParentComponent?.vnode?.props?.__v_inspector;
7225
- vueInstance = el.__vueParentComponent ?? null;
7226
- vnode = el.__vueParentComponent?.vnode ?? null;
7229
+ const parentComponent = el.__vueParentComponent;
7230
+ if (parentComponent?.vnode) {
7231
+ codeLocation = parentComponent.vnode.props?.__v_inspector;
7232
+ vueInstance = parentComponent;
7233
+ vnode = parentComponent.vnode;
7234
+ }
7227
7235
  }
7228
7236
  if (!codeLocation) {
7229
7237
  codeLocation = el.__v_inspector;
@@ -7250,6 +7258,28 @@
7250
7258
  componentLocation: codeLocation,
7251
7259
  framework: "vue"
7252
7260
  };
7261
+ const match = codeLocation.match(/^(.+\.vue):(\d+):(\d+)$/);
7262
+ if (match) {
7263
+ const [, file, line, column] = match;
7264
+ const componentName = file.split("/").pop()?.replace(".vue", "") || "Anonymous";
7265
+ componentInfo.componentName = componentName;
7266
+ componentInfo.elementLocation = {
7267
+ file,
7268
+ line: parseInt(line, 10),
7269
+ column: parseInt(column, 10)
7270
+ };
7271
+ componentInfo.sourceMap = {
7272
+ originalLine: parseInt(line, 10),
7273
+ originalColumn: parseInt(column, 10),
7274
+ originalSource: file
7275
+ };
7276
+ } else {
7277
+ const pathParts = codeLocation.split("/");
7278
+ const lastPart = pathParts[pathParts.length - 1];
7279
+ if (lastPart) {
7280
+ componentInfo.componentName = lastPart.replace(".vue", "").split("@")[0];
7281
+ }
7282
+ }
7253
7283
  const elementLocationInfo = extractVueElementLocation(element, vnode);
7254
7284
  if (elementLocationInfo) {
7255
7285
  Object.assign(componentInfo, elementLocationInfo);
@@ -7516,6 +7546,7 @@
7516
7546
  this.tooltipCleanup = null;
7517
7547
  this.toastTimeout = null;
7518
7548
  this.clickListenerTimeout = null;
7549
+ this.skipNextClickOutside = false;
7519
7550
  this.socket = null;
7520
7551
  this.rpc = null;
7521
7552
  this.selectionManager = null;
@@ -7533,6 +7564,10 @@
7533
7564
  };
7534
7565
  this.handleClickOutside = (e5) => {
7535
7566
  if (!this.commentPopover.visible) return;
7567
+ if (this.skipNextClickOutside) {
7568
+ this.skipNextClickOutside = false;
7569
+ return;
7570
+ }
7536
7571
  const popoverEl = this.shadowRoot?.querySelector(".popover");
7537
7572
  if (!popoverEl) return;
7538
7573
  const path = e5.composedPath();
@@ -7566,29 +7601,36 @@
7566
7601
  }
7567
7602
  initializeConsoleCapture() {
7568
7603
  try {
7604
+ const originals = {};
7569
7605
  CONSOLE_METHODS.forEach((method) => {
7570
- this.originalConsoleMethods[method] = console[method].bind(console);
7606
+ originals[method] = console[method].bind(console);
7571
7607
  });
7572
7608
  CONSOLE_METHODS.forEach((method) => {
7573
7609
  console[method] = (...args) => {
7574
- this.consoleBuffer.push({
7575
- type: method,
7576
- args: args.map((arg) => {
7610
+ try {
7611
+ const serializedArgs = args.map((arg) => {
7577
7612
  try {
7578
7613
  const str = typeof arg === "object" ? JSON.stringify(arg) : String(arg);
7579
7614
  return str.length > CONSOLE_LIMITS.MAX_ARG_LENGTH ? str.slice(0, CONSOLE_LIMITS.MAX_ARG_LENGTH) + "...[truncated]" : str;
7580
7615
  } catch {
7581
7616
  return "[circular or unserializable]";
7582
7617
  }
7583
- }),
7584
- timestamp: Date.now()
7585
- });
7586
- if (this.consoleBuffer.length > CONSOLE_LIMITS.BUFFER_MAX) {
7587
- this.consoleBuffer = this.consoleBuffer.slice(-CONSOLE_LIMITS.BUFFER_TRIM_TO);
7618
+ });
7619
+ this.consoleBuffer.push({
7620
+ type: method,
7621
+ args: serializedArgs,
7622
+ timestamp: Date.now()
7623
+ });
7624
+ if (this.consoleBuffer.length > CONSOLE_LIMITS.BUFFER_MAX) {
7625
+ this.consoleBuffer = this.consoleBuffer.slice(-CONSOLE_LIMITS.BUFFER_TRIM_TO);
7626
+ }
7627
+ originals[method]?.(...args);
7628
+ } catch (captureError) {
7629
+ originals[method]?.(...args);
7588
7630
  }
7589
- this.originalConsoleMethods[method]?.(...args);
7590
7631
  };
7591
7632
  });
7633
+ this.originalConsoleMethods = originals;
7592
7634
  } catch (error) {
7593
7635
  this.restoreConsoleMethods();
7594
7636
  throw error;
@@ -7630,19 +7672,71 @@
7630
7672
  }
7631
7673
  registerRpcHandlers() {
7632
7674
  if (!this.rpc) return;
7633
- this.rpc.handle.getPageContext(async () => this.getPageContext());
7634
- this.rpc.handle.getSelectedElements(async () => this.getSelectedElements());
7635
- this.rpc.handle.triggerSelection(
7636
- async (mode, selector, selectorType) => this.triggerSelection(mode, selector, selectorType)
7637
- );
7638
- this.rpc.handle.captureScreenshot(
7639
- async (type, selector, quality) => this.captureScreenshot(type, selector, quality)
7640
- );
7641
- this.rpc.handle.clearSelection(async () => this.clearSelection());
7675
+ this.rpc.handle.getPageContext(async () => {
7676
+ try {
7677
+ return await this.getPageContext();
7678
+ } catch (error) {
7679
+ this.log("[getPageContext] Handler error:", error);
7680
+ throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
7681
+ }
7682
+ });
7683
+ this.rpc.handle.getSelectedElements(async () => {
7684
+ try {
7685
+ return await this.getSelectedElements();
7686
+ } catch (error) {
7687
+ this.log("[getSelectedElements] Handler error:", error);
7688
+ throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
7689
+ }
7690
+ });
7691
+ this.rpc.handle.triggerSelection(async (mode, selector, selectorType) => {
7692
+ try {
7693
+ return await this.triggerSelection(mode, selector, selectorType);
7694
+ } catch (error) {
7695
+ this.log("[triggerSelection] Handler error:", error);
7696
+ throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
7697
+ }
7698
+ });
7699
+ this.rpc.handle.captureScreenshot(async (type, selector, quality) => {
7700
+ try {
7701
+ return await this.captureScreenshot(type, selector, quality);
7702
+ } catch (error) {
7703
+ this.log("[captureScreenshot] Handler error:", error);
7704
+ throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
7705
+ }
7706
+ });
7707
+ this.rpc.handle.clearSelection(async () => {
7708
+ try {
7709
+ return await this.clearSelection();
7710
+ } catch (error) {
7711
+ this.log("[clearSelection] Handler error:", error);
7712
+ throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
7713
+ }
7714
+ });
7642
7715
  this.rpc.handle.ping(async () => "pong");
7643
- this.rpc.handle.injectCSS(async (css) => this.injectCSS(css));
7644
- this.rpc.handle.injectJS(async (code) => this.injectJS(code));
7645
- this.rpc.handle.getConsole(async (clear) => this.getConsoleLogs(clear));
7716
+ this.rpc.handle.injectCSS(async (css) => {
7717
+ try {
7718
+ return await this.injectCSS(css);
7719
+ } catch (error) {
7720
+ this.log("[injectCSS] Handler error:", error);
7721
+ throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
7722
+ }
7723
+ });
7724
+ this.rpc.handle.injectJS(async (code) => {
7725
+ try {
7726
+ return await this.injectJS(code);
7727
+ } catch (error) {
7728
+ this.log("[injectJS] Handler error:", error);
7729
+ throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
7730
+ }
7731
+ });
7732
+ this.rpc.handle.getConsole(async (clear) => {
7733
+ try {
7734
+ return await this.getConsoleLogs(clear);
7735
+ } catch (error) {
7736
+ this.log("[getConsole] Handler error:", error);
7737
+ throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
7738
+ }
7739
+ });
7646
7740
  }
7647
7741
  reportPageContext() {
7648
7742
  if (!this.socket?.connected) return;
@@ -7744,10 +7838,12 @@
7744
7838
  cacheBust: true,
7745
7839
  pixelRatio: 1,
7746
7840
  includeQueryParams: true,
7747
- // Filter out elements that cause issues
7841
+ // Filter out all annotator UI elements
7748
7842
  filter: (node) => {
7749
7843
  if (node instanceof Element) {
7750
- if (node.classList?.contains("annotator-badge") || node.classList?.contains("annotator-highlight-overlay") || node.tagName?.toLowerCase() === "annotator-toolbar") {
7844
+ const className = node.className || "";
7845
+ const tagName = node.tagName?.toLowerCase() || "";
7846
+ if (className.includes("annotator-") || tagName === "annotator-toolbar") {
7751
7847
  return false;
7752
7848
  }
7753
7849
  }
@@ -7878,6 +7974,7 @@
7878
7974
  if (this.clickListenerTimeout) {
7879
7975
  clearTimeout(this.clickListenerTimeout);
7880
7976
  }
7977
+ this.skipNextClickOutside = true;
7881
7978
  this.clickListenerTimeout = setTimeout(() => {
7882
7979
  document.addEventListener("click", this.handleClickOutside, true);
7883
7980
  this.clickListenerTimeout = null;
@@ -7931,6 +8028,9 @@
7931
8028
  this.popoverCleanup();
7932
8029
  this.popoverCleanup = null;
7933
8030
  }
8031
+ const popoverEl = this.shadowRoot?.querySelector(".popover");
8032
+ if (popoverEl) {
8033
+ }
7934
8034
  this.commentPopover = { visible: false, element: null, comment: "" };
7935
8035
  }
7936
8036
  handlePopoverInput(e5) {
@@ -8136,7 +8236,7 @@
8136
8236
  render() {
8137
8237
  if (!this.connected) {
8138
8238
  return x`
8139
- <div class="toolbar">
8239
+ <div class="toolbar" role="alert" aria-live="polite">
8140
8240
  <div class="error-message">
8141
8241
  ${this.renderErrorIcon()}
8142
8242
  <span>Cannot connect to AI Annotator server</span>
@@ -8145,12 +8245,15 @@
8145
8245
  `;
8146
8246
  }
8147
8247
  return x`
8148
- <div class="toolbar">
8248
+ <div class="toolbar" role="toolbar" aria-label="AI Annotator tools">
8149
8249
  <button
8150
8250
  class="toolbar-btn ${this.isInspecting ? "active" : ""}"
8151
8251
  @click=${this.toggleInspect}
8152
8252
  @mouseenter=${(e5) => this.showTooltip(this.isInspecting ? "Press ESC to exit" : "Inspect elements", e5.currentTarget)}
8153
8253
  @mouseleave=${() => this.hideTooltip()}
8254
+ aria-label="${this.isInspecting ? "Exit inspection mode" : "Inspect elements"}"
8255
+ aria-pressed="${this.isInspecting}"
8256
+ title="${this.isInspecting ? "Press ESC to exit" : "Inspect elements"}"
8154
8257
  >
8155
8258
  ${this.renderCursorIcon()}
8156
8259
  </button>
@@ -8162,19 +8265,24 @@
8162
8265
  @mouseenter=${(e5) => this.showTooltip("Clear selections", e5.currentTarget)}
8163
8266
  @mouseleave=${() => this.hideTooltip()}
8164
8267
  ?disabled=${this.selectionCount === 0}
8268
+ aria-label="Clear all selections"
8269
+ aria-disabled="${this.selectionCount === 0}"
8270
+ title="Clear selections"
8165
8271
  >
8166
8272
  ${this.renderTrashIcon()}
8167
8273
  </button>
8168
- ${this.selectionCount > 0 ? x`<span class="badge">${this.selectionCount}</span>` : ""}
8274
+ ${this.selectionCount > 0 ? x`<span class="badge" aria-label="${this.selectionCount} selections">${this.selectionCount}</span>` : ""}
8169
8275
  </div>
8170
8276
 
8171
- <div class="divider"></div>
8277
+ <div class="divider" role="separator" aria-orientation="vertical"></div>
8172
8278
 
8173
8279
  <button
8174
8280
  class="toolbar-btn"
8175
8281
  @click=${this.copySessionId}
8176
8282
  @mouseenter=${(e5) => this.showTooltip("Copy session", e5.currentTarget)}
8177
8283
  @mouseleave=${() => this.hideTooltip()}
8284
+ aria-label="Copy session ID to clipboard"
8285
+ title="Copy session"
8178
8286
  >
8179
8287
  ${this.renderClipboardIcon()}
8180
8288
  </button>
@@ -8184,17 +8292,19 @@
8184
8292
  @click=${this.openHelpPage}
8185
8293
  @mouseenter=${(e5) => this.showTooltip("Help", e5.currentTarget)}
8186
8294
  @mouseleave=${() => this.hideTooltip()}
8295
+ aria-label="Open help documentation"
8296
+ title="Help"
8187
8297
  >
8188
8298
  ${this.renderHelpIcon()}
8189
8299
  </button>
8190
8300
 
8191
- ${this.toastMessage ? x`<div class="toast">${this.toastMessage}</div>` : ""}
8301
+ ${this.toastMessage ? x`<div class="toast" role="status" aria-live="polite">${this.toastMessage}</div>` : ""}
8192
8302
  </div>
8193
8303
 
8194
- ${this.tooltip.visible ? x`<div class="tooltip">${this.tooltip.text}</div>` : ""}
8304
+ ${this.tooltip.visible ? x`<div class="tooltip" role="tooltip">${this.tooltip.text}</div>` : ""}
8195
8305
 
8196
8306
  ${this.commentPopover.visible ? x`
8197
- <div class="popover">
8307
+ <div class="popover" role="dialog" aria-label="Element comment" aria-modal="false">
8198
8308
  <textarea
8199
8309
  class="popover-input"
8200
8310
  placeholder="Add a note... (↵ to close)"
@@ -8202,12 +8312,13 @@
8202
8312
  @input=${this.handlePopoverInput}
8203
8313
  @keydown=${this.handlePopoverInputKeydown}
8204
8314
  rows="1"
8315
+ aria-label="Add a comment for the selected element"
8205
8316
  ></textarea>
8206
- <div class="popover-actions">
8207
- <button class="popover-btn danger" @click=${this.removeSelectedElement} title="Remove selection">
8317
+ <div class="popover-actions" role="group" aria-label="Comment actions">
8318
+ <button class="popover-btn danger" @click=${this.removeSelectedElement} title="Remove selection" aria-label="Remove this selection">
8208
8319
  ${this.renderTrashIcon()}
8209
8320
  </button>
8210
- <button class="popover-btn" @click=${this.hideCommentPopover} title="Close (Esc)">
8321
+ <button class="popover-btn" @click=${this.hideCommentPopover} title="Close (Esc)" aria-label="Close comment">
8211
8322
  ${this.renderCloseIcon()}
8212
8323
  </button>
8213
8324
  </div>
@@ -0,0 +1,5 @@
1
+ import { type AiAnnotatorOptions } from './vite-plugin';
2
+ export interface NuxtAiAnnotatorOptions extends AiAnnotatorOptions {
3
+ }
4
+ declare const _default: import("@nuxt/schema").NuxtModule<NuxtAiAnnotatorOptions, NuxtAiAnnotatorOptions, false>;
5
+ export default _default;