@akccakcctw/vue-grab 1.0.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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "packages": {
3
+ ".": {
4
+ "release-type": "node",
5
+ "package-name": "@akccakcctw/vue-grab",
6
+ "changelog-path": "CHANGELOG.md"
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.0.0"
3
+ }
@@ -0,0 +1,37 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: write
10
+ pull-requests: write
11
+
12
+ jobs:
13
+ release-please:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: googleapis/release-please-action@v4
17
+ with:
18
+ config-file: .github/release-please-config.json
19
+ manifest-file: .github/release-please-manifest.json
20
+
21
+ publish:
22
+ runs-on: ubuntu-latest
23
+ needs: release-please
24
+ if: ${{ needs.release-please.outputs.release_created }}
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: pnpm/action-setup@v4
28
+ with:
29
+ version: 10
30
+ - uses: actions/setup-node@v4
31
+ with:
32
+ node-version: 20
33
+ registry-url: https://registry.npmjs.org
34
+ - run: pnpm install --frozen-lockfile
35
+ - run: pnpm publish --access public
36
+ env:
37
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/AGENTS.md ADDED
@@ -0,0 +1,75 @@
1
+ # AI Agent Interaction Guide: vue-grab
2
+
3
+ This document provides instructions for AI agents on how to utilize `vue-grab` to inspect and modify Vue/Nuxt applications.
4
+
5
+ ## 1. Overview for Agents
6
+
7
+ `vue-grab` exposes a global bridge in the browser's `window` object that allows you to query the internal structure of a Vue application directly from the DOM. This is particularly useful when combined with `chrome-devtools-mcp`.
8
+
9
+ Quickstart: see `README.md` for Vue/Nuxt install examples.
10
+
11
+ ## 2. Global API Bridge
12
+
13
+ When `vue-grab` is active in a development environment, it exposes `window.__VUE_GRAB__`.
14
+
15
+ ### Methods
16
+
17
+ | Method | Description | Parameters | Return Type |
18
+ | :--- | :--- | :--- | :--- |
19
+ | `grabAt(x, y)` | Get component info at viewport coordinates. | `x: number, y: number` | `ComponentInfo \| null` |
20
+ | `grabFromSelector(selector)` | Get component info from a CSS selector. | `selector: string` | `ComponentInfo \| null` |
21
+ | `grabFromElement(element)` | Get component info from a DOM element. | `element: Element` | `ComponentInfo \| null` |
22
+ | `activate()` | Enable visual inspection mode (highlights on hover). | N/A | `void` |
23
+ | `deactivate()` | Disable visual inspection mode. | N/A | `void` |
24
+ | `highlight(selector)` | Programmatically highlight an element. | `selector: string` | `void` |
25
+ | `enable()` | Alias for `activate()`. | N/A | `void` |
26
+ | `disable()` | Alias for `deactivate()`. | N/A | `void` |
27
+ | `getComponentDetails(selectorOrElement)` | Alias for grab via selector or element. | `selectorOrElement: string \| Element` | `ComponentInfo \| null` |
28
+ | `setOverlayStyle(style)` | Update overlay styling at runtime. | `style: Record<string, string>` | `void` |
29
+
30
+ ### Data Structures
31
+
32
+ #### `ComponentInfo`
33
+ ```json
34
+ {
35
+ "name": "MyButton",
36
+ "file": "/Users/user/project/src/components/MyButton.vue",
37
+ "props": { "label": "Click Me" },
38
+ "data": { "count": 0 },
39
+ "line": 12,
40
+ "column": 8,
41
+ "vnode": { ... } // Internal Vue vnode structure
42
+ }
43
+ ```
44
+
45
+ ## 3. Interaction Patterns with MCP
46
+
47
+ ### Scenario: Find source code for a button
48
+ 1. **Step 1:** Use MCP's `inspect_element` or `get_dom_tree` to find the coordinates or selector of the button.
49
+ 2. **Step 2:** Execute a JS snippet via MCP to call `vue-grab`:
50
+ ```javascript
51
+ const info = window.__VUE_GRAB__.grabFromSelector('.my-button');
52
+ console.log(JSON.stringify(info));
53
+ ```
54
+ 3. **Step 3:** Use the returned `file` path to read the source code using your file system tools.
55
+
56
+ ### Scenario: Debugging State
57
+ 1. **Step 1:** Query the component:
58
+ ```javascript
59
+ const info = window.__VUE_GRAB__.grabAt(100, 250);
60
+ ```
61
+ 2. **Step 2:** Analyze `props` and `data` to understand why the UI is rendering a certain way.
62
+
63
+ ## 4. Nuxt Support
64
+ In Nuxt applications, `vue-grab` is automatically injected via a Nuxt Module. The API remains identical.
65
+ Configuration: `vueGrab` accepts `enabled`, optional `overlayStyle` (CSS style map), and `copyOnClick`.
66
+
67
+ Note: `onCopy` is only supported when using the Vue plugin directly.
68
+
69
+ ## 5. Development Tips for Agents
70
+ * **Absolute Paths:** `vue-grab` provides absolute file paths in development mode, allowing you to immediately open the correct file without guessing.
71
+ * **Anonymous Components:** If a component doesn't have a name, `vue-grab` defaults to `AnonymousComponent`. Try to look at the `file` property to identify it.
72
+ * **Package Manager:** Use `pnpm` instead of `npm` for all package scripts and installs.
73
+ * **Types:** `VueGrabOptions`, `OverlayOptions`, and `OverlayStyle` are exported from the package.
74
+ * **UI:** A floating toggle button is injected in dev to enable/disable grab mode.
75
+ * **Hover:** Tooltip shows `file:line:column` when available.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # vue-grab
2
+
3
+ Developer-only bridge for inspecting Vue/Nuxt component context from the DOM.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add -D vue-grab
9
+ ```
10
+
11
+ ## Vue Usage
12
+
13
+ ```ts
14
+ import { createApp } from 'vue'
15
+ import VueGrab from 'vue-grab'
16
+
17
+ const app = createApp(App)
18
+
19
+ // Use import.meta.env.DEV for Vite, or process.env.NODE_ENV === 'development' for Webpack
20
+ if (import.meta.env.DEV) {
21
+ app.use(VueGrab, {
22
+ overlayStyle: {
23
+ border: '2px dashed #111'
24
+ },
25
+ onCopy(payload) {
26
+ console.log('vue-grab payload', payload)
27
+ },
28
+ copyOnClick: true
29
+ })
30
+ }
31
+
32
+ app.mount('#app')
33
+ ```
34
+
35
+ ## Nuxt Usage
36
+
37
+ ```ts
38
+ export default defineNuxtConfig({
39
+ modules: ['vue-grab'],
40
+ vueGrab: {
41
+ enabled: true,
42
+ overlayStyle: {
43
+ border: '2px dashed #111'
44
+ },
45
+ copyOnClick: true
46
+ }
47
+ })
48
+ ```
49
+
50
+ ## Browser API
51
+
52
+ When active, `window.__VUE_GRAB__` exposes:
53
+
54
+ - `activate()` / `deactivate()`
55
+ - `grabAt(x, y)`
56
+ - `grabFromSelector(selector)`
57
+ - `grabFromElement(element)`
58
+ - `highlight(selector)`
59
+ - `setOverlayStyle(style)`
60
+ - Aliases: `enable()` / `disable()` / `getComponentDetails(selectorOrElement)`
61
+
62
+ A floating toggle button is injected in dev to switch grab mode on/off. Hover shows file:line:column.
63
+
64
+ ## MCP Example
65
+
66
+ ```js
67
+ const info = window.__VUE_GRAB__.grabFromSelector('.my-button');
68
+ console.log(JSON.stringify(info));
69
+ ```
70
+
71
+ ## Development
72
+
73
+ For maintainers of this package (local dev + manual testing):
74
+
75
+ ```bash
76
+ pnpm install
77
+ pnpm test
78
+ ```
79
+
80
+ To test in a real app, link this package into a separate Vue/Nuxt project:
81
+
82
+ ```bash
83
+ # In this repo
84
+ pnpm link --global
85
+
86
+ # In your app repo
87
+ pnpm link --global vue-grab
88
+ ```
89
+
90
+ Note: `pnpm link --global vue-grab` does not update your app's `package.json` by default.
91
+ If you want it recorded in dependencies, use one of the following in your app repo:
92
+
93
+ ```bash
94
+ pnpm add -D link:vue-grab
95
+ # or
96
+ pnpm add -D link:/Users/rex.tsou/vbox/vue-grab
97
+ ```
98
+
99
+ Then run your app and verify `window.__VUE_GRAB__` in the browser console.
100
+
101
+ Cleanup when finished:
102
+
103
+ ```bash
104
+ # In your app repo
105
+ pnpm unlink --global vue-grab
106
+ ```
107
+
108
+ Manual verification checklist:
109
+ - Hover highlights appear when `activate()` is called.
110
+ - Clicking copies metadata (or triggers `onCopy` if configured).
111
+ - `grabFromSelector` returns component info for a known element.
112
+
113
+ ## Acknowledgment
114
+
115
+ Special thanks to [react-grab](https://www.react-grab.com/) ([GitHub](https://github.com/aidenybai/react-grab)). This project was inspired by and references the excellent work done by the `react-grab` team. `vue-grab` aims to bring a similar developer experience to the Vue and Nuxt ecosystem.
116
+
Binary file
package/docs/SDD.md ADDED
@@ -0,0 +1,188 @@
1
+ # Software Design Document: vue-grab
2
+
3
+ ## 1. Introduction
4
+
5
+ `vue-grab` is a development tool designed to bridge the gap between Vue.js applications and AI coding agents. Inspired by `react-grab`, it allows developers (and AI agents) to inspect the UI of a running Vue application and instantly retrieve context about the underlying components. This context includes the component name, file path, current props, and state.
6
+
7
+ This tool is specifically designed to work with `chrome-devtools-mcp` to facilitate seamless interaction for AI agents.
8
+
9
+ ## 2. Goals & Requirements
10
+
11
+ ### 2.1 Functional Requirements
12
+ * **Component Inspection:** Ability to identify the Vue component associated with a specific DOM element.
13
+ * **Context Extraction:** Retrieve component metadata:
14
+ * Component Name
15
+ * Source File Path (absolute path for direct IDE opening/reading)
16
+ * Line/Column numbers (if available)
17
+ * Current Props
18
+ * Current Data/State
19
+ * VNode (if available)
20
+ * **Visual Feedback:** Highlight components on hover when "Grab Mode" is active.
21
+ * **Toggle Widget:** Provide a floating UI to toggle Grab Mode in the browser.
22
+ * **Hover Tooltip:** Show component file location (file:line:column) on hover.
23
+ * **Clipboard Integration:** Copy component details to clipboard on click.
24
+ * **Framework Support:**
25
+ * Vue 3 (Composition & Options API)
26
+ * Vue 2 (Legacy support if feasible, primarily focusing on Vue 3)
27
+ * Nuxt 3 (via Nuxt Module/Plugin)
28
+ * **MCP Integration:** Expose a global API accessible by `chrome-devtools-mcp` (via Puppeteer/Console) to allow AI agents to programmatically "grab" or query the page.
29
+
30
+ ### 2.2 Non-Functional Requirements
31
+ * **Performance:** Minimal impact on the application performance when inactive.
32
+ * **Dev-Only:** Should only be active in development mode.
33
+ * **Zero-Config (Ideal):** Should work with standard Vite/Webpack setups out of the box.
34
+
35
+ ## 3. Architecture
36
+
37
+ ### 3.1 High-Level Overview
38
+ `vue-grab` consists of a client-side library that injects itself into the Vue application. It listens for user interactions (hover/click) and leverages Vue's internal properties on DOM elements to traverse the component tree.
39
+
40
+ ### 3.2 Component Identification Strategy
41
+ Vue applications attach internal instance data to DOM elements.
42
+ * **Vue 3:** Look for properties starting with `__vueParentComponent` or `__vnode` on the DOM element.
43
+ * **Vue 2:** Look for `__vue__` on the DOM element.
44
+
45
+ ### 3.3 Source Code Mapping
46
+ Modern build tools (Vite/Webpack) inject location metadata into components during development.
47
+ * **`__file`**: The absolute file path of the SFC.
48
+ * **`__name`**: The component name.
49
+
50
+ We will traverse the fiber/component tree to find the nearest component boundary for a given DOM node.
51
+
52
+ ### 3.4 MCP Integration
53
+ To allow `chrome-devtools-mcp` (which uses Puppeteer) to interact with `vue-grab`, we will expose a global object on the window: `window.__VUE_GRAB__`.
54
+
55
+ **Contract:**
56
+ * `window.__VUE_GRAB__.activate()`: Turn on inspection mode.
57
+ * `window.__VUE_GRAB__.deactivate()`: Turn off inspection mode.
58
+ * `window.__VUE_GRAB__.grabFromSelector(selector)`: Return a JSON object with component info.
59
+ * `window.__VUE_GRAB__.highlight(selector)`: Programmatically highlight an element.
60
+ * `window.__VUE_GRAB__.enable()`/`disable()` and `getComponentDetails(...)` are supported as aliases.
61
+
62
+ ## 4. API Design
63
+
64
+ ### 4.1 Global API (Window)
65
+ ```typescript
66
+ interface VueGrabAPI {
67
+ // Activation
68
+ activate(): void;
69
+ deactivate(): void;
70
+ isActive: boolean;
71
+
72
+ // Direct Query (for AI Agents)
73
+ grabAt(x: number, y: number): ComponentInfo | null;
74
+ grabFromSelector(selector: string): ComponentInfo | null;
75
+ grabFromElement(element: Element): ComponentInfo | null;
76
+ highlight(selector: string): void;
77
+ setOverlayStyle(style: Record<string, string>): void;
78
+ }
79
+
80
+ Overlay options:
81
+ - `overlayStyle`: override overlay CSS styles.
82
+ - `onCopy`: optional callback invoked instead of clipboard copy (Vue plugin only).
83
+ - `copyOnClick`: disable click-to-copy behavior when set to `false`.
84
+ ```
85
+
86
+ ### 4.2 Component Info Structure
87
+ ```typescript
88
+ interface ComponentInfo {
89
+ name: string;
90
+ file: string; // Absolute path
91
+ props: Record<string, any>;
92
+ data: Record<string, any>;
93
+ element: HTMLElement; // Reference to the root DOM element
94
+ line?: number;
95
+ column?: number;
96
+ vnode?: any;
97
+ }
98
+ ```
99
+
100
+ ## 5. Integration Modules
101
+
102
+ ### 5.1 Vue 3 Plugin
103
+ A simple Vue plugin `VueGrab` that installs the global handler and sets up the event listeners.
104
+
105
+ ```javascript
106
+ import { createApp } from 'vue'
107
+ import VueGrab from 'vue-grab'
108
+ import type { VueGrabOptions } from 'vue-grab'
109
+
110
+ const vueGrabOptions: VueGrabOptions = {
111
+ overlayStyle: {
112
+ border: '2px dashed #111'
113
+ }
114
+ }
115
+
116
+ const app = createApp(App)
117
+ if (process.env.NODE_ENV === 'development') {
118
+ app.use(VueGrab, vueGrabOptions)
119
+ }
120
+ app.mount('#app')
121
+ ```
122
+
123
+ ### 5.2 Nuxt Module
124
+ A Nuxt module that automatically injects the plugin in development mode.
125
+ Configuration key: `vueGrab` (example below).
126
+
127
+ ```ts
128
+ export default defineNuxtConfig({
129
+ modules: ['vue-grab'],
130
+ vueGrab: {
131
+ enabled: true,
132
+ overlayStyle: {
133
+ border: '2px dashed #111'
134
+ }
135
+ }
136
+ })
137
+ ```
138
+
139
+ ## 6. Development Plan (TDD)
140
+
141
+ We will follow Test-Driven Development.
142
+
143
+ 1. **Phase 1: Core Logic (Unit Tests)**
144
+ * Create a dummy Vue 3 component tree in JSDOM environment.
145
+ * Test `identifyComponent(domNode)`: Verify it finds the correct component instance.
146
+ * Test `extractMetadata(componentInstance)`: Verify it extracts file path and name.
147
+
148
+ 2. **Phase 2: Interaction (E2E/Integration)**
149
+ * Implement the overlay/highlight logic.
150
+ * Implement the click-to-copy logic.
151
+
152
+ 3. **Phase 3: Integration**
153
+ * Build the Vue Plugin.
154
+ * Build the Nuxt Module.
155
+ * Verify `window.__VUE_GRAB__` API.
156
+
157
+ 4. **Phase 4: MCP Verification**
158
+ * Simulate MCP calls to ensure the API returns data in the expected format.
159
+
160
+ ## 7. Quickstart
161
+
162
+ ### 7.1 Vue
163
+ ```ts
164
+ import { createApp } from 'vue'
165
+ import VueGrab from 'vue-grab'
166
+
167
+ const app = createApp(App)
168
+ if (process.env.NODE_ENV === 'development') {
169
+ app.use(VueGrab)
170
+ }
171
+ app.mount('#app')
172
+ ```
173
+
174
+ ### 7.2 Nuxt
175
+ ```ts
176
+ export default defineNuxtConfig({
177
+ modules: ['vue-grab'],
178
+ vueGrab: {
179
+ enabled: true
180
+ }
181
+ })
182
+ ```
183
+
184
+ ### 7.3 Browser API
185
+ ```js
186
+ const info = window.__VUE_GRAB__.grabFromSelector('.my-button');
187
+ console.log(JSON.stringify(info));
188
+ ```
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@akccakcctw/vue-grab",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "src/index.ts",
6
+ "module": "src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts",
9
+ "./module": "./src/nuxt/module.ts"
10
+ },
11
+ "nuxt": "src/nuxt/module.ts",
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "MIT",
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "devDependencies": {
19
+ "@nuxt/kit": "^4.2.2",
20
+ "@vitejs/plugin-vue": "^6.0.3",
21
+ "@vue/test-utils": "^2.4.6",
22
+ "jsdom": "^27.4.0",
23
+ "typescript": "^5.9.3",
24
+ "vite": "^7.3.1",
25
+ "vitest": "^4.0.17",
26
+ "vue": "^3.5.26"
27
+ },
28
+ "scripts": {
29
+ "test": "vitest run"
30
+ }
31
+ }
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
+ import * as api from '../core/api'
3
+ import { createVueGrabPlugin } from '../plugin'
4
+
5
+ describe('Vue plugin', () => {
6
+ const originalWindow = globalThis.window
7
+
8
+ beforeEach(() => {
9
+ ;(globalThis as any).window = originalWindow || ({} as Window)
10
+ })
11
+
12
+ afterEach(() => {
13
+ ;(globalThis as any).window = originalWindow
14
+ vi.restoreAllMocks()
15
+ })
16
+
17
+ it('installs when enabled', () => {
18
+ const installSpy = vi.spyOn(api, 'installVueGrab').mockReturnValue({
19
+ activate: vi.fn(),
20
+ deactivate: vi.fn(),
21
+ isActive: false,
22
+ grabAt: vi.fn(),
23
+ grabFromSelector: vi.fn(),
24
+ grabFromElement: vi.fn(),
25
+ highlight: vi.fn(),
26
+ enable: vi.fn(),
27
+ disable: vi.fn(),
28
+ getComponentDetails: vi.fn(),
29
+ setOverlayStyle: vi.fn(),
30
+ setDomFileResolver: vi.fn()
31
+ })
32
+ const onCopy = vi.fn()
33
+
34
+ const plugin = createVueGrabPlugin({
35
+ enabled: true,
36
+ overlayStyle: {
37
+ border: '1px solid red'
38
+ },
39
+ onCopy,
40
+ copyOnClick: false
41
+ })
42
+ plugin.install()
43
+
44
+ expect(installSpy).toHaveBeenCalledTimes(1)
45
+ expect(installSpy).toHaveBeenCalledWith(window, {
46
+ overlayStyle: {
47
+ border: '1px solid red'
48
+ },
49
+ onCopy,
50
+ copyOnClick: false
51
+ })
52
+ })
53
+
54
+ it('skips install when disabled', () => {
55
+ const installSpy = vi.spyOn(api, 'installVueGrab')
56
+ const plugin = createVueGrabPlugin({ enabled: false })
57
+ plugin.install()
58
+ expect(installSpy).not.toHaveBeenCalled()
59
+ })
60
+ })