vite-plugin-ai-annotator 1.14.8 → 1.14.10
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 +48 -3
- package/dist/annotator-toolbar.d.ts +2 -0
- package/dist/annotator-toolbar.js +34 -81
- package/dist/auto-setup-skills.d.ts +24 -0
- package/dist/index.cjs +35 -35
- package/dist/mcp-tools.d.ts +25 -0
- package/dist/nuxt-module.js +173 -29
- package/dist/nuxt-module.js.map +4 -4
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/version.d.ts +1 -0
- package/dist/utils/xpath.d.ts +0 -4
- package/dist/vite-plugin.d.ts +6 -0
- package/dist/vite-plugin.js +172 -27
- package/dist/vite-plugin.js.map +4 -4
- package/dist/ws-server.d.ts +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -144,9 +144,10 @@ Example prompt: *"Make the selected button larger and change its color to blue"*
|
|
|
144
144
|
|
|
145
145
|
```typescript
|
|
146
146
|
annotator({
|
|
147
|
-
port: 7318,
|
|
148
|
-
autoSetupMcp: true,
|
|
149
|
-
|
|
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)
|
|
150
151
|
})
|
|
151
152
|
```
|
|
152
153
|
|
|
@@ -166,4 +167,48 @@ When `autoSetupMcp: true`, the plugin automatically:
|
|
|
166
167
|
|
|
167
168
|
3. **Preserves existing config** - merges with other MCP servers, doesn't overwrite
|
|
168
169
|
|
|
170
|
+
### Auto AI Skills Setup
|
|
171
|
+
|
|
172
|
+
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:
|
|
173
|
+
|
|
174
|
+
| AI Tool | File | Format |
|
|
175
|
+
|---------|------|--------|
|
|
176
|
+
| Claude Code | `.claude/skills/ai-annotator/SKILL.md` | YAML frontmatter (`name`, `description`) |
|
|
177
|
+
| Cursor | `.cursor/rules/ai-annotator.mdc` | `alwaysApply: true` |
|
|
178
|
+
| Windsurf | `.windsurf/rules/ai-annotator.md` | `trigger: always_on` |
|
|
179
|
+
| Codex | `AGENTS.md` | Marker-delimited section |
|
|
180
|
+
| Copilot | `.github/instructions/ai-annotator.instructions.md` | `applyTo: "**"` |
|
|
181
|
+
| Cline | `.clinerules/ai-annotator.md` | Plain markdown |
|
|
182
|
+
|
|
183
|
+
Files are updated on every server restart, so the address is always correct.
|
|
184
|
+
|
|
185
|
+
## REST API
|
|
186
|
+
|
|
187
|
+
The server exposes a plain HTTP REST API at `/api/*`, usable by any HTTP client — no MCP required.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# List sessions
|
|
191
|
+
curl http://localhost:7318/api/sessions
|
|
192
|
+
|
|
193
|
+
# Get feedback
|
|
194
|
+
curl http://localhost:7318/api/sessions/<id>/feedback
|
|
195
|
+
|
|
196
|
+
# Inject JS
|
|
197
|
+
curl -X POST http://localhost:7318/api/sessions/<id>/inject-js \
|
|
198
|
+
-H 'Content-Type: application/json' \
|
|
199
|
+
-d '{"code": "document.title"}'
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
| Method | Endpoint | Body/Query | Description |
|
|
203
|
+
|--------|----------|------------|-------------|
|
|
204
|
+
| `GET` | `/api/sessions` | — | List connected browser sessions |
|
|
205
|
+
| `GET` | `/api/sessions/:id/page-context` | — | Page URL, title, selection count |
|
|
206
|
+
| `POST` | `/api/sessions/:id/select` | `{mode?, selector?, selectorType?}` | Trigger feedback selection |
|
|
207
|
+
| `GET` | `/api/sessions/:id/feedback` | `?fields=xpath,attributes,styles,children` | Get selected feedback items |
|
|
208
|
+
| `DELETE` | `/api/sessions/:id/feedback` | — | Clear all selections |
|
|
209
|
+
| `POST` | `/api/sessions/:id/screenshot` | `{type?, selector?, quality?}` | Capture screenshot |
|
|
210
|
+
| `POST` | `/api/sessions/:id/inject-css` | `{css}` | Inject CSS into page |
|
|
211
|
+
| `POST` | `/api/sessions/:id/inject-js` | `{code}` | Execute JS in page context |
|
|
212
|
+
| `GET` | `/api/sessions/:id/console` | `?clear=true` | Get captured console logs |
|
|
213
|
+
|
|
169
214
|
**Happy coding! 🚀**
|
|
@@ -11,6 +11,7 @@ import { LitElement } from 'lit';
|
|
|
11
11
|
export declare class AnnotatorToolbar extends LitElement {
|
|
12
12
|
wsEndpoint: string;
|
|
13
13
|
verbose: boolean;
|
|
14
|
+
demo: boolean;
|
|
14
15
|
private connected;
|
|
15
16
|
private sessionId;
|
|
16
17
|
private selectionCount;
|
|
@@ -37,6 +38,7 @@ export declare class AnnotatorToolbar extends LitElement {
|
|
|
37
38
|
private initializeConsoleCapture;
|
|
38
39
|
private restoreConsoleMethods;
|
|
39
40
|
private connectToServer;
|
|
41
|
+
private wrapRpcHandler;
|
|
40
42
|
private registerRpcHandlers;
|
|
41
43
|
private reportPageContext;
|
|
42
44
|
private getPageContext;
|
|
@@ -6236,12 +6236,6 @@
|
|
|
6236
6236
|
static isValidId(id) {
|
|
6237
6237
|
return Boolean(id) && /^\S.*$/.test(id) && !/[[\](){}<>]/.test(id);
|
|
6238
6238
|
}
|
|
6239
|
-
/**
|
|
6240
|
-
* Generate optimized CSS selector using optimal-select library
|
|
6241
|
-
*/
|
|
6242
|
-
static generateOptimalSelector(element) {
|
|
6243
|
-
return this.generateEnhancedCSSSelector(element);
|
|
6244
|
-
}
|
|
6245
6239
|
/**
|
|
6246
6240
|
* Generate enhanced CSS selector optimized for browser environment
|
|
6247
6241
|
*/
|
|
@@ -6324,7 +6318,7 @@
|
|
|
6324
6318
|
*/
|
|
6325
6319
|
static generateElementSelector(element) {
|
|
6326
6320
|
const xpath = this.generateXPath(element);
|
|
6327
|
-
const cssSelector = this.
|
|
6321
|
+
const cssSelector = this.generateEnhancedCSSSelector(element);
|
|
6328
6322
|
const siblings = Array.from(element.parentElement?.children || []).filter((sibling) => sibling.tagName === element.tagName);
|
|
6329
6323
|
const position = siblings.length > 1 ? {
|
|
6330
6324
|
index: siblings.indexOf(element) + 1,
|
|
@@ -6365,13 +6359,6 @@
|
|
|
6365
6359
|
if (element.getAttribute("aria-label")) {
|
|
6366
6360
|
fallbacks.push(`[aria-label="${element.getAttribute("aria-label")}"]`);
|
|
6367
6361
|
}
|
|
6368
|
-
const textContent = element.textContent?.trim();
|
|
6369
|
-
if (textContent && textContent.length > 0 && textContent.length < 50) {
|
|
6370
|
-
const elementsWithSameText = Array.from(document.querySelectorAll("*")).filter((el) => el.textContent?.trim() === textContent);
|
|
6371
|
-
if (elementsWithSameText.length === 1) {
|
|
6372
|
-
fallbacks.push(`${element.tagName.toLowerCase()}:contains("${textContent}")`);
|
|
6373
|
-
}
|
|
6374
|
-
}
|
|
6375
6362
|
let confidence = "low";
|
|
6376
6363
|
if (this.validateXPath(primary.xpath, element) && this.validateCSSSelector(primary.cssSelector, element)) {
|
|
6377
6364
|
confidence = "high";
|
|
@@ -6563,6 +6550,8 @@
|
|
|
6563
6550
|
cleanup?.();
|
|
6564
6551
|
badge.remove();
|
|
6565
6552
|
overlay.remove();
|
|
6553
|
+
selectedElements.delete(element);
|
|
6554
|
+
selectionGroups.delete(element);
|
|
6566
6555
|
return;
|
|
6567
6556
|
}
|
|
6568
6557
|
const rect = element.getBoundingClientRect();
|
|
@@ -7054,6 +7043,7 @@
|
|
|
7054
7043
|
}
|
|
7055
7044
|
return componentInfo;
|
|
7056
7045
|
}
|
|
7046
|
+
if (!element.parentElement) return null;
|
|
7057
7047
|
return findNearestComponent(element.parentElement, verbose);
|
|
7058
7048
|
} catch (e5) {
|
|
7059
7049
|
logger.error("Error finding nearest component:", e5);
|
|
@@ -7293,7 +7283,7 @@
|
|
|
7293
7283
|
const locationInfo = {};
|
|
7294
7284
|
if (vnode?.loc) {
|
|
7295
7285
|
locationInfo.elementLocation = {
|
|
7296
|
-
file:
|
|
7286
|
+
file: "",
|
|
7297
7287
|
line: vnode.loc.start?.line || 0,
|
|
7298
7288
|
column: vnode.loc.start?.column || 0,
|
|
7299
7289
|
endLine: vnode.loc.end?.line,
|
|
@@ -7537,6 +7527,7 @@
|
|
|
7537
7527
|
super(...arguments);
|
|
7538
7528
|
this.wsEndpoint = "http://localhost:7318";
|
|
7539
7529
|
this.verbose = false;
|
|
7530
|
+
this.demo = false;
|
|
7540
7531
|
this.connected = false;
|
|
7541
7532
|
this.sessionId = "";
|
|
7542
7533
|
this.selectionCount = 0;
|
|
@@ -7580,8 +7571,12 @@
|
|
|
7580
7571
|
connectedCallback() {
|
|
7581
7572
|
super.connectedCallback();
|
|
7582
7573
|
this.initializeManagers();
|
|
7583
|
-
this.
|
|
7584
|
-
|
|
7574
|
+
if (this.demo) {
|
|
7575
|
+
this.connected = true;
|
|
7576
|
+
} else {
|
|
7577
|
+
this.initializeConsoleCapture();
|
|
7578
|
+
this.connectToServer();
|
|
7579
|
+
}
|
|
7585
7580
|
}
|
|
7586
7581
|
disconnectedCallback() {
|
|
7587
7582
|
super.disconnectedCallback();
|
|
@@ -7672,73 +7667,27 @@
|
|
|
7672
7667
|
this.log("Connection error:", error.message);
|
|
7673
7668
|
});
|
|
7674
7669
|
}
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
this.rpc.handle.getPageContext(async () => {
|
|
7670
|
+
wrapRpcHandler(name, handler) {
|
|
7671
|
+
return (async (...args) => {
|
|
7678
7672
|
try {
|
|
7679
|
-
return await
|
|
7673
|
+
return await handler(...args);
|
|
7680
7674
|
} catch (error) {
|
|
7681
|
-
this.log(
|
|
7682
|
-
throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
|
|
7683
|
-
}
|
|
7684
|
-
});
|
|
7685
|
-
this.rpc.handle.getSelectedElements(async () => {
|
|
7686
|
-
try {
|
|
7687
|
-
return await this.getSelectedElements();
|
|
7688
|
-
} catch (error) {
|
|
7689
|
-
this.log("[getSelectedElements] Handler error:", error);
|
|
7690
|
-
throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
|
|
7691
|
-
}
|
|
7692
|
-
});
|
|
7693
|
-
this.rpc.handle.triggerSelection(async (mode, selector, selectorType) => {
|
|
7694
|
-
try {
|
|
7695
|
-
return await this.triggerSelection(mode, selector, selectorType);
|
|
7696
|
-
} catch (error) {
|
|
7697
|
-
this.log("[triggerSelection] Handler error:", error);
|
|
7698
|
-
throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
|
|
7699
|
-
}
|
|
7700
|
-
});
|
|
7701
|
-
this.rpc.handle.captureScreenshot(async (type, selector, quality) => {
|
|
7702
|
-
try {
|
|
7703
|
-
return await this.captureScreenshot(type, selector, quality);
|
|
7704
|
-
} catch (error) {
|
|
7705
|
-
this.log("[captureScreenshot] Handler error:", error);
|
|
7706
|
-
throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
|
|
7707
|
-
}
|
|
7708
|
-
});
|
|
7709
|
-
this.rpc.handle.clearSelection(async () => {
|
|
7710
|
-
try {
|
|
7711
|
-
return await this.clearSelection();
|
|
7712
|
-
} catch (error) {
|
|
7713
|
-
this.log("[clearSelection] Handler error:", error);
|
|
7675
|
+
this.log(`[${name}] Handler error:`, error);
|
|
7714
7676
|
throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
|
|
7715
7677
|
}
|
|
7716
7678
|
});
|
|
7679
|
+
}
|
|
7680
|
+
registerRpcHandlers() {
|
|
7681
|
+
if (!this.rpc) return;
|
|
7682
|
+
this.rpc.handle.getPageContext(this.wrapRpcHandler("getPageContext", async () => this.getPageContext()));
|
|
7683
|
+
this.rpc.handle.getSelectedElements(this.wrapRpcHandler("getSelectedElements", async () => this.getSelectedElements()));
|
|
7684
|
+
this.rpc.handle.triggerSelection(this.wrapRpcHandler("triggerSelection", async (mode, selector, selectorType) => this.triggerSelection(mode, selector, selectorType)));
|
|
7685
|
+
this.rpc.handle.captureScreenshot(this.wrapRpcHandler("captureScreenshot", async (type, selector, quality) => this.captureScreenshot(type, selector, quality)));
|
|
7686
|
+
this.rpc.handle.clearSelection(this.wrapRpcHandler("clearSelection", async () => this.clearSelection()));
|
|
7717
7687
|
this.rpc.handle.ping(async () => "pong");
|
|
7718
|
-
this.rpc.handle.injectCSS(async (css) =>
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
} catch (error) {
|
|
7722
|
-
this.log("[injectCSS] Handler error:", error);
|
|
7723
|
-
throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
|
|
7724
|
-
}
|
|
7725
|
-
});
|
|
7726
|
-
this.rpc.handle.injectJS(async (code) => {
|
|
7727
|
-
try {
|
|
7728
|
-
return await this.injectJS(code);
|
|
7729
|
-
} catch (error) {
|
|
7730
|
-
this.log("[injectJS] Handler error:", error);
|
|
7731
|
-
throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
|
|
7732
|
-
}
|
|
7733
|
-
});
|
|
7734
|
-
this.rpc.handle.getConsole(async (clear) => {
|
|
7735
|
-
try {
|
|
7736
|
-
return await this.getConsoleLogs(clear);
|
|
7737
|
-
} catch (error) {
|
|
7738
|
-
this.log("[getConsole] Handler error:", error);
|
|
7739
|
-
throw { message: error instanceof Error ? error.message : String(error), code: "INTERNAL_ERROR", data: void 0 };
|
|
7740
|
-
}
|
|
7741
|
-
});
|
|
7688
|
+
this.rpc.handle.injectCSS(this.wrapRpcHandler("injectCSS", async (css) => this.injectCSS(css)));
|
|
7689
|
+
this.rpc.handle.injectJS(this.wrapRpcHandler("injectJS", async (code) => this.injectJS(code)));
|
|
7690
|
+
this.rpc.handle.getConsole(this.wrapRpcHandler("getConsole", async (clear) => this.getConsoleLogs(clear)));
|
|
7742
7691
|
}
|
|
7743
7692
|
reportPageContext() {
|
|
7744
7693
|
if (!this.socket?.connected) return;
|
|
@@ -8030,9 +7979,6 @@
|
|
|
8030
7979
|
this.popoverCleanup();
|
|
8031
7980
|
this.popoverCleanup = null;
|
|
8032
7981
|
}
|
|
8033
|
-
const popoverEl = this.shadowRoot?.querySelector(".popover");
|
|
8034
|
-
if (popoverEl) {
|
|
8035
|
-
}
|
|
8036
7982
|
this.commentPopover = { visible: false, element: null, comment: "" };
|
|
8037
7983
|
}
|
|
8038
7984
|
handlePopoverInput(e5) {
|
|
@@ -8212,6 +8158,10 @@
|
|
|
8212
8158
|
}
|
|
8213
8159
|
async copySessionId() {
|
|
8214
8160
|
this.exitInspectingMode();
|
|
8161
|
+
if (this.demo) {
|
|
8162
|
+
this.showToast("Demo mode \u2014 no server");
|
|
8163
|
+
return;
|
|
8164
|
+
}
|
|
8215
8165
|
if (!this.sessionId) {
|
|
8216
8166
|
this.showToast("No session ID");
|
|
8217
8167
|
return;
|
|
@@ -8600,6 +8550,9 @@
|
|
|
8600
8550
|
__decorateClass([
|
|
8601
8551
|
n4({ attribute: "verbose", type: Boolean })
|
|
8602
8552
|
], AnnotatorToolbar.prototype, "verbose", 2);
|
|
8553
|
+
__decorateClass([
|
|
8554
|
+
n4({ attribute: "demo", type: Boolean })
|
|
8555
|
+
], AnnotatorToolbar.prototype, "demo", 2);
|
|
8603
8556
|
__decorateClass([
|
|
8604
8557
|
r5()
|
|
8605
8558
|
], AnnotatorToolbar.prototype, "connected", 2);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-setup AI tool skill/instruction files with the running server address.
|
|
3
|
+
*
|
|
4
|
+
* Writes proper skill/rule files for each AI tool:
|
|
5
|
+
* - Claude Code: .claude/skills/ai-annotator/SKILL.md
|
|
6
|
+
* - Cursor: .cursor/rules/ai-annotator.mdc (alwaysApply)
|
|
7
|
+
* - Windsurf: .windsurf/rules/ai-annotator.md (trigger: always_on)
|
|
8
|
+
* - Codex: AGENTS.md (marker-delimited section)
|
|
9
|
+
* - Copilot: .github/instructions/ai-annotator.instructions.md (applyTo: **)
|
|
10
|
+
* - Cline: .clinerules/ai-annotator.md
|
|
11
|
+
*
|
|
12
|
+
* Each file contains REST API docs with the actual server URL baked in.
|
|
13
|
+
* Updated on every server start so the address is always correct.
|
|
14
|
+
*/
|
|
15
|
+
export interface AutoSetupSkillsOptions {
|
|
16
|
+
projectRoot: string;
|
|
17
|
+
serverUrl: string;
|
|
18
|
+
verbose?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface AutoSetupSkillsResult {
|
|
21
|
+
updated: string[];
|
|
22
|
+
alreadyConfigured: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare function autoSetupSkills(options: AutoSetupSkillsOptions): AutoSetupSkillsResult;
|