@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.
- package/.github/release-please-config.json +9 -0
- package/.github/release-please-manifest.json +3 -0
- package/.github/workflows/release.yml +37 -0
- package/AGENTS.md +75 -0
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/akccakcctw-vue-grab-1.0.0.tgz +0 -0
- package/docs/SDD.md +188 -0
- package/package.json +31 -0
- package/src/__tests__/plugin.spec.ts +60 -0
- package/src/core/__tests__/api.spec.ts +178 -0
- package/src/core/__tests__/identifier.spec.ts +126 -0
- package/src/core/__tests__/overlay.spec.ts +431 -0
- package/src/core/__tests__/widget.spec.ts +57 -0
- package/src/core/api.ts +144 -0
- package/src/core/identifier.ts +89 -0
- package/src/core/overlay.ts +348 -0
- package/src/core/widget.ts +289 -0
- package/src/index.ts +8 -0
- package/src/nuxt/module.ts +102 -0
- package/src/nuxt/runtime/plugin.ts +13 -0
- package/src/plugin.ts +48 -0
- package/tsconfig.json +44 -0
- package/vitest.config.ts +9 -0
|
@@ -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
|
+
})
|