vite-plugin-ai-annotator 1.1.9 → 1.1.11

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.
@@ -6587,9 +6587,17 @@
6587
6587
  deselectElement(element) {
6588
6588
  const elementData = selectedElements.get(element);
6589
6589
  if (elementData) {
6590
- ;
6591
- element.style.outline = elementData.originalOutline;
6592
- element.style.outlineOffset = elementData.originalOutlineOffset;
6590
+ const el = element;
6591
+ if (elementData.originalOutline) {
6592
+ el.style.outline = elementData.originalOutline;
6593
+ } else {
6594
+ el.style.removeProperty("outline");
6595
+ }
6596
+ if (elementData.originalOutlineOffset) {
6597
+ el.style.outlineOffset = elementData.originalOutlineOffset;
6598
+ } else {
6599
+ el.style.removeProperty("outline-offset");
6600
+ }
6593
6601
  const badge = badges.get(element);
6594
6602
  if (badge) {
6595
6603
  badge._cleanup?.();
@@ -6602,9 +6610,17 @@
6602
6610
  },
6603
6611
  clearAllSelections() {
6604
6612
  selectedElements.forEach((data, element) => {
6605
- ;
6606
- element.style.outline = data.originalOutline;
6607
- element.style.outlineOffset = data.originalOutlineOffset;
6613
+ const el = element;
6614
+ if (data.originalOutline) {
6615
+ el.style.outline = data.originalOutline;
6616
+ } else {
6617
+ el.style.removeProperty("outline");
6618
+ }
6619
+ if (data.originalOutlineOffset) {
6620
+ el.style.outlineOffset = data.originalOutlineOffset;
6621
+ } else {
6622
+ el.style.removeProperty("outline-offset");
6623
+ }
6608
6624
  });
6609
6625
  badges.forEach((badge) => {
6610
6626
  badge._cleanup?.();
@@ -7674,9 +7690,13 @@
7674
7690
  document.addEventListener("keydown", this.handlePopoverKeydown);
7675
7691
  this.updateComplete.then(() => {
7676
7692
  const popoverEl = this.shadowRoot?.querySelector(".popover");
7677
- const inputEl = this.shadowRoot?.querySelector(".popover-input");
7693
+ const textareaEl = this.shadowRoot?.querySelector(".popover-input");
7678
7694
  if (!popoverEl || !element) return;
7679
- inputEl?.focus();
7695
+ if (textareaEl) {
7696
+ textareaEl.focus();
7697
+ textareaEl.style.height = "auto";
7698
+ textareaEl.style.height = Math.min(textareaEl.scrollHeight, 120) + "px";
7699
+ }
7680
7700
  this.popoverCleanup = autoUpdate(element, popoverEl, () => {
7681
7701
  computePosition2(element, popoverEl, {
7682
7702
  strategy: "fixed",
@@ -7696,7 +7716,7 @@
7696
7716
  });
7697
7717
  }
7698
7718
  handlePopoverInputKeydown(e5) {
7699
- if (e5.key === "Enter") {
7719
+ if (e5.key === "Enter" && (e5.metaKey || e5.ctrlKey)) {
7700
7720
  e5.preventDefault();
7701
7721
  this.hideCommentPopover();
7702
7722
  } else if (e5.key === "Escape") {
@@ -7717,6 +7737,8 @@
7717
7737
  const target = e5.target;
7718
7738
  const comment = target.value;
7719
7739
  const element = this.commentPopover.element;
7740
+ target.style.height = "auto";
7741
+ target.style.height = Math.min(target.scrollHeight, 120) + "px";
7720
7742
  this.commentPopover = { ...this.commentPopover, comment };
7721
7743
  if (element) {
7722
7744
  const hasComment = comment.trim().length > 0;
@@ -7777,7 +7799,7 @@
7777
7799
  this.showToast("No elements selected");
7778
7800
  return;
7779
7801
  }
7780
- const text = `I have selected ${elements.length} element(s) in the browser. Use the \`annotator_get_selected_elements\` tool to retrieve them and modify the code.`;
7802
+ const text = `I have selected ${elements.length} feedback item(s) in the browser. Use the \`annotator_get_feedback\` tool to retrieve them and modify the code.`;
7781
7803
  try {
7782
7804
  await navigator.clipboard.writeText(text);
7783
7805
  this.showToast(`Copied ${elements.length} element(s)`);
@@ -7845,7 +7867,7 @@
7845
7867
  this.showToast("No session ID");
7846
7868
  return;
7847
7869
  }
7848
- const text = `I have selected elements in the browser (session: ${this.sessionId}). Use the \`annotator_get_selected_elements\` tool to retrieve them and modify the code.`;
7870
+ const text = `I have feedback in the browser (session: ${this.sessionId}). Use the \`annotator_get_feedback\` tool to retrieve them.`;
7849
7871
  try {
7850
7872
  await navigator.clipboard.writeText(text);
7851
7873
  this.showToast("Copied!");
@@ -7920,14 +7942,14 @@
7920
7942
 
7921
7943
  ${this.commentPopover.visible ? x`
7922
7944
  <div class="popover">
7923
- <input
7924
- type="text"
7945
+ <textarea
7925
7946
  class="popover-input"
7926
- placeholder="Add a note..."
7947
+ placeholder="Add a note... (⌘↵ to close)"
7927
7948
  .value=${this.commentPopover.comment}
7928
7949
  @input=${this.handlePopoverInput}
7929
7950
  @keydown=${this.handlePopoverInputKeydown}
7930
- />
7951
+ rows="1"
7952
+ ></textarea>
7931
7953
  <div class="popover-actions">
7932
7954
  <button class="popover-btn danger" @click=${this.removeSelectedElement} title="Remove selection">
7933
7955
  ${this.renderTrashIcon()}
@@ -8050,12 +8072,10 @@
8050
8072
  top: 0;
8051
8073
  left: 0;
8052
8074
  z-index: 1000000;
8053
- display: flex;
8054
- align-items: stretch;
8055
8075
  background: #1a1a1a;
8076
+ border: 1px solid rgba(99, 102, 241, 0.5);
8056
8077
  border-radius: 8px;
8057
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.08);
8058
- overflow: hidden;
8078
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5), 0 0 20px rgba(99, 102, 241, 0.3), 0 0 40px rgba(99, 102, 241, 0.1);
8059
8079
  animation: popover-in 0.15s ease-out;
8060
8080
  }
8061
8081
 
@@ -8071,16 +8091,20 @@
8071
8091
  }
8072
8092
 
8073
8093
  .popover-input {
8074
- flex: 1;
8075
- min-width: 180px;
8076
- max-width: 260px;
8094
+ width: 240px;
8095
+ min-height: 52px;
8096
+ max-height: 120px;
8077
8097
  padding: 10px 12px;
8098
+ padding-bottom: 32px;
8078
8099
  border: none;
8079
8100
  background: transparent;
8080
8101
  color: #fff;
8081
8102
  font-size: 13px;
8082
8103
  font-family: inherit;
8083
8104
  outline: none;
8105
+ resize: none;
8106
+ overflow-y: auto;
8107
+ line-height: 1.4;
8084
8108
  }
8085
8109
 
8086
8110
  .popover-input::placeholder {
@@ -8088,37 +8112,41 @@
8088
8112
  }
8089
8113
 
8090
8114
  .popover-actions {
8115
+ position: absolute;
8116
+ bottom: 4px;
8117
+ right: 4px;
8091
8118
  display: flex;
8092
8119
  align-items: center;
8093
- border-left: 1px solid rgba(255, 255, 255, 0.06);
8120
+ gap: 2px;
8094
8121
  }
8095
8122
 
8096
8123
  .popover-btn {
8097
8124
  display: flex;
8098
8125
  align-items: center;
8099
8126
  justify-content: center;
8100
- width: 36px;
8101
- height: 100%;
8127
+ width: 24px;
8128
+ height: 24px;
8102
8129
  border: none;
8103
- background: transparent;
8104
- color: #666;
8130
+ border-radius: 4px;
8131
+ background: rgba(255, 255, 255, 0.1);
8132
+ color: #888;
8105
8133
  cursor: pointer;
8106
8134
  transition: all 0.12s ease;
8107
8135
  }
8108
8136
 
8109
8137
  .popover-btn:hover {
8110
- background: rgba(255, 255, 255, 0.06);
8138
+ background: rgba(255, 255, 255, 0.15);
8111
8139
  color: #fff;
8112
8140
  }
8113
8141
 
8114
8142
  .popover-btn.danger:hover {
8115
- background: rgba(239, 68, 68, 0.15);
8143
+ background: rgba(239, 68, 68, 0.2);
8116
8144
  color: #f87171;
8117
8145
  }
8118
8146
 
8119
8147
  .popover-btn svg {
8120
- width: 14px;
8121
- height: 14px;
8148
+ width: 12px;
8149
+ height: 12px;
8122
8150
  }
8123
8151
 
8124
8152
  .hidden {
package/dist/index.cjs CHANGED
@@ -111,7 +111,7 @@ const toolbar = document.createElement('annotator-toolbar');
111
111
  toolbar.setAttribute('ws-endpoint', '${e.replace("http://","ws://").replace("https://","wss://")}');
112
112
  toolbar.setAttribute('verbose', '${n}');
113
113
  document.body.prepend(toolbar);
114
- `;i.send(s+u)}catch(a){console.error("Error reading annotator-toolbar.js:",a),i.status(404).send("File not found")}}),t.get("/health",(r,i)=>{i.json({status:"ok",sessions:Hn.size})}),t.get("/api/sessions",(r,i)=>{i.json(Zp())})}function zce(t,e){t.on("connection",n=>{if(n.handshake.query.clientType==="mcp"){Rce(n,e);return}let i=Tce(),a={id:i,url:"",title:"",connectedAt:Date.now(),lastActivity:Date.now()},o=Qq(n);Hn.set(i,{socket:n,rpc:o,session:a}),e.log(`Browser connected: ${i} (total: ${Hn.size})`),o.handle.getSessions(async()=>Zp()),o.handle.ping(async()=>"pong"),o.handle.rpcError(s=>{e.error("RPC Error:",s)}),n.emit("connected",{sessionId:i}),n.on("pageContextChanged",s=>{a.url=s.url,a.title=s.title,a.lastActivity=Date.now()}),n.on("disconnect",()=>{o.dispose(),Hn.delete(i),e.log(`Browser disconnected: ${i} (total: ${Hn.size})`)})})}function Rce(t,e){e.log("MCP client connected"),t.on("mcp:listSessions",n=>{n({success:!0,data:Zp()})}),t.on("mcp:getPageContext",async(n,r)=>{let i=St(n);if("error"in i){r({success:!1,error:i.error});return}try{let a=await i.rpc.client.getPageContext(1e4);Ct(a)?r({success:!1,error:a.message}):r({success:!0,data:a})}catch(a){r({success:!1,error:a instanceof Error?a.message:String(a)})}}),t.on("mcp:triggerSelection",async(n,r,i,a,o)=>{let s=St(n);if("error"in s){o({success:!1,error:s.error});return}try{let c=await s.rpc.client.triggerSelection(r,i,a,1e4);Ct(c)?o({success:!1,error:c.message}):o({success:!0,data:c})}catch(c){o({success:!1,error:c instanceof Error?c.message:String(c)})}}),t.on("mcp:getSelectedElements",async(n,r)=>{let i=St(n);if("error"in i){r({success:!1,error:i.error});return}try{let a=await i.rpc.client.getSelectedElements(15e3);Ct(a)?r({success:!1,error:a.message}):r({success:!0,data:a})}catch(a){r({success:!1,error:a instanceof Error?a.message:String(a)})}}),t.on("mcp:captureScreenshot",async(n,r,i,a,o,s)=>{let c=St(n);if("error"in c){s({success:!1,error:c.error});return}try{let u=await c.rpc.client.captureScreenshot(r,i,a,o,3e4);Ct(u)?s({success:!1,error:u.message}):s({success:!0,data:u})}catch(u){s({success:!1,error:u instanceof Error?u.message:String(u)})}}),t.on("mcp:clearSelection",async(n,r)=>{let i=St(n);if("error"in i){r({success:!1,error:i.error});return}i.rpc.client.clearSelection(),r({success:!0})}),t.on("mcp:injectCSS",async(n,r,i)=>{let a=St(n);if("error"in a){i({success:!1,error:a.error});return}try{let o=await a.rpc.client.injectCSS(r,1e4);Ct(o)?i({success:!1,error:o.message}):i({success:!0,data:o})}catch(o){i({success:!1,error:o instanceof Error?o.message:String(o)})}}),t.on("mcp:injectJS",async(n,r,i)=>{let a=St(n);if("error"in a){i({success:!1,error:a.error});return}try{let o=await a.rpc.client.injectJS(r,15e3);Ct(o)?i({success:!1,error:o.message}):i({success:!0,data:o})}catch(o){i({success:!1,error:o instanceof Error?o.message:String(o)})}}),t.on("mcp:getConsole",async(n,r,i)=>{let a=St(n);if("error"in a){i({success:!1,error:a.error});return}try{let o=await a.rpc.client.getConsole(r,15e3);Ct(o)?i({success:!1,error:o.message}):i({success:!0,data:o})}catch(o){i({success:!1,error:o instanceof Error?o.message:String(o)})}}),t.on("disconnect",()=>{e.log("MCP client disconnected")})}function St(t){let e=jce(t);if(!e){let n=Zp();return n.length===0?{error:"No browser connected. Add the annotator script to your webpage."}:{error:`Multiple sessions available. Specify sessionId. Available: ${n.map(r=>r.id).join(", ")}`}}return{rpc:e.rpc,sessionId:e.sessionId}}function Cce(){let t=new As({name:"ai-annotator",version:"1.0.0"}),e=Pe.string().optional().describe("Browser session ID (optional if only one session)");function n(r){return{content:[{type:"text",text:r}]}}return t.tool("annotator_list_sessions","List all connected browser sessions",{},async()=>{let r=Zp();return n(r.length>0?JSON.stringify(r,null,2):"No browser sessions connected. Add the annotator script to your webpage.")}),t.tool("annotator_get_page_context","Get current page context from browser session (URL, title, selection count)",{sessionId:e},async({sessionId:r})=>{let i=St(r);if("error"in i)return n(i.error);let a=await i.rpc.client.getPageContext(1e4);return Ct(a)?n(`Error: ${a.message}`):n(JSON.stringify(a,null,2))}),t.tool("annotator_select_element","Enter element inspection mode or select element by CSS/XPath selector",{sessionId:e,mode:Pe.enum(["inspect","selector"]).default("inspect").describe("Selection mode"),selector:Pe.string().optional().describe('CSS or XPath selector (required when mode is "selector")'),selectorType:Pe.enum(["css","xpath"]).default("css").describe("Type of selector")},async({sessionId:r,mode:i,selector:a,selectorType:o})=>{let s=St(r);if("error"in s)return n(s.error);let c=await s.rpc.client.triggerSelection(i,a,o,1e4);return Ct(c)?n(`Error: ${c.message}`):n(c.success?`Selection triggered. ${c.count} element(s) selected.`:`Selection failed: ${c.error}`)}),t.tool("annotator_get_selected_elements","Get data about currently selected elements in the browser",{sessionId:e},async({sessionId:r})=>{let i=St(r);if("error"in i)return n(i.error);let a=await i.rpc.client.getSelectedElements(15e3);return Ct(a)?n(`Error: ${a.message}`):n(a.length>0?JSON.stringify(a,null,2):"No elements selected. Use annotator_select_element first.")}),t.tool("annotator_capture_screenshot","Capture a screenshot of the viewport or a specific element. Returns the file path where the screenshot is saved.",{sessionId:e,type:Pe.enum(["viewport","element"]).default("viewport").describe("Type of screenshot"),selector:Pe.string().optional().describe("CSS selector for element screenshot"),format:Pe.enum(["png","jpeg"]).default("png").describe("Image format"),quality:Pe.number().min(0).max(1).default(.8).describe("Image quality (0-1)")},async({sessionId:r,type:i,selector:a,format:o,quality:s})=>{let c=St(r);if("error"in c)return n(c.error);let u=await c.rpc.client.captureScreenshot(i,a,o,s,3e4);if(Ct(u))return n(`Error: ${u.message}`);if(u.success&&u.base64){let p=Oce(u.base64,o);return n(p)}return n(`Screenshot failed: ${u.error}`)}),t.tool("annotator_clear_selection","Clear all selected elements in the browser",{sessionId:e},async({sessionId:r})=>{let i=St(r);return"error"in i?n(i.error):(i.rpc.client.clearSelection(),n("Selection cleared."))}),t.tool("annotator_inject_css","Inject CSS styles into the page",{sessionId:e,css:Pe.string().describe("CSS code to inject into the page")},async({sessionId:r,css:i})=>{let a=St(r);if("error"in a)return n(a.error);let o=await a.rpc.client.injectCSS(i,1e4);return Ct(o)?n(`Error: ${o.message}`):n(o.success?"CSS injected successfully.":`CSS injection failed: ${o.error}`)}),t.tool("annotator_inject_js","Inject and execute JavaScript code in the page context",{sessionId:e,code:Pe.string().describe("JavaScript code to execute in the page")},async({sessionId:r,code:i})=>{let a=St(r);if("error"in a)return n(a.error);let o=await a.rpc.client.injectJS(i,15e3);return Ct(o)?n(`Error: ${o.message}`):o.success?n(o.result!==void 0?`Result: ${JSON.stringify(o.result,null,2)}`:"JavaScript executed successfully (no return value)."):n(`JavaScript execution failed: ${o.error}`)}),t.tool("annotator_get_console","Get console logs captured from the browser",{sessionId:e,clear:Pe.boolean().default(!1).describe("Clear the console buffer after reading")},async({sessionId:r,clear:i})=>{let a=St(r);if("error"in a)return n(a.error);let o=await a.rpc.client.getConsole(i,15e3);return Ct(o)?n(`Error: ${o.message}`):n(o.length>0?JSON.stringify(o,null,2):"No console logs captured.")}),t}function Ace(t,e){let n=Cce(),r=new Map;t.all("/mcp",async(i,a)=>{let o=i.headers["mcp-session-id"],s;o&&r.has(o)?s=r.get(o):(s=new Wv({sessionIdGenerator:()=>crypto.randomUUID()}),await n.connect(s),s.onclose=()=>{for(let[c,u]of r.entries())if(u===s){r.delete(c),e.log(`MCP session closed: ${c}`);break}});try{await s.handleRequest(i,a,i.body);let c=a.getHeader("mcp-session-id");c&&!r.has(c)&&(r.set(c,s),e.log(`MCP session created: ${c}`))}catch(c){e.error("MCP request error:",c),a.headersSent||a.status(500).json({error:"MCP request failed"})}}),t.get("/mcp/info",(i,a)=>{a.json({name:"ai-annotator",version:"1.0.0",capabilities:{tools:!0},endpoint:"/mcp"})})}async function r9(t,e,n,r=!1){let i=e9(r),a=(0,CS.default)();a.use((0,n9.default)({origin:"*",methods:["GET","POST","OPTIONS"],allowedHeaders:["Content-Type","Authorization"]})),a.use(CS.default.json({limit:"10mb"})),Ice(a,n,r),Ace(a,i);let o=await Nce(a,t,e),s=new zR(o,{cors:{origin:"*",methods:["GET","POST"]},path:"/socket.io"});return zce(s,i),{app:a,server:o,io:s,port:t,listenAddress:e,publicAddress:n,verbose:r}}async function i9(t){return new Promise((e,n)=>{let r=!1,i=!1,a=!1;function o(){r&&i&&!a&&e()}Hn.forEach(({rpc:s})=>{s.dispose()}),Hn.clear(),t.io.close(s=>{s&&!a&&t.verbose&&console.error("Error closing Socket.IO server:",s),r=!0,o()}),t.server.listening?t.server.close(s=>{s&&!a?(a=!0,n(s)):(i=!0,o())}):(i=!0,o())})}function Nce(t,e,n){return new Promise((r,i)=>{let a=t.listen(e,n,()=>r(a));a.on("error",o=>{i(o)})})}var pD=require("net"),pg=require("fs"),ug=require("path");function Sue(){let t=[(0,ug.join)(__dirname,"..","package.json"),(0,ug.join)(__dirname,"package.json"),(0,ug.join)(process.cwd(),"package.json")];for(let e of t)if((0,pg.existsSync)(e))try{let n=JSON.parse((0,pg.readFileSync)(e,"utf-8"));if(n.name==="vite-plugin-ai-annotator"&&n.version)return n.version}catch{continue}return"0.0.0"}var Eue=Sue(),Et=process.argv.slice(2),$ue=Et[0];$ue==="mcp"?Promise.resolve().then(()=>(uD(),kue)).catch(t=>{console.error("Failed to start MCP CLI:",t),process.exit(1)}):Tue();async function Tue(){let t=Et.includes("--help")||Et.includes("-h"),e=Et.includes("--version")||Et.includes("-v"),n=Et.includes("--verbose")||Et.includes("-V"),r=Et.findIndex(m=>m==="--port"||m==="-p"),i=Et.findIndex(m=>m==="--listen"||m==="-l"),a=Et.findIndex(m=>m==="--public-address"||m==="-a");t&&(console.log(`
114
+ `;i.send(s+u)}catch(a){console.error("Error reading annotator-toolbar.js:",a),i.status(404).send("File not found")}}),t.get("/health",(r,i)=>{i.json({status:"ok",sessions:Hn.size})}),t.get("/api/sessions",(r,i)=>{i.json(Zp())})}function zce(t,e){t.on("connection",n=>{if(n.handshake.query.clientType==="mcp"){Rce(n,e);return}let i=Tce(),a={id:i,url:"",title:"",connectedAt:Date.now(),lastActivity:Date.now()},o=Qq(n);Hn.set(i,{socket:n,rpc:o,session:a}),e.log(`Browser connected: ${i} (total: ${Hn.size})`),o.handle.getSessions(async()=>Zp()),o.handle.ping(async()=>"pong"),o.handle.rpcError(s=>{e.error("RPC Error:",s)}),n.emit("connected",{sessionId:i}),n.on("pageContextChanged",s=>{a.url=s.url,a.title=s.title,a.lastActivity=Date.now()}),n.on("disconnect",()=>{o.dispose(),Hn.delete(i),e.log(`Browser disconnected: ${i} (total: ${Hn.size})`)})})}function Rce(t,e){e.log("MCP client connected"),t.on("mcp:listSessions",n=>{n({success:!0,data:Zp()})}),t.on("mcp:getPageContext",async(n,r)=>{let i=St(n);if("error"in i){r({success:!1,error:i.error});return}try{let a=await i.rpc.client.getPageContext(1e4);Ct(a)?r({success:!1,error:a.message}):r({success:!0,data:a})}catch(a){r({success:!1,error:a instanceof Error?a.message:String(a)})}}),t.on("mcp:triggerSelection",async(n,r,i,a,o)=>{let s=St(n);if("error"in s){o({success:!1,error:s.error});return}try{let c=await s.rpc.client.triggerSelection(r,i,a,1e4);Ct(c)?o({success:!1,error:c.message}):o({success:!0,data:c})}catch(c){o({success:!1,error:c instanceof Error?c.message:String(c)})}}),t.on("mcp:getSelectedElements",async(n,r)=>{let i=St(n);if("error"in i){r({success:!1,error:i.error});return}try{let a=await i.rpc.client.getSelectedElements(15e3);Ct(a)?r({success:!1,error:a.message}):r({success:!0,data:a})}catch(a){r({success:!1,error:a instanceof Error?a.message:String(a)})}}),t.on("mcp:captureScreenshot",async(n,r,i,a,o,s)=>{let c=St(n);if("error"in c){s({success:!1,error:c.error});return}try{let u=await c.rpc.client.captureScreenshot(r,i,a,o,3e4);Ct(u)?s({success:!1,error:u.message}):s({success:!0,data:u})}catch(u){s({success:!1,error:u instanceof Error?u.message:String(u)})}}),t.on("mcp:clearSelection",async(n,r)=>{let i=St(n);if("error"in i){r({success:!1,error:i.error});return}i.rpc.client.clearSelection(),r({success:!0})}),t.on("mcp:injectCSS",async(n,r,i)=>{let a=St(n);if("error"in a){i({success:!1,error:a.error});return}try{let o=await a.rpc.client.injectCSS(r,1e4);Ct(o)?i({success:!1,error:o.message}):i({success:!0,data:o})}catch(o){i({success:!1,error:o instanceof Error?o.message:String(o)})}}),t.on("mcp:injectJS",async(n,r,i)=>{let a=St(n);if("error"in a){i({success:!1,error:a.error});return}try{let o=await a.rpc.client.injectJS(r,15e3);Ct(o)?i({success:!1,error:o.message}):i({success:!0,data:o})}catch(o){i({success:!1,error:o instanceof Error?o.message:String(o)})}}),t.on("mcp:getConsole",async(n,r,i)=>{let a=St(n);if("error"in a){i({success:!1,error:a.error});return}try{let o=await a.rpc.client.getConsole(r,15e3);Ct(o)?i({success:!1,error:o.message}):i({success:!0,data:o})}catch(o){i({success:!1,error:o instanceof Error?o.message:String(o)})}}),t.on("disconnect",()=>{e.log("MCP client disconnected")})}function St(t){let e=jce(t);if(!e){let n=Zp();return n.length===0?{error:"No browser connected. Add the annotator script to your webpage."}:{error:`Multiple sessions available. Specify sessionId. Available: ${n.map(r=>r.id).join(", ")}`}}return{rpc:e.rpc,sessionId:e.sessionId}}function Cce(){let t=new As({name:"ai-annotator",version:"1.0.0"}),e=Pe.string().optional().describe("Browser session ID (optional if only one session)");function n(r){return{content:[{type:"text",text:r}]}}return t.tool("annotator_list_sessions","List all connected browser sessions",{},async()=>{let r=Zp();return n(r.length>0?JSON.stringify(r,null,2):"No browser sessions connected. Add the annotator script to your webpage.")}),t.tool("annotator_get_page_context","Get current page context from browser session (URL, title, selection count)",{sessionId:e},async({sessionId:r})=>{let i=St(r);if("error"in i)return n(i.error);let a=await i.rpc.client.getPageContext(1e4);return Ct(a)?n(`Error: ${a.message}`):n(JSON.stringify(a,null,2))}),t.tool("annotator_select_feedback","Enter feedback inspection mode or select feedback by CSS/XPath selector. Use this to let users mark UI elements they want to provide feedback on.",{sessionId:e,mode:Pe.enum(["inspect","selector"]).default("inspect").describe("Feedback selection mode"),selector:Pe.string().optional().describe('CSS or XPath selector (required when mode is "selector")'),selectorType:Pe.enum(["css","xpath"]).default("css").describe("Type of selector")},async({sessionId:r,mode:i,selector:a,selectorType:o})=>{let s=St(r);if("error"in s)return n(s.error);let c=await s.rpc.client.triggerSelection(i,a,o,1e4);return Ct(c)?n(`Error: ${c.message}`):n(c.success?`Feedback selection triggered. ${c.count} feedback item(s) selected.`:`Feedback selection failed: ${c.error}`)}),t.tool("annotator_get_feedback","Get data about currently selected feedback items in the browser. Returns details of UI elements the user has marked for feedback.",{sessionId:e},async({sessionId:r})=>{let i=St(r);if("error"in i)return n(i.error);let a=await i.rpc.client.getSelectedElements(15e3);return Ct(a)?n(`Error: ${a.message}`):n(a.length>0?JSON.stringify(a,null,2):"No feedback selected. Use annotator_select_feedback first.")}),t.tool("annotator_capture_screenshot","Capture a screenshot of the viewport or a specific element. Returns the file path where the screenshot is saved.",{sessionId:e,type:Pe.enum(["viewport","element"]).default("viewport").describe("Type of screenshot"),selector:Pe.string().optional().describe("CSS selector for element screenshot"),format:Pe.enum(["png","jpeg"]).default("png").describe("Image format"),quality:Pe.number().min(0).max(1).default(.8).describe("Image quality (0-1)")},async({sessionId:r,type:i,selector:a,format:o,quality:s})=>{let c=St(r);if("error"in c)return n(c.error);let u=await c.rpc.client.captureScreenshot(i,a,o,s,3e4);if(Ct(u))return n(`Error: ${u.message}`);if(u.success&&u.base64){let p=Oce(u.base64,o);return n(p)}return n(`Screenshot failed: ${u.error}`)}),t.tool("annotator_clear_feedback","Clear all selected feedback items in the browser. Removes all UI element selections made for feedback.",{sessionId:e},async({sessionId:r})=>{let i=St(r);return"error"in i?n(i.error):(i.rpc.client.clearSelection(),n("Feedback cleared."))}),t.tool("annotator_inject_css","Inject CSS styles into the page",{sessionId:e,css:Pe.string().describe("CSS code to inject into the page")},async({sessionId:r,css:i})=>{let a=St(r);if("error"in a)return n(a.error);let o=await a.rpc.client.injectCSS(i,1e4);return Ct(o)?n(`Error: ${o.message}`):n(o.success?"CSS injected successfully.":`CSS injection failed: ${o.error}`)}),t.tool("annotator_inject_js","Inject and execute JavaScript code in the page context",{sessionId:e,code:Pe.string().describe("JavaScript code to execute in the page")},async({sessionId:r,code:i})=>{let a=St(r);if("error"in a)return n(a.error);let o=await a.rpc.client.injectJS(i,15e3);return Ct(o)?n(`Error: ${o.message}`):o.success?n(o.result!==void 0?`Result: ${JSON.stringify(o.result,null,2)}`:"JavaScript executed successfully (no return value)."):n(`JavaScript execution failed: ${o.error}`)}),t.tool("annotator_get_console","Get console logs captured from the browser",{sessionId:e,clear:Pe.boolean().default(!1).describe("Clear the console buffer after reading")},async({sessionId:r,clear:i})=>{let a=St(r);if("error"in a)return n(a.error);let o=await a.rpc.client.getConsole(i,15e3);return Ct(o)?n(`Error: ${o.message}`):n(o.length>0?JSON.stringify(o,null,2):"No console logs captured.")}),t}function Ace(t,e){let n=Cce(),r=new Map;t.all("/mcp",async(i,a)=>{let o=i.headers["mcp-session-id"],s;o&&r.has(o)?s=r.get(o):(s=new Wv({sessionIdGenerator:()=>crypto.randomUUID()}),await n.connect(s),s.onclose=()=>{for(let[c,u]of r.entries())if(u===s){r.delete(c),e.log(`MCP session closed: ${c}`);break}});try{await s.handleRequest(i,a,i.body);let c=a.getHeader("mcp-session-id");c&&!r.has(c)&&(r.set(c,s),e.log(`MCP session created: ${c}`))}catch(c){e.error("MCP request error:",c),a.headersSent||a.status(500).json({error:"MCP request failed"})}}),t.get("/mcp/info",(i,a)=>{a.json({name:"ai-annotator",version:"1.0.0",capabilities:{tools:!0},endpoint:"/mcp"})})}async function r9(t,e,n,r=!1){let i=e9(r),a=(0,CS.default)();a.use((0,n9.default)({origin:"*",methods:["GET","POST","OPTIONS"],allowedHeaders:["Content-Type","Authorization"]})),a.use(CS.default.json({limit:"10mb"})),Ice(a,n,r),Ace(a,i);let o=await Nce(a,t,e),s=new zR(o,{cors:{origin:"*",methods:["GET","POST"]},path:"/socket.io"});return zce(s,i),{app:a,server:o,io:s,port:t,listenAddress:e,publicAddress:n,verbose:r}}async function i9(t){return new Promise((e,n)=>{let r=!1,i=!1,a=!1;function o(){r&&i&&!a&&e()}Hn.forEach(({rpc:s})=>{s.dispose()}),Hn.clear(),t.io.close(s=>{s&&!a&&t.verbose&&console.error("Error closing Socket.IO server:",s),r=!0,o()}),t.server.listening?t.server.close(s=>{s&&!a?(a=!0,n(s)):(i=!0,o())}):(i=!0,o())})}function Nce(t,e,n){return new Promise((r,i)=>{let a=t.listen(e,n,()=>r(a));a.on("error",o=>{i(o)})})}var pD=require("net"),pg=require("fs"),ug=require("path");function Sue(){let t=[(0,ug.join)(__dirname,"..","package.json"),(0,ug.join)(__dirname,"package.json"),(0,ug.join)(process.cwd(),"package.json")];for(let e of t)if((0,pg.existsSync)(e))try{let n=JSON.parse((0,pg.readFileSync)(e,"utf-8"));if(n.name==="vite-plugin-ai-annotator"&&n.version)return n.version}catch{continue}return"0.0.0"}var Eue=Sue(),Et=process.argv.slice(2),$ue=Et[0];$ue==="mcp"?Promise.resolve().then(()=>(uD(),kue)).catch(t=>{console.error("Failed to start MCP CLI:",t),process.exit(1)}):Tue();async function Tue(){let t=Et.includes("--help")||Et.includes("-h"),e=Et.includes("--version")||Et.includes("-v"),n=Et.includes("--verbose")||Et.includes("-V"),r=Et.findIndex(m=>m==="--port"||m==="-p"),i=Et.findIndex(m=>m==="--listen"||m==="-l"),a=Et.findIndex(m=>m==="--public-address"||m==="-a");t&&(console.log(`
115
115
  AI Annotator - AI-powered web inspection tool
116
116
 
117
117
  Usage:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-ai-annotator",
3
- "version": "1.1.9",
3
+ "version": "1.1.11",
4
4
  "description": "AI-powered element annotator for Vite - Pick elements and get instant AI code modifications",
5
5
  "type": "module",
6
6
  "main": "dist/vite-plugin.js",