@visulima/dev-toolbar 1.0.10 → 1.0.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,5 @@
1
+ ## @visulima/dev-toolbar [1.0.11](https://github.com/visulima/visulima/compare/%40visulima%2Fdev-toolbar%401.0.10...%40visulima%2Fdev-toolbar%401.0.11) (2026-07-27)
2
+
1
3
  ## @visulima/dev-toolbar [1.0.10](https://github.com/visulima/visulima/compare/%40visulima%2Fdev-toolbar%401.0.9...%40visulima%2Fdev-toolbar%401.0.10) (2026-07-27)
2
4
 
3
5
  ## @visulima/dev-toolbar [1.0.9](https://github.com/visulima/visulima/compare/%40visulima%2Fdev-toolbar%401.0.8...%40visulima%2Fdev-toolbar%401.0.9) (2026-07-27)
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const a11yApp: DevToolbarApp;
4
4
  export { a11yApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const annotationsApp: DevToolbarApp;
4
4
  export { annotationsApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const assetsApp: DevToolbarApp;
4
4
  export { assetsApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const inspectorApp: DevToolbarApp;
4
4
  export { inspectorApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const moduleGraphApp: DevToolbarApp;
4
4
  export { moduleGraphApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const performanceApp: DevToolbarApp;
4
4
  export { performanceApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const seoApp: DevToolbarApp;
4
4
  export { seoApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const settingsApp: DevToolbarApp;
4
4
  export { settingsApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const tailwindApp: DevToolbarApp;
4
4
  export { tailwindApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const timelineApp: DevToolbarApp;
4
4
  export { timelineApp as default };
@@ -1,4 +1,4 @@
1
- import { D as DevToolbarApp } from "../../packem_shared/app.d-SmKEDxsI.js";
1
+ import { D as DevToolbarApp } from "../../packem_shared/app.d-BV5WIojP.js";
2
2
  import 'preact';
3
3
  declare const viteConfigApp: DevToolbarApp;
4
4
  export { viteConfigApp as default };
package/dist/index.d.ts CHANGED
@@ -1,133 +1,133 @@
1
- import { D as DevToolbarApp, T as ToolbarSettings } from "./packem_shared/app.d-SmKEDxsI.js";
2
- export type { A as AppView, b as DEFAULT_TOOLBAR_SETTINGS, a as DevToolbarAppState, F as FrameState, N as NotificationLevel, P as PositionAnchor, S as ServerFunctions, c as ServerHelpers, d as ToolbarAppEventTarget, e as ToolbarPlacement } from "./packem_shared/app.d-SmKEDxsI.js";
3
- import { T as TimelineEvent, D as DevToolbarHook, C as ClientFunctions, b as ClientRPCContext, c as TimelineGroup } from "./packem_shared/global-api.d-DeaCvII9.js";
4
- export type { A as AccessibilityInfo, d as Annotation, e as AnnotationIntent, f as AnnotationSeverity, g as AnnotationStatus, B as BoundingBox, h as CreateAnnotationData, i as DEFAULT_TIMELINE_GROUPS, F as FrameworkContext, H as HookEvents, S as RPCServerFunctions, a as ServerRPCContext, j as ThreadMessage, k as TimelineEventLevel, U as UpdateAnnotationData, V as VisulimaDevTools } from "./packem_shared/global-api.d-DeaCvII9.js";
5
- export { type R as ReadFileOptions, type S as ServerRPCOptions, c as createServerRPCContext } from "./packem_shared/server.d-ZkcY0pge.js";
1
+ import { D as DevToolbarApp, T as ToolbarSettings } from "./packem_shared/app.d-BV5WIojP.js";
2
+ export type { A as AppView, b as DEFAULT_TOOLBAR_SETTINGS, a as DevToolbarAppState, F as FrameState, N as NotificationLevel, P as PositionAnchor, S as ServerFunctions, c as ServerHelpers, d as ToolbarAppEventTarget, e as ToolbarPlacement } from "./packem_shared/app.d-BV5WIojP.js";
3
+ import { T as TimelineEvent, D as DevToolbarHook, C as ClientFunctions, b as ClientRPCContext, c as TimelineGroup } from "./packem_shared/global-api.d-PUBE6Od9.js";
4
+ export type { A as AccessibilityInfo, d as Annotation, e as AnnotationIntent, f as AnnotationSeverity, g as AnnotationStatus, B as BoundingBox, h as CreateAnnotationData, i as DEFAULT_TIMELINE_GROUPS, F as FrameworkContext, H as HookEvents, S as RPCServerFunctions, a as ServerRPCContext, j as ThreadMessage, k as TimelineEventLevel, U as UpdateAnnotationData, V as VisulimaDevTools } from "./packem_shared/global-api.d-PUBE6Od9.js";
5
+ export { type R as ReadFileOptions, type S as ServerRPCOptions, c as createServerRPCContext } from "./packem_shared/server.d-CF1fcO17.js";
6
6
  export { DevToolbar } from "./toolbar/index.js";
7
7
  import 'preact';
8
8
  import 'vite';
9
9
  /**
10
- * Creates a dev toolbar hook instance.
11
- * @param onRegisterApp Callback when app is registered.
12
- * @param onTimelineEvent Callback when timeline event is added.
13
- * @returns Hook instance.
14
- */
10
+ * Creates a dev toolbar hook instance.
11
+ * @param onRegisterApp Callback when app is registered.
12
+ * @param onTimelineEvent Callback when timeline event is added.
13
+ * @returns Hook instance.
14
+ */
15
15
  declare const createDevToolbarHook: (onRegisterApp?: (app: DevToolbarApp) => void, onTimelineEvent?: (groupId: string, event: TimelineEvent) => void) => DevToolbarHook;
16
16
  /**
17
- * Setup global hook on window object.
18
- * @param onRegisterApp Callback when app is registered.
19
- * @param onTimelineEvent Callback when timeline event is added.
20
- * @returns Hook instance.
21
- */
17
+ * Setup global hook on window object.
18
+ * @param onRegisterApp Callback when app is registered.
19
+ * @param onTimelineEvent Callback when timeline event is added.
20
+ * @returns Hook instance.
21
+ */
22
22
  declare const setupGlobalHook: (onRegisterApp?: (app: DevToolbarApp) => void, onTimelineEvent?: (groupId: string, event: TimelineEvent) => void) => DevToolbarHook;
23
23
  /**
24
- * Get the global hook instance.
25
- * @returns Hook instance or undefined.
26
- */
24
+ * Get the global hook instance.
25
+ * @returns Hook instance or undefined.
26
+ */
27
27
  declare const getGlobalHook: () => DevToolbarHook | undefined;
28
28
  /**
29
- * Creates client-side RPC context.
30
- * @param customFunctions Custom client functions to register.
31
- * @returns Client RPC context.
32
- */
29
+ * Creates client-side RPC context.
30
+ * @param customFunctions Custom client functions to register.
31
+ * @returns Client RPC context.
32
+ */
33
33
  declare const createClientRPCContext: (customFunctions?: Partial<ClientFunctions>) => ClientRPCContext;
34
34
  /**
35
- * Timeline store for managing timeline events.
36
- */
35
+ * Timeline store for managing timeline events.
36
+ */
37
37
  declare class TimelineStore {
38
38
  private groups;
39
39
  private maxEvents;
40
40
  constructor(maxEvents?: number);
41
41
  /**
42
- * Adds an event to a group.
43
- */
42
+ * Adds an event to a group.
43
+ */
44
44
  addEvent(groupId: string, event: TimelineEvent): void;
45
45
  /**
46
- * Gets all timeline groups.
47
- */
46
+ * Gets all timeline groups.
47
+ */
48
48
  getGroups(): TimelineGroup[];
49
49
  /**
50
- * Gets events for a specific group.
51
- */
50
+ * Gets events for a specific group.
51
+ */
52
52
  getGroupEvents(groupId: string): TimelineEvent[];
53
53
  /**
54
- * Gets all events across all groups.
55
- */
54
+ * Gets all events across all groups.
55
+ */
56
56
  getAllEvents(): TimelineEvent[];
57
57
  /**
58
- * Clears events for a specific group.
59
- */
58
+ * Clears events for a specific group.
59
+ */
60
60
  clearGroup(groupId: string): void;
61
61
  /**
62
- * Clears all events from all groups.
63
- */
62
+ * Clears all events from all groups.
63
+ */
64
64
  clearAll(): void;
65
65
  /**
66
- * Gets events within a time range.
67
- */
66
+ * Gets events within a time range.
67
+ */
68
68
  getEventsInRange(startTime: number, endTime: number): TimelineEvent[];
69
69
  }
70
70
  /**
71
- * Gets or creates the timeline store singleton instance.
72
- */
71
+ * Gets or creates the timeline store singleton instance.
72
+ */
73
73
  declare const getTimelineStore: () => TimelineStore;
74
74
  /**
75
- * Message channel interface for bidirectional communication
76
- */
75
+ * Message channel interface for bidirectional communication
76
+ */
77
77
  interface MessageChannel<TEvents extends Record<string, (...args: any[]) => void>> {
78
78
  /**
79
- * Unsubscribe from an event
80
- * @param event Event name
81
- * @param handler Optional specific handler to remove
82
- */
79
+ * Unsubscribe from an event
80
+ * @param event Event name
81
+ * @param handler Optional specific handler to remove
82
+ */
83
83
  off: <K extends keyof TEvents>(event: K, handler?: TEvents[K]) => void;
84
84
  /**
85
- * Subscribe to an event
86
- * @param event Event name
87
- * @param handler Event handler
88
- * @returns Unsubscribe function
89
- */
85
+ * Subscribe to an event
86
+ * @param event Event name
87
+ * @param handler Event handler
88
+ * @returns Unsubscribe function
89
+ */
90
90
  on: <K extends keyof TEvents>(event: K, handler: TEvents[K]) => () => void;
91
91
  /**
92
- * Subscribe to an event once
93
- * @param event Event name
94
- * @param handler Event handler
95
- */
92
+ * Subscribe to an event once
93
+ * @param event Event name
94
+ * @param handler Event handler
95
+ */
96
96
  once: <K extends keyof TEvents>(event: K, handler: TEvents[K]) => void;
97
97
  /**
98
- * Send a message/event
99
- * @param event Event name
100
- * @param data Event data
101
- */
98
+ * Send a message/event
99
+ * @param event Event name
100
+ * @param data Event data
101
+ */
102
102
  send: <K extends keyof TEvents>(event: K, ...args: Parameters<TEvents[K]>) => void;
103
103
  }
104
104
  /**
105
- * Channel factory function
106
- */
105
+ * Channel factory function
106
+ */
107
107
  type ChannelFactory<TEvents extends Record<string, (...args: any[]) => void>> = () => MessageChannel<TEvents>;
108
108
  /**
109
- * Factory result providing a method to instantiate named message channels.
110
- */
109
+ * Factory result providing a method to instantiate named message channels.
110
+ */
111
111
  interface MessageChannelContext<TEvents extends Record<string, (...args: any[]) => void>> {
112
112
  /**
113
- * Creates and returns a new channel instance.
114
- */
113
+ * Creates and returns a new channel instance.
114
+ */
115
115
  createChannel: () => MessageChannel<TEvents>;
116
116
  }
117
117
  /**
118
- * Loads settings from localStorage, merging with defaults.
119
- * @returns Toolbar settings.
120
- */
118
+ * Loads settings from localStorage, merging with defaults.
119
+ * @returns Toolbar settings.
120
+ */
121
121
  declare const loadSettings: () => ToolbarSettings;
122
122
  /**
123
- * Saves settings to localStorage.
124
- * @param settings Toolbar settings to save.
125
- */
123
+ * Saves settings to localStorage.
124
+ * @param settings Toolbar settings to save.
125
+ */
126
126
  declare const saveSettings: (settings: ToolbarSettings) => void;
127
127
  /**
128
- * Merges partial updates into the current settings and persists them.
129
- * @param updates Partial settings to update.
130
- * @returns Updated settings.
131
- */
128
+ * Merges partial updates into the current settings and persists them.
129
+ * @param updates Partial settings to update.
130
+ * @returns Updated settings.
131
+ */
132
132
  declare const updateSettings: (updates: Partial<ToolbarSettings>) => ToolbarSettings;
133
133
  export { type ChannelFactory, type ClientRPCContext, type DevToolbarApp, type DevToolbarHook, type MessageChannel, type MessageChannelContext, type ClientFunctions as RPCClientFunctions, type TimelineEvent, type TimelineGroup, type TimelineStore, type ToolbarSettings, createClientRPCContext, createDevToolbarHook, getGlobalHook, getTimelineStore, loadSettings, saveSettings, setupGlobalHook, updateSettings };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{createClientRPCContext as t}from"./packem_shared/createClientRPCContext-5Bh0Q7ai.js";import{createServerRPCContext as a}from"./packem_shared/createServerRPCContext-0Fs78uvP.js";import{DevToolbar as p}from"./toolbar/index.js";import{loadSettings as m,saveSettings as f,updateSettings as n}from"./packem_shared/loadSettings-C0pUyx9j.js";import{createDevToolbarHook as s}from"./packem_shared/createDevToolbarHook-C_TF-RyL.js";import{getGlobalHook as C,setupGlobalHook as S}from"./packem_shared/getGlobalHook-D6jRtv7N.js";import{u as v}from"./packem_shared/store-Dqy0FbGw.js";export{p as DevToolbar,t as createClientRPCContext,s as createDevToolbarHook,a as createServerRPCContext,C as getGlobalHook,v as getTimelineStore,m as loadSettings,f as saveSettings,S as setupGlobalHook,n as updateSettings};
1
+ import{createClientRPCContext as t}from"./packem_shared/createClientRPCContext-5Bh0Q7ai.js";import{createServerRPCContext as a}from"./packem_shared/createServerRPCContext-DAu3942i.js";import{DevToolbar as p}from"./toolbar/index.js";import{loadSettings as m,saveSettings as f,updateSettings as n}from"./packem_shared/loadSettings-C0pUyx9j.js";import{createDevToolbarHook as s}from"./packem_shared/createDevToolbarHook-C_TF-RyL.js";import{getGlobalHook as C,setupGlobalHook as S}from"./packem_shared/getGlobalHook-D6jRtv7N.js";import{u as v}from"./packem_shared/store-Dqy0FbGw.js";export{p as DevToolbar,t as createClientRPCContext,s as createDevToolbarHook,a as createServerRPCContext,C as getGlobalHook,v as getTimelineStore,m as loadSettings,f as saveSettings,S as setupGlobalHook,n as updateSettings};
@@ -1 +1 @@
1
- import{createRequire as k}from"node:module";import{r as d,S as D,a as E,i as j,w as v,d as M,b as x}from"../packem_shared/annotation-store-WIJhHG5y.js";const J=k(import.meta.url),p=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,O=f=>{if(typeof p<"u"&&p.versions&&p.versions.node){const[m,s]=p.versions.node.split(".").map(Number);if(m>22||m===22&&s>=3||m===20&&s>=16)return p.getBuiltinModule(f)}return J(f)},A=O("node:fs/promises"),I=O("node:path"),U=async()=>{const{McpServer:f}=await import("@modelcontextprotocol/sdk/server/mcp.js"),{StdioServerTransport:m}=await import("@modelcontextprotocol/sdk/server/stdio.js"),{z:s}=await import("zod"),a=process.cwd(),u=new f({name:"visulima-dev-toolbar",version:"1.0.0-alpha.5"});u.tool("get_pending_annotations","Get all pending visual annotations from the dev toolbar. Returns annotations with metadata but not screenshot binary data — use get_screenshot for that.",{},async()=>{const i=(await d(a)).filter(t=>t.status==="pending").map(t=>({...t,screenshot:!!t.screenshot}));return{content:[{text:JSON.stringify({annotations:i,count:i.length},void 0,2),type:"text"}]}}),u.tool("get_screenshot","Get the screenshot image for an annotation as base64 data. Use this after get_pending_annotations to see what the user annotated.",{annotation_id:s.string().describe("The annotation ID to get the screenshot for")},async({annotation_id:i})=>{const t=(await d(a)).find(o=>o.id===i);if(!t)return{content:[{text:JSON.stringify({error:"Annotation not found"}),type:"text"}],isError:!0};if(!t.screenshot)return{content:[{text:JSON.stringify({error:"No screenshot for this annotation"}),type:"text"}],isError:!0};if(!t.screenshot.startsWith(`${D}/`))return{content:[{text:JSON.stringify({error:"Invalid screenshot path"}),type:"text"}],isError:!0};const{base:n}=E(a),e=I.join(n,t.screenshot);if(!j(e,n))return{content:[{text:JSON.stringify({error:"Invalid screenshot path"}),type:"text"}],isError:!0};try{const o=await A.readFile(e),r=I.extname(e).slice(1),g=r==="svg"?"image/svg+xml":r==="jpg"?"image/jpeg":`image/${r}`;return{content:[{data:o.toString("base64"),mimeType:g,type:"image"}]}}catch{return{content:[{text:JSON.stringify({error:"Screenshot file not found"}),type:"text"}],isError:!0}}}),u.tool("resolve_annotation","Mark an annotation as resolved after fixing the issue. Automatically deletes the associated screenshot.",{annotation_id:s.string().describe("The annotation ID to resolve")},async({annotation_id:i})=>v(async()=>{const t=await d(a),n=t.findIndex(o=>o.id===i);if(n===-1)return{content:[{text:JSON.stringify({error:"Annotation not found"}),type:"text"}],isError:!0};const e=t[n];return e.status="resolved",e.resolvedBy="agent",e.updatedAt=new Date().toISOString(),e.screenshot&&(await M(a,e.screenshot),e.screenshot=void 0),t[n]=e,await x(a,t),{content:[{text:JSON.stringify({annotation:e,ok:!0},void 0,2),type:"text"}]}})),u.tool("add_thread_message","Add a message to an annotation's conversation thread. Use this to communicate with the developer about the annotation.",{annotation_id:s.string().describe("The annotation ID to add a message to"),message:s.string().describe("The message content")},async({annotation_id:i,message:t})=>v(async()=>{const n=await d(a),e=n.findIndex(r=>r.id===i);if(e===-1)return{content:[{text:JSON.stringify({error:"Annotation not found"}),type:"text"}],isError:!0};const o=n[e];return o.thread||(o.thread=[]),o.thread.push({content:t,id:crypto.randomUUID(),role:"agent",timestamp:new Date().toISOString()}),o.updatedAt=new Date().toISOString(),n[e]=o,await x(a,n),{content:[{text:JSON.stringify({annotation:o,ok:!0},void 0,2),type:"text"}]}})),u.tool("acknowledge_annotation","Mark an annotation as acknowledged — signals to the developer that the agent has seen it and will work on it. Use this before starting to fix an issue.",{annotation_id:s.string().describe("The annotation ID to acknowledge")},async({annotation_id:i})=>v(async()=>{const t=await d(a),n=t.findIndex(o=>o.id===i);if(n===-1)return{content:[{text:JSON.stringify({error:"Annotation not found"}),type:"text"}],isError:!0};const e=t[n];return e.status="acknowledged",e.updatedAt=new Date().toISOString(),t[n]=e,await x(a,t),{content:[{text:JSON.stringify({annotation:e,ok:!0},void 0,2),type:"text"}]}})),u.tool("watch_annotations","Block until new pending annotations appear. Use this for hands-free mode: call in a loop to automatically detect when the developer adds feedback. Returns after the batch window (default 10s) to collect multiple annotations submitted in quick succession.",{batch_window_ms:s.number().optional().describe("How long to wait for additional annotations after the first one arrives (default: 10000ms)"),timeout_ms:s.number().optional().describe("Maximum time to wait before returning empty (default: 300000ms = 5 minutes)")},async({batch_window_ms:i,timeout_ms:t})=>{const n=i??1e4,e=t??3e5,o=2e3,r=new Set((await d(a)).filter(l=>l.status==="pending").map(l=>l.id));let g,w;const y=await Promise.race([new Promise(l=>{w=setTimeout(l,e,{timedOut:!0})}),new Promise((l,S)=>{g=setInterval(()=>{d(a).then(_=>{_.filter(h=>h.status==="pending").filter(h=>!r.has(h.id)).map(h=>h.id).length>0&&(clearInterval(g),clearTimeout(w),setTimeout(()=>{d(a).then(h=>{const b=h.filter(c=>c.status==="pending"),T=b.filter(c=>!r.has(c.id)).map(c=>c.id);l({annotations:b.map(c=>({...c,screenshot:!!c.screenshot})),newIds:T})}).catch(S)},n))}).catch(_=>{clearInterval(g),clearTimeout(w),S(_)})},o)})]);return clearInterval(g),clearTimeout(w),"timedOut"in y?{content:[{text:JSON.stringify({annotations:[],count:0,newCount:0,timedOut:!0}),type:"text"}]}:{content:[{text:JSON.stringify({annotations:y.annotations,count:y.annotations.length,newCount:y.newIds.length},void 0,2),type:"text"}]}});const N=new m;await u.connect(N)};export{U as startMcpServer};
1
+ import{createRequire as k}from"node:module";import{r as d,S as E,a as j,i as M,w as v,d as R,b as x}from"../packem_shared/annotation-store-CeWVEy1K.js";let J;const A=g=>(J??=k(import.meta.url))(g),f=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,O=g=>{if(typeof f<"u"&&f.versions&&f.versions.node){const[p,r]=f.versions.node.split(".").map(Number);if(p>22||p===22&&r>=3||p===20&&r>=16)return f.getBuiltinModule(g)}return A(g)},D=O("node:fs/promises"),I=O("node:path"),q=async()=>{const{McpServer:g}=await import("@modelcontextprotocol/sdk/server/mcp.js"),{StdioServerTransport:p}=await import("@modelcontextprotocol/sdk/server/stdio.js"),{z:r}=await import("zod"),a=process.cwd(),u=new g({name:"visulima-dev-toolbar",version:"1.0.0-alpha.5"});u.tool("get_pending_annotations","Get all pending visual annotations from the dev toolbar. Returns annotations with metadata but not screenshot binary data — use get_screenshot for that.",{},async()=>{const i=(await d(a)).filter(t=>t.status==="pending").map(t=>({...t,screenshot:!!t.screenshot}));return{content:[{text:JSON.stringify({annotations:i,count:i.length},void 0,2),type:"text"}]}}),u.tool("get_screenshot","Get the screenshot image for an annotation as base64 data. Use this after get_pending_annotations to see what the user annotated.",{annotation_id:r.string().describe("The annotation ID to get the screenshot for")},async({annotation_id:i})=>{const t=(await d(a)).find(o=>o.id===i);if(!t)return{content:[{text:JSON.stringify({error:"Annotation not found"}),type:"text"}],isError:!0};if(!t.screenshot)return{content:[{text:JSON.stringify({error:"No screenshot for this annotation"}),type:"text"}],isError:!0};if(!t.screenshot.startsWith(`${E}/`))return{content:[{text:JSON.stringify({error:"Invalid screenshot path"}),type:"text"}],isError:!0};const{base:n}=j(a),e=I.join(n,t.screenshot);if(!M(e,n))return{content:[{text:JSON.stringify({error:"Invalid screenshot path"}),type:"text"}],isError:!0};try{const o=await D.readFile(e),s=I.extname(e).slice(1),m=s==="svg"?"image/svg+xml":s==="jpg"?"image/jpeg":`image/${s}`;return{content:[{data:o.toString("base64"),mimeType:m,type:"image"}]}}catch{return{content:[{text:JSON.stringify({error:"Screenshot file not found"}),type:"text"}],isError:!0}}}),u.tool("resolve_annotation","Mark an annotation as resolved after fixing the issue. Automatically deletes the associated screenshot.",{annotation_id:r.string().describe("The annotation ID to resolve")},async({annotation_id:i})=>v(async()=>{const t=await d(a),n=t.findIndex(o=>o.id===i);if(n===-1)return{content:[{text:JSON.stringify({error:"Annotation not found"}),type:"text"}],isError:!0};const e=t[n];return e.status="resolved",e.resolvedBy="agent",e.updatedAt=new Date().toISOString(),e.screenshot&&(await R(a,e.screenshot),e.screenshot=void 0),t[n]=e,await x(a,t),{content:[{text:JSON.stringify({annotation:e,ok:!0},void 0,2),type:"text"}]}})),u.tool("add_thread_message","Add a message to an annotation's conversation thread. Use this to communicate with the developer about the annotation.",{annotation_id:r.string().describe("The annotation ID to add a message to"),message:r.string().describe("The message content")},async({annotation_id:i,message:t})=>v(async()=>{const n=await d(a),e=n.findIndex(s=>s.id===i);if(e===-1)return{content:[{text:JSON.stringify({error:"Annotation not found"}),type:"text"}],isError:!0};const o=n[e];return o.thread||(o.thread=[]),o.thread.push({content:t,id:crypto.randomUUID(),role:"agent",timestamp:new Date().toISOString()}),o.updatedAt=new Date().toISOString(),n[e]=o,await x(a,n),{content:[{text:JSON.stringify({annotation:o,ok:!0},void 0,2),type:"text"}]}})),u.tool("acknowledge_annotation","Mark an annotation as acknowledged — signals to the developer that the agent has seen it and will work on it. Use this before starting to fix an issue.",{annotation_id:r.string().describe("The annotation ID to acknowledge")},async({annotation_id:i})=>v(async()=>{const t=await d(a),n=t.findIndex(o=>o.id===i);if(n===-1)return{content:[{text:JSON.stringify({error:"Annotation not found"}),type:"text"}],isError:!0};const e=t[n];return e.status="acknowledged",e.updatedAt=new Date().toISOString(),t[n]=e,await x(a,t),{content:[{text:JSON.stringify({annotation:e,ok:!0},void 0,2),type:"text"}]}})),u.tool("watch_annotations","Block until new pending annotations appear. Use this for hands-free mode: call in a loop to automatically detect when the developer adds feedback. Returns after the batch window (default 10s) to collect multiple annotations submitted in quick succession.",{batch_window_ms:r.number().optional().describe("How long to wait for additional annotations after the first one arrives (default: 10000ms)"),timeout_ms:r.number().optional().describe("Maximum time to wait before returning empty (default: 300000ms = 5 minutes)")},async({batch_window_ms:i,timeout_ms:t})=>{const n=i??1e4,e=t??3e5,o=2e3,s=new Set((await d(a)).filter(l=>l.status==="pending").map(l=>l.id));let m,w;const y=await Promise.race([new Promise(l=>{w=setTimeout(l,e,{timedOut:!0})}),new Promise((l,S)=>{m=setInterval(()=>{d(a).then(_=>{_.filter(h=>h.status==="pending").filter(h=>!s.has(h.id)).map(h=>h.id).length>0&&(clearInterval(m),clearTimeout(w),setTimeout(()=>{d(a).then(h=>{const b=h.filter(c=>c.status==="pending"),T=b.filter(c=>!s.has(c.id)).map(c=>c.id);l({annotations:b.map(c=>({...c,screenshot:!!c.screenshot})),newIds:T})}).catch(S)},n))}).catch(_=>{clearInterval(m),clearTimeout(w),S(_)})},o)})]);return clearInterval(m),clearTimeout(w),"timedOut"in y?{content:[{text:JSON.stringify({annotations:[],count:0,newCount:0,timedOut:!0}),type:"text"}]}:{content:[{text:JSON.stringify({annotations:y.annotations,count:y.annotations.length,newCount:y.newIds.length},void 0,2),type:"text"}]}});const N=new p;await u.connect(N)};export{q as startMcpServer};
@@ -0,0 +1 @@
1
+ import{createRequire as f}from"node:module";let p;const y=e=>(p??=f(import.meta.url))(e),i=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,_=e=>{if(typeof i<"u"&&i.versions&&i.versions.node){const[s,t]=i.versions.node.split(".").map(Number);if(s>22||s===22&&t>=3||s===20&&t>=16)return i.getBuiltinModule(e)}return y(e)},o=_("node:fs/promises"),n=_("node:path"),A=".devtoolbar",m="annotations.json",h="screenshots",S=1e3,l=500,u=1e5;function j(e){return typeof e=="string"&&e.length>u?e.slice(0,u):e}const c=e=>{const s=n.join(e,A);return{annotationsFile:n.join(s,m),base:s,screenshotsDir:n.join(s,h)}},T=(e,s)=>{const t=n.resolve(e),r=n.resolve(s);return t.startsWith(r+n.sep)||t===r},N=e=>e.replaceAll(/[^a-z0-9-]/gi,""),w=async e=>{const{screenshotsDir:s}=c(e);await o.mkdir(s,{recursive:!0})};let d=Promise.resolve();const v=async e=>{const s=d;let t;d=new Promise(r=>{t=r}),await s;try{return await e()}finally{t()}},E=async e=>{const{annotationsFile:s}=c(e);try{const t=await o.readFile(s,"utf8"),r=JSON.parse(t);return Array.isArray(r)?r.slice(0,S).map(a=>Array.isArray(a?.thread)&&a.thread.length>l?{...a,thread:a.thread.slice(0,l)}:a):[]}catch{return[]}},F=async(e,s)=>{await w(e);const{annotationsFile:t}=c(e);await o.writeFile(t,JSON.stringify(s,void 0,2),"utf8")},b=async(e,s)=>{if(!s.startsWith(`${h}/`))return;const{base:t}=c(e),r=n.join(t,s);if(T(r,t))try{await o.unlink(r)}catch{}};export{l as M,h as S,c as a,F as b,j as c,b as d,w as e,S as f,T as i,E as r,N as s,v as w};
@@ -0,0 +1,229 @@
1
+ import { ComponentType } from 'preact';
2
+ /**
3
+ * Toolbar placement options (for backward compatibility)
4
+ */
5
+ type ToolbarPlacement = "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
6
+ /**
7
+ * Notification levels
8
+ */
9
+ type NotificationLevel = "info" | "warning" | "error";
10
+ /**
11
+ * Position anchor (which edge the toolbar is attached to)
12
+ * Matches Vue DevTools positioning
13
+ */
14
+ type PositionAnchor = "top" | "bottom" | "left" | "right";
15
+ /**
16
+ * Frame state - matches Vue DevTools DevToolsFrameState
17
+ * @see https://github.com/vuejs/devtools/blob/main/packages/overlay/src/composables/state.ts
18
+ */
19
+ interface FrameState {
20
+ /**
21
+ * Horizontal position as percentage (0-100)
22
+ */
23
+ left: number;
24
+ /**
25
+ * Whether panel is open
26
+ */
27
+ open: boolean;
28
+ /**
29
+ * Which edge the toolbar is anchored to
30
+ */
31
+ position: PositionAnchor;
32
+ /**
33
+ * Vertical position as percentage (0-100)
34
+ */
35
+ top: number;
36
+ }
37
+ /**
38
+ * Configuration options controlling the toolbar's initial appearance.
39
+ */
40
+ interface ToolbarSettings {
41
+ /**
42
+ * Whether toolbar is visible by default
43
+ */
44
+ defaultVisible: boolean;
45
+ /**
46
+ * Toolbar placement on screen
47
+ */
48
+ placement: ToolbarPlacement;
49
+ /**
50
+ * Whether to show notifications
51
+ */
52
+ showNotifications: boolean;
53
+ }
54
+ /**
55
+ * Default toolbar settings
56
+ */
57
+ declare const DEFAULT_TOOLBAR_SETTINGS: ToolbarSettings;
58
+ /**
59
+ * Server helpers available to apps
60
+ */
61
+ interface ServerHelpers {
62
+ /**
63
+ * RPC client for calling server functions
64
+ */
65
+ rpc: { [K in keyof ServerFunctions]: ServerFunctions[K]; };
66
+ }
67
+ /**
68
+ * Props passed to app tooltip (hover mini-canvas) components.
69
+ * Keep compact — tooltip components should be lightweight.
70
+ */
71
+ interface AppTooltipProps {
72
+ /**
73
+ * Server helpers (RPC, etc.)
74
+ */
75
+ helpers: ServerHelpers;
76
+ }
77
+ /**
78
+ * Event target for app communication
79
+ */
80
+ interface ToolbarAppEventTarget extends EventTarget {
81
+ /**
82
+ * Dispatch a custom event
83
+ */
84
+ dispatchEvent: (event: Event) => boolean;
85
+ }
86
+ /**
87
+ * App view configuration
88
+ */
89
+ type AppView = {
90
+ /**
91
+ * Render app inline in shadow DOM (default)
92
+ */
93
+ type: "inline";
94
+ } | {
95
+ /**
96
+ * URL to load in iframe
97
+ */
98
+ src: string;
99
+ /**
100
+ * Render app in iframe for isolation
101
+ */
102
+ type: "iframe";
103
+ };
104
+ /**
105
+ * Props passed to Preact component apps
106
+ */
107
+ interface AppComponentProps {
108
+ /**
109
+ * Event target for app communication
110
+ */
111
+ eventTarget: ToolbarAppEventTarget;
112
+ /**
113
+ * Server helpers (RPC, etc.)
114
+ */
115
+ helpers: ServerHelpers;
116
+ }
117
+ /**
118
+ * Dev toolbar app definition
119
+ */
120
+ interface DevToolbarApp {
121
+ /**
122
+ * Called before app is toggled off
123
+ * Return false to prevent closing
124
+ * @param canvas Shadow root of the app
125
+ * @returns Whether to allow closing
126
+ */
127
+ beforeTogglingOff?: (canvas: ShadowRoot) => boolean | Promise<boolean>;
128
+ /**
129
+ * Preact component for rendering (alternative to init)
130
+ * If provided, this will be used instead of init
131
+ */
132
+ component?: ComponentType<AppComponentProps>;
133
+ /**
134
+ * When true, this app is automatically activated when the toolbar opens for
135
+ * the first time (or when no other app has been activated yet).
136
+ * Only the first registered app with defaultOpen: true is used.
137
+ * @default false
138
+ */
139
+ defaultOpen?: boolean;
140
+ /**
141
+ * Called when the app is unregistered / removed from the toolbar.
142
+ * Use this for final cleanup (event listeners, timers, subscriptions).
143
+ * @param canvas Shadow root of the app
144
+ */
145
+ destroy?: (canvas: ShadowRoot) => Promise<void> | void;
146
+ /**
147
+ * Icon HTML string (SVG)
148
+ */
149
+ icon: string;
150
+ /**
151
+ * Unique identifier for the app
152
+ */
153
+ id: string;
154
+ /**
155
+ * Initialize the app when opened (vanilla JS/CSS/HTML)
156
+ * @param canvas Shadow root to render into
157
+ * @param eventTarget Event target for app communication
158
+ * @param helpers Server helpers (RPC, etc.)
159
+ */
160
+ init?: (canvas: ShadowRoot, eventTarget: ToolbarAppEventTarget, helpers: ServerHelpers) => void | Promise<void>;
161
+ /**
162
+ * Display name of the app
163
+ */
164
+ name: string;
165
+ /**
166
+ * Action button callback — called when the button is activated (active: false → true).
167
+ * When present, clicking the toolbar button will NOT open a panel.
168
+ * Instead the button toggles its active state and calls onClick (activate)
169
+ * or onDeactivate (deactivate).
170
+ */
171
+ onClick?: () => Promise<void> | void;
172
+ /**
173
+ * Called when the action button is deactivated (active: true → false).
174
+ * Only meaningful when onClick is also provided.
175
+ */
176
+ onDeactivate?: () => Promise<void> | void;
177
+ /**
178
+ * Optional hover tooltip component — renders a compact live preview when the
179
+ * user hovers over this app's button in the toolbar pill.
180
+ * The component should be small (≤280px wide) and self-contained.
181
+ * If omitted, hovering shows the native title tooltip only.
182
+ */
183
+ tooltip?: ComponentType<AppTooltipProps>;
184
+ /**
185
+ * App rendering mode
186
+ */
187
+ view?: AppView;
188
+ }
189
+ /**
190
+ * Internal app state (extends DevToolbarApp)
191
+ */
192
+ interface DevToolbarAppState extends DevToolbarApp {
193
+ /**
194
+ * Whether the app is currently active/open
195
+ */
196
+ active: boolean;
197
+ /**
198
+ * Whether this is a built-in app
199
+ */
200
+ builtIn: boolean;
201
+ /**
202
+ * Event target for this app
203
+ */
204
+ eventTarget: ToolbarAppEventTarget;
205
+ /**
206
+ * Notification state
207
+ */
208
+ notification: {
209
+ /**
210
+ * Notification level
211
+ */
212
+ level?: NotificationLevel;
213
+ /**
214
+ * Whether notification is active
215
+ */
216
+ state: boolean;
217
+ };
218
+ /**
219
+ * App initialization status
220
+ */
221
+ status: "ready" | "loading" | "pending" | "error";
222
+ }
223
+ /**
224
+ * Placeholder for ServerFunctions (defined in rpc.ts)
225
+ */
226
+ interface ServerFunctions {
227
+ [key: string]: (...args: any[]) => Promise<any>;
228
+ }
229
+ export { AppView as A, DevToolbarApp as D, FrameState as F, NotificationLevel as N, PositionAnchor as P, ServerFunctions as S, ToolbarSettings as T, DevToolbarAppState as a, DEFAULT_TOOLBAR_SETTINGS as b, ServerHelpers as c, ToolbarAppEventTarget as d, ToolbarPlacement as e };
@@ -0,0 +1 @@
1
+ import{createRequire as _}from"node:module";import{w as b,r as h,c as p,d as E,M as S,b as x,e as O,a as P,s as z,i as v,S as F,f as j}from"./annotation-store-CeWVEy1K.js";import{normalizePath as $}from"vite";let k;const R=r=>(k??=_(import.meta.url))(r),f=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,w=r=>{if(typeof f<"u"&&f.versions&&f.versions.node){const[e,t]=f.versions.node.split(".").map(Number);if(e>22||e===22&&t>=3||e===20&&t>=16)return f.getBuiltinModule(r)}return R(r)},m=w("node:fs/promises"),u=w("node:path"),{createRequire:B}=w("node:module"),L=async r=>h(r.config.root),W=async(r,e)=>b(async()=>{const{root:t}=r.config,s=new Date().toISOString(),o=await h(t);if(o.length>=j)throw new Error(`Annotation limit reached (${j}). Resolve or delete existing annotations before creating new ones.`);const n={accessibility:e.accessibility,boundingBox:e.boundingBox,comment:p(e.comment),computedStyles:p(e.computedStyles),createdAt:s,cssClasses:p(e.cssClasses),elementBoundingBoxes:e.elementBoundingBoxes,elementLabel:p(e.elementLabel),elementPath:p(e.elementPath),elementTag:e.elementTag,frameworkContext:e.frameworkContext,fullPath:p(e.fullPath),id:crypto.randomUUID(),intent:e.intent,isFixed:e.isFixed,isMultiSelect:e.isMultiSelect,nearbyElements:p(e.nearbyElements),nearbyText:p(e.nearbyText),screenshot:e.screenshot,selectedText:p(e.selectedText),severity:e.severity,source:e.source,status:"pending",updatedAt:s,url:e.url,x:e.x,y:e.y};return o.push(n),await x(t,o),n}),U=async(r,e,t)=>b(async()=>{const{root:s}=r.config,o=await h(s),n=o.findIndex(c=>c.id===e);if(n===-1)return null;const i=o[n],a=new Date().toISOString();if(t.comment!==void 0&&(i.comment=p(t.comment)),t.intent!==void 0&&(i.intent=t.intent),t.severity!==void 0&&(i.severity=t.severity),t.status!==void 0&&(i.status=t.status,(t.status==="resolved"||t.status==="dismissed")&&(i.resolvedBy=t.resolvedBy??"human",i.resolvedAt=a,i.screenshot&&(await E(s,i.screenshot),i.screenshot=void 0))),t.threadMessage){if(i.thread||(i.thread=[]),i.thread.length>=S)throw new Error(`Thread message limit reached (${S}) for this annotation.`);i.thread.push({...t.threadMessage,content:p(t.threadMessage.content),id:crypto.randomUUID(),timestamp:a})}return i.updatedAt=a,o[n]=i,await x(s,o),i}),q=async(r,e)=>b(async()=>{const{root:t}=r.config,s=await h(t),o=s.findIndex(i=>i.id===e);if(o===-1)return!1;const n=s[o];return n.screenshot&&await E(t,n.screenshot),s.splice(o,1),await x(t,s),!0}),G=async(r,e,t)=>{const{root:s}=r.config;await O(s);const{base:o,screenshotsDir:n}=P(s),i=z(e);if(!i)throw new Error("Invalid annotation ID");let a,c;if(t.startsWith("data:image/png;base64,"))a="png",c=Buffer.from(t.slice(22),"base64");else if(t.startsWith("data:image/jpeg;base64,"))a="jpg",c=Buffer.from(t.slice(23),"base64");else if(t.startsWith("data:image/webp;base64,"))a="webp",c=Buffer.from(t.slice(23),"base64");else throw new Error("Unsupported screenshot format. Expected PNG, JPEG, or WebP data URL.");const l=`${i}.${a}`,d=u.join(n,l);if(!v(d,o))throw new Error("Invalid screenshot path");return await m.writeFile(d,c),`${F}/${l}`},N=async(r,e)=>{const{root:t}=r.config,s=(await h(t)).find(i=>i.id===e);if(!s?.screenshot||!s.screenshot.startsWith(`${F}/`))return null;const{base:o}=P(t),n=u.join(o,s.screenshot);if(!v(n,o))return null;try{const i=await m.readFile(n),a=u.extname(n).slice(1);return a==="svg"?`data:image/svg+xml,${encodeURIComponent(i.toString("utf8"))}`:`data:${a==="jpg"?"image/jpeg":`image/${a}`};base64,${i.toString("base64")}`}catch{return null}},H=new Set(["apng","avif","bmp","gif","ico","jpeg","jpg","png","svg","tiff","webp"]),X=new Set(["avi","mkv","mov","mp4","ogv","webm"]),J=new Set(["aac","flac","m4a","mp3","ogg","opus","wav"]),V=new Set(["eot","otf","ttf","woff","woff2"]),K=new Set(["css","csv","html","js","json","md","txt","xml"]),g=5e3,A=r=>{const e=r.toLowerCase();return H.has(e)?"image":X.has(e)?"video":J.has(e)?"audio":V.has(e)?"font":K.has(e)?"text":"other"},Q=r=>{let e=0;const t=[];return{acquire:()=>new Promise(s=>{e<r?(e+=1,s()):t.push(()=>{e+=1,s()})}),release:()=>{e-=1,t.shift()?.()}}},T=async(r,e,t,s)=>{if(s.length>=g)return;let o;await t.acquire();try{o=await m.readdir(r,{withFileTypes:!0})}catch{return}finally{t.release()}await Promise.all(o.map(async n=>{if(s.length>=g)return;const i=u.join(r,n.name);if(n.isDirectory())await T(i,e,t,s);else if(n.isFile()||n.isSymbolicLink()){await t.acquire();try{const a=await m.lstat(i);if(a.isSymbolicLink()){let c;try{c=await m.realpath(i)}catch{return}if(!c.startsWith(e+u.sep)&&c!==e)return;const l=await m.stat(i),d=u.relative(e,i),C=`/${$(d)}`,M=u.extname(n.name).slice(1);s.length<g&&s.push({mtime:l.mtimeMs,publicPath:C,size:l.size,type:A(M)})}else if(a.isFile()){const c=u.relative(e,i),l=`/${$(c)}`,d=u.extname(n.name).slice(1);s.length<g&&s.push({mtime:a.mtimeMs,publicPath:l,size:a.size,type:A(d)})}}catch{}finally{t.release()}}}))},Y=async r=>{const{publicDir:e}=r.config;if(!e)return[];let t;try{t=await m.realpath(e)}catch{return[]}const s=[],o=Q(20);return await T(t,t,o,s),s.sort((n,i)=>n.publicPath.localeCompare(i.publicPath)),s},Z=async r=>{const{moduleGraph:e}=r,t=[];for(const s of e.idToModuleMap.values()){if(t.length>=5e3)break;const o=s.id??s.url,n=s.url??s.id;if(!o||!n||o.startsWith("/@"))continue;const i=[];s.importers.forEach(a=>{const c=a.url??a.id;c&&i.push(c)}),t.push({id:o,importerCount:s.importers.size,importerUrls:i,url:n})}return t},ee=async(r,e,t,s,o)=>{const n=u.resolve(r.config.root,e);if(!v(n,r.config.root))throw new Error(`Refusing to open file outside project root: ${e}`);const i=s===void 0?"":`:${s}`,a=t===void 0?"":`:${t}${i}`,c=`${n}${a}`,{default:l}=await import("launch-editor");l(c,o)},te=/\/\*[\s\S]*?\*\//g,se=/\s+/g,D=/@theme\s+default[^{]*\{[\s\S]*?\}/g,y=/(--[\w-]+)\s*:\s*([\s\S]*?);/g,I=r=>{const e={},t=r.replaceAll(te,"");let s=0;for(;s<t.length;){const o=t.indexOf("@theme",s);if(o===-1)break;const n=t.indexOf("{",o);if(n===-1)break;let i=1,a=n+1;for(;a<t.length&&i>0;)t[a]==="{"?i+=1:t[a]==="}"&&(i-=1),a+=1;const c=t.slice(n+1,a-1);y.lastIndex=0;let l=y.exec(c);for(;l!==null;){const d=l[2].replaceAll(se," ").trim();d&&!e[l[1]]&&(e[l[1]]=d),l=y.exec(c)}s=a}return e},re=async r=>{const e=[],t=async(s,o)=>{if(o>4)return;let n;try{n=await m.readdir(s,{encoding:"utf8",withFileTypes:!0})}catch{return}for(const i of n){const a=u.join(s,i.name);if(i.isDirectory()){if(i.name==="node_modules"||i.name===".git"||i.name==="dist")continue;await t(a,o+1)}else if(i.name.endsWith(".css"))try{const c=await m.readFile(a,"utf8");c.includes("@import")&&(c.includes('"tailwindcss"')||c.includes("'tailwindcss'"))&&e.push(a)}catch{}}};return await t(r,0),e},ne=async r=>{const e={};for(const t of r)try{const s=await m.readFile(t,"utf8");D.lastIndex=0;const o=s.replaceAll(D,""),n=I(o);Object.assign(e,n)}catch{}return e},ie=async r=>{const{root:e}=r.config;let t="unknown",s={};const o=["tailwind.config.js","tailwind.config.ts","tailwind.config.mjs","tailwind.config.cjs"];for(const a of o)try{await m.access(u.join(e,a)),t="v3";break}catch{}try{const a=B(import.meta.url).resolve("tailwindcss/theme.css"),c=await m.readFile(a,"utf8");s=I(c),Object.keys(s).length>0&&t!=="v3"&&(t="v4")}catch{}const n=await re(e),i=await ne(n);return{cssFiles:n.map(a=>u.relative(e,a)),customTheme:i,defaultTheme:s,version:t}},oe=r=>{if(Array.isArray(r))return r.filter(e=>e!=null&&(e.find!==void 0||e.replacement!==void 0)).map(e=>({find:e.find instanceof RegExp?e.find.toString():String(e.find??""),replacement:String(e.replacement??"")}));if(r!=null&&typeof r=="object")return Object.fromEntries(Object.entries(r).map(([e,t])=>[e,String(t)]))},ae=r=>{if(typeof r=="boolean"||Array.isArray(r))return r},ce=async r=>{const{config:e}=r,t=[];for(const l of e.plugins.filter(d=>!!d?.name))t.push({enforce:l?.enforce,name:l.name});const s=oe(e.resolve?.alias),o=e.server?.proxy?Object.keys(e.server.proxy):void 0,n=e.server?.hmr,i=n!==!1,a=typeof n=="object"&&n!==null?n.port:void 0,c=ae(e.ssr?.noExternal);return{base:e.base,build:{assetsDir:e.build?.assetsDir,assetsInlineLimit:typeof e.build?.assetsInlineLimit=="number"?e.build.assetsInlineLimit:void 0,chunkSizeWarningLimit:e.build?.chunkSizeWarningLimit,cssCodeSplit:e.build?.cssCodeSplit,emptyOutDir:e.build?.emptyOutDir,minify:e.build?.minify,outDir:e.build?.outDir,reportCompressedSize:e.build?.reportCompressedSize,sourcemap:e.build?.sourcemap,target:e.build?.target},cacheDir:e.cacheDir,css:{devSourcemap:e.css?.devSourcemap,preprocessors:e.css?.preprocessorOptions?Object.keys(e.css.preprocessorOptions):[]},define:e.define,env:e.env,envDir:typeof e.envDir=="string"?e.envDir:void 0,envPrefix:e.envPrefix,esbuild:e.esbuild?{jsx:e.esbuild.jsx,jsxFactory:e.esbuild.jsxFactory,jsxFragment:e.esbuild.jsxFragment,jsxImportSource:e.esbuild.jsxImportSource,target:e.esbuild.target}:void 0,mode:e.mode,optimizeDeps:{exclude:e.optimizeDeps?.exclude,include:e.optimizeDeps?.include},plugins:t,publicDir:e.publicDir,resolve:{alias:s,conditions:e.resolve?.conditions,dedupe:e.resolve?.dedupe,extensions:e.resolve?.extensions,mainFields:e.resolve?.mainFields,preserveSymlinks:e.resolve?.preserveSymlinks},root:e.root,server:{cors:e.server?.cors===void 0?void 0:!!e.server.cors,hmrEnabled:i,hmrPort:a,host:e.server?.host,https:e.server?.https!==void 0,middlewareMode:(()=>{if(e.server?.middlewareMode!==void 0)return typeof e.server.middlewareMode=="object"?!0:!!e.server.middlewareMode})(),open:e.server?.open,origin:e.server?.origin,port:e.server?.port,proxy:o,strictPort:e.server?.strictPort},ssr:e.ssr===void 0?void 0:{external:Array.isArray(e.ssr?.external)?e.ssr.external:void 0,noExternal:c,target:e.ssr?.target}}},le=["astro","cjs","css","cts","html","js","json","jsx","less","md","mdx","mjs","mts","sass","scss","svelte","ts","tsx","txt","vue"],de=(r,e={})=>{const t={createAnnotation:async s=>W(r,s),deleteAnnotation:async s=>q(r,s),getAnnotations:async()=>L(r),getModuleGraph:async()=>Z(r),getScreenshot:async s=>N(r,s),getStaticAssets:async()=>Y(r),getTailwindConfig:async()=>ie(r),getViteConfig:async()=>ce(r),openInEditor:async(s,o,n)=>ee(r,s,o,n,e.editor),saveScreenshot:async(s,o)=>G(r,s,o),updateAnnotation:async(s,o)=>U(r,s,o)};if(e.readFile!==!1){const s=new Set((e.readFile?.extensions??le).map(o=>o.toLowerCase().replace(/^\./,"")));t.readFile=async o=>{const{readFile:n}=await import("node:fs/promises"),{extname:i,resolve:a}=await import("node:path"),{root:c}=r.config,l=a(c,o);if(!v(l,c))throw new Error(`Refusing to read file outside project root: ${o}`);const d=i(l).slice(1).toLowerCase();if(!s.has(d))throw new Error(`Refusing to read file with disallowed extension ".${d}": ${o}`);return n(l,"utf8")}}return t},fe=(r,e,t={})=>{const s={...de(r,t),...e};return r.ws.on("dev-toolbar:rpc",async(o,n)=>{const{args:i,id:a,method:c}=o,l=s[c];if(!l){n.send("dev-toolbar:rpc:error",{error:`Unknown RPC method: ${c}`,id:a});return}try{const d=await l(...i);n.send("dev-toolbar:rpc:response",{id:a,result:d})}catch(d){n.send("dev-toolbar:rpc:error",{error:d instanceof Error?d.message:String(d),id:a})}}),{callClient(o,...n){r.ws.send({data:{args:n,method:o},event:"dev-toolbar:client",type:"custom"})},registerFunction(o,n){s[o]=n},server:r}};export{fe as createServerRPCContext};