react-grep 0.1.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/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/index.cjs +533 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.global.js +3 -0
- package/dist/index.js +530 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Max Murray
|
|
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,67 @@
|
|
|
1
|
+
# react-grep
|
|
2
|
+
|
|
3
|
+
Hold **Cmd** to see React component names + file:line overlaid on any element.
|
|
4
|
+
|
|
5
|
+
- **Inspect** — Hold `Cmd` (Mac) / `Ctrl` (Windows/Linux) and hover over any element to see the React component name and source file location
|
|
6
|
+
- **Toggle source** — Tap `Shift` (while holding modifier) to switch between the component definition and the call site where it's rendered
|
|
7
|
+
- **Copy** — `Cmd+Shift+Click` to copy the active file path and line number to your clipboard
|
|
8
|
+
|
|
9
|
+
Zero dependencies. Works with any React app in development mode.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install react-grep
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### ESM import
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import "react-grep";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's it. The inspector activates automatically on page load.
|
|
26
|
+
|
|
27
|
+
If you need manual control:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { init, destroy } from "react-grep";
|
|
31
|
+
|
|
32
|
+
init(); // start the inspector
|
|
33
|
+
destroy(); // stop and clean up
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Script tag
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<script src="https://unpkg.com/react-grep/dist/index.global.js"></script>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The inspector starts automatically when the script loads.
|
|
43
|
+
|
|
44
|
+
## How it works
|
|
45
|
+
|
|
46
|
+
react-grep reads React's internal fiber tree to find component names and source locations (`_debugSource` / `_debugStack`). This data is only available in **development builds** of React — production builds strip it out.
|
|
47
|
+
|
|
48
|
+
When the modifier key is held:
|
|
49
|
+
|
|
50
|
+
1. The hovered DOM element is highlighted with a blue overlay
|
|
51
|
+
2. A tooltip shows the component name, file path, and call site (if available)
|
|
52
|
+
3. Tap `Shift` to toggle between the component source and call site — the active source is highlighted, the inactive one is dimmed
|
|
53
|
+
4. `Cmd+Shift+Click` copies the active `file:line` to clipboard (without toggling)
|
|
54
|
+
|
|
55
|
+
## API
|
|
56
|
+
|
|
57
|
+
### `init()`
|
|
58
|
+
|
|
59
|
+
Start the inspector. Called automatically on import — only needed if you previously called `destroy()`.
|
|
60
|
+
|
|
61
|
+
### `destroy()`
|
|
62
|
+
|
|
63
|
+
Stop the inspector and remove all event listeners and DOM elements.
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/source-map.ts
|
|
4
|
+
var cache = /* @__PURE__ */ new Map();
|
|
5
|
+
var VLQ_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
6
|
+
var charToInt = [];
|
|
7
|
+
for (let i = 0; i < VLQ_CHARS.length; i++) charToInt[VLQ_CHARS.charCodeAt(i)] = i;
|
|
8
|
+
var decodeVLQ = (str, pos) => {
|
|
9
|
+
let shift = 0;
|
|
10
|
+
let value = 0;
|
|
11
|
+
while (pos.i < str.length) {
|
|
12
|
+
const digit = charToInt[str.charCodeAt(pos.i++)];
|
|
13
|
+
value += (digit & 31) << shift;
|
|
14
|
+
if (!(digit & 32)) return value & 1 ? -(value >> 1) : value >> 1;
|
|
15
|
+
shift += 5;
|
|
16
|
+
}
|
|
17
|
+
return 0;
|
|
18
|
+
};
|
|
19
|
+
var decodeMappings = (raw) => {
|
|
20
|
+
const result = [];
|
|
21
|
+
let srcIdx = 0;
|
|
22
|
+
let origLine = 0;
|
|
23
|
+
let origCol = 0;
|
|
24
|
+
for (const line of raw.split(";")) {
|
|
25
|
+
const segments = [];
|
|
26
|
+
let genCol = 0;
|
|
27
|
+
if (line) {
|
|
28
|
+
const pos = { i: 0 };
|
|
29
|
+
while (pos.i < line.length) {
|
|
30
|
+
if (line.charCodeAt(pos.i) === 44) {
|
|
31
|
+
pos.i++;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
genCol += decodeVLQ(line, pos);
|
|
35
|
+
if (pos.i >= line.length || line.charCodeAt(pos.i) === 44) continue;
|
|
36
|
+
srcIdx += decodeVLQ(line, pos);
|
|
37
|
+
origLine += decodeVLQ(line, pos);
|
|
38
|
+
origCol += decodeVLQ(line, pos);
|
|
39
|
+
if (pos.i < line.length && line.charCodeAt(pos.i) !== 44) decodeVLQ(line, pos);
|
|
40
|
+
segments.push([genCol, srcIdx, origLine, origCol]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
result.push(segments);
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
var DATA_URI_RE = /^data:application\/json[^,]*;base64,([A-Za-z0-9+/=]+)$/;
|
|
48
|
+
var isSameOrigin = (a, b) => {
|
|
49
|
+
try {
|
|
50
|
+
return new URL(a).origin === new URL(b).origin;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var fetchAndParse = async (url) => {
|
|
56
|
+
try {
|
|
57
|
+
const res = await fetch(url);
|
|
58
|
+
const text = await res.text();
|
|
59
|
+
const match = text.match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)$/m);
|
|
60
|
+
if (!match) return null;
|
|
61
|
+
const ref = match[1].trim();
|
|
62
|
+
let mapJson;
|
|
63
|
+
if (ref.startsWith("data:")) {
|
|
64
|
+
const dataMatch = DATA_URI_RE.exec(ref);
|
|
65
|
+
if (!dataMatch) return null;
|
|
66
|
+
mapJson = atob(dataMatch[1]);
|
|
67
|
+
} else {
|
|
68
|
+
const mapUrl = new URL(ref, url).href;
|
|
69
|
+
if (!isSameOrigin(url, mapUrl)) return null;
|
|
70
|
+
const mapRes = await fetch(mapUrl);
|
|
71
|
+
mapJson = await mapRes.text();
|
|
72
|
+
}
|
|
73
|
+
const raw = JSON.parse(mapJson);
|
|
74
|
+
return { sources: raw.sources, mappings: decodeMappings(raw.mappings) };
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var getSourceMap = (url) => {
|
|
80
|
+
let promise = cache.get(url);
|
|
81
|
+
if (!promise) {
|
|
82
|
+
promise = fetchAndParse(url);
|
|
83
|
+
cache.set(url, promise);
|
|
84
|
+
}
|
|
85
|
+
return promise;
|
|
86
|
+
};
|
|
87
|
+
var lookup = (map, genLine, genCol) => {
|
|
88
|
+
if (genLine < 0 || genLine >= map.mappings.length) return null;
|
|
89
|
+
const segments = map.mappings[genLine];
|
|
90
|
+
if (!segments.length) return null;
|
|
91
|
+
let lo = 0;
|
|
92
|
+
let hi = segments.length - 1;
|
|
93
|
+
while (lo < hi) {
|
|
94
|
+
const mid = lo + hi + 1 >> 1;
|
|
95
|
+
if (segments[mid][0] <= genCol) lo = mid;
|
|
96
|
+
else hi = mid - 1;
|
|
97
|
+
}
|
|
98
|
+
return segments[lo];
|
|
99
|
+
};
|
|
100
|
+
var resolveOriginalPosition = async (url, line, column) => {
|
|
101
|
+
const map = await getSourceMap(url);
|
|
102
|
+
if (!map) return null;
|
|
103
|
+
const seg = lookup(map, line - 1, column - 1);
|
|
104
|
+
if (!seg) return null;
|
|
105
|
+
const fileName = map.sources[seg[1]];
|
|
106
|
+
return {
|
|
107
|
+
fileName,
|
|
108
|
+
lineNumber: seg[2] + 1,
|
|
109
|
+
columnNumber: seg[3] + 1
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/fiber.ts
|
|
114
|
+
var COMPOSITE_TAGS = /* @__PURE__ */ new Set([
|
|
115
|
+
0,
|
|
116
|
+
// FunctionComponent
|
|
117
|
+
1,
|
|
118
|
+
// ClassComponent
|
|
119
|
+
11,
|
|
120
|
+
// ForwardRef
|
|
121
|
+
14,
|
|
122
|
+
// MemoComponent
|
|
123
|
+
15
|
|
124
|
+
// SimpleMemoComponent
|
|
125
|
+
]);
|
|
126
|
+
var getFiberFromElement = (el) => {
|
|
127
|
+
try {
|
|
128
|
+
const key = Object.keys(el).find((k) => k.startsWith("__reactFiber$"));
|
|
129
|
+
if (!key) return null;
|
|
130
|
+
return el[key];
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var getCompositeComponentFiber = (fiber) => {
|
|
136
|
+
let current = fiber;
|
|
137
|
+
while (current) {
|
|
138
|
+
if (COMPOSITE_TAGS.has(current.tag)) return current;
|
|
139
|
+
current = current.return;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
};
|
|
143
|
+
var getInnerFunction = (type) => {
|
|
144
|
+
if (typeof type === "function") return type;
|
|
145
|
+
if (type && typeof type === "object") {
|
|
146
|
+
if ("render" in type && typeof type.render === "function")
|
|
147
|
+
return type.render;
|
|
148
|
+
if ("type" in type && typeof type.type === "function")
|
|
149
|
+
return type.type;
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
};
|
|
153
|
+
var getComponentName = (fiber) => {
|
|
154
|
+
const { type } = fiber;
|
|
155
|
+
if (typeof type === "function") {
|
|
156
|
+
return type.displayName || type.name || "Anonymous";
|
|
157
|
+
}
|
|
158
|
+
if (type && typeof type === "object") {
|
|
159
|
+
if ("displayName" in type && type.displayName) return type.displayName;
|
|
160
|
+
const inner = getInnerFunction(type);
|
|
161
|
+
if (inner) return inner.displayName || inner.name || "Anonymous";
|
|
162
|
+
}
|
|
163
|
+
return "Anonymous";
|
|
164
|
+
};
|
|
165
|
+
var SKIP_FRAMES = /* @__PURE__ */ new Set([
|
|
166
|
+
"jsxDEV",
|
|
167
|
+
"jsxs",
|
|
168
|
+
"jsx",
|
|
169
|
+
"react-stack-top-frame",
|
|
170
|
+
"react_stack_bottom_frame"
|
|
171
|
+
]);
|
|
172
|
+
var FRAME_RE = /at (?:(\S+) )?\(?(.+):(\d+):(\d+)\)?$/;
|
|
173
|
+
var parseFirstUserFrame = (fiber) => {
|
|
174
|
+
const stack = fiber._debugStack?.stack;
|
|
175
|
+
if (!stack) return null;
|
|
176
|
+
for (const line of stack.split("\n")) {
|
|
177
|
+
const match = FRAME_RE.exec(line.trim());
|
|
178
|
+
if (!match) continue;
|
|
179
|
+
const [, fnName, url, lineStr, colStr] = match;
|
|
180
|
+
if (fnName && SKIP_FRAMES.has(fnName)) continue;
|
|
181
|
+
if (url.includes("/node_modules/")) continue;
|
|
182
|
+
return { url, line: Number(lineStr), column: Number(colStr) };
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
};
|
|
186
|
+
var resolveFrame = async (frame) => {
|
|
187
|
+
const resolved = await resolveOriginalPosition(frame.url, frame.line, frame.column);
|
|
188
|
+
if (resolved) return resolved;
|
|
189
|
+
let fileName = frame.url;
|
|
190
|
+
try {
|
|
191
|
+
const parsed = new URL(frame.url);
|
|
192
|
+
fileName = decodeURIComponent(parsed.pathname);
|
|
193
|
+
const qIdx = fileName.indexOf("?");
|
|
194
|
+
if (qIdx !== -1) fileName = fileName.substring(0, qIdx);
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
fileName = fileName.replace(/\.\.\//g, "");
|
|
198
|
+
if (fileName.startsWith("/")) fileName = fileName.substring(1);
|
|
199
|
+
return { fileName, lineNumber: frame.line, columnNumber: frame.column };
|
|
200
|
+
};
|
|
201
|
+
var getCompositeDebugSource = async (fiber) => {
|
|
202
|
+
if (fiber._debugSource) return fiber._debugSource;
|
|
203
|
+
if (fiber._debugOwner?._debugSource) return fiber._debugOwner._debugSource;
|
|
204
|
+
const frame = parseFirstUserFrame(fiber);
|
|
205
|
+
if (frame) return resolveFrame(frame);
|
|
206
|
+
if (fiber._debugOwner) {
|
|
207
|
+
const ownerFrame = parseFirstUserFrame(fiber._debugOwner);
|
|
208
|
+
if (ownerFrame) return resolveFrame(ownerFrame);
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
};
|
|
212
|
+
var getDomDebugSource = async (fiber) => {
|
|
213
|
+
if (fiber._debugSource) return fiber._debugSource;
|
|
214
|
+
const frame = parseFirstUserFrame(fiber);
|
|
215
|
+
if (frame) return resolveFrame(frame);
|
|
216
|
+
return null;
|
|
217
|
+
};
|
|
218
|
+
var getComponentInfo = async (el) => {
|
|
219
|
+
const domFiber = getFiberFromElement(el);
|
|
220
|
+
if (!domFiber) return null;
|
|
221
|
+
const composite = getCompositeComponentFiber(domFiber);
|
|
222
|
+
if (!composite) return null;
|
|
223
|
+
const isComponentRoot = domFiber.return != null && COMPOSITE_TAGS.has(domFiber.return.tag);
|
|
224
|
+
if (isComponentRoot) {
|
|
225
|
+
return {
|
|
226
|
+
kind: "component",
|
|
227
|
+
name: getComponentName(composite),
|
|
228
|
+
elementTag: null,
|
|
229
|
+
source: await getCompositeDebugSource(composite),
|
|
230
|
+
callSite: null
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const owner = domFiber._debugOwner;
|
|
234
|
+
const elementTag = typeof domFiber.type === "string" ? domFiber.type : null;
|
|
235
|
+
if (owner && owner === composite) {
|
|
236
|
+
return {
|
|
237
|
+
kind: "element",
|
|
238
|
+
name: getComponentName(owner),
|
|
239
|
+
elementTag,
|
|
240
|
+
source: await getDomDebugSource(domFiber),
|
|
241
|
+
callSite: await getCompositeDebugSource(composite)
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const nameSource = owner && COMPOSITE_TAGS.has(owner.tag) ? owner : composite;
|
|
245
|
+
return {
|
|
246
|
+
kind: "children",
|
|
247
|
+
name: getComponentName(nameSource),
|
|
248
|
+
elementTag,
|
|
249
|
+
source: await getDomDebugSource(domFiber),
|
|
250
|
+
callSite: null
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// src/overlay.ts
|
|
255
|
+
var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
|
|
256
|
+
var COLORS = {
|
|
257
|
+
name: "#93c5fd",
|
|
258
|
+
tag: "#a78bfa",
|
|
259
|
+
path: "#71717a",
|
|
260
|
+
pathActive: "#a1a1aa",
|
|
261
|
+
pathDim: "#3f3f46",
|
|
262
|
+
hint: "#52525b"
|
|
263
|
+
};
|
|
264
|
+
var HIGHLIGHT_STYLES = {
|
|
265
|
+
position: "fixed",
|
|
266
|
+
pointerEvents: "none",
|
|
267
|
+
zIndex: "2147483646",
|
|
268
|
+
backgroundColor: "rgba(66, 135, 245, 0.15)",
|
|
269
|
+
border: "1.5px solid rgba(66, 135, 245, 0.6)",
|
|
270
|
+
borderRadius: "3px",
|
|
271
|
+
display: "none",
|
|
272
|
+
transition: "top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out"
|
|
273
|
+
};
|
|
274
|
+
var TOOLTIP_STYLES = {
|
|
275
|
+
position: "fixed",
|
|
276
|
+
pointerEvents: "none",
|
|
277
|
+
zIndex: "2147483647",
|
|
278
|
+
display: "none",
|
|
279
|
+
fontFamily: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace",
|
|
280
|
+
fontSize: "12px",
|
|
281
|
+
lineHeight: "1.4",
|
|
282
|
+
color: "#e4e4e7",
|
|
283
|
+
backgroundColor: "#18181b",
|
|
284
|
+
border: "1px solid #3f3f46",
|
|
285
|
+
borderRadius: "6px",
|
|
286
|
+
padding: "4px 8px",
|
|
287
|
+
whiteSpace: "nowrap",
|
|
288
|
+
maxWidth: "500px",
|
|
289
|
+
overflow: "hidden",
|
|
290
|
+
textOverflow: "ellipsis",
|
|
291
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.4)"
|
|
292
|
+
};
|
|
293
|
+
var applyStyles = (el, styles) => {
|
|
294
|
+
Object.assign(el.style, styles);
|
|
295
|
+
};
|
|
296
|
+
var truncatePath = (filePath) => {
|
|
297
|
+
const parts = filePath.split("/");
|
|
298
|
+
if (parts.length <= 3) return filePath;
|
|
299
|
+
return `.../${parts.slice(-3).join("/")}`;
|
|
300
|
+
};
|
|
301
|
+
var createSpan = (text, styles) => {
|
|
302
|
+
const span = document.createElement("span");
|
|
303
|
+
span.textContent = text;
|
|
304
|
+
Object.assign(span.style, styles);
|
|
305
|
+
return span;
|
|
306
|
+
};
|
|
307
|
+
var OverlayManager = class {
|
|
308
|
+
highlight = null;
|
|
309
|
+
tooltip = null;
|
|
310
|
+
init() {
|
|
311
|
+
if (this.highlight) return;
|
|
312
|
+
this.highlight = document.createElement("div");
|
|
313
|
+
this.highlight.dataset.reactGrep = "highlight";
|
|
314
|
+
applyStyles(this.highlight, HIGHLIGHT_STYLES);
|
|
315
|
+
document.body.appendChild(this.highlight);
|
|
316
|
+
this.tooltip = document.createElement("div");
|
|
317
|
+
this.tooltip.dataset.reactGrep = "tooltip";
|
|
318
|
+
applyStyles(this.tooltip, TOOLTIP_STYLES);
|
|
319
|
+
document.body.appendChild(this.tooltip);
|
|
320
|
+
}
|
|
321
|
+
show(el, info, activeSource = "source") {
|
|
322
|
+
if (!this.highlight || !this.tooltip) return;
|
|
323
|
+
const rect = el.getBoundingClientRect();
|
|
324
|
+
this.highlight.style.top = `${rect.top}px`;
|
|
325
|
+
this.highlight.style.left = `${rect.left}px`;
|
|
326
|
+
this.highlight.style.width = `${rect.width}px`;
|
|
327
|
+
this.highlight.style.height = `${rect.height}px`;
|
|
328
|
+
this.highlight.style.display = "block";
|
|
329
|
+
this.tooltip.textContent = "";
|
|
330
|
+
this.tooltip.appendChild(createSpan(info.name, { color: COLORS.name, fontWeight: "600" }));
|
|
331
|
+
if (info.elementTag != null) {
|
|
332
|
+
this.tooltip.appendChild(createSpan(" > ", { color: COLORS.path }));
|
|
333
|
+
this.tooltip.appendChild(
|
|
334
|
+
createSpan(info.elementTag, { color: COLORS.tag, fontWeight: "600" })
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
const filePath = info.source ? `${info.source.fileName}:${info.source.lineNumber}` : null;
|
|
338
|
+
const callSitePath = info.callSite ? `${info.callSite.fileName}:${info.callSite.lineNumber}` : null;
|
|
339
|
+
if (filePath && callSitePath) {
|
|
340
|
+
const sourceColor = activeSource === "source" ? COLORS.pathActive : COLORS.pathDim;
|
|
341
|
+
const callSiteColor = activeSource === "callSite" ? COLORS.pathActive : COLORS.pathDim;
|
|
342
|
+
const shiftHint = isMac ? "\u21E7" : "Shift";
|
|
343
|
+
const sourceText = truncatePath(filePath);
|
|
344
|
+
const callSiteText = truncatePath(callSitePath);
|
|
345
|
+
this.tooltip.appendChild(
|
|
346
|
+
createSpan(
|
|
347
|
+
` ${activeSource === "callSite" ? "(" : ""}${sourceText}${activeSource === "callSite" ? ")" : ""}`,
|
|
348
|
+
{ color: sourceColor }
|
|
349
|
+
)
|
|
350
|
+
);
|
|
351
|
+
this.tooltip.appendChild(createSpan(` ${shiftHint} `, { color: COLORS.hint }));
|
|
352
|
+
this.tooltip.appendChild(
|
|
353
|
+
createSpan(
|
|
354
|
+
`${activeSource === "source" ? "(" : ""}${callSiteText}${activeSource === "source" ? ")" : ""}`,
|
|
355
|
+
{ color: callSiteColor }
|
|
356
|
+
)
|
|
357
|
+
);
|
|
358
|
+
} else if (filePath) {
|
|
359
|
+
this.tooltip.appendChild(createSpan(` ${truncatePath(filePath)}`, { color: COLORS.path }));
|
|
360
|
+
}
|
|
361
|
+
const tooltipRect = this.tooltip.getBoundingClientRect();
|
|
362
|
+
let top = rect.top - tooltipRect.height - 6;
|
|
363
|
+
let left = rect.left;
|
|
364
|
+
if (top < 4) {
|
|
365
|
+
top = rect.bottom + 6;
|
|
366
|
+
}
|
|
367
|
+
if (left + tooltipRect.width > window.innerWidth - 4) {
|
|
368
|
+
left = window.innerWidth - tooltipRect.width - 4;
|
|
369
|
+
}
|
|
370
|
+
this.tooltip.style.top = `${top}px`;
|
|
371
|
+
this.tooltip.style.left = `${Math.max(4, left)}px`;
|
|
372
|
+
this.tooltip.style.display = "block";
|
|
373
|
+
}
|
|
374
|
+
showCopied(location) {
|
|
375
|
+
if (!this.tooltip) return;
|
|
376
|
+
this.tooltip.textContent = "";
|
|
377
|
+
this.tooltip.appendChild(createSpan("Copied!", { color: "#4ade80", fontWeight: "600" }));
|
|
378
|
+
this.tooltip.appendChild(createSpan(` ${truncatePath(location)}`, { color: "#a1a1aa" }));
|
|
379
|
+
this.tooltip.style.display = "block";
|
|
380
|
+
setTimeout(() => this.hide(), 1500);
|
|
381
|
+
}
|
|
382
|
+
hide() {
|
|
383
|
+
if (this.highlight) this.highlight.style.display = "none";
|
|
384
|
+
if (this.tooltip) this.tooltip.style.display = "none";
|
|
385
|
+
}
|
|
386
|
+
destroy() {
|
|
387
|
+
this.highlight?.remove();
|
|
388
|
+
this.tooltip?.remove();
|
|
389
|
+
this.highlight = null;
|
|
390
|
+
this.tooltip = null;
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// src/inspector.ts
|
|
395
|
+
var isMac2 = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
|
|
396
|
+
var Inspector = class {
|
|
397
|
+
overlay = new OverlayManager();
|
|
398
|
+
moveGeneration = 0;
|
|
399
|
+
lastTarget = null;
|
|
400
|
+
lastInfo = null;
|
|
401
|
+
sourceToggled = false;
|
|
402
|
+
shiftPressedClean = false;
|
|
403
|
+
boundHandlers;
|
|
404
|
+
constructor() {
|
|
405
|
+
this.boundHandlers = {
|
|
406
|
+
mousemove: this.handleMouseMove.bind(this),
|
|
407
|
+
click: this.handleClick.bind(this),
|
|
408
|
+
keydown: this.handleKeyDown.bind(this),
|
|
409
|
+
keyup: this.handleKeyUp.bind(this)
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
start() {
|
|
413
|
+
window.addEventListener("mousemove", this.boundHandlers.mousemove);
|
|
414
|
+
window.addEventListener("click", this.boundHandlers.click, true);
|
|
415
|
+
window.addEventListener("keydown", this.boundHandlers.keydown);
|
|
416
|
+
window.addEventListener("keyup", this.boundHandlers.keyup);
|
|
417
|
+
}
|
|
418
|
+
stop() {
|
|
419
|
+
window.removeEventListener("mousemove", this.boundHandlers.mousemove);
|
|
420
|
+
window.removeEventListener("click", this.boundHandlers.click, true);
|
|
421
|
+
window.removeEventListener("keydown", this.boundHandlers.keydown);
|
|
422
|
+
window.removeEventListener("keyup", this.boundHandlers.keyup);
|
|
423
|
+
this.overlay.destroy();
|
|
424
|
+
document.body.style.cursor = "";
|
|
425
|
+
this.lastTarget = null;
|
|
426
|
+
this.lastInfo = null;
|
|
427
|
+
this.sourceToggled = false;
|
|
428
|
+
this.shiftPressedClean = false;
|
|
429
|
+
}
|
|
430
|
+
isModifierHeld(e) {
|
|
431
|
+
return isMac2 ? e.metaKey : e.ctrlKey;
|
|
432
|
+
}
|
|
433
|
+
async handleMouseMove(e) {
|
|
434
|
+
if (!this.isModifierHeld(e)) {
|
|
435
|
+
this.overlay.hide();
|
|
436
|
+
document.body.style.cursor = "";
|
|
437
|
+
this.lastTarget = null;
|
|
438
|
+
this.lastInfo = null;
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
this.overlay.init();
|
|
442
|
+
document.body.style.cursor = "crosshair";
|
|
443
|
+
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
444
|
+
if (!target || target.closest("[data-react-grep]")) return;
|
|
445
|
+
if (target !== this.lastTarget) {
|
|
446
|
+
this.sourceToggled = false;
|
|
447
|
+
}
|
|
448
|
+
const gen = ++this.moveGeneration;
|
|
449
|
+
const info = await getComponentInfo(target);
|
|
450
|
+
if (gen !== this.moveGeneration) return;
|
|
451
|
+
if (!info) {
|
|
452
|
+
this.overlay.hide();
|
|
453
|
+
this.lastTarget = null;
|
|
454
|
+
this.lastInfo = null;
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
this.lastTarget = target;
|
|
458
|
+
this.lastInfo = info;
|
|
459
|
+
this.overlay.show(target, info, this.getActiveSource());
|
|
460
|
+
}
|
|
461
|
+
async handleClick(e) {
|
|
462
|
+
if (!this.isModifierHeld(e) || !e.shiftKey) return;
|
|
463
|
+
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
464
|
+
if (!target || target.closest("[data-react-grep]")) return;
|
|
465
|
+
e.preventDefault();
|
|
466
|
+
e.stopPropagation();
|
|
467
|
+
e.stopImmediatePropagation();
|
|
468
|
+
this.shiftPressedClean = false;
|
|
469
|
+
const info = await getComponentInfo(target);
|
|
470
|
+
if (!info) return;
|
|
471
|
+
const source = this.getActiveCopySource(info);
|
|
472
|
+
if (!source) return;
|
|
473
|
+
const { fileName, lineNumber, columnNumber } = source;
|
|
474
|
+
const location = columnNumber != null ? `${fileName}:${lineNumber}:${columnNumber}` : `${fileName}:${lineNumber}`;
|
|
475
|
+
await this.copyToClipboard(location);
|
|
476
|
+
this.overlay.showCopied(location);
|
|
477
|
+
}
|
|
478
|
+
handleKeyDown(e) {
|
|
479
|
+
if (e.key === "Shift" && this.isModifierHeld(e)) {
|
|
480
|
+
this.shiftPressedClean = true;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
handleKeyUp(e) {
|
|
484
|
+
if (isMac2 && e.key === "Meta" || !isMac2 && e.key === "Control") {
|
|
485
|
+
this.overlay.hide();
|
|
486
|
+
document.body.style.cursor = "";
|
|
487
|
+
this.lastTarget = null;
|
|
488
|
+
this.lastInfo = null;
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
if (e.key === "Shift" && this.shiftPressedClean && this.lastTarget && this.lastInfo && this.lastInfo.callSite) {
|
|
492
|
+
this.sourceToggled = !this.sourceToggled;
|
|
493
|
+
this.overlay.show(this.lastTarget, this.lastInfo, this.getActiveSource());
|
|
494
|
+
}
|
|
495
|
+
this.shiftPressedClean = false;
|
|
496
|
+
}
|
|
497
|
+
getActiveSource() {
|
|
498
|
+
return this.sourceToggled ? "callSite" : "source";
|
|
499
|
+
}
|
|
500
|
+
getActiveCopySource(info) {
|
|
501
|
+
return this.sourceToggled && info.callSite ? info.callSite : info.source;
|
|
502
|
+
}
|
|
503
|
+
async copyToClipboard(text) {
|
|
504
|
+
try {
|
|
505
|
+
await navigator.clipboard.writeText(text);
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// src/index.ts
|
|
512
|
+
var inspector = null;
|
|
513
|
+
var init = () => {
|
|
514
|
+
if (inspector) return;
|
|
515
|
+
inspector = new Inspector();
|
|
516
|
+
inspector.start();
|
|
517
|
+
};
|
|
518
|
+
var destroy = () => {
|
|
519
|
+
if (!inspector) return;
|
|
520
|
+
inspector.stop();
|
|
521
|
+
inspector = null;
|
|
522
|
+
};
|
|
523
|
+
if (typeof window !== "undefined") {
|
|
524
|
+
const bootstrap = () => init();
|
|
525
|
+
if (document.readyState === "loading") {
|
|
526
|
+
document.addEventListener("DOMContentLoaded", bootstrap);
|
|
527
|
+
} else {
|
|
528
|
+
bootstrap();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
exports.destroy = destroy;
|
|
533
|
+
exports.init = init;
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var ReactGrep=(function(exports){'use strict';var x=new Map,k="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",E=[];for(let e=0;e<k.length;e++)E[k.charCodeAt(e)]=e;var d=(e,t)=>{let n=0,i=0;for(;t.i<e.length;){let o=E[e.charCodeAt(t.i++)];if(i+=(o&31)<<n,!(o&32))return i&1?-(i>>1):i>>1;n+=5;}return 0},L=e=>{let t=[],n=0,i=0,o=0;for(let s of e.split(";")){let l=[],a=0;if(s){let r={i:0};for(;r.i<s.length;){if(s.charCodeAt(r.i)===44){r.i++;continue}a+=d(s,r),!(r.i>=s.length||s.charCodeAt(r.i)===44)&&(n+=d(s,r),i+=d(s,r),o+=d(s,r),r.i<s.length&&s.charCodeAt(r.i)!==44&&d(s,r),l.push([a,n,i,o]));}}t.push(l);}return t},_=/^data:application\/json[^,]*;base64,([A-Za-z0-9+/=]+)$/,A=(e,t)=>{try{return new URL(e).origin===new URL(t).origin}catch{return false}},H=async e=>{try{let i=(await(await fetch(e)).text()).match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)$/m);if(!i)return null;let o=i[1].trim(),s;if(o.startsWith("data:")){let a=_.exec(o);if(!a)return null;s=atob(a[1]);}else {let a=new URL(o,e).href;if(!A(e,a))return null;s=await(await fetch(a)).text();}let l=JSON.parse(s);return {sources:l.sources,mappings:L(l.mappings)}}catch{return null}},O=e=>{let t=x.get(e);return t||(t=H(e),x.set(e,t)),t},K=(e,t,n)=>{if(t<0||t>=e.mappings.length)return null;let i=e.mappings[t];if(!i.length)return null;let o=0,s=i.length-1;for(;o<s;){let l=o+s+1>>1;i[l][0]<=n?o=l:s=l-1;}return i[o]},M=async(e,t,n)=>{let i=await O(e);if(!i)return null;let o=K(i,t-1,n-1);return o?{fileName:i.sources[o[1]],lineNumber:o[2]+1,columnNumber:o[3]+1}:null};var b=new Set([0,1,11,14,15]),U=e=>{try{let t=Object.keys(e).find(n=>n.startsWith("__reactFiber$"));return t?e[t]:null}catch{return null}},j=e=>{let t=e;for(;t;){if(b.has(t.tag))return t;t=t.return;}return null},W=e=>{if(typeof e=="function")return e;if(e&&typeof e=="object"){if("render"in e&&typeof e.render=="function")return e.render;if("type"in e&&typeof e.type=="function")return e.type}return null},y=e=>{let{type:t}=e;if(typeof t=="function")return t.displayName||t.name||"Anonymous";if(t&&typeof t=="object"){if("displayName"in t&&t.displayName)return t.displayName;let n=W(t);if(n)return n.displayName||n.name||"Anonymous"}return "Anonymous"},G=new Set(["jsxDEV","jsxs","jsx","react-stack-top-frame","react_stack_bottom_frame"]),z=/at (?:(\S+) )?\(?(.+):(\d+):(\d+)\)?$/,v=e=>{let t=e._debugStack?.stack;if(!t)return null;for(let n of t.split(`
|
|
2
|
+
`)){let i=z.exec(n.trim());if(!i)continue;let[,o,s,l,a]=i;if(!(o&&G.has(o))&&!s.includes("/node_modules/"))return {url:s,line:Number(l),column:Number(a)}}return null},S=async e=>{let t=await M(e.url,e.line,e.column);if(t)return t;let n=e.url;try{let i=new URL(e.url);n=decodeURIComponent(i.pathname);let o=n.indexOf("?");o!==-1&&(n=n.substring(0,o));}catch{}return n=n.replace(/\.\.\//g,""),n.startsWith("/")&&(n=n.substring(1)),{fileName:n,lineNumber:e.line,columnNumber:e.column}},T=async e=>{if(e._debugSource)return e._debugSource;if(e._debugOwner?._debugSource)return e._debugOwner._debugSource;let t=v(e);if(t)return S(t);if(e._debugOwner){let n=v(e._debugOwner);if(n)return S(n)}return null},F=async e=>{if(e._debugSource)return e._debugSource;let t=v(e);return t?S(t):null},w=async e=>{let t=U(e);if(!t)return null;let n=j(t);if(!n)return null;if(t.return!=null&&b.has(t.return.tag))return {kind:"component",name:y(n),elementTag:null,source:await T(n),callSite:null};let o=t._debugOwner,s=typeof t.type=="string"?t.type:null;if(o&&o===n)return {kind:"element",name:y(o),elementTag:s,source:await F(t),callSite:await T(n)};let l=o&&b.has(o.tag)?o:n;return {kind:"children",name:y(l),elementTag:s,source:await F(t),callSite:null}};var Y=typeof navigator<"u"&&/Mac|iPhone|iPad/.test(navigator.platform),c={name:"#93c5fd",tag:"#a78bfa",path:"#71717a",pathActive:"#a1a1aa",pathDim:"#3f3f46",hint:"#52525b"},V={position:"fixed",pointerEvents:"none",zIndex:"2147483646",backgroundColor:"rgba(66, 135, 245, 0.15)",border:"1.5px solid rgba(66, 135, 245, 0.6)",borderRadius:"3px",display:"none",transition:"top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out"},B={position:"fixed",pointerEvents:"none",zIndex:"2147483647",display:"none",fontFamily:"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace",fontSize:"12px",lineHeight:"1.4",color:"#e4e4e7",backgroundColor:"#18181b",border:"1px solid #3f3f46",borderRadius:"6px",padding:"4px 8px",whiteSpace:"nowrap",maxWidth:"500px",overflow:"hidden",textOverflow:"ellipsis",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.4)"},P=(e,t)=>{Object.assign(e.style,t);},p=e=>{let t=e.split("/");return t.length<=3?e:`.../${t.slice(-3).join("/")}`},u=(e,t)=>{let n=document.createElement("span");return n.textContent=e,Object.assign(n.style,t),n},m=class{highlight=null;tooltip=null;init(){this.highlight||(this.highlight=document.createElement("div"),this.highlight.dataset.reactGrep="highlight",P(this.highlight,V),document.body.appendChild(this.highlight),this.tooltip=document.createElement("div"),this.tooltip.dataset.reactGrep="tooltip",P(this.tooltip,B),document.body.appendChild(this.tooltip));}show(t,n,i="source"){if(!this.highlight||!this.tooltip)return;let o=t.getBoundingClientRect();this.highlight.style.top=`${o.top}px`,this.highlight.style.left=`${o.left}px`,this.highlight.style.width=`${o.width}px`,this.highlight.style.height=`${o.height}px`,this.highlight.style.display="block",this.tooltip.textContent="",this.tooltip.appendChild(u(n.name,{color:c.name,fontWeight:"600"})),n.elementTag!=null&&(this.tooltip.appendChild(u(" > ",{color:c.path})),this.tooltip.appendChild(u(n.elementTag,{color:c.tag,fontWeight:"600"})));let s=n.source?`${n.source.fileName}:${n.source.lineNumber}`:null,l=n.callSite?`${n.callSite.fileName}:${n.callSite.lineNumber}`:null;if(s&&l){let I=i==="source"?c.pathActive:c.pathDim,D=i==="callSite"?c.pathActive:c.pathDim,N=Y?"\u21E7":"Shift",$=p(s),R=p(l);this.tooltip.appendChild(u(` ${i==="callSite"?"(":""}${$}${i==="callSite"?")":""}`,{color:I})),this.tooltip.appendChild(u(` ${N} `,{color:c.hint})),this.tooltip.appendChild(u(`${i==="source"?"(":""}${R}${i==="source"?")":""}`,{color:D}));}else s&&this.tooltip.appendChild(u(` ${p(s)}`,{color:c.path}));let a=this.tooltip.getBoundingClientRect(),r=o.top-a.height-6,f=o.left;r<4&&(r=o.bottom+6),f+a.width>window.innerWidth-4&&(f=window.innerWidth-a.width-4),this.tooltip.style.top=`${r}px`,this.tooltip.style.left=`${Math.max(4,f)}px`,this.tooltip.style.display="block";}showCopied(t){this.tooltip&&(this.tooltip.textContent="",this.tooltip.appendChild(u("Copied!",{color:"#4ade80",fontWeight:"600"})),this.tooltip.appendChild(u(` ${p(t)}`,{color:"#a1a1aa"})),this.tooltip.style.display="block",setTimeout(()=>this.hide(),1500));}hide(){this.highlight&&(this.highlight.style.display="none"),this.tooltip&&(this.tooltip.style.display="none");}destroy(){this.highlight?.remove(),this.tooltip?.remove(),this.highlight=null,this.tooltip=null;}};var C=typeof navigator<"u"&&/Mac|iPhone|iPad/.test(navigator.platform),g=class{overlay=new m;moveGeneration=0;lastTarget=null;lastInfo=null;sourceToggled=false;shiftPressedClean=false;boundHandlers;constructor(){this.boundHandlers={mousemove:this.handleMouseMove.bind(this),click:this.handleClick.bind(this),keydown:this.handleKeyDown.bind(this),keyup:this.handleKeyUp.bind(this)};}start(){window.addEventListener("mousemove",this.boundHandlers.mousemove),window.addEventListener("click",this.boundHandlers.click,true),window.addEventListener("keydown",this.boundHandlers.keydown),window.addEventListener("keyup",this.boundHandlers.keyup);}stop(){window.removeEventListener("mousemove",this.boundHandlers.mousemove),window.removeEventListener("click",this.boundHandlers.click,true),window.removeEventListener("keydown",this.boundHandlers.keydown),window.removeEventListener("keyup",this.boundHandlers.keyup),this.overlay.destroy(),document.body.style.cursor="",this.lastTarget=null,this.lastInfo=null,this.sourceToggled=false,this.shiftPressedClean=false;}isModifierHeld(t){return C?t.metaKey:t.ctrlKey}async handleMouseMove(t){if(!this.isModifierHeld(t)){this.overlay.hide(),document.body.style.cursor="",this.lastTarget=null,this.lastInfo=null;return}this.overlay.init(),document.body.style.cursor="crosshair";let n=document.elementFromPoint(t.clientX,t.clientY);if(!n||n.closest("[data-react-grep]"))return;n!==this.lastTarget&&(this.sourceToggled=false);let i=++this.moveGeneration,o=await w(n);if(i===this.moveGeneration){if(!o){this.overlay.hide(),this.lastTarget=null,this.lastInfo=null;return}this.lastTarget=n,this.lastInfo=o,this.overlay.show(n,o,this.getActiveSource());}}async handleClick(t){if(!this.isModifierHeld(t)||!t.shiftKey)return;let n=document.elementFromPoint(t.clientX,t.clientY);if(!n||n.closest("[data-react-grep]"))return;t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation(),this.shiftPressedClean=false;let i=await w(n);if(!i)return;let o=this.getActiveCopySource(i);if(!o)return;let{fileName:s,lineNumber:l,columnNumber:a}=o,r=a!=null?`${s}:${l}:${a}`:`${s}:${l}`;await this.copyToClipboard(r),this.overlay.showCopied(r);}handleKeyDown(t){t.key==="Shift"&&this.isModifierHeld(t)&&(this.shiftPressedClean=true);}handleKeyUp(t){if(C&&t.key==="Meta"||!C&&t.key==="Control"){this.overlay.hide(),document.body.style.cursor="",this.lastTarget=null,this.lastInfo=null;return}t.key==="Shift"&&this.shiftPressedClean&&this.lastTarget&&this.lastInfo&&this.lastInfo.callSite&&(this.sourceToggled=!this.sourceToggled,this.overlay.show(this.lastTarget,this.lastInfo,this.getActiveSource())),this.shiftPressedClean=false;}getActiveSource(){return this.sourceToggled?"callSite":"source"}getActiveCopySource(t){return this.sourceToggled&&t.callSite?t.callSite:t.source}async copyToClipboard(t){try{await navigator.clipboard.writeText(t);}catch{}}};var h=null,J=()=>{h||(h=new g,h.start());},it=()=>{h&&(h.stop(),h=null);};if(typeof window<"u"){let e=()=>J();document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e();}
|
|
3
|
+
exports.destroy=it;exports.init=J;return exports;})({});
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
// src/source-map.ts
|
|
2
|
+
var cache = /* @__PURE__ */ new Map();
|
|
3
|
+
var VLQ_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
4
|
+
var charToInt = [];
|
|
5
|
+
for (let i = 0; i < VLQ_CHARS.length; i++) charToInt[VLQ_CHARS.charCodeAt(i)] = i;
|
|
6
|
+
var decodeVLQ = (str, pos) => {
|
|
7
|
+
let shift = 0;
|
|
8
|
+
let value = 0;
|
|
9
|
+
while (pos.i < str.length) {
|
|
10
|
+
const digit = charToInt[str.charCodeAt(pos.i++)];
|
|
11
|
+
value += (digit & 31) << shift;
|
|
12
|
+
if (!(digit & 32)) return value & 1 ? -(value >> 1) : value >> 1;
|
|
13
|
+
shift += 5;
|
|
14
|
+
}
|
|
15
|
+
return 0;
|
|
16
|
+
};
|
|
17
|
+
var decodeMappings = (raw) => {
|
|
18
|
+
const result = [];
|
|
19
|
+
let srcIdx = 0;
|
|
20
|
+
let origLine = 0;
|
|
21
|
+
let origCol = 0;
|
|
22
|
+
for (const line of raw.split(";")) {
|
|
23
|
+
const segments = [];
|
|
24
|
+
let genCol = 0;
|
|
25
|
+
if (line) {
|
|
26
|
+
const pos = { i: 0 };
|
|
27
|
+
while (pos.i < line.length) {
|
|
28
|
+
if (line.charCodeAt(pos.i) === 44) {
|
|
29
|
+
pos.i++;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
genCol += decodeVLQ(line, pos);
|
|
33
|
+
if (pos.i >= line.length || line.charCodeAt(pos.i) === 44) continue;
|
|
34
|
+
srcIdx += decodeVLQ(line, pos);
|
|
35
|
+
origLine += decodeVLQ(line, pos);
|
|
36
|
+
origCol += decodeVLQ(line, pos);
|
|
37
|
+
if (pos.i < line.length && line.charCodeAt(pos.i) !== 44) decodeVLQ(line, pos);
|
|
38
|
+
segments.push([genCol, srcIdx, origLine, origCol]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
result.push(segments);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
var DATA_URI_RE = /^data:application\/json[^,]*;base64,([A-Za-z0-9+/=]+)$/;
|
|
46
|
+
var isSameOrigin = (a, b) => {
|
|
47
|
+
try {
|
|
48
|
+
return new URL(a).origin === new URL(b).origin;
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var fetchAndParse = async (url) => {
|
|
54
|
+
try {
|
|
55
|
+
const res = await fetch(url);
|
|
56
|
+
const text = await res.text();
|
|
57
|
+
const match = text.match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)$/m);
|
|
58
|
+
if (!match) return null;
|
|
59
|
+
const ref = match[1].trim();
|
|
60
|
+
let mapJson;
|
|
61
|
+
if (ref.startsWith("data:")) {
|
|
62
|
+
const dataMatch = DATA_URI_RE.exec(ref);
|
|
63
|
+
if (!dataMatch) return null;
|
|
64
|
+
mapJson = atob(dataMatch[1]);
|
|
65
|
+
} else {
|
|
66
|
+
const mapUrl = new URL(ref, url).href;
|
|
67
|
+
if (!isSameOrigin(url, mapUrl)) return null;
|
|
68
|
+
const mapRes = await fetch(mapUrl);
|
|
69
|
+
mapJson = await mapRes.text();
|
|
70
|
+
}
|
|
71
|
+
const raw = JSON.parse(mapJson);
|
|
72
|
+
return { sources: raw.sources, mappings: decodeMappings(raw.mappings) };
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var getSourceMap = (url) => {
|
|
78
|
+
let promise = cache.get(url);
|
|
79
|
+
if (!promise) {
|
|
80
|
+
promise = fetchAndParse(url);
|
|
81
|
+
cache.set(url, promise);
|
|
82
|
+
}
|
|
83
|
+
return promise;
|
|
84
|
+
};
|
|
85
|
+
var lookup = (map, genLine, genCol) => {
|
|
86
|
+
if (genLine < 0 || genLine >= map.mappings.length) return null;
|
|
87
|
+
const segments = map.mappings[genLine];
|
|
88
|
+
if (!segments.length) return null;
|
|
89
|
+
let lo = 0;
|
|
90
|
+
let hi = segments.length - 1;
|
|
91
|
+
while (lo < hi) {
|
|
92
|
+
const mid = lo + hi + 1 >> 1;
|
|
93
|
+
if (segments[mid][0] <= genCol) lo = mid;
|
|
94
|
+
else hi = mid - 1;
|
|
95
|
+
}
|
|
96
|
+
return segments[lo];
|
|
97
|
+
};
|
|
98
|
+
var resolveOriginalPosition = async (url, line, column) => {
|
|
99
|
+
const map = await getSourceMap(url);
|
|
100
|
+
if (!map) return null;
|
|
101
|
+
const seg = lookup(map, line - 1, column - 1);
|
|
102
|
+
if (!seg) return null;
|
|
103
|
+
const fileName = map.sources[seg[1]];
|
|
104
|
+
return {
|
|
105
|
+
fileName,
|
|
106
|
+
lineNumber: seg[2] + 1,
|
|
107
|
+
columnNumber: seg[3] + 1
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// src/fiber.ts
|
|
112
|
+
var COMPOSITE_TAGS = /* @__PURE__ */ new Set([
|
|
113
|
+
0,
|
|
114
|
+
// FunctionComponent
|
|
115
|
+
1,
|
|
116
|
+
// ClassComponent
|
|
117
|
+
11,
|
|
118
|
+
// ForwardRef
|
|
119
|
+
14,
|
|
120
|
+
// MemoComponent
|
|
121
|
+
15
|
|
122
|
+
// SimpleMemoComponent
|
|
123
|
+
]);
|
|
124
|
+
var getFiberFromElement = (el) => {
|
|
125
|
+
try {
|
|
126
|
+
const key = Object.keys(el).find((k) => k.startsWith("__reactFiber$"));
|
|
127
|
+
if (!key) return null;
|
|
128
|
+
return el[key];
|
|
129
|
+
} catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var getCompositeComponentFiber = (fiber) => {
|
|
134
|
+
let current = fiber;
|
|
135
|
+
while (current) {
|
|
136
|
+
if (COMPOSITE_TAGS.has(current.tag)) return current;
|
|
137
|
+
current = current.return;
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
};
|
|
141
|
+
var getInnerFunction = (type) => {
|
|
142
|
+
if (typeof type === "function") return type;
|
|
143
|
+
if (type && typeof type === "object") {
|
|
144
|
+
if ("render" in type && typeof type.render === "function")
|
|
145
|
+
return type.render;
|
|
146
|
+
if ("type" in type && typeof type.type === "function")
|
|
147
|
+
return type.type;
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
};
|
|
151
|
+
var getComponentName = (fiber) => {
|
|
152
|
+
const { type } = fiber;
|
|
153
|
+
if (typeof type === "function") {
|
|
154
|
+
return type.displayName || type.name || "Anonymous";
|
|
155
|
+
}
|
|
156
|
+
if (type && typeof type === "object") {
|
|
157
|
+
if ("displayName" in type && type.displayName) return type.displayName;
|
|
158
|
+
const inner = getInnerFunction(type);
|
|
159
|
+
if (inner) return inner.displayName || inner.name || "Anonymous";
|
|
160
|
+
}
|
|
161
|
+
return "Anonymous";
|
|
162
|
+
};
|
|
163
|
+
var SKIP_FRAMES = /* @__PURE__ */ new Set([
|
|
164
|
+
"jsxDEV",
|
|
165
|
+
"jsxs",
|
|
166
|
+
"jsx",
|
|
167
|
+
"react-stack-top-frame",
|
|
168
|
+
"react_stack_bottom_frame"
|
|
169
|
+
]);
|
|
170
|
+
var FRAME_RE = /at (?:(\S+) )?\(?(.+):(\d+):(\d+)\)?$/;
|
|
171
|
+
var parseFirstUserFrame = (fiber) => {
|
|
172
|
+
const stack = fiber._debugStack?.stack;
|
|
173
|
+
if (!stack) return null;
|
|
174
|
+
for (const line of stack.split("\n")) {
|
|
175
|
+
const match = FRAME_RE.exec(line.trim());
|
|
176
|
+
if (!match) continue;
|
|
177
|
+
const [, fnName, url, lineStr, colStr] = match;
|
|
178
|
+
if (fnName && SKIP_FRAMES.has(fnName)) continue;
|
|
179
|
+
if (url.includes("/node_modules/")) continue;
|
|
180
|
+
return { url, line: Number(lineStr), column: Number(colStr) };
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
};
|
|
184
|
+
var resolveFrame = async (frame) => {
|
|
185
|
+
const resolved = await resolveOriginalPosition(frame.url, frame.line, frame.column);
|
|
186
|
+
if (resolved) return resolved;
|
|
187
|
+
let fileName = frame.url;
|
|
188
|
+
try {
|
|
189
|
+
const parsed = new URL(frame.url);
|
|
190
|
+
fileName = decodeURIComponent(parsed.pathname);
|
|
191
|
+
const qIdx = fileName.indexOf("?");
|
|
192
|
+
if (qIdx !== -1) fileName = fileName.substring(0, qIdx);
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
fileName = fileName.replace(/\.\.\//g, "");
|
|
196
|
+
if (fileName.startsWith("/")) fileName = fileName.substring(1);
|
|
197
|
+
return { fileName, lineNumber: frame.line, columnNumber: frame.column };
|
|
198
|
+
};
|
|
199
|
+
var getCompositeDebugSource = async (fiber) => {
|
|
200
|
+
if (fiber._debugSource) return fiber._debugSource;
|
|
201
|
+
if (fiber._debugOwner?._debugSource) return fiber._debugOwner._debugSource;
|
|
202
|
+
const frame = parseFirstUserFrame(fiber);
|
|
203
|
+
if (frame) return resolveFrame(frame);
|
|
204
|
+
if (fiber._debugOwner) {
|
|
205
|
+
const ownerFrame = parseFirstUserFrame(fiber._debugOwner);
|
|
206
|
+
if (ownerFrame) return resolveFrame(ownerFrame);
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
};
|
|
210
|
+
var getDomDebugSource = async (fiber) => {
|
|
211
|
+
if (fiber._debugSource) return fiber._debugSource;
|
|
212
|
+
const frame = parseFirstUserFrame(fiber);
|
|
213
|
+
if (frame) return resolveFrame(frame);
|
|
214
|
+
return null;
|
|
215
|
+
};
|
|
216
|
+
var getComponentInfo = async (el) => {
|
|
217
|
+
const domFiber = getFiberFromElement(el);
|
|
218
|
+
if (!domFiber) return null;
|
|
219
|
+
const composite = getCompositeComponentFiber(domFiber);
|
|
220
|
+
if (!composite) return null;
|
|
221
|
+
const isComponentRoot = domFiber.return != null && COMPOSITE_TAGS.has(domFiber.return.tag);
|
|
222
|
+
if (isComponentRoot) {
|
|
223
|
+
return {
|
|
224
|
+
kind: "component",
|
|
225
|
+
name: getComponentName(composite),
|
|
226
|
+
elementTag: null,
|
|
227
|
+
source: await getCompositeDebugSource(composite),
|
|
228
|
+
callSite: null
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const owner = domFiber._debugOwner;
|
|
232
|
+
const elementTag = typeof domFiber.type === "string" ? domFiber.type : null;
|
|
233
|
+
if (owner && owner === composite) {
|
|
234
|
+
return {
|
|
235
|
+
kind: "element",
|
|
236
|
+
name: getComponentName(owner),
|
|
237
|
+
elementTag,
|
|
238
|
+
source: await getDomDebugSource(domFiber),
|
|
239
|
+
callSite: await getCompositeDebugSource(composite)
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const nameSource = owner && COMPOSITE_TAGS.has(owner.tag) ? owner : composite;
|
|
243
|
+
return {
|
|
244
|
+
kind: "children",
|
|
245
|
+
name: getComponentName(nameSource),
|
|
246
|
+
elementTag,
|
|
247
|
+
source: await getDomDebugSource(domFiber),
|
|
248
|
+
callSite: null
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// src/overlay.ts
|
|
253
|
+
var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
|
|
254
|
+
var COLORS = {
|
|
255
|
+
name: "#93c5fd",
|
|
256
|
+
tag: "#a78bfa",
|
|
257
|
+
path: "#71717a",
|
|
258
|
+
pathActive: "#a1a1aa",
|
|
259
|
+
pathDim: "#3f3f46",
|
|
260
|
+
hint: "#52525b"
|
|
261
|
+
};
|
|
262
|
+
var HIGHLIGHT_STYLES = {
|
|
263
|
+
position: "fixed",
|
|
264
|
+
pointerEvents: "none",
|
|
265
|
+
zIndex: "2147483646",
|
|
266
|
+
backgroundColor: "rgba(66, 135, 245, 0.15)",
|
|
267
|
+
border: "1.5px solid rgba(66, 135, 245, 0.6)",
|
|
268
|
+
borderRadius: "3px",
|
|
269
|
+
display: "none",
|
|
270
|
+
transition: "top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out"
|
|
271
|
+
};
|
|
272
|
+
var TOOLTIP_STYLES = {
|
|
273
|
+
position: "fixed",
|
|
274
|
+
pointerEvents: "none",
|
|
275
|
+
zIndex: "2147483647",
|
|
276
|
+
display: "none",
|
|
277
|
+
fontFamily: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace",
|
|
278
|
+
fontSize: "12px",
|
|
279
|
+
lineHeight: "1.4",
|
|
280
|
+
color: "#e4e4e7",
|
|
281
|
+
backgroundColor: "#18181b",
|
|
282
|
+
border: "1px solid #3f3f46",
|
|
283
|
+
borderRadius: "6px",
|
|
284
|
+
padding: "4px 8px",
|
|
285
|
+
whiteSpace: "nowrap",
|
|
286
|
+
maxWidth: "500px",
|
|
287
|
+
overflow: "hidden",
|
|
288
|
+
textOverflow: "ellipsis",
|
|
289
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.4)"
|
|
290
|
+
};
|
|
291
|
+
var applyStyles = (el, styles) => {
|
|
292
|
+
Object.assign(el.style, styles);
|
|
293
|
+
};
|
|
294
|
+
var truncatePath = (filePath) => {
|
|
295
|
+
const parts = filePath.split("/");
|
|
296
|
+
if (parts.length <= 3) return filePath;
|
|
297
|
+
return `.../${parts.slice(-3).join("/")}`;
|
|
298
|
+
};
|
|
299
|
+
var createSpan = (text, styles) => {
|
|
300
|
+
const span = document.createElement("span");
|
|
301
|
+
span.textContent = text;
|
|
302
|
+
Object.assign(span.style, styles);
|
|
303
|
+
return span;
|
|
304
|
+
};
|
|
305
|
+
var OverlayManager = class {
|
|
306
|
+
highlight = null;
|
|
307
|
+
tooltip = null;
|
|
308
|
+
init() {
|
|
309
|
+
if (this.highlight) return;
|
|
310
|
+
this.highlight = document.createElement("div");
|
|
311
|
+
this.highlight.dataset.reactGrep = "highlight";
|
|
312
|
+
applyStyles(this.highlight, HIGHLIGHT_STYLES);
|
|
313
|
+
document.body.appendChild(this.highlight);
|
|
314
|
+
this.tooltip = document.createElement("div");
|
|
315
|
+
this.tooltip.dataset.reactGrep = "tooltip";
|
|
316
|
+
applyStyles(this.tooltip, TOOLTIP_STYLES);
|
|
317
|
+
document.body.appendChild(this.tooltip);
|
|
318
|
+
}
|
|
319
|
+
show(el, info, activeSource = "source") {
|
|
320
|
+
if (!this.highlight || !this.tooltip) return;
|
|
321
|
+
const rect = el.getBoundingClientRect();
|
|
322
|
+
this.highlight.style.top = `${rect.top}px`;
|
|
323
|
+
this.highlight.style.left = `${rect.left}px`;
|
|
324
|
+
this.highlight.style.width = `${rect.width}px`;
|
|
325
|
+
this.highlight.style.height = `${rect.height}px`;
|
|
326
|
+
this.highlight.style.display = "block";
|
|
327
|
+
this.tooltip.textContent = "";
|
|
328
|
+
this.tooltip.appendChild(createSpan(info.name, { color: COLORS.name, fontWeight: "600" }));
|
|
329
|
+
if (info.elementTag != null) {
|
|
330
|
+
this.tooltip.appendChild(createSpan(" > ", { color: COLORS.path }));
|
|
331
|
+
this.tooltip.appendChild(
|
|
332
|
+
createSpan(info.elementTag, { color: COLORS.tag, fontWeight: "600" })
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
const filePath = info.source ? `${info.source.fileName}:${info.source.lineNumber}` : null;
|
|
336
|
+
const callSitePath = info.callSite ? `${info.callSite.fileName}:${info.callSite.lineNumber}` : null;
|
|
337
|
+
if (filePath && callSitePath) {
|
|
338
|
+
const sourceColor = activeSource === "source" ? COLORS.pathActive : COLORS.pathDim;
|
|
339
|
+
const callSiteColor = activeSource === "callSite" ? COLORS.pathActive : COLORS.pathDim;
|
|
340
|
+
const shiftHint = isMac ? "\u21E7" : "Shift";
|
|
341
|
+
const sourceText = truncatePath(filePath);
|
|
342
|
+
const callSiteText = truncatePath(callSitePath);
|
|
343
|
+
this.tooltip.appendChild(
|
|
344
|
+
createSpan(
|
|
345
|
+
` ${activeSource === "callSite" ? "(" : ""}${sourceText}${activeSource === "callSite" ? ")" : ""}`,
|
|
346
|
+
{ color: sourceColor }
|
|
347
|
+
)
|
|
348
|
+
);
|
|
349
|
+
this.tooltip.appendChild(createSpan(` ${shiftHint} `, { color: COLORS.hint }));
|
|
350
|
+
this.tooltip.appendChild(
|
|
351
|
+
createSpan(
|
|
352
|
+
`${activeSource === "source" ? "(" : ""}${callSiteText}${activeSource === "source" ? ")" : ""}`,
|
|
353
|
+
{ color: callSiteColor }
|
|
354
|
+
)
|
|
355
|
+
);
|
|
356
|
+
} else if (filePath) {
|
|
357
|
+
this.tooltip.appendChild(createSpan(` ${truncatePath(filePath)}`, { color: COLORS.path }));
|
|
358
|
+
}
|
|
359
|
+
const tooltipRect = this.tooltip.getBoundingClientRect();
|
|
360
|
+
let top = rect.top - tooltipRect.height - 6;
|
|
361
|
+
let left = rect.left;
|
|
362
|
+
if (top < 4) {
|
|
363
|
+
top = rect.bottom + 6;
|
|
364
|
+
}
|
|
365
|
+
if (left + tooltipRect.width > window.innerWidth - 4) {
|
|
366
|
+
left = window.innerWidth - tooltipRect.width - 4;
|
|
367
|
+
}
|
|
368
|
+
this.tooltip.style.top = `${top}px`;
|
|
369
|
+
this.tooltip.style.left = `${Math.max(4, left)}px`;
|
|
370
|
+
this.tooltip.style.display = "block";
|
|
371
|
+
}
|
|
372
|
+
showCopied(location) {
|
|
373
|
+
if (!this.tooltip) return;
|
|
374
|
+
this.tooltip.textContent = "";
|
|
375
|
+
this.tooltip.appendChild(createSpan("Copied!", { color: "#4ade80", fontWeight: "600" }));
|
|
376
|
+
this.tooltip.appendChild(createSpan(` ${truncatePath(location)}`, { color: "#a1a1aa" }));
|
|
377
|
+
this.tooltip.style.display = "block";
|
|
378
|
+
setTimeout(() => this.hide(), 1500);
|
|
379
|
+
}
|
|
380
|
+
hide() {
|
|
381
|
+
if (this.highlight) this.highlight.style.display = "none";
|
|
382
|
+
if (this.tooltip) this.tooltip.style.display = "none";
|
|
383
|
+
}
|
|
384
|
+
destroy() {
|
|
385
|
+
this.highlight?.remove();
|
|
386
|
+
this.tooltip?.remove();
|
|
387
|
+
this.highlight = null;
|
|
388
|
+
this.tooltip = null;
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// src/inspector.ts
|
|
393
|
+
var isMac2 = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
|
|
394
|
+
var Inspector = class {
|
|
395
|
+
overlay = new OverlayManager();
|
|
396
|
+
moveGeneration = 0;
|
|
397
|
+
lastTarget = null;
|
|
398
|
+
lastInfo = null;
|
|
399
|
+
sourceToggled = false;
|
|
400
|
+
shiftPressedClean = false;
|
|
401
|
+
boundHandlers;
|
|
402
|
+
constructor() {
|
|
403
|
+
this.boundHandlers = {
|
|
404
|
+
mousemove: this.handleMouseMove.bind(this),
|
|
405
|
+
click: this.handleClick.bind(this),
|
|
406
|
+
keydown: this.handleKeyDown.bind(this),
|
|
407
|
+
keyup: this.handleKeyUp.bind(this)
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
start() {
|
|
411
|
+
window.addEventListener("mousemove", this.boundHandlers.mousemove);
|
|
412
|
+
window.addEventListener("click", this.boundHandlers.click, true);
|
|
413
|
+
window.addEventListener("keydown", this.boundHandlers.keydown);
|
|
414
|
+
window.addEventListener("keyup", this.boundHandlers.keyup);
|
|
415
|
+
}
|
|
416
|
+
stop() {
|
|
417
|
+
window.removeEventListener("mousemove", this.boundHandlers.mousemove);
|
|
418
|
+
window.removeEventListener("click", this.boundHandlers.click, true);
|
|
419
|
+
window.removeEventListener("keydown", this.boundHandlers.keydown);
|
|
420
|
+
window.removeEventListener("keyup", this.boundHandlers.keyup);
|
|
421
|
+
this.overlay.destroy();
|
|
422
|
+
document.body.style.cursor = "";
|
|
423
|
+
this.lastTarget = null;
|
|
424
|
+
this.lastInfo = null;
|
|
425
|
+
this.sourceToggled = false;
|
|
426
|
+
this.shiftPressedClean = false;
|
|
427
|
+
}
|
|
428
|
+
isModifierHeld(e) {
|
|
429
|
+
return isMac2 ? e.metaKey : e.ctrlKey;
|
|
430
|
+
}
|
|
431
|
+
async handleMouseMove(e) {
|
|
432
|
+
if (!this.isModifierHeld(e)) {
|
|
433
|
+
this.overlay.hide();
|
|
434
|
+
document.body.style.cursor = "";
|
|
435
|
+
this.lastTarget = null;
|
|
436
|
+
this.lastInfo = null;
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
this.overlay.init();
|
|
440
|
+
document.body.style.cursor = "crosshair";
|
|
441
|
+
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
442
|
+
if (!target || target.closest("[data-react-grep]")) return;
|
|
443
|
+
if (target !== this.lastTarget) {
|
|
444
|
+
this.sourceToggled = false;
|
|
445
|
+
}
|
|
446
|
+
const gen = ++this.moveGeneration;
|
|
447
|
+
const info = await getComponentInfo(target);
|
|
448
|
+
if (gen !== this.moveGeneration) return;
|
|
449
|
+
if (!info) {
|
|
450
|
+
this.overlay.hide();
|
|
451
|
+
this.lastTarget = null;
|
|
452
|
+
this.lastInfo = null;
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
this.lastTarget = target;
|
|
456
|
+
this.lastInfo = info;
|
|
457
|
+
this.overlay.show(target, info, this.getActiveSource());
|
|
458
|
+
}
|
|
459
|
+
async handleClick(e) {
|
|
460
|
+
if (!this.isModifierHeld(e) || !e.shiftKey) return;
|
|
461
|
+
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
462
|
+
if (!target || target.closest("[data-react-grep]")) return;
|
|
463
|
+
e.preventDefault();
|
|
464
|
+
e.stopPropagation();
|
|
465
|
+
e.stopImmediatePropagation();
|
|
466
|
+
this.shiftPressedClean = false;
|
|
467
|
+
const info = await getComponentInfo(target);
|
|
468
|
+
if (!info) return;
|
|
469
|
+
const source = this.getActiveCopySource(info);
|
|
470
|
+
if (!source) return;
|
|
471
|
+
const { fileName, lineNumber, columnNumber } = source;
|
|
472
|
+
const location = columnNumber != null ? `${fileName}:${lineNumber}:${columnNumber}` : `${fileName}:${lineNumber}`;
|
|
473
|
+
await this.copyToClipboard(location);
|
|
474
|
+
this.overlay.showCopied(location);
|
|
475
|
+
}
|
|
476
|
+
handleKeyDown(e) {
|
|
477
|
+
if (e.key === "Shift" && this.isModifierHeld(e)) {
|
|
478
|
+
this.shiftPressedClean = true;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
handleKeyUp(e) {
|
|
482
|
+
if (isMac2 && e.key === "Meta" || !isMac2 && e.key === "Control") {
|
|
483
|
+
this.overlay.hide();
|
|
484
|
+
document.body.style.cursor = "";
|
|
485
|
+
this.lastTarget = null;
|
|
486
|
+
this.lastInfo = null;
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
if (e.key === "Shift" && this.shiftPressedClean && this.lastTarget && this.lastInfo && this.lastInfo.callSite) {
|
|
490
|
+
this.sourceToggled = !this.sourceToggled;
|
|
491
|
+
this.overlay.show(this.lastTarget, this.lastInfo, this.getActiveSource());
|
|
492
|
+
}
|
|
493
|
+
this.shiftPressedClean = false;
|
|
494
|
+
}
|
|
495
|
+
getActiveSource() {
|
|
496
|
+
return this.sourceToggled ? "callSite" : "source";
|
|
497
|
+
}
|
|
498
|
+
getActiveCopySource(info) {
|
|
499
|
+
return this.sourceToggled && info.callSite ? info.callSite : info.source;
|
|
500
|
+
}
|
|
501
|
+
async copyToClipboard(text) {
|
|
502
|
+
try {
|
|
503
|
+
await navigator.clipboard.writeText(text);
|
|
504
|
+
} catch {
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// src/index.ts
|
|
510
|
+
var inspector = null;
|
|
511
|
+
var init = () => {
|
|
512
|
+
if (inspector) return;
|
|
513
|
+
inspector = new Inspector();
|
|
514
|
+
inspector.start();
|
|
515
|
+
};
|
|
516
|
+
var destroy = () => {
|
|
517
|
+
if (!inspector) return;
|
|
518
|
+
inspector.stop();
|
|
519
|
+
inspector = null;
|
|
520
|
+
};
|
|
521
|
+
if (typeof window !== "undefined") {
|
|
522
|
+
const bootstrap = () => init();
|
|
523
|
+
if (document.readyState === "loading") {
|
|
524
|
+
document.addEventListener("DOMContentLoaded", bootstrap);
|
|
525
|
+
} else {
|
|
526
|
+
bootstrap();
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export { destroy, init };
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-grep",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Hold CMD to see React component names + file:line overlaid on any element",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"component",
|
|
7
|
+
"debug",
|
|
8
|
+
"devtools",
|
|
9
|
+
"fiber",
|
|
10
|
+
"inspect",
|
|
11
|
+
"inspector",
|
|
12
|
+
"react",
|
|
13
|
+
"source"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/maxmurr/react-grep",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/maxmurr/react-grep/issues"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "Max Murray",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/maxmurr/react-grep.git",
|
|
24
|
+
"directory": "packages/react-grep"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"README.md",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"type": "module",
|
|
32
|
+
"sideEffects": true,
|
|
33
|
+
"main": "dist/index.cjs",
|
|
34
|
+
"module": "dist/index.js",
|
|
35
|
+
"browser": "dist/index.global.js",
|
|
36
|
+
"types": "dist/index.d.ts",
|
|
37
|
+
"exports": {
|
|
38
|
+
"./package.json": "./package.json",
|
|
39
|
+
".": {
|
|
40
|
+
"import": {
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"default": "./dist/index.js"
|
|
43
|
+
},
|
|
44
|
+
"require": {
|
|
45
|
+
"types": "./dist/index.d.cts",
|
|
46
|
+
"default": "./dist/index.cjs"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"dev": "tsup --watch",
|
|
56
|
+
"check-types": "tsc --noEmit"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"tsup": "^8.2.4",
|
|
60
|
+
"typescript": "^5.7.0"
|
|
61
|
+
}
|
|
62
|
+
}
|