chrometools-mcp 2.4.2 → 2.5.0
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 +120 -0
- package/README.md +92 -4
- package/RELEASE_NOTES_v2.5.0.md +109 -0
- package/element-finder-utils.js +138 -28
- package/figma-tools.js +120 -0
- package/index.js +391 -8
- package/npm_publish_output.txt +0 -0
- package/package.json +1 -1
- package/server/tool-definitions.js +62 -5
- package/server/tool-groups.js +3 -2
- package/server/tool-schemas.js +30 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,126 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.5.0] - 2026-01-21
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **`selectOption` tool** - Select options in HTML dropdown elements with intelligent priority-based selection
|
|
9
|
+
- Parameters: `selector` (required), `value`, `text`, or `index` (specify at least one)
|
|
10
|
+
- Selection priority: value → text → index (tries value first, falls back to text, then index)
|
|
11
|
+
- Automatically triggers `input` and `change` events for React and other frameworks
|
|
12
|
+
- Returns selected option details (value, text, index)
|
|
13
|
+
- Location: `index.js:911-979`, schemas in `server/tool-schemas.js:40-45`, definitions in `server/tool-definitions.js:234-247`, tool group in `server/tool-groups.js:10`
|
|
14
|
+
|
|
15
|
+
- **`drag` tool** - Drag element by mouse (click-hold-move-release) in any direction
|
|
16
|
+
- Parameters: `selector` (required), `direction` (required: 'up', 'down', 'left', 'right', 'up-left', 'up-right', 'down-left', 'down-right'), `distance` (optional, default: 100), `duration` (optional, default: 500ms)
|
|
17
|
+
- Emulates real mouse drag: moves to element center, presses button, drags, releases button
|
|
18
|
+
- Supports 8 directions including 4 diagonal directions for maximum flexibility
|
|
19
|
+
- Use for: interactive maps (Google Maps, Leaflet), Gantt charts, SVG diagrams, canvas, drag-to-pan interfaces
|
|
20
|
+
- NOT for: standard overflow scrollbars (use `scrollTo` or `scrollHorizontal` instead)
|
|
21
|
+
- Location: `index.js:982-1091`, schemas in `server/tool-schemas.js:47-53`, definitions in `server/tool-definitions.js:248-261`, tool group in `server/tool-groups.js:10`
|
|
22
|
+
|
|
23
|
+
- **`scrollHorizontal` tool** - Scroll element horizontally for tables, carousels, and wide content
|
|
24
|
+
- Parameters: `selector` (required), `direction` (required: 'left' or 'right'), `amount` (required: pixels or 'full'), `behavior` (optional: 'auto' or 'smooth')
|
|
25
|
+
- Supports precise pixel-based scrolling or 'full' to scroll to the end
|
|
26
|
+
- Returns detailed scroll state: position, total width, visible width, and scroll availability (canScrollLeft, canScrollRight)
|
|
27
|
+
- Uses native `scrollTo` API with smooth/auto behavior options
|
|
28
|
+
- Location: `index.js:1055-1117`, schemas in `server/tool-schemas.js:55-60`, definitions in `server/tool-definitions.js:262-275`, tool group in `server/tool-groups.js:10`
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- **🔥 CRITICAL: Fixed `drag` tool implementation** - Now correctly emulates mouse drag instead of changing scrollLeft/scrollTop
|
|
32
|
+
- Problem: Previous implementation used `scrollLeft`/`scrollTop` animation which only works for `overflow: auto/scroll` containers
|
|
33
|
+
- Impact: **Did not work with custom drag-to-scroll interfaces** like:
|
|
34
|
+
- ❌ Interactive maps (Google Maps, Leaflet, Mapbox)
|
|
35
|
+
- ❌ Gantt charts and timeline diagrams (SVG-based)
|
|
36
|
+
- ❌ Canvas elements with pan/zoom
|
|
37
|
+
- ❌ Custom drag handlers (React DnD, interact.js)
|
|
38
|
+
- Solution: Complete rewrite using Puppeteer's `page.mouse` API:
|
|
39
|
+
1. Finds element center position
|
|
40
|
+
2. Moves mouse to center (`page.mouse.move`)
|
|
41
|
+
3. Presses mouse button (`page.mouse.down`)
|
|
42
|
+
4. Drags to target position with smooth motion (`page.mouse.move` with steps)
|
|
43
|
+
5. Releases mouse button (`page.mouse.up`)
|
|
44
|
+
- Result: **Now works with ANY drag-scrollable element** including SVG diagrams, maps, and custom implementations
|
|
45
|
+
- Location: `index.js:982-1091`, updated description in `README.md:277-285`
|
|
46
|
+
- Reported by: User testing on Gantt chart with `<svg class="gantt">` element
|
|
47
|
+
|
|
48
|
+
- **Fixed `analyzePage` crash with `includeAll: true` on SVG elements** - Now handles both HTML and SVG className types
|
|
49
|
+
- Problem: `className.split is not a function` error when page contains SVG elements
|
|
50
|
+
- Cause: SVG elements have `className` as `SVGAnimatedString` object (with `.baseVal` property), not a string
|
|
51
|
+
- Solution: Added type checking - uses `className.baseVal` for SVG elements, direct string for HTML
|
|
52
|
+
- Location: `index.js:2126-2137`
|
|
53
|
+
|
|
54
|
+
- **🔥 CRITICAL: Fixed Tailwind CSS selector generation bug** - `getUniqueSelectorInPage` now works correctly with Tailwind/utility-first CSS frameworks
|
|
55
|
+
- Problem: Generated invalid CSS selectors like `button.hover:bg-blue-700` containing special characters (`:`, `/`, `[]`)
|
|
56
|
+
- Impact: **ALL AI-powered tools failed** with `SyntaxError: invalid selector` on Tailwind/styled-components apps:
|
|
57
|
+
- ❌ `analyzePage` - couldn't read page state
|
|
58
|
+
- ❌ `findElementsByText` - couldn't find elements by text
|
|
59
|
+
- ❌ `smartFindElement` - couldn't find elements by description
|
|
60
|
+
- ❌ `getAllInteractiveElements` - couldn't list interactive elements
|
|
61
|
+
- Solution: Complete rewrite of selector generation logic with intelligent filtering:
|
|
62
|
+
1. **New priority hierarchy** (most reliable first):
|
|
63
|
+
- `#id` (ID attribute)
|
|
64
|
+
- `[data-testid="..."]` (test IDs, very common in modern apps)
|
|
65
|
+
- `[data-*="..."]` (other data attributes)
|
|
66
|
+
- `[aria-label="..."]` (accessibility labels)
|
|
67
|
+
- `[role="..."]` (ARIA roles)
|
|
68
|
+
- `[name="..."]` (form element names)
|
|
69
|
+
- `tag.semantic-class` (non-Tailwind classes only)
|
|
70
|
+
- `tag:nth-of-type(n)` (fallback with path)
|
|
71
|
+
2. **Tailwind class filtering** - New `isTailwindClass()` function detects and excludes:
|
|
72
|
+
- Variant classes with `:` (hover:, focus:, md:, lg:, etc.)
|
|
73
|
+
- Fraction classes with `/` (w-1/2, space-x-1/2)
|
|
74
|
+
- Arbitrary values with `[]` (bg-[#1da1f2], w-[500px])
|
|
75
|
+
- 60+ common Tailwind prefixes (bg-, text-, p-, m-, flex-, etc.)
|
|
76
|
+
3. **CSS.escape() integration** - All selectors properly escaped (with fallback for old browsers)
|
|
77
|
+
4. **Semantic attribute prioritization** - Prefers stable, meaningful selectors over utility classes
|
|
78
|
+
- Result: **Unblocks testing of ALL modern apps** using Tailwind, styled-components, CSS modules, Emotion, etc.
|
|
79
|
+
- Location: `element-finder-utils.js:316-509` (complete rewrite, ~200 lines)
|
|
80
|
+
- Reported by: AI agent encountering `SyntaxError` on every tool call in React+Tailwind app
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
- **Improved tool descriptions for better AI agent behavior** - Prevents premature use of `executeScript`
|
|
84
|
+
- `click` - Emphasized as PRIMARY tool for clicking, works with React/Vue/Angular synthetic events
|
|
85
|
+
- `type` - Emphasized as PRIMARY tool for input, updates React hooks and Vue reactive data correctly
|
|
86
|
+
- `executeScript` - ⚠️ Marked as LAST RESORT with strict warnings, never use for clicking/typing/reading
|
|
87
|
+
- `findElementsByText` - Highlighted as alternative to executeScript for finding elements
|
|
88
|
+
- `analyzePage` - Emphasized as PRIMARY tool for reading page state, more efficient than executeScript
|
|
89
|
+
- Location: `server/tool-definitions.js:31,45,162,510,489`
|
|
90
|
+
|
|
91
|
+
- **Added "Tool Usage Priority" section to README** - Clear hierarchy preventing executeScript abuse
|
|
92
|
+
- Three workflows: Clicking/Interaction, Filling Forms, Reading Page State
|
|
93
|
+
- Each shows specialized tools first (click, type, analyzePage), executeScript last
|
|
94
|
+
- Explains why specialized tools work with React/Vue/Angular while executeScript may fail
|
|
95
|
+
- Location: `README.md:116-147`
|
|
96
|
+
|
|
97
|
+
- **`analyzePage` enhancement** - Now detects and reports HTML select elements with all available options
|
|
98
|
+
- Select fields in forms and inputs sections now include `options` array with value, text, index, selected, and disabled status
|
|
99
|
+
- Includes `selectedIndex`, `selectedValue`, and `selectedText` for current selection
|
|
100
|
+
- Enables AI agents to see all dropdown options without additional queries
|
|
101
|
+
- Makes `selectOption` tool usage more intelligent and reliable
|
|
102
|
+
- Location: `index.js:1632-1660` (forms), `index.js:1691-1713` (inputs)
|
|
103
|
+
|
|
104
|
+
- **Tool groups** - Added 3 new tools to `interaction` group: `selectOption`, `dragScroll`, `scrollHorizontal`
|
|
105
|
+
- Total interaction tools: 8 (was 5)
|
|
106
|
+
- Total tools in project: 44+ (was 40+)
|
|
107
|
+
- Location: `server/tool-groups.js:10`
|
|
108
|
+
|
|
109
|
+
- **`convertFigmaToCode` tool** - Convert Figma designs to React/Tailwind code with AI assistance
|
|
110
|
+
- Parameters: `figmaToken` (optional), `fileKey` (required), `nodeId` (required), `framework` (optional: 'react', 'react-typescript', 'html'), `includeComments` (optional, default: true)
|
|
111
|
+
- Fetches design structure (layout, colors, typography, spacing) and rendered image at 2x scale
|
|
112
|
+
- Returns AI-optimized instruction prompt with simplified JSON structure and framework-specific guidelines
|
|
113
|
+
- Supports React (JavaScript), React (TypeScript), and pure HTML with Tailwind CSS
|
|
114
|
+
- Generates clean, semantic code with proper spacing, accessibility, and component structure
|
|
115
|
+
- Uses existing Figma token mechanism (from parameter or FIGMA_TOKEN env var)
|
|
116
|
+
- Location: `index.js:1676-1779`, schemas in `server/tool-schemas.js:225-231`, definitions in `server/tool-definitions.js:448-462`, tool group in `server/tool-groups.js:53`, helper in `figma-tools.js:381-499`
|
|
117
|
+
|
|
118
|
+
- **`simplifyNode` helper** - New function in figma-tools.js for code generation
|
|
119
|
+
- Recursively extracts essential design properties from Figma node structure
|
|
120
|
+
- Captures: layout (flexbox), dimensions, padding/gaps, colors (fills/strokes), effects (shadows), typography, border radius
|
|
121
|
+
- Filters out invisible elements and rounds numeric values for cleaner output
|
|
122
|
+
- Used by `convertFigmaToCode` to provide AI with actionable design data
|
|
123
|
+
- Location: `figma-tools.js:381-499`
|
|
124
|
+
|
|
5
125
|
## [2.4.2] - 2026-01-05
|
|
6
126
|
|
|
7
127
|
### Added
|
package/README.md
CHANGED
|
@@ -8,10 +8,10 @@ MCP server for Chrome automation using Puppeteer with persistent browser session
|
|
|
8
8
|
- [Usage](#usage)
|
|
9
9
|
- [AI Optimization Features](#ai-optimization-features) ⭐ **NEW**
|
|
10
10
|
- [Scenario Recorder](#scenario-recorder) ⭐ **NEW** - Visual UI-based recording with smart optimization
|
|
11
|
-
- [Available Tools](#available-tools) - **
|
|
11
|
+
- [Available Tools](#available-tools) - **44+ Tools Total**
|
|
12
12
|
- [AI-Powered Tools](#ai-powered-tools) ⭐ **NEW** - smartFindElement, analyzePage, getAllInteractiveElements, findElementsByText
|
|
13
13
|
- [Core Tools](#1-core-tools) - ping, openBrowser
|
|
14
|
-
- [Interaction Tools](#2-interaction-tools) - click, type, scrollTo
|
|
14
|
+
- [Interaction Tools](#2-interaction-tools) - click, type, scrollTo, selectOption, dragScroll, scrollHorizontal
|
|
15
15
|
- [Inspection Tools](#3-inspection-tools) - getElement, getComputedCss, getBoxModel, screenshot
|
|
16
16
|
- [Advanced Tools](#4-advanced-tools) - executeScript, getConsoleLogs, listNetworkRequests, getNetworkRequest, filterNetworkRequests, hover, setStyles, setViewport, getViewport, navigateTo
|
|
17
17
|
- [Recorder Tools](#6-recorder-tools) ⭐ **NEW** - enableRecorder, executeScenario, listScenarios, searchScenarios, getScenarioInfo, deleteScenario, exportScenarioAsCode, appendScenarioToFile, generatePageObject
|
|
@@ -113,6 +113,39 @@ executeScenario({ name: "login_flow", parameters: { email: "user@test.com" } })
|
|
|
113
113
|
|
|
114
114
|
## Available Tools
|
|
115
115
|
|
|
116
|
+
### ⚠️ Tool Usage Priority
|
|
117
|
+
|
|
118
|
+
**CRITICAL: Always use specialized tools first. Never jump to `executeScript` as first choice.**
|
|
119
|
+
|
|
120
|
+
#### For Clicking/Interaction
|
|
121
|
+
1. ✅ **`click()`** - PRIMARY tool for all clicks
|
|
122
|
+
- Works correctly with React/Vue/Angular synthetic events
|
|
123
|
+
- Handles button clicks, link navigation, form submissions
|
|
124
|
+
2. ✅ **`findElementsByText()` + action** - When selector is unknown, find by text
|
|
125
|
+
3. ⚠️ **`executeScript()`** - LAST RESORT, only if above failed
|
|
126
|
+
|
|
127
|
+
#### For Filling Forms
|
|
128
|
+
1. ✅ **`type()`** - PRIMARY tool for all text input
|
|
129
|
+
- Properly updates React hooks, Vue reactive data
|
|
130
|
+
- Auto-clears field before typing (configurable)
|
|
131
|
+
2. ⚠️ **`executeScript()`** - LAST RESORT, only if above failed
|
|
132
|
+
|
|
133
|
+
#### For Reading Page State
|
|
134
|
+
1. ✅ **`analyzePage()`** - PRIMARY tool for reading page content
|
|
135
|
+
- Gets forms, inputs, buttons, links with current values
|
|
136
|
+
- Use `refresh: true` after interactions to see updated state
|
|
137
|
+
- Efficient: 2-5k tokens vs screenshot 15-25k
|
|
138
|
+
2. ✅ **`findElementsByText()`** - Find specific elements by visible text
|
|
139
|
+
3. ✅ **`getElement()`** - Get HTML of specific element
|
|
140
|
+
4. ⚠️ **`executeScript()`** - LAST RESORT, only if above failed
|
|
141
|
+
|
|
142
|
+
**Why specialized tools matter:**
|
|
143
|
+
- ✅ Trigger proper browser events (click, input, change)
|
|
144
|
+
- ✅ Work with React/Vue/Angular synthetic event systems
|
|
145
|
+
- ✅ Update framework state correctly (React hooks, Vue reactivity)
|
|
146
|
+
- ✅ Handle animations, navigation, and async updates
|
|
147
|
+
- ❌ `executeScript` bypasses framework events and may fail silently
|
|
148
|
+
|
|
116
149
|
### AI-Powered Tools
|
|
117
150
|
|
|
118
151
|
#### smartFindElement ⭐
|
|
@@ -229,6 +262,40 @@ Scroll page to bring element into view.
|
|
|
229
262
|
- **Use case**: Lazy loading, sticky elements, visibility checks
|
|
230
263
|
- **Returns**: Final scroll position
|
|
231
264
|
|
|
265
|
+
#### selectOption
|
|
266
|
+
Select option in dropdown (HTML select elements). Automatically detected by analyzePage with all available options.
|
|
267
|
+
- **Parameters**:
|
|
268
|
+
- `selector` (required): CSS selector for select element
|
|
269
|
+
- `value` (optional): Option value attribute (priority 1)
|
|
270
|
+
- `text` (optional): Option text content (priority 2)
|
|
271
|
+
- `index` (optional): Option index, 0-based (priority 3)
|
|
272
|
+
- **Use case**: Form dropdowns, filtering, selection menus
|
|
273
|
+
- **Returns**: Selected option details (value, text, index)
|
|
274
|
+
- **Selection priority**: If multiple parameters specified, tries value → text → index
|
|
275
|
+
- **AI Integration**: Use `analyzePage` to see all available options with their values, text, and indices
|
|
276
|
+
|
|
277
|
+
#### drag
|
|
278
|
+
Drag element by mouse (click-hold-move-release). Simulates real mouse drag, not scrollbar scrolling.
|
|
279
|
+
- **Parameters**:
|
|
280
|
+
- `selector` (required): CSS selector for element to drag
|
|
281
|
+
- `direction` (required): 'up', 'down', 'left', 'right', 'up-left', 'up-right', 'down-left', 'down-right'
|
|
282
|
+
- `distance` (optional): Distance in pixels (default: 100)
|
|
283
|
+
- `duration` (optional): Drag duration in milliseconds (default: 500)
|
|
284
|
+
- **Use case**: Interactive maps (Google Maps, Leaflet), Gantt charts, SVG diagrams, canvas elements, sliders, drag-to-pan interfaces
|
|
285
|
+
- **How it works**: Moves mouse to element center, presses mouse button, drags to target position, releases button
|
|
286
|
+
- **NOT for**: Standard overflow scrollbars (use `scrollTo` or `scrollHorizontal` instead)
|
|
287
|
+
- **Returns**: Start/end mouse positions and drag delta
|
|
288
|
+
|
|
289
|
+
#### scrollHorizontal
|
|
290
|
+
Scroll element horizontally (for tables, carousels, wide content).
|
|
291
|
+
- **Parameters**:
|
|
292
|
+
- `selector` (required): CSS selector for element to scroll
|
|
293
|
+
- `direction` (required): 'left' or 'right'
|
|
294
|
+
- `amount` (required): Number of pixels to scroll, or 'full' to scroll to the end
|
|
295
|
+
- `behavior` (optional): 'auto' or 'smooth' (default: 'auto')
|
|
296
|
+
- **Use case**: Wide tables, image carousels, horizontally scrollable containers
|
|
297
|
+
- **Returns**: Scroll state (position, total width, visible width, scroll availability)
|
|
298
|
+
|
|
232
299
|
### 3. Inspection Tools
|
|
233
300
|
|
|
234
301
|
#### getElement
|
|
@@ -472,6 +539,27 @@ Extract complete color palette with usage statistics.
|
|
|
472
539
|
- Usage examples (where the color is used)
|
|
473
540
|
- Sorted by usage frequency
|
|
474
541
|
|
|
542
|
+
#### convertFigmaToCode ⭐ NEW
|
|
543
|
+
Convert Figma designs to React/Tailwind code with AI assistance.
|
|
544
|
+
- **Parameters**:
|
|
545
|
+
- `figmaToken` (optional): Figma API token
|
|
546
|
+
- `fileKey` (required): Figma file key
|
|
547
|
+
- `nodeId` (required): Frame/component ID (formats: '123:456' or '123-456')
|
|
548
|
+
- `framework` (optional): 'react', 'react-typescript', or 'html' (default: 'react')
|
|
549
|
+
- `includeComments` (optional): Include code comments (default: true)
|
|
550
|
+
- **Use case**: Rapid prototyping, design-to-code workflow, implementing Figma designs
|
|
551
|
+
- **How it works**:
|
|
552
|
+
1. Fetches design structure (layout, colors, typography, spacing)
|
|
553
|
+
2. Gets rendered design image at 2x resolution
|
|
554
|
+
3. Returns AI-optimized instructions with simplified JSON structure
|
|
555
|
+
4. AI generates clean React/Tailwind code matching the design
|
|
556
|
+
- **Returns**: Formatted instruction prompt containing:
|
|
557
|
+
- Design image reference
|
|
558
|
+
- Simplified JSON structure with layout, styling, text properties
|
|
559
|
+
- Framework-specific guidelines (React components, TypeScript types, Tailwind classes)
|
|
560
|
+
- Quality requirements (semantic HTML, accessibility, accurate spacing)
|
|
561
|
+
- **Best for**: UI components, landing pages, card designs, navigation bars
|
|
562
|
+
|
|
475
563
|
#### getFigmaFrame
|
|
476
564
|
Export and download a Figma frame as PNG/JPG image with automatic compression.
|
|
477
565
|
- **Parameters**:
|
|
@@ -997,9 +1085,9 @@ Each tool definition is sent to the AI in every request, consuming context token
|
|
|
997
1085
|
| `debug` | Debugging & network | `getConsoleLogs`, `listNetworkRequests`, `getNetworkRequest`, `filterNetworkRequests` (4) |
|
|
998
1086
|
| `advanced` | Advanced automation & AI | `executeScript`, `setStyles`, `setViewport`, `getViewport`, `navigateTo`, `smartFindElement`, `analyzePage`, `getAllInteractiveElements`, `findElementsByText` (9) |
|
|
999
1087
|
| `recorder` | Scenario recording | `enableRecorder`, `executeScenario`, `listScenarios`, `searchScenarios`, `getScenarioInfo`, `deleteScenario`, `exportScenarioAsCode`, `appendScenarioToFile`, `generatePageObject` (9) |
|
|
1000
|
-
| `figma` | Figma integration | `getFigmaFrame`, `compareFigmaToElement`, `getFigmaSpecs`, `parseFigmaUrl`, `listFigmaPages`, `searchFigmaFrames`, `getFigmaComponents`, `getFigmaStyles`, `getFigmaColorPalette` (
|
|
1088
|
+
| `figma` | Figma integration | `getFigmaFrame`, `compareFigmaToElement`, `getFigmaSpecs`, `parseFigmaUrl`, `listFigmaPages`, `searchFigmaFrames`, `getFigmaComponents`, `getFigmaStyles`, `getFigmaColorPalette`, `convertFigmaToCode` (10) |
|
|
1001
1089
|
|
|
1002
|
-
**Total:**
|
|
1090
|
+
**Total:** 44 tools across 7 groups
|
|
1003
1091
|
|
|
1004
1092
|
**Configuration:**
|
|
1005
1093
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Release v2.5.0 - Major Update: 4 New Tools + Critical Fixes
|
|
2
|
+
|
|
3
|
+
## 🎉 New Tools (4)
|
|
4
|
+
|
|
5
|
+
### 1. `selectOption` - Dropdown Selection
|
|
6
|
+
Select options in HTML dropdown elements with intelligent priority-based selection.
|
|
7
|
+
- **Parameters**: `selector`, `value`, `text`, or `index`
|
|
8
|
+
- **Priority**: value → text → index
|
|
9
|
+
- **React/Vue support**: Automatically triggers input/change events
|
|
10
|
+
- Perfect for automated form filling
|
|
11
|
+
|
|
12
|
+
### 2. `drag` - Mouse Drag Emulation
|
|
13
|
+
Drag elements by mouse (click-hold-move-release) in any direction.
|
|
14
|
+
- **8 directions**: up, down, left, right, + 4 diagonals
|
|
15
|
+
- **Use cases**: Interactive maps, Gantt charts, SVG diagrams, canvas
|
|
16
|
+
- **How it works**: Puppeteer's `page.mouse` API for real drag events
|
|
17
|
+
- **NOT for**: Standard overflow scrollbars (use `scrollTo` instead)
|
|
18
|
+
|
|
19
|
+
### 3. `scrollHorizontal` - Horizontal Scrolling
|
|
20
|
+
Scroll elements horizontally for tables, carousels, and wide content.
|
|
21
|
+
- **Directions**: left, right
|
|
22
|
+
- **Amount**: Pixels or 'full' to scroll to end
|
|
23
|
+
- **Behavior**: smooth or auto
|
|
24
|
+
- Returns detailed scroll state
|
|
25
|
+
|
|
26
|
+
### 4. `convertFigmaToCode` ⭐
|
|
27
|
+
Convert Figma designs to React+Tailwind code.
|
|
28
|
+
- **Frameworks**: React, React TypeScript, HTML
|
|
29
|
+
- **Styling**: Tailwind CSS optimized
|
|
30
|
+
- **Input**: Figma file key + node ID
|
|
31
|
+
- **Output**: Clean, semantic component code with AI-generated instructions
|
|
32
|
+
|
|
33
|
+
## 🔥 Critical Fixes
|
|
34
|
+
|
|
35
|
+
### Fixed Tailwind CSS Selector Generation Bug
|
|
36
|
+
**Impact**: ALL AI tools failed on Tailwind/styled-components apps with `SyntaxError: invalid selector`
|
|
37
|
+
|
|
38
|
+
**Affected tools**:
|
|
39
|
+
- ❌ `analyzePage` - couldn't read page state
|
|
40
|
+
- ❌ `findElementsByText` - couldn't find elements
|
|
41
|
+
- ❌ `smartFindElement` - couldn't locate elements
|
|
42
|
+
- ❌ `getAllInteractiveElements` - couldn't list interactive elements
|
|
43
|
+
|
|
44
|
+
**Solution**:
|
|
45
|
+
- New `isTailwindClass()` function filters utility classes with `:`, `/`, `[]`
|
|
46
|
+
- Smart priority: id → data-testid → aria-label → semantic classes
|
|
47
|
+
- CSS.escape() for all selectors
|
|
48
|
+
- **Result**: ✅ Unblocks testing of ALL modern apps (Tailwind, styled-components, CSS modules, Emotion)
|
|
49
|
+
|
|
50
|
+
### Fixed `drag` Tool Implementation
|
|
51
|
+
Previously used `scrollLeft`/`scrollTop` which only works with `overflow: auto/scroll` containers.
|
|
52
|
+
|
|
53
|
+
**Now works with**:
|
|
54
|
+
- ✅ Interactive maps (Google Maps, Leaflet, Mapbox)
|
|
55
|
+
- ✅ Gantt charts and SVG diagrams
|
|
56
|
+
- ✅ Canvas elements with pan/zoom
|
|
57
|
+
- ✅ Custom drag handlers (React DnD, interact.js)
|
|
58
|
+
|
|
59
|
+
### Fixed `analyzePage` SVG Crash
|
|
60
|
+
Fixed `className.split is not a function` error when page contains SVG elements.
|
|
61
|
+
- Now handles both HTML (string) and SVG (SVGAnimatedString) className types
|
|
62
|
+
|
|
63
|
+
## 🔧 Improvements
|
|
64
|
+
|
|
65
|
+
### Better AI Agent Behavior
|
|
66
|
+
- Improved tool descriptions to prevent premature `executeScript` usage
|
|
67
|
+
- `click`, `type`, `analyzePage` emphasized as PRIMARY tools
|
|
68
|
+
- `executeScript` marked as ⚠️ LAST RESORT
|
|
69
|
+
- New "Tool Usage Priority" section in README
|
|
70
|
+
|
|
71
|
+
### Enhanced `analyzePage`
|
|
72
|
+
- Now detects HTML select elements with all options
|
|
73
|
+
- Returns: value, text, index, selected, disabled status
|
|
74
|
+
- Enables smarter `selectOption` usage
|
|
75
|
+
|
|
76
|
+
## 📊 Stats
|
|
77
|
+
|
|
78
|
+
- **Total tools**: 44 (was 40)
|
|
79
|
+
- **Files changed**: 9
|
|
80
|
+
- **Lines added**: +957
|
|
81
|
+
- **Lines removed**: -48
|
|
82
|
+
|
|
83
|
+
## 🚀 Installation
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx chrometools-mcp
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Or update in your MCP config:
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"chrometools": {
|
|
94
|
+
"command": "npx",
|
|
95
|
+
"args": ["chrometools-mcp"]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 🙏 Contributors
|
|
102
|
+
|
|
103
|
+
Special thanks to users who reported issues:
|
|
104
|
+
- Tailwind CSS selector bug reporter
|
|
105
|
+
- Gantt chart drag testing feedback
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
**Full Changelog**: https://github.com/docentovich/chrometools-mcp/blob/main/CHANGELOG.md
|
package/element-finder-utils.js
CHANGED
|
@@ -305,73 +305,181 @@ function isSafeSelectorValue(value) {
|
|
|
305
305
|
return !/["'\\[\]{}()]/.test(value);
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Check if a class name is a Tailwind CSS utility class
|
|
310
|
+
* Tailwind classes contain special characters that break CSS selectors:
|
|
311
|
+
* - Colons (:) for variants like hover:, focus:, md:, etc.
|
|
312
|
+
* - Slashes (/) for fractions like w-1/2
|
|
313
|
+
* - Brackets ([]) for arbitrary values like bg-[#1da1f2]
|
|
314
|
+
* - Dots (.) in decimal values
|
|
315
|
+
*/
|
|
316
|
+
function isTailwindClass(className) {
|
|
317
|
+
if (!className || typeof className !== 'string') return false;
|
|
318
|
+
|
|
319
|
+
// Check for special characters that indicate Tailwind variants/utilities
|
|
320
|
+
if (/[:\/\[\]]/.test(className)) {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Common Tailwind utility prefixes
|
|
325
|
+
const tailwindPrefixes = [
|
|
326
|
+
'bg-', 'text-', 'border-', 'rounded-', 'shadow-', 'font-', 'leading-',
|
|
327
|
+
'flex-', 'grid-', 'p-', 'px-', 'py-', 'pt-', 'pb-', 'pl-', 'pr-',
|
|
328
|
+
'm-', 'mx-', 'my-', 'mt-', 'mb-', 'ml-', 'mr-', 'space-',
|
|
329
|
+
'w-', 'h-', 'min-', 'max-', 'gap-', 'inset-', 'top-', 'right-', 'bottom-', 'left-',
|
|
330
|
+
'justify-', 'items-', 'content-', 'self-', 'place-',
|
|
331
|
+
'overflow-', 'opacity-', 'cursor-', 'transition-', 'transform-',
|
|
332
|
+
'duration-', 'ease-', 'delay-', 'animate-', 'scale-', 'rotate-', 'translate-',
|
|
333
|
+
'z-', 'order-', 'col-', 'row-', 'auto-', 'tracking-', 'select-',
|
|
334
|
+
'sr-', 'not-', 'pointer-', 'resize-', 'list-', 'appearance-',
|
|
335
|
+
'underline', 'line-through', 'no-underline', 'uppercase', 'lowercase', 'capitalize',
|
|
336
|
+
'italic', 'not-italic', 'ordinal', 'slashed-zero', 'lining-nums', 'oldstyle-nums',
|
|
337
|
+
'proportional-nums', 'tabular-nums', 'diagonal-fractions', 'stacked-fractions',
|
|
338
|
+
'hidden', 'block', 'inline', 'flex', 'grid', 'table', 'contents',
|
|
339
|
+
'absolute', 'relative', 'fixed', 'sticky', 'static'
|
|
340
|
+
];
|
|
341
|
+
|
|
342
|
+
// Check if className starts with known Tailwind prefix
|
|
343
|
+
return tailwindPrefixes.some(prefix => className.startsWith(prefix));
|
|
344
|
+
}
|
|
345
|
+
|
|
308
346
|
/**
|
|
309
347
|
* Generate unique CSS selector for an element
|
|
348
|
+
* Priority: id → data-testid → data-* → aria-label → role → semantic classes → nth-child
|
|
349
|
+
* Filters out Tailwind CSS classes to avoid invalid selectors
|
|
310
350
|
*/
|
|
311
351
|
function getUniqueSelectorInPage(element) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
352
|
+
const tagName = element.tagName.toLowerCase();
|
|
353
|
+
|
|
354
|
+
// 1. Try ID first (highest priority)
|
|
355
|
+
if (element.id && isSafeSelectorValue(element.id)) {
|
|
356
|
+
try {
|
|
357
|
+
return `#${CSS.escape(element.id)}`;
|
|
358
|
+
} catch (e) {
|
|
359
|
+
// CSS.escape not available in old browsers, use simple escape
|
|
360
|
+
return `#${element.id.replace(/[^\w-]/g, '\\$&')}`;
|
|
361
|
+
}
|
|
315
362
|
}
|
|
316
363
|
|
|
317
|
-
// Try
|
|
318
|
-
if (element.
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
const selector = `${element.tagName.toLowerCase()}.${classes.join('.')}`;
|
|
364
|
+
// 2. Try data-testid (very common in modern apps)
|
|
365
|
+
if (element.dataset && element.dataset.testid && isSafeSelectorValue(element.dataset.testid)) {
|
|
366
|
+
const selector = `[data-testid="${element.dataset.testid}"]`;
|
|
367
|
+
try {
|
|
322
368
|
if (document.querySelectorAll(selector).length === 1) {
|
|
323
369
|
return selector;
|
|
324
370
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
371
|
+
} catch (e) {
|
|
372
|
+
// Invalid selector, continue
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// 3. Try other data-* attributes (only if values are safe)
|
|
377
|
+
const dataAttrs = Array.from(element.attributes)
|
|
378
|
+
.filter(attr => attr.name.startsWith('data-') &&
|
|
379
|
+
attr.name !== 'data-testid' &&
|
|
380
|
+
isSafeSelectorValue(attr.value))
|
|
381
|
+
.slice(0, 2);
|
|
382
|
+
|
|
383
|
+
for (const attr of dataAttrs) {
|
|
384
|
+
const selector = `${tagName}[${attr.name}="${attr.value}"]`;
|
|
385
|
+
try {
|
|
386
|
+
if (document.querySelectorAll(selector).length === 1) {
|
|
387
|
+
return selector;
|
|
329
388
|
}
|
|
389
|
+
} catch (e) {
|
|
390
|
+
continue;
|
|
330
391
|
}
|
|
331
392
|
}
|
|
332
393
|
|
|
333
|
-
// Try
|
|
334
|
-
|
|
335
|
-
|
|
394
|
+
// 4. Try aria-label
|
|
395
|
+
const ariaLabel = element.getAttribute('aria-label');
|
|
396
|
+
if (ariaLabel && isSafeSelectorValue(ariaLabel)) {
|
|
397
|
+
const selector = `${tagName}[aria-label="${ariaLabel}"]`;
|
|
336
398
|
try {
|
|
337
399
|
if (document.querySelectorAll(selector).length === 1) {
|
|
338
400
|
return selector;
|
|
339
401
|
}
|
|
340
402
|
} catch (e) {
|
|
341
|
-
// Invalid selector,
|
|
403
|
+
// Invalid selector, continue
|
|
342
404
|
}
|
|
343
405
|
}
|
|
344
406
|
|
|
345
|
-
// Try
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
407
|
+
// 5. Try role attribute
|
|
408
|
+
const role = element.getAttribute('role');
|
|
409
|
+
if (role && isSafeSelectorValue(role)) {
|
|
410
|
+
const selector = `${tagName}[role="${role}"]`;
|
|
411
|
+
try {
|
|
412
|
+
if (document.querySelectorAll(selector).length === 1) {
|
|
413
|
+
return selector;
|
|
414
|
+
}
|
|
415
|
+
} catch (e) {
|
|
416
|
+
// Invalid selector, continue
|
|
417
|
+
}
|
|
418
|
+
}
|
|
349
419
|
|
|
350
|
-
|
|
351
|
-
|
|
420
|
+
// 6. Try name attribute (common for form inputs)
|
|
421
|
+
if (element.name && isSafeSelectorValue(element.name)) {
|
|
422
|
+
const selector = `${tagName}[name="${element.name}"]`;
|
|
352
423
|
try {
|
|
353
424
|
if (document.querySelectorAll(selector).length === 1) {
|
|
354
425
|
return selector;
|
|
355
426
|
}
|
|
356
427
|
} catch (e) {
|
|
357
|
-
// Invalid selector,
|
|
358
|
-
|
|
428
|
+
// Invalid selector, continue
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// 7. Try semantic classes (filter out Tailwind)
|
|
433
|
+
if (element.className && typeof element.className === 'string') {
|
|
434
|
+
const classes = element.className.split(' ')
|
|
435
|
+
.filter(c => c.trim() && !isTailwindClass(c))
|
|
436
|
+
.slice(0, 3); // Limit to 3 classes max
|
|
437
|
+
|
|
438
|
+
if (classes.length > 0) {
|
|
439
|
+
try {
|
|
440
|
+
// Try with all filtered classes
|
|
441
|
+
const escapedClasses = classes.map(c => {
|
|
442
|
+
try {
|
|
443
|
+
return CSS.escape(c);
|
|
444
|
+
} catch (e) {
|
|
445
|
+
return c.replace(/[^\w-]/g, '\\$&');
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
const selector = `${tagName}.${escapedClasses.join('.')}`;
|
|
449
|
+
if (document.querySelectorAll(selector).length === 1) {
|
|
450
|
+
return selector;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Try with first class only
|
|
454
|
+
const firstClassSelector = `${tagName}.${escapedClasses[0]}`;
|
|
455
|
+
if (document.querySelectorAll(firstClassSelector).length === 1) {
|
|
456
|
+
return firstClassSelector;
|
|
457
|
+
}
|
|
458
|
+
} catch (e) {
|
|
459
|
+
// Invalid selector, continue to fallback
|
|
460
|
+
}
|
|
359
461
|
}
|
|
360
462
|
}
|
|
361
463
|
|
|
362
|
-
// Fallback: nth-
|
|
464
|
+
// 8. Fallback: nth-of-type with path
|
|
363
465
|
let current = element;
|
|
364
466
|
const path = [];
|
|
365
467
|
|
|
366
468
|
while (current && current.tagName) {
|
|
367
469
|
let selector = current.tagName.toLowerCase();
|
|
368
470
|
|
|
369
|
-
|
|
370
|
-
|
|
471
|
+
// Stop at element with ID
|
|
472
|
+
if (current.id && isSafeSelectorValue(current.id)) {
|
|
473
|
+
try {
|
|
474
|
+
selector = `#${CSS.escape(current.id)}`;
|
|
475
|
+
} catch (e) {
|
|
476
|
+
selector = `#${current.id.replace(/[^\w-]/g, '\\$&')}`;
|
|
477
|
+
}
|
|
371
478
|
path.unshift(selector);
|
|
372
479
|
break;
|
|
373
480
|
}
|
|
374
481
|
|
|
482
|
+
// Calculate nth-of-type
|
|
375
483
|
let sibling = current;
|
|
376
484
|
let nth = 1;
|
|
377
485
|
|
|
@@ -382,7 +490,9 @@ function getUniqueSelectorInPage(element) {
|
|
|
382
490
|
}
|
|
383
491
|
}
|
|
384
492
|
|
|
385
|
-
|
|
493
|
+
// Only add nth-of-type if there are multiple siblings of same type
|
|
494
|
+
if (nth > 1 || (current.parentElement &&
|
|
495
|
+
Array.from(current.parentElement.children).filter(c => c.tagName === current.tagName).length > 1)) {
|
|
386
496
|
selector += `:nth-of-type(${nth})`;
|
|
387
497
|
}
|
|
388
498
|
|