browsertrack 0.2.0 → 0.2.1
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/AGENTS.md +2 -0
- package/dist/{chunk-UP5JKCFY.js → chunk-464D4U2U.js} +3 -2
- package/dist/chunk-464D4U2U.js.map +1 -0
- package/dist/{chunk-WB7ZKWK7.js → chunk-INXDWPJW.js} +354 -9
- package/dist/chunk-INXDWPJW.js.map +1 -0
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.cjs +357 -10
- package/dist/client/index.d.ts +36 -4
- package/dist/client/index.js +7 -3
- package/dist/client.iife.js +42 -19
- package/dist/{notes-BMnonq46.d.ts → commands-fjuqKzkm.d.ts} +125 -114
- package/dist/core/index.d.ts +2 -2
- package/dist/daemon/index.d.ts +3 -3
- package/dist/{engine-B43IohQY.d.ts → engine-CmchnMDq.d.ts} +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/mcp/index.d.ts +4 -4
- package/dist/mcp/index.js +1 -1
- package/dist/{projects-D5J-egVN.d.ts → projects-DB7S312i.d.ts} +1 -1
- package/dist/{server-BztYp1Zc.d.ts → server-DiVmTrIR.d.ts} +1 -1
- package/docs/component-resolver.md +108 -0
- package/docs/index.md +1 -0
- package/docs/mcp-reference.md +2 -2
- package/docs/visual-notes.md +36 -0
- package/package.json +1 -1
- package/packages/client/src/client.ts +18 -1
- package/packages/client/src/config.ts +84 -1
- package/packages/client/src/index.ts +4 -1
- package/packages/client/src/interceptors/interaction.ts +3 -0
- package/packages/client/src/notes/inspector.ts +107 -5
- package/packages/client/src/source/resolver.ts +272 -0
- package/packages/core/src/types/events.ts +3 -0
- package/packages/core/src/types/notes.ts +11 -0
- package/packages/mcp/src/handlers.ts +1 -0
- package/test/client/component-resolver.test.ts +141 -0
- package/test/client/interceptors.test.ts +56 -0
- package/dist/chunk-UP5JKCFY.js.map +0 -1
- package/dist/chunk-WB7ZKWK7.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -33,6 +33,7 @@ graph TD
|
|
|
33
33
|
|
|
34
34
|
1. **Find open notes:** Call `list_notes({ status: "OPEN" })`.
|
|
35
35
|
2. **Inspect note context:** Call `get_note({ noteId: "note_xxx" })`.
|
|
36
|
+
- **Check `elementContext.componentSource`**: In React, Vue, or Svelte apps, this gives you the exact component name, source file (`sourceFile`), and line number (`sourceLine`). Open that file directly!
|
|
36
37
|
- Review the target selector (`target.selector`), viewport dimensions, bounding box (`boundingRect`), and screenshot path.
|
|
37
38
|
- Check if layout overflow (`overflow-x`, min-width, flex wrapping) is causing the issue.
|
|
38
39
|
3. **Apply the fix:** Edit the corresponding CSS, style rules, or component markup.
|
|
@@ -49,6 +50,7 @@ When an error or unexpected behavior occurs in the frontend:
|
|
|
49
50
|
1. **List open incidents:** Call `list_incidents({ status: "OPEN" })`.
|
|
50
51
|
2. **Retrieve incident details:** Call `get_incident({ incidentId: "inc_xxx" })`.
|
|
51
52
|
- Inspect stack traces, exact source line/column, last user interactions, and failing network calls.
|
|
53
|
+
- **Check `lastInteractedElement.componentSource`** to see which React/Vue component and source file was clicked right before the crash.
|
|
52
54
|
3. **Inspect interaction timeline (if needed):** Call `get_breadcrumbs({ incidentId: "inc_xxx" })` to see what clicks, inputs, or navigations led to the failure.
|
|
53
55
|
4. **Inspect failed network requests (if needed):** Call `get_network_failures()` to examine 4xx/5xx status codes, request URLs, and response details.
|
|
54
56
|
5. **Apply code fix:** Edit the source file causing the bug.
|
|
@@ -357,7 +357,8 @@ async function handleToolCall(name, args, ctx) {
|
|
|
357
357
|
tag: incident.lastElement.tag,
|
|
358
358
|
visible: incident.lastElement.visible,
|
|
359
359
|
innerText: incident.lastElement.innerText,
|
|
360
|
-
outerHTML: incident.lastElement.outerHTML
|
|
360
|
+
outerHTML: incident.lastElement.outerHTML,
|
|
361
|
+
componentSource: incident.lastElement.componentSource
|
|
361
362
|
} : void 0,
|
|
362
363
|
recentBreadcrumbs: breadcrumbsTimeline,
|
|
363
364
|
networkFailures: incident.networkFailures,
|
|
@@ -738,4 +739,4 @@ export {
|
|
|
738
739
|
handleToolCall,
|
|
739
740
|
createMcpServer
|
|
740
741
|
};
|
|
741
|
-
//# sourceMappingURL=chunk-
|
|
742
|
+
//# sourceMappingURL=chunk-464D4U2U.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/mcp/src/tools.ts","../packages/mcp/src/handlers.ts","../packages/mcp/src/server.ts"],"sourcesContent":["import type { Tool } from '@modelcontextprotocol/sdk/types.js';\n\nexport const TOOLS: Tool[] = [\n {\n name: 'list_projects',\n description: 'List all registered projects tracked by BrowserTrack/BrowserDiag',\n inputSchema: {\n type: 'object',\n properties: {},\n },\n },\n {\n name: 'list_sessions',\n description: 'List active and recent browser sessions connected to the local development daemon',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter sessions by project ID or name' },\n activeOnly: { type: 'boolean', description: 'Show only currently active WebSocket sessions (default: true)' },\n },\n },\n },\n {\n name: 'list_incidents',\n description: 'List recorded browser runtime errors, unhandled rejections, and console errors grouped into incidents',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter by project ID or name' },\n status: {\n type: 'string',\n enum: ['OPEN', 'FIX_ATTEMPTED', 'VERIFYING', 'VERIFIED', 'FAILED', 'INCONCLUSIVE'],\n description: 'Filter by incident status',\n },\n severity: {\n type: 'string',\n enum: ['error', 'warn', 'fatal'],\n description: 'Filter by severity',\n },\n limit: { type: 'number', description: 'Maximum number of incidents to return (default: 20)' },\n },\n },\n },\n {\n name: 'get_incident',\n description: 'Retrieve compact and high-signal debugging context for a specific error incident (stack trace, breadcrumbs, network failures, last interacted element, error screenshot)',\n inputSchema: {\n type: 'object',\n properties: {\n incidentId: { type: 'string', description: 'The unique ID of the incident (e.g. inc_42)' },\n },\n required: ['incidentId'],\n },\n },\n {\n name: 'get_console',\n description: 'Get recent console logs, warnings, and errors from a browser session',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Browser session ID (optional, defaults to active session)' },\n limit: { type: 'number', description: 'Number of console logs to retrieve (default: 30)' },\n },\n },\n },\n {\n name: 'get_network_failures',\n description: 'Get recent failed HTTP network requests (4xx, 5xx, network errors, timeouts, aborts)',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n limit: { type: 'number', description: 'Maximum number of failed requests to return (default: 20)' },\n },\n },\n },\n {\n name: 'get_breadcrumbs',\n description: 'Get the sequence of recent user interactions, navigations, console logs, and network events prior to an error',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n incidentId: { type: 'string', description: 'Incident ID to fetch breadcrumbs from (optional)' },\n limit: { type: 'number', description: 'Maximum number of breadcrumbs (default: 50)' },\n },\n },\n },\n {\n name: 'get_page_state',\n description: 'Query the live page state (URL, route, document title, readyState, active element) from the connected browser session',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'Target session ID (optional, defaults to active)' },\n },\n },\n },\n {\n name: 'capture_element',\n description: 'Capture a screenshot of a specific DOM element or the entire visible page in the active browser tab',\n inputSchema: {\n type: 'object',\n properties: {\n selector: { type: 'string', description: 'CSS selector of the element to capture (e.g. [data-testid=\"user-card\"])' },\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n },\n },\n },\n {\n name: 'verify_incident',\n description: 'Trigger closed-loop verification of a bug fix: reloads the browser, checks if the incident reoccurs, evaluates optional verification probes, and records before/after screenshots',\n inputSchema: {\n type: 'object',\n properties: {\n incidentId: { type: 'string', description: 'The ID of the incident to verify' },\n route: { type: 'string', description: 'Optional route to navigate to for verification (defaults to incident route)' },\n targetSelector: { type: 'string', description: 'Optional element selector to inspect and capture after-fix screenshot' },\n expect: {\n type: 'array',\n description: 'Optional verification probes to evaluate after reload',\n items: {\n type: 'object',\n properties: {\n type: {\n type: 'string',\n enum: ['element_exists', 'element_visible', 'text_contains', 'route_is', 'no_incident'],\n },\n selector: { type: 'string' },\n text: { type: 'string' },\n route: { type: 'string' },\n },\n required: ['type'],\n },\n },\n observationWindowMs: { type: 'number', description: 'Observation window in milliseconds (default: 2000)' },\n },\n required: ['incidentId'],\n },\n },\n {\n name: 'get_verification',\n description: 'Retrieve the latest verification result and before/after screenshot artifacts for an incident',\n inputSchema: {\n type: 'object',\n properties: {\n incidentId: { type: 'string', description: 'The ID of the incident' },\n },\n required: ['incidentId'],\n },\n },\n {\n name: 'list_notes',\n description: 'List visual development notes / screen annotations left on elements, regions, or pages during development',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter notes by project ID or name' },\n scenarioId: { type: 'string', description: 'Filter notes by scenario / flow ID' },\n status: {\n type: 'string',\n enum: ['OPEN', 'IN_PROGRESS', 'VERIFYING', 'RESOLVED', 'FAILED', 'INCONCLUSIVE'],\n description: 'Filter by note status (default: OPEN)',\n },\n limit: { type: 'number', description: 'Maximum number of notes to return (default: 20)' },\n },\n },\n },\n {\n name: 'list_scenarios',\n description: 'List recorded reproduction scenarios and multi-step user interaction flows',\n inputSchema: {\n type: 'object',\n properties: {\n projectId: { type: 'string', description: 'Filter scenarios by project ID or name' },\n status: {\n type: 'string',\n enum: ['OPEN', 'RESOLVED'],\n description: 'Filter by scenario status',\n },\n limit: { type: 'number', description: 'Maximum number of scenarios to return (default: 20)' },\n },\n },\n },\n {\n name: 'get_scenario',\n description: 'Retrieve full chronological step-by-step reproduction flow with selectors, route, screenshots, and action details',\n inputSchema: {\n type: 'object',\n properties: {\n scenarioId: { type: 'string', description: 'The unique ID of the scenario / flow (e.g. scen_123)' },\n },\n required: ['scenarioId'],\n },\n },\n {\n name: 'get_note',\n description: 'Retrieve full debugging context for a visual note (message, route, viewport dimensions, target element selector, DOM context, screenshot file path, project path)',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The unique ID of the visual note (e.g. note_42)' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'resolve_note',\n description: 'Mark a visual note as RESOLVED once the layout or styling issue has been fixed',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note to resolve' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'reopen_note',\n description: 'Reopen a previously resolved visual note',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note to reopen' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'verify_note',\n description: 'Run closed-loop layout & visual verification for a note: checks route, element existence/visibility, evaluates viewport overflow probes, captures after-screenshot, and computes geometry diff',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note to verify' },\n observationWindowMs: { type: 'number', description: 'Observation window in milliseconds (default: 1000)' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'get_note_verification',\n description: 'Retrieve the latest verification result, before/after screenshot references, and layout geometry diff for a visual note',\n inputSchema: {\n type: 'object',\n properties: {\n noteId: { type: 'string', description: 'The ID of the visual note' },\n },\n required: ['noteId'],\n },\n },\n {\n name: 'capture_note_context',\n description: 'Inspect live DOM context, bounding box, overflow, and styles for a target element in the active browser tab',\n inputSchema: {\n type: 'object',\n properties: {\n selector: { type: 'string', description: 'CSS selector of the target element' },\n sessionId: { type: 'string', description: 'Browser session ID (optional)' },\n },\n required: ['selector'],\n },\n },\n];\n","import type { StorageDB } from '../../daemon/src/storage/db.js';\nimport type { VerificationEngine } from '../../daemon/src/verification/engine.js';\nimport type { NoteVerificationEngine } from '../../daemon/src/notes/verification.js';\nimport type { SessionManager } from '../../daemon/src/session/manager.js';\n\nexport interface McpContext {\n db: StorageDB;\n sessionManager?: SessionManager;\n verificationEngine?: VerificationEngine;\n noteVerificationEngine?: NoteVerificationEngine;\n daemonUrl?: string;\n}\n\nexport async function handleToolCall(name: string, args: any, ctx: McpContext): Promise<any> {\n const { db, sessionManager, verificationEngine, noteVerificationEngine } = ctx;\n\n switch (name) {\n case 'list_projects': {\n const projects = db.listProjects();\n return {\n projects: projects.map((p) => ({\n id: p.id,\n name: p.name,\n origin: p.origin,\n path: p.path,\n updatedAt: p.updatedAt,\n })),\n };\n }\n\n case 'list_sessions': {\n const activeOnly = args.activeOnly !== false;\n const sessions = db.listSessions(args.projectId, activeOnly);\n return {\n count: sessions.length,\n sessions: sessions.map((s) => ({\n id: s.id,\n projectId: s.projectId,\n origin: s.origin,\n url: s.url,\n title: s.title,\n active: s.active,\n lastSeenAt: s.lastSeenAt,\n })),\n };\n }\n\n case 'list_incidents': {\n const incidents = db.listIncidents({\n projectId: args.projectId,\n status: args.status,\n severity: args.severity,\n limit: args.limit || 20,\n });\n return {\n total: incidents.length,\n incidents: incidents.map((inc) => ({\n id: inc.id,\n type: inc.type,\n severity: inc.severity,\n message: inc.message,\n source: `${inc.source.file}:${inc.source.line}`,\n route: inc.route,\n status: inc.status,\n occurrences: inc.occurrences,\n firstSeen: inc.firstSeen,\n lastSeen: inc.lastSeen,\n })),\n };\n }\n\n case 'get_incident': {\n const incident = db.getIncident(args.incidentId);\n if (!incident) {\n throw new Error(`Incident '${args.incidentId}' not found.`);\n }\n\n // Compact breadcrumbs summary (last 10 most relevant)\n const breadcrumbsTimeline = incident.breadcrumbs.slice(-15).map((b) => {\n const time = new Date(b.timestamp).toISOString().split('T')[1]?.slice(0, 8);\n return `[${time}] ${b.message}`;\n });\n\n return {\n id: incident.id,\n status: incident.status,\n type: incident.type,\n severity: incident.severity,\n message: incident.message,\n source: {\n file: incident.source.file,\n line: incident.source.line,\n column: incident.source.column,\n },\n route: incident.route,\n occurrences: incident.occurrences,\n firstSeen: incident.firstSeen,\n lastSeen: incident.lastSeen,\n stack: incident.stack,\n lastInteractedElement: incident.lastElement\n ? {\n selector: incident.lastElement.selector,\n tag: incident.lastElement.tag,\n visible: incident.lastElement.visible,\n innerText: incident.lastElement.innerText,\n outerHTML: incident.lastElement.outerHTML,\n componentSource: incident.lastElement.componentSource,\n }\n : undefined,\n recentBreadcrumbs: breadcrumbsTimeline,\n networkFailures: incident.networkFailures,\n screenshot: incident.screenshots?.error,\n };\n }\n\n case 'get_console': {\n const limit = args.limit || 30;\n const events = db.getEvents({\n sessionId: args.sessionId,\n eventType: 'console',\n limit,\n });\n\n return {\n logs: events.map((e) => ({\n level: e.payload.level,\n message: e.payload.message,\n timestamp: new Date(e.timestamp).toISOString(),\n route: e.route,\n })),\n };\n }\n\n case 'get_network_failures': {\n const limit = args.limit || 20;\n const events = db.getEvents({\n sessionId: args.sessionId,\n limit: 100,\n });\n\n const failures = events\n .filter((e) => (e.eventType === 'fetch' || e.eventType === 'xhr') && (e.payload.status >= 400 || e.payload.error))\n .slice(0, limit)\n .map((e) => ({\n url: e.payload.url,\n method: e.payload.method,\n status: e.payload.status,\n error: e.payload.error,\n durationMs: e.payload.durationMs,\n timestamp: new Date(e.timestamp).toISOString(),\n }));\n\n return { failures };\n }\n\n case 'get_breadcrumbs': {\n if (args.incidentId) {\n const incident = db.getIncident(args.incidentId);\n if (!incident) {\n throw new Error(`Incident '${args.incidentId}' not found.`);\n }\n return {\n incidentId: args.incidentId,\n breadcrumbs: incident.breadcrumbs.slice(-(args.limit || 50)),\n };\n }\n\n const events = db.getEvents({\n sessionId: args.sessionId,\n limit: args.limit || 50,\n });\n\n return {\n breadcrumbs: events.map((e) => ({\n type: e.eventType,\n message: e.payload.message || `${e.eventType} on ${e.route}`,\n timestamp: new Date(e.timestamp).toISOString(),\n route: e.route,\n })),\n };\n }\n\n case 'get_page_state': {\n if (!sessionManager) {\n throw new Error('Live browser connection not available: Daemon session manager not attached.');\n }\n let session = args.sessionId ? db.getSession(args.sessionId) : sessionManager.getAnyActiveSession();\n if (!session) {\n throw new Error('No active browser session connected.');\n }\n\n const cmdRes = await sessionManager.sendCommand(session.id, {\n id: `cmd_mcp_${Date.now()}`,\n type: 'get_page_state',\n });\n\n if (!cmdRes.ok) {\n throw new Error(cmdRes.error || 'Failed to retrieve page state from browser.');\n }\n return cmdRes.result;\n }\n\n case 'capture_element': {\n if (!sessionManager) {\n throw new Error('Live browser connection not available: Daemon session manager not attached.');\n }\n let session = args.sessionId ? db.getSession(args.sessionId) : sessionManager.getAnyActiveSession();\n if (!session) {\n throw new Error('No active browser session connected.');\n }\n\n const cmdRes = await sessionManager.sendCommand(session.id, {\n id: `cmd_mcp_${Date.now()}`,\n type: 'capture_element',\n params: { selector: args.selector },\n });\n\n if (!cmdRes.ok) {\n throw new Error(cmdRes.error || cmdRes.reason || 'Failed to capture element screenshot.');\n }\n return {\n ok: true,\n format: cmdRes.result?.format || 'webp',\n width: cmdRes.result?.width,\n height: cmdRes.result?.height,\n dataUrlPreview: cmdRes.result?.dataUrl ? `${cmdRes.result.dataUrl.slice(0, 100)}...` : undefined,\n };\n }\n\n case 'verify_incident': {\n if (!verificationEngine) {\n throw new Error('Verification engine not available: Daemon session manager not attached.');\n }\n\n const res = await verificationEngine.verifyIncident(args.incidentId, {\n route: args.route,\n targetSelector: args.targetSelector,\n expect: args.expect,\n observationWindowMs: args.observationWindowMs,\n });\n\n return res;\n }\n\n case 'get_verification': {\n const v = db.getLatestVerification(args.incidentId);\n if (!v) {\n throw new Error(`No verification records found for incident '${args.incidentId}'.`);\n }\n return v;\n }\n\n case 'list_notes': {\n const notes = db.listNotes({\n projectId: args.projectId,\n scenarioId: args.scenarioId,\n status: args.status,\n limit: args.limit || 20,\n });\n\n return {\n total: notes.length,\n notes: notes.map((n) => ({\n id: n.id,\n type: n.type,\n status: n.status,\n route: n.route,\n scenarioId: n.scenarioId,\n stepNumber: n.stepNumber,\n scenarioTitle: n.scenarioTitle,\n viewport: `${n.viewport.width} × ${n.viewport.height}`,\n target: n.target?.selector || n.type,\n message: n.message,\n screenshotAvailable: !!n.screenshots?.original,\n createdAt: n.createdAt,\n })),\n };\n }\n\n case 'list_scenarios': {\n const scenarios = db.listScenarios({\n projectId: args.projectId,\n status: args.status,\n limit: args.limit || 20,\n });\n\n return {\n total: scenarios.length,\n scenarios: scenarios.map((s) => ({\n id: s.id,\n title: s.title,\n stepsCount: s.stepsCount,\n status: s.status,\n route: s.route,\n firstStepAt: s.firstStepAt,\n lastStepAt: s.lastStepAt,\n })),\n };\n }\n\n case 'get_scenario': {\n const scenario = db.getScenario(args.scenarioId);\n if (!scenario) {\n throw new Error(`Scenario '${args.scenarioId}' not found.`);\n }\n\n return {\n id: scenario.id,\n title: scenario.title,\n stepsCount: scenario.stepsCount,\n status: scenario.status,\n steps: scenario.steps.map((step) => ({\n id: step.id,\n stepNumber: step.stepNumber,\n type: step.type,\n message: step.message,\n route: step.route,\n url: step.url,\n targetSelector: step.target?.selector,\n boundingRect: step.target?.boundingRect || step.region,\n elementContext: step.elementContext,\n screenshot: step.screenshots?.original,\n status: step.status,\n createdAt: step.createdAt,\n })),\n createdAt: scenario.createdAt,\n updatedAt: scenario.updatedAt,\n };\n }\n\n case 'get_note': {\n const note = db.getNote(args.noteId);\n if (!note) {\n throw new Error(`Visual note '${args.noteId}' not found.`);\n }\n\n const project = db.getProject(note.projectId);\n\n return {\n id: note.id,\n type: note.type,\n status: note.status,\n message: note.message,\n scenarioId: note.scenarioId,\n stepNumber: note.stepNumber,\n scenarioTitle: note.scenarioTitle,\n route: note.route,\n url: note.url,\n viewport: note.viewport,\n scroll: note.scroll,\n target: note.target,\n elementContext: note.elementContext,\n region: note.region,\n screenshot: {\n available: !!note.screenshots?.original,\n original: note.screenshots?.original,\n after: note.screenshots?.after,\n },\n project: project\n ? {\n id: project.id,\n name: project.name,\n origin: project.origin,\n path: project.path,\n }\n : undefined,\n relatedIncidentId: note.incidentId,\n createdAt: note.createdAt,\n updatedAt: note.updatedAt,\n resolvedAt: note.resolvedAt,\n };\n }\n\n case 'resolve_note': {\n const note = db.getNote(args.noteId);\n if (!note) {\n throw new Error(`Visual note '${args.noteId}' not found.`);\n }\n db.updateNoteStatus(args.noteId, 'RESOLVED');\n return {\n ok: true,\n noteId: args.noteId,\n status: 'RESOLVED',\n resolvedAt: new Date().toISOString(),\n };\n }\n\n case 'reopen_note': {\n const note = db.getNote(args.noteId);\n if (!note) {\n throw new Error(`Visual note '${args.noteId}' not found.`);\n }\n db.updateNoteStatus(args.noteId, 'OPEN');\n return {\n ok: true,\n noteId: args.noteId,\n status: 'OPEN',\n };\n }\n\n case 'verify_note': {\n if (!noteVerificationEngine) {\n throw new Error('Note verification engine not available: Daemon session manager not attached.');\n }\n const res = await noteVerificationEngine.verifyNote(args.noteId, {\n observationWindowMs: args.observationWindowMs,\n });\n return res;\n }\n\n case 'get_note_verification': {\n const v = db.getLatestNoteVerification(args.noteId);\n if (!v) {\n throw new Error(`No verification records found for visual note '${args.noteId}'.`);\n }\n return v;\n }\n\n case 'capture_note_context': {\n if (!sessionManager) {\n throw new Error('Live browser connection not available: Daemon session manager not attached.');\n }\n let session = args.sessionId ? db.getSession(args.sessionId) : sessionManager.getAnyActiveSession();\n if (!session) {\n throw new Error('No active browser session connected.');\n }\n\n const queryCmd = await sessionManager.sendCommand(session.id, {\n id: `cmd_ctx_${Date.now()}`,\n type: 'query_element',\n params: { selector: args.selector },\n });\n\n const overflowCmd = await sessionManager.sendCommand(session.id, {\n id: `cmd_ovf_${Date.now()}`,\n type: 'check_overflow',\n params: { selector: args.selector },\n });\n\n const styleCmd = await sessionManager.sendCommand(session.id, {\n id: `cmd_sty_${Date.now()}`,\n type: 'get_element_style',\n params: { selector: args.selector },\n });\n\n return {\n selector: args.selector,\n element: queryCmd.result,\n overflow: overflowCmd.result,\n styles: styleCmd.result?.styles,\n };\n }\n\n default:\n throw new Error(`Unknown MCP tool: ${name}`);\n }\n}\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { getDaemonConfig } from '../../daemon/src/config.js';\nimport { StorageDB } from '../../daemon/src/storage/db.js';\nimport { ScreenshotStore } from '../../daemon/src/storage/screenshot-store.js';\nimport { SessionManager } from '../../daemon/src/session/manager.js';\nimport { NotesEngine } from '../../daemon/src/notes/engine.js';\nimport { NoteVerificationEngine } from '../../daemon/src/notes/verification.js';\nimport { VerificationEngine } from '../../daemon/src/verification/engine.js';\nimport type { McpContext } from './handlers.js';\nimport { handleToolCall } from './handlers.js';\nimport { TOOLS } from './tools.js';\n\nexport interface McpServerOptions {\n dbPath?: string;\n context?: Partial<McpContext>;\n}\n\nexport function createMcpServer(options: McpServerOptions = {}) {\n const config = getDaemonConfig({ dbPath: options.dbPath });\n const db = options.context?.db || new StorageDB(config.dbPath);\n const screenshotStore = new ScreenshotStore(config.screenshotsDir);\n const sessionManager = options.context?.sessionManager || new SessionManager(db);\n const verificationEngine = options.context?.verificationEngine || new VerificationEngine(db, sessionManager, screenshotStore);\n const notesEngine = new NotesEngine(db, screenshotStore);\n const noteVerificationEngine = options.context?.noteVerificationEngine || new NoteVerificationEngine(db, sessionManager, notesEngine);\n\n const ctx: McpContext = {\n db,\n sessionManager,\n verificationEngine,\n noteVerificationEngine,\n daemonUrl: `http://${config.host}:${config.port}`,\n ...options.context,\n };\n\n const server = new Server(\n {\n name: 'browsertrack-mcp',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: TOOLS };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n try {\n const result = await handleToolCall(name, args || {}, ctx);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (err: any) {\n return {\n isError: true,\n content: [\n {\n type: 'text',\n text: `Error executing tool ${name}: ${err?.message || String(err)}`,\n },\n ],\n };\n }\n });\n\n return {\n server,\n async startStdio() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -29,12 +29,220 @@ var BreadcrumbBuffer = class {
|
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
// packages/client/src/source/resolver.ts
|
|
33
|
+
function resolveComponentSource(el) {
|
|
34
|
+
if (!el || typeof el !== "object") return void 0;
|
|
35
|
+
const reactInfo = resolveReactComponent(el);
|
|
36
|
+
if (reactInfo) return reactInfo;
|
|
37
|
+
const vueInfo = resolveVueComponent(el);
|
|
38
|
+
if (vueInfo) return vueInfo;
|
|
39
|
+
const svelteInfo = resolveSvelteComponent(el);
|
|
40
|
+
if (svelteInfo) return svelteInfo;
|
|
41
|
+
if (el.tagName && el.tagName.includes("-")) {
|
|
42
|
+
return {
|
|
43
|
+
framework: "web-component",
|
|
44
|
+
componentName: el.tagName.toLowerCase(),
|
|
45
|
+
hierarchy: [el.tagName.toLowerCase()]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const attrInfo = resolveDataAttributeComponent(el);
|
|
49
|
+
if (attrInfo) return attrInfo;
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
function resolveReactComponent(el) {
|
|
53
|
+
try {
|
|
54
|
+
const fiberKey = Object.keys(el).find(
|
|
55
|
+
(key) => key.startsWith("__reactFiber$") || key.startsWith("__reactInternalInstance$")
|
|
56
|
+
);
|
|
57
|
+
if (!fiberKey) return void 0;
|
|
58
|
+
const hostFiber = el[fiberKey];
|
|
59
|
+
if (!hostFiber) return void 0;
|
|
60
|
+
let sourceFile;
|
|
61
|
+
let sourceLine;
|
|
62
|
+
let sourceColumn;
|
|
63
|
+
let componentName;
|
|
64
|
+
const hierarchy = [];
|
|
65
|
+
let props;
|
|
66
|
+
if (hostFiber._debugSource) {
|
|
67
|
+
sourceFile = hostFiber._debugSource.fileName;
|
|
68
|
+
sourceLine = hostFiber._debugSource.lineNumber;
|
|
69
|
+
sourceColumn = hostFiber._debugSource.columnNumber;
|
|
70
|
+
}
|
|
71
|
+
if (hostFiber._debugOwner) {
|
|
72
|
+
const ownerType = hostFiber._debugOwner.type;
|
|
73
|
+
componentName = getReactComponentName(ownerType);
|
|
74
|
+
if (!sourceFile && hostFiber._debugOwner._debugSource) {
|
|
75
|
+
sourceFile = hostFiber._debugOwner._debugSource.fileName;
|
|
76
|
+
sourceLine = hostFiber._debugOwner._debugSource.lineNumber;
|
|
77
|
+
sourceColumn = hostFiber._debugOwner._debugSource.columnNumber;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
let curr = hostFiber;
|
|
81
|
+
while (curr) {
|
|
82
|
+
const type = curr.type;
|
|
83
|
+
const name = getReactComponentName(type);
|
|
84
|
+
if (name && !name.startsWith("html:") && name !== "Fragment") {
|
|
85
|
+
if (!componentName) {
|
|
86
|
+
componentName = name;
|
|
87
|
+
}
|
|
88
|
+
if (!hierarchy.includes(name)) {
|
|
89
|
+
hierarchy.unshift(name);
|
|
90
|
+
}
|
|
91
|
+
if (!sourceFile && curr._debugSource) {
|
|
92
|
+
sourceFile = curr._debugSource.fileName;
|
|
93
|
+
sourceLine = curr._debugSource.lineNumber;
|
|
94
|
+
sourceColumn = curr._debugSource.columnNumber;
|
|
95
|
+
}
|
|
96
|
+
if (!props && curr.memoizedProps && typeof curr.memoizedProps === "object") {
|
|
97
|
+
props = sanitizeProps(curr.memoizedProps);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
curr = curr.return;
|
|
101
|
+
}
|
|
102
|
+
if (!componentName && !sourceFile) {
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
framework: "react",
|
|
107
|
+
componentName,
|
|
108
|
+
sourceFile: normalizeFilePath(sourceFile),
|
|
109
|
+
sourceLine,
|
|
110
|
+
sourceColumn,
|
|
111
|
+
hierarchy: hierarchy.length > 0 ? hierarchy : componentName ? [componentName] : void 0,
|
|
112
|
+
props
|
|
113
|
+
};
|
|
114
|
+
} catch {
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function getReactComponentName(type) {
|
|
119
|
+
if (!type) return void 0;
|
|
120
|
+
if (typeof type === "string") return void 0;
|
|
121
|
+
if (type.displayName) return type.displayName;
|
|
122
|
+
if (type.name) return type.name;
|
|
123
|
+
if (type.render?.displayName) return type.render.displayName;
|
|
124
|
+
if (type.render?.name) return type.render.name;
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
function resolveVueComponent(el) {
|
|
128
|
+
try {
|
|
129
|
+
const vueParent = el.__vueParentComponent || el.__vnode?.ctx;
|
|
130
|
+
if (vueParent) {
|
|
131
|
+
const type = vueParent.type || {};
|
|
132
|
+
const componentName = type.name || type.__name || type.displayName || "AnonymousComponent";
|
|
133
|
+
const sourceFile = type.__file;
|
|
134
|
+
const hierarchy = [];
|
|
135
|
+
let curr = vueParent;
|
|
136
|
+
while (curr) {
|
|
137
|
+
const cType = curr.type || {};
|
|
138
|
+
const cName = cType.name || cType.__name || cType.displayName;
|
|
139
|
+
if (cName && !hierarchy.includes(cName)) {
|
|
140
|
+
hierarchy.unshift(cName);
|
|
141
|
+
}
|
|
142
|
+
curr = curr.parent;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
framework: "vue",
|
|
146
|
+
componentName,
|
|
147
|
+
sourceFile: normalizeFilePath(sourceFile),
|
|
148
|
+
hierarchy: hierarchy.length > 0 ? hierarchy : [componentName],
|
|
149
|
+
props: vueParent.props ? sanitizeProps(vueParent.props) : void 0
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const vue2Instance = el.__vue__;
|
|
153
|
+
if (vue2Instance) {
|
|
154
|
+
const options = vue2Instance.$options || {};
|
|
155
|
+
const componentName = options.name || options._componentTag || "VueComponent";
|
|
156
|
+
const sourceFile = options.__file;
|
|
157
|
+
return {
|
|
158
|
+
framework: "vue",
|
|
159
|
+
componentName,
|
|
160
|
+
sourceFile: normalizeFilePath(sourceFile),
|
|
161
|
+
hierarchy: [componentName],
|
|
162
|
+
props: vue2Instance.$props ? sanitizeProps(vue2Instance.$props) : void 0
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return void 0;
|
|
166
|
+
} catch {
|
|
167
|
+
return void 0;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function resolveSvelteComponent(el) {
|
|
171
|
+
try {
|
|
172
|
+
let curr = el;
|
|
173
|
+
while (curr) {
|
|
174
|
+
const meta = curr.__svelte_meta;
|
|
175
|
+
if (meta && meta.loc) {
|
|
176
|
+
return {
|
|
177
|
+
framework: "svelte",
|
|
178
|
+
sourceFile: normalizeFilePath(meta.loc.file),
|
|
179
|
+
sourceLine: meta.loc.line,
|
|
180
|
+
sourceColumn: meta.loc.column,
|
|
181
|
+
componentName: meta.loc.file ? getBaseNameWithoutExt(meta.loc.file) : void 0,
|
|
182
|
+
hierarchy: meta.loc.file ? [getBaseNameWithoutExt(meta.loc.file)] : void 0
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
curr = curr.parentElement;
|
|
186
|
+
}
|
|
187
|
+
return void 0;
|
|
188
|
+
} catch {
|
|
189
|
+
return void 0;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function resolveDataAttributeComponent(el) {
|
|
193
|
+
try {
|
|
194
|
+
const compEl = el.closest("[data-component], [data-component-name], [data-source-file]");
|
|
195
|
+
if (!compEl) return void 0;
|
|
196
|
+
const componentName = compEl.getAttribute("data-component") || compEl.getAttribute("data-component-name") || void 0;
|
|
197
|
+
const sourceFile = compEl.getAttribute("data-source-file") || void 0;
|
|
198
|
+
const lineAttr = compEl.getAttribute("data-source-line");
|
|
199
|
+
const sourceLine = lineAttr ? parseInt(lineAttr, 10) : void 0;
|
|
200
|
+
if (!componentName && !sourceFile) return void 0;
|
|
201
|
+
return {
|
|
202
|
+
framework: "vanilla",
|
|
203
|
+
componentName,
|
|
204
|
+
sourceFile: normalizeFilePath(sourceFile),
|
|
205
|
+
sourceLine: isNaN(sourceLine) ? void 0 : sourceLine,
|
|
206
|
+
hierarchy: componentName ? [componentName] : void 0
|
|
207
|
+
};
|
|
208
|
+
} catch {
|
|
209
|
+
return void 0;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function normalizeFilePath(filePath) {
|
|
213
|
+
if (!filePath) return void 0;
|
|
214
|
+
const cleaned = filePath.split("?")[0];
|
|
215
|
+
return cleaned;
|
|
216
|
+
}
|
|
217
|
+
function getBaseNameWithoutExt(filePath) {
|
|
218
|
+
const parts = filePath.split("/");
|
|
219
|
+
const last = parts[parts.length - 1] || filePath;
|
|
220
|
+
return last.split(".")[0] || last;
|
|
221
|
+
}
|
|
222
|
+
function sanitizeProps(props) {
|
|
223
|
+
const result = {};
|
|
224
|
+
for (const [key, value] of Object.entries(props)) {
|
|
225
|
+
if (key.startsWith("__") || typeof value === "function") continue;
|
|
226
|
+
if (value === null || value === void 0) {
|
|
227
|
+
result[key] = value;
|
|
228
|
+
} else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
229
|
+
result[key] = value;
|
|
230
|
+
} else if (Array.isArray(value)) {
|
|
231
|
+
result[key] = `Array(${value.length})`;
|
|
232
|
+
} else if (typeof value === "object") {
|
|
233
|
+
result[key] = "[Object]";
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
|
|
32
239
|
// packages/client/src/interceptors/interaction.ts
|
|
33
240
|
function extractElementSummary(el) {
|
|
34
241
|
const selector = getSemanticSelector(el);
|
|
35
242
|
const tag = el.tagName.toLowerCase();
|
|
36
243
|
const id = el.id || void 0;
|
|
37
244
|
const classes = Array.from(el.classList || []);
|
|
245
|
+
const componentSource = resolveComponentSource(el);
|
|
38
246
|
let boundingRect = void 0;
|
|
39
247
|
let visible = true;
|
|
40
248
|
try {
|
|
@@ -66,7 +274,8 @@ function extractElementSummary(el) {
|
|
|
66
274
|
boundingRect,
|
|
67
275
|
visible,
|
|
68
276
|
innerText,
|
|
69
|
-
outerHTML
|
|
277
|
+
outerHTML,
|
|
278
|
+
componentSource
|
|
70
279
|
};
|
|
71
280
|
}
|
|
72
281
|
function setupInteractionInterceptors(onInteraction) {
|
|
@@ -384,10 +593,62 @@ var DEFAULT_OPTIONS = {
|
|
|
384
593
|
enabled: true,
|
|
385
594
|
shortcut: "Alt+Click",
|
|
386
595
|
showBadges: true,
|
|
596
|
+
showToolbar: true,
|
|
387
597
|
maskSelectors: ['input[type="password"]', "[data-sensitive]"]
|
|
388
598
|
},
|
|
599
|
+
hidden: false,
|
|
600
|
+
hideQueryParam: void 0,
|
|
389
601
|
debug: false
|
|
390
602
|
};
|
|
603
|
+
var HIDE_VALUES = /* @__PURE__ */ new Set(["0", "false", "hidden", "hide", "off", "none", "disabled", "ui_off", "silent"]);
|
|
604
|
+
function shouldHideUIFromUrl(customParam, searchString) {
|
|
605
|
+
let search = searchString;
|
|
606
|
+
if (search === void 0) {
|
|
607
|
+
if (typeof window === "undefined" || !window.location) return false;
|
|
608
|
+
search = window.location.search;
|
|
609
|
+
}
|
|
610
|
+
if (!search) return false;
|
|
611
|
+
try {
|
|
612
|
+
const params = new URLSearchParams(search);
|
|
613
|
+
if (customParam) {
|
|
614
|
+
const customKeys = Array.isArray(customParam) ? customParam : [customParam];
|
|
615
|
+
for (const key of customKeys) {
|
|
616
|
+
if (params.has(key)) {
|
|
617
|
+
const val = (params.get(key) || "").toLowerCase().trim();
|
|
618
|
+
if (val === "" || val === "1" || val === "true" || HIDE_VALUES.has(val)) {
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
for (const flag of ["no_bt", "no_browsertrack", "hide_bt", "hide_browsertrack"]) {
|
|
625
|
+
if (params.has(flag)) {
|
|
626
|
+
const val = (params.get(flag) || "").toLowerCase().trim();
|
|
627
|
+
if (val === "" || val === "1" || val === "true" || val === "yes") {
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (params.has("bt")) {
|
|
633
|
+
const val = (params.get("bt") || "").toLowerCase().trim();
|
|
634
|
+
if (HIDE_VALUES.has(val)) return true;
|
|
635
|
+
}
|
|
636
|
+
if (params.has("browsertrack")) {
|
|
637
|
+
const val = (params.get("browsertrack") || "").toLowerCase().trim();
|
|
638
|
+
if (HIDE_VALUES.has(val)) return true;
|
|
639
|
+
}
|
|
640
|
+
if (params.has("bt_ui")) {
|
|
641
|
+
const val = (params.get("bt_ui") || "").toLowerCase().trim();
|
|
642
|
+
if (HIDE_VALUES.has(val) || val === "0" || val === "false") return true;
|
|
643
|
+
}
|
|
644
|
+
if (params.has("bt_hide")) {
|
|
645
|
+
const val = (params.get("bt_hide") || "").toLowerCase().trim();
|
|
646
|
+
if (val === "" || val === "1" || val === "true") return true;
|
|
647
|
+
}
|
|
648
|
+
} catch {
|
|
649
|
+
}
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
391
652
|
|
|
392
653
|
// packages/client/src/interceptors/console.ts
|
|
393
654
|
function setupConsoleInterceptors(onConsole) {
|
|
@@ -1054,14 +1315,19 @@ var NoteInspector = class {
|
|
|
1054
1315
|
dragStartY = 0;
|
|
1055
1316
|
savedNotes = [];
|
|
1056
1317
|
showMarkers = true;
|
|
1318
|
+
isHidden = false;
|
|
1057
1319
|
cleanups = [];
|
|
1058
1320
|
constructor(transport, screenshotDriver, options = {}) {
|
|
1059
1321
|
this.transport = transport;
|
|
1060
1322
|
this.screenshotDriver = screenshotDriver;
|
|
1323
|
+
const hiddenByQuery = shouldHideUIFromUrl(options.hideQueryParam);
|
|
1324
|
+
this.isHidden = options.hidden === true || hiddenByQuery;
|
|
1325
|
+
this.showMarkers = options.showBadges !== false && !this.isHidden;
|
|
1061
1326
|
this.options = {
|
|
1062
1327
|
shortcut: "Alt+Click",
|
|
1063
1328
|
maskSelectors: ['input[type="password"]', "[data-sensitive]"],
|
|
1064
1329
|
showToolbar: true,
|
|
1330
|
+
showBadges: true,
|
|
1065
1331
|
...options
|
|
1066
1332
|
};
|
|
1067
1333
|
}
|
|
@@ -1139,12 +1405,48 @@ var NoteInspector = class {
|
|
|
1139
1405
|
this.hideHighlight();
|
|
1140
1406
|
}
|
|
1141
1407
|
}
|
|
1408
|
+
isVisible() {
|
|
1409
|
+
return !this.isHidden;
|
|
1410
|
+
}
|
|
1411
|
+
setVisible(visible) {
|
|
1412
|
+
this.isHidden = !visible;
|
|
1413
|
+
this.showMarkers = this.options.showBadges !== false && !this.isHidden;
|
|
1414
|
+
if (this.container) {
|
|
1415
|
+
this.container.style.display = this.isHidden ? "none" : "block";
|
|
1416
|
+
}
|
|
1417
|
+
if (this.toolbarElement) {
|
|
1418
|
+
this.toolbarElement.style.display = this.isHidden ? "none" : "flex";
|
|
1419
|
+
} else if (!this.isHidden && this.options.showToolbar !== false) {
|
|
1420
|
+
this.createToolbar();
|
|
1421
|
+
}
|
|
1422
|
+
if (this.isHidden) {
|
|
1423
|
+
this.hideHighlight();
|
|
1424
|
+
this.hideRegionOverlay();
|
|
1425
|
+
if (this.modalOverlay && this.shadowRoot) {
|
|
1426
|
+
this.shadowRoot.removeChild(this.modalOverlay);
|
|
1427
|
+
this.modalOverlay = null;
|
|
1428
|
+
}
|
|
1429
|
+
if (this.cardOverlay && this.shadowRoot) {
|
|
1430
|
+
this.shadowRoot.removeChild(this.cardOverlay);
|
|
1431
|
+
this.cardOverlay = null;
|
|
1432
|
+
}
|
|
1433
|
+
if (this.markersContainer) {
|
|
1434
|
+
this.markersContainer.innerHTML = "";
|
|
1435
|
+
}
|
|
1436
|
+
} else {
|
|
1437
|
+
this.renderMarkers();
|
|
1438
|
+
this.updateToolbarCount();
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1142
1441
|
ensureContainer() {
|
|
1143
1442
|
if (typeof document === "undefined") return null;
|
|
1144
1443
|
if (!this.container) {
|
|
1145
1444
|
this.container = document.createElement("div");
|
|
1146
1445
|
this.container.id = "browsertrack-inspector-host";
|
|
1147
1446
|
this.container.style.cssText = "all: initial; position: fixed; top: 0; left: 0; width: 0; height: 0; z-index: 2147483647; pointer-events: none;";
|
|
1447
|
+
if (this.isHidden) {
|
|
1448
|
+
this.container.style.display = "none";
|
|
1449
|
+
}
|
|
1148
1450
|
this.shadowRoot = this.container.attachShadow({ mode: "open" });
|
|
1149
1451
|
const style = document.createElement("style");
|
|
1150
1452
|
style.textContent = `
|
|
@@ -1587,6 +1889,22 @@ var NoteInspector = class {
|
|
|
1587
1889
|
color: #cbd5e1;
|
|
1588
1890
|
}
|
|
1589
1891
|
|
|
1892
|
+
.bt-component-pill {
|
|
1893
|
+
display: flex;
|
|
1894
|
+
align-items: center;
|
|
1895
|
+
gap: 6px;
|
|
1896
|
+
background: rgba(15, 23, 42, 0.95);
|
|
1897
|
+
border: 1px solid rgba(99, 102, 241, 0.35);
|
|
1898
|
+
border-radius: 6px;
|
|
1899
|
+
padding: 6px 10px;
|
|
1900
|
+
font-size: 11.5px;
|
|
1901
|
+
color: #c7d2fe;
|
|
1902
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
1903
|
+
overflow: hidden;
|
|
1904
|
+
text-overflow: ellipsis;
|
|
1905
|
+
white-space: nowrap;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1590
1908
|
.bt-scenario-stepper {
|
|
1591
1909
|
display: flex;
|
|
1592
1910
|
align-items: center;
|
|
@@ -1826,7 +2144,7 @@ var NoteInspector = class {
|
|
|
1826
2144
|
this.toastContainer = document.createElement("div");
|
|
1827
2145
|
this.toastContainer.className = "bt-toast-container";
|
|
1828
2146
|
this.shadowRoot.appendChild(this.toastContainer);
|
|
1829
|
-
if (this.options.showToolbar) {
|
|
2147
|
+
if (this.options.showToolbar && !this.isHidden) {
|
|
1830
2148
|
this.createToolbar();
|
|
1831
2149
|
}
|
|
1832
2150
|
}
|
|
@@ -1927,7 +2245,7 @@ var NoteInspector = class {
|
|
|
1927
2245
|
const root = this.ensureContainer();
|
|
1928
2246
|
if (!root || !this.markersContainer) return;
|
|
1929
2247
|
this.markersContainer.innerHTML = "";
|
|
1930
|
-
if (!this.showMarkers) return;
|
|
2248
|
+
if (!this.showMarkers || this.isHidden) return;
|
|
1931
2249
|
const currentPath = window.location.pathname;
|
|
1932
2250
|
const activeNotes = this.savedNotes.filter(
|
|
1933
2251
|
(n) => n.status === "OPEN" && (n.route === currentPath || !n.route || n.route === "/" || window.location.href.includes(n.route))
|
|
@@ -2058,6 +2376,13 @@ var NoteInspector = class {
|
|
|
2058
2376
|
|
|
2059
2377
|
${scenarioStepsHtml}
|
|
2060
2378
|
|
|
2379
|
+
${note.elementContext?.componentSource?.componentName ? `<div class="bt-component-pill" title="${note.elementContext.componentSource.sourceFile ? note.elementContext.componentSource.sourceFile + (note.elementContext.componentSource.sourceLine ? ":" + note.elementContext.componentSource.sourceLine : "") : note.elementContext.componentSource.componentName}">
|
|
2380
|
+
<span>\u{1F9EC}</span>
|
|
2381
|
+
<span style="font-weight: 600; color: #a5b4fc;"><${note.elementContext.componentSource.componentName}></span>
|
|
2382
|
+
${note.elementContext.componentSource.sourceFile ? `<span style="color: #64748b; font-size: 11px; margin-left: 4px;">${note.elementContext.componentSource.sourceFile}${note.elementContext.componentSource.sourceLine ? ":" + note.elementContext.componentSource.sourceLine : ""}</span>` : ""}
|
|
2383
|
+
${note.elementContext.componentSource.framework ? `<span class="bt-mode-badge" style="background: rgba(99, 102, 241, 0.15); color: #818cf8; margin-left: auto; font-size: 10px;">${note.elementContext.componentSource.framework.toUpperCase()}</span>` : ""}
|
|
2384
|
+
</div>` : ""}
|
|
2385
|
+
|
|
2061
2386
|
<div class="bt-target-pill" title="${contextText}">
|
|
2062
2387
|
<span>\u{1F3F7}\uFE0F</span>
|
|
2063
2388
|
<span class="bt-pill-content">${contextText}</span>
|
|
@@ -2149,7 +2474,7 @@ var NoteInspector = class {
|
|
|
2149
2474
|
}
|
|
2150
2475
|
setupListeners() {
|
|
2151
2476
|
const onMouseMove = (e) => {
|
|
2152
|
-
if (this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
|
|
2477
|
+
if (this.isHidden || this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
|
|
2153
2478
|
const path = e.composedPath ? e.composedPath() : [];
|
|
2154
2479
|
if (this.container && path.includes(this.container)) {
|
|
2155
2480
|
this.hideHighlight();
|
|
@@ -2168,7 +2493,7 @@ var NoteInspector = class {
|
|
|
2168
2493
|
}
|
|
2169
2494
|
};
|
|
2170
2495
|
const onClick = (e) => {
|
|
2171
|
-
if (this.modalOverlay || this.cardOverlay) return;
|
|
2496
|
+
if (this.isHidden || this.modalOverlay || this.cardOverlay) return;
|
|
2172
2497
|
const path = e.composedPath ? e.composedPath() : [];
|
|
2173
2498
|
if (this.container && path.includes(this.container)) {
|
|
2174
2499
|
return;
|
|
@@ -2356,10 +2681,12 @@ var NoteInspector = class {
|
|
|
2356
2681
|
if (noteType === "element") {
|
|
2357
2682
|
this.updateHighlight(targetEl);
|
|
2358
2683
|
}
|
|
2684
|
+
const comp = noteType === "element" ? resolveComponentSource(targetEl) : void 0;
|
|
2685
|
+
const compPrefix = comp?.componentName ? `\u{1F9EC} <${comp.componentName}>${comp.sourceFile ? " (" + comp.sourceFile + (comp.sourceLine ? ":" + comp.sourceLine : "") + ")" : ""} \xB7 ` : "";
|
|
2359
2686
|
this.renderNoteModal({
|
|
2360
2687
|
title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Visual Note",
|
|
2361
2688
|
modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : noteType.toUpperCase(),
|
|
2362
|
-
pillText: noteType === "page" ? `Page Viewport: ${window.innerWidth} \xD7 ${window.innerHeight} px` : `${getSemanticSelector(targetEl)} (${Math.round(targetEl.getBoundingClientRect().width)}\xD7${Math.round(targetEl.getBoundingClientRect().height)}) \xB7 Viewport: ${window.innerWidth}\xD7${window.innerHeight}`,
|
|
2689
|
+
pillText: noteType === "page" ? `Page Viewport: ${window.innerWidth} \xD7 ${window.innerHeight} px` : `${compPrefix}${getSemanticSelector(targetEl)} (${Math.round(targetEl.getBoundingClientRect().width)}\xD7${Math.round(targetEl.getBoundingClientRect().height)}) \xB7 Viewport: ${window.innerWidth}\xD7${window.innerHeight}`,
|
|
2363
2690
|
onSave: async (message, action) => {
|
|
2364
2691
|
let scenarioParam;
|
|
2365
2692
|
if (action === "next_step" && !this.activeScenario) {
|
|
@@ -2629,12 +2956,14 @@ var NoteInspector = class {
|
|
|
2629
2956
|
if (innerText && innerText.length > 200) {
|
|
2630
2957
|
innerText = truncate(innerText, 200);
|
|
2631
2958
|
}
|
|
2959
|
+
const componentSource = resolveComponentSource(el);
|
|
2632
2960
|
return {
|
|
2633
2961
|
selector,
|
|
2634
2962
|
tag: el.tagName.toLowerCase(),
|
|
2635
2963
|
attributes,
|
|
2636
2964
|
outerHTML,
|
|
2637
2965
|
innerText,
|
|
2966
|
+
componentSource,
|
|
2638
2967
|
parent: el.parentElement ? {
|
|
2639
2968
|
selector: getSemanticSelector(el.parentElement),
|
|
2640
2969
|
tag: el.parentElement.tagName.toLowerCase()
|
|
@@ -2717,7 +3046,11 @@ var BrowserTrackClient = class {
|
|
|
2717
3046
|
if (this.options.notes.enabled) {
|
|
2718
3047
|
this.inspector = new NoteInspector(this.transport, this.screenshotDriver, {
|
|
2719
3048
|
shortcut: this.options.notes.shortcut,
|
|
2720
|
-
maskSelectors: this.options.notes.maskSelectors
|
|
3049
|
+
maskSelectors: this.options.notes.maskSelectors,
|
|
3050
|
+
showToolbar: this.options.notes.showToolbar,
|
|
3051
|
+
showBadges: this.options.notes.showBadges,
|
|
3052
|
+
hidden: this.options.hidden,
|
|
3053
|
+
hideQueryParam: this.options.hideQueryParam
|
|
2721
3054
|
});
|
|
2722
3055
|
}
|
|
2723
3056
|
}
|
|
@@ -2783,6 +3116,14 @@ var BrowserTrackClient = class {
|
|
|
2783
3116
|
this.inspector.setMode("element");
|
|
2784
3117
|
}
|
|
2785
3118
|
}
|
|
3119
|
+
isUIVisible() {
|
|
3120
|
+
return this.inspector ? this.inspector.isVisible() : false;
|
|
3121
|
+
}
|
|
3122
|
+
setUIVisible(visible) {
|
|
3123
|
+
if (this.inspector) {
|
|
3124
|
+
this.inspector.setVisible(visible);
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
2786
3127
|
setInspectMode(mode) {
|
|
2787
3128
|
if (this.inspector) {
|
|
2788
3129
|
this.inspector.setMode(mode);
|
|
@@ -2923,8 +3264,10 @@ if (typeof window !== "undefined") {
|
|
|
2923
3264
|
const autoInit = currentScript?.getAttribute("data-auto-init") !== "false";
|
|
2924
3265
|
const daemonUrl = currentScript?.getAttribute("data-daemon-url") || void 0;
|
|
2925
3266
|
const projectId = currentScript?.getAttribute("data-project-id") || void 0;
|
|
3267
|
+
const hidden = currentScript?.getAttribute("data-hidden") === "true";
|
|
3268
|
+
const hideQueryParam = currentScript?.getAttribute("data-hide-query-param") || void 0;
|
|
2926
3269
|
if (autoInit) {
|
|
2927
|
-
init({ daemonUrl, projectId });
|
|
3270
|
+
init({ daemonUrl, projectId, hidden, hideQueryParam });
|
|
2928
3271
|
}
|
|
2929
3272
|
} catch {
|
|
2930
3273
|
}
|
|
@@ -2932,11 +3275,13 @@ if (typeof window !== "undefined") {
|
|
|
2932
3275
|
|
|
2933
3276
|
export {
|
|
2934
3277
|
BreadcrumbBuffer,
|
|
3278
|
+
resolveComponentSource,
|
|
2935
3279
|
DEFAULT_OPTIONS,
|
|
3280
|
+
shouldHideUIFromUrl,
|
|
2936
3281
|
BrowserScriptScreenshotDriver,
|
|
2937
3282
|
NoteInspector,
|
|
2938
3283
|
BrowserTrackClient,
|
|
2939
3284
|
init,
|
|
2940
3285
|
getClient
|
|
2941
3286
|
};
|
|
2942
|
-
//# sourceMappingURL=chunk-
|
|
3287
|
+
//# sourceMappingURL=chunk-INXDWPJW.js.map
|