@treelocator/runtime 0.4.0 → 0.4.5
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/dist/_generated_styles.js +115 -15
- package/dist/adapters/getElementInfo.js +3 -17
- package/dist/adapters/getParentsPath.js +3 -33
- package/dist/adapters/getTree.js +3 -33
- package/dist/adapters/jsx/getJSXComponentBoundingBox.js +0 -1
- package/dist/adapters/jsx/jsxAdapter.js +0 -3
- package/dist/adapters/jsx/runtimeStore.js +0 -12
- package/dist/adapters/react/reactAdapter.js +0 -8
- package/dist/adapters/resolveAdapter.d.ts +8 -0
- package/dist/adapters/resolveAdapter.js +28 -0
- package/dist/adapters/vue/vueAdapter.js +0 -14
- package/dist/browserApi.d.ts +1 -1
- package/dist/browserApi.js +8 -10
- package/dist/components/MaybeOutline.d.ts +1 -0
- package/dist/components/MaybeOutline.js +38 -29
- package/dist/components/Outline.d.ts +1 -0
- package/dist/components/Outline.js +20 -16
- package/dist/components/Runtime.js +30 -18
- package/dist/dejitter/recorder.js +8 -13
- package/dist/functions/formatAncestryChain.d.ts +6 -0
- package/dist/functions/formatAncestryChain.js +11 -0
- package/dist/functions/formatAncestryChain.test.js +75 -1
- package/dist/functions/getUsableName.js +0 -21
- package/dist/output.css +10 -40
- package/dist/types/types.d.ts +1 -32
- package/package.json +1 -1
- package/src/_generated_styles.ts +115 -15
- package/src/adapters/getElementInfo.tsx +3 -23
- package/src/adapters/getParentsPath.tsx +3 -42
- package/src/adapters/getTree.tsx +3 -42
- package/src/adapters/jsx/getJSXComponentBoundingBox.ts +0 -1
- package/src/adapters/jsx/jsxAdapter.ts +0 -2
- package/src/adapters/jsx/runtimeStore.ts +0 -11
- package/src/adapters/react/reactAdapter.ts +0 -7
- package/src/adapters/resolveAdapter.ts +38 -0
- package/src/adapters/vue/vueAdapter.ts +0 -14
- package/src/browserApi.ts +9 -12
- package/src/components/MaybeOutline.tsx +4 -2
- package/src/components/Outline.tsx +2 -1
- package/src/components/Runtime.tsx +27 -18
- package/src/dejitter/recorder.ts +51 -56
- package/src/functions/formatAncestryChain.test.ts +47 -0
- package/src/functions/formatAncestryChain.ts +11 -0
- package/src/functions/getUsableName.ts +0 -21
- package/src/types/types.ts +1 -32
- package/src/adapters/react/fiberToSimple.tsx +0 -72
- package/src/adapters/react/gatherFiberRoots.tsx +0 -36
- package/src/adapters/react/makeFiberId.tsx +0 -19
- package/src/adapters/react/searchDevtoolsRenderersForClosestTarget.ts +0 -15
- package/src/components/Button.tsx +0 -14
- package/src/components/ComponentOutline.tsx +0 -98
- package/src/components/SimpleNodeOutline.tsx +0 -27
- package/src/components/Tooltip.tsx +0 -28
- package/src/functions/cropPath.test.ts +0 -18
- package/src/functions/cropPath.ts +0 -12
- package/src/functions/evalTemplate.test.ts +0 -12
- package/src/functions/evalTemplate.ts +0 -8
- package/src/functions/findNames.ts +0 -20
- package/src/functions/getBoundingRect.tsx +0 -11
- package/src/functions/getComposedBoundingBox.tsx +0 -25
- package/src/functions/getIdsOnPathToRoot.tsx +0 -21
- package/src/functions/getMultipleElementsBoundingBox.tsx +0 -25
- package/src/functions/getPathToParent.tsx +0 -17
- package/src/functions/getUsableFileName.test.tsx +0 -24
- package/src/functions/getUsableFileName.tsx +0 -19
- package/src/functions/transformPath.test.ts +0 -28
- package/src/functions/transformPath.ts +0 -7
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { template as _$template } from "solid-js/web";
|
|
2
|
+
import { className as _$className } from "solid-js/web";
|
|
2
3
|
import { effect as _$effect } from "solid-js/web";
|
|
3
4
|
import { insert as _$insert } from "solid-js/web";
|
|
4
5
|
import { setStyleProperty as _$setStyleProperty } from "solid-js/web";
|
|
5
6
|
import { createComponent as _$createComponent } from "solid-js/web";
|
|
6
7
|
import { memo as _$memo } from "solid-js/web";
|
|
7
|
-
var _tmpl$ = /*#__PURE__*/_$template(`<div><div
|
|
8
|
-
_tmpl$2 = /*#__PURE__*/_$template(`<div><div class="fixed rounded border border-solid border-gray-500"style=z-index:2></div><div class="fixed text-xs font-medium rounded-md"style="z-index:3;box-shadow:0 4px 16px rgba(0, 0, 0, 0.2);font-family:ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, Consolas, monospace;letter-spacing:0.01em">`);
|
|
8
|
+
var _tmpl$ = /*#__PURE__*/_$template(`<div><div style=z-index:2></div><div class="fixed text-xs font-medium rounded-md"style="z-index:3;box-shadow:0 4px 16px rgba(0, 0, 0, 0.2);font-family:ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, Consolas, monospace;letter-spacing:0.01em">`);
|
|
9
9
|
import { createMemo } from "solid-js";
|
|
10
10
|
import { getElementInfo } from "../adapters/getElementInfo";
|
|
11
11
|
import { Outline } from "./Outline";
|
|
@@ -37,6 +37,9 @@ export function MaybeOutline(props) {
|
|
|
37
37
|
},
|
|
38
38
|
get targets() {
|
|
39
39
|
return props.targets;
|
|
40
|
+
},
|
|
41
|
+
get dashed() {
|
|
42
|
+
return props.dashed;
|
|
40
43
|
}
|
|
41
44
|
}) : _$memo(() => !!phoenixInfo())() ? (() => {
|
|
42
45
|
var _el$ = _tmpl$(),
|
|
@@ -48,18 +51,20 @@ export function MaybeOutline(props) {
|
|
|
48
51
|
_$setStyleProperty(_el$3, "border", "1px solid rgba(255, 255, 255, 0.15)");
|
|
49
52
|
_$insert(_el$3, () => phoenixInfo().filter(sc => sc.type === "component").map(sc => sc.name.split(".").pop()).join(" > "));
|
|
50
53
|
_$effect(_p$ => {
|
|
51
|
-
var _v$ =
|
|
52
|
-
_v$2 = box().
|
|
53
|
-
_v$3 = box().
|
|
54
|
-
_v$4 = box().
|
|
55
|
-
_v$5 = box().
|
|
56
|
-
_v$6 = box().
|
|
57
|
-
|
|
58
|
-
_v$
|
|
59
|
-
_v$
|
|
60
|
-
_v$
|
|
61
|
-
_v$
|
|
62
|
-
_v$
|
|
54
|
+
var _v$ = `fixed rounded border ${props.dashed ? "border-dashed" : "border-solid"} border-amber-500`,
|
|
55
|
+
_v$2 = box().x + "px",
|
|
56
|
+
_v$3 = box().y + "px",
|
|
57
|
+
_v$4 = box().width + "px",
|
|
58
|
+
_v$5 = box().height + "px",
|
|
59
|
+
_v$6 = box().x + 4 + "px",
|
|
60
|
+
_v$7 = box().y + 4 + "px";
|
|
61
|
+
_v$ !== _p$.e && _$className(_el$2, _p$.e = _v$);
|
|
62
|
+
_v$2 !== _p$.t && _$setStyleProperty(_el$2, "left", _p$.t = _v$2);
|
|
63
|
+
_v$3 !== _p$.a && _$setStyleProperty(_el$2, "top", _p$.a = _v$3);
|
|
64
|
+
_v$4 !== _p$.o && _$setStyleProperty(_el$2, "width", _p$.o = _v$4);
|
|
65
|
+
_v$5 !== _p$.i && _$setStyleProperty(_el$2, "height", _p$.i = _v$5);
|
|
66
|
+
_v$6 !== _p$.n && _$setStyleProperty(_el$3, "left", _p$.n = _v$6);
|
|
67
|
+
_v$7 !== _p$.s && _$setStyleProperty(_el$3, "top", _p$.s = _v$7);
|
|
63
68
|
return _p$;
|
|
64
69
|
}, {
|
|
65
70
|
e: undefined,
|
|
@@ -67,11 +72,12 @@ export function MaybeOutline(props) {
|
|
|
67
72
|
a: undefined,
|
|
68
73
|
o: undefined,
|
|
69
74
|
i: undefined,
|
|
70
|
-
n: undefined
|
|
75
|
+
n: undefined,
|
|
76
|
+
s: undefined
|
|
71
77
|
});
|
|
72
78
|
return _el$;
|
|
73
79
|
})() : (() => {
|
|
74
|
-
var _el$4 = _tmpl$
|
|
80
|
+
var _el$4 = _tmpl$(),
|
|
75
81
|
_el$5 = _el$4.firstChild,
|
|
76
82
|
_el$6 = _el$5.nextSibling;
|
|
77
83
|
_$setStyleProperty(_el$6, "padding", "4px 10px");
|
|
@@ -88,18 +94,20 @@ export function MaybeOutline(props) {
|
|
|
88
94
|
return () => _c$2() ? `.${props.currentElement.getAttribute('class').split(" ")[0]}` : "";
|
|
89
95
|
})(), null);
|
|
90
96
|
_$effect(_p$ => {
|
|
91
|
-
var _v$
|
|
92
|
-
_v$
|
|
93
|
-
_v$
|
|
94
|
-
_v$
|
|
95
|
-
_v$
|
|
96
|
-
_v$
|
|
97
|
-
|
|
98
|
-
_v$8 !== _p$.
|
|
99
|
-
_v$9 !== _p$.
|
|
100
|
-
_v$0 !== _p$.
|
|
101
|
-
_v$1 !== _p$.
|
|
102
|
-
_v$10 !== _p$.
|
|
97
|
+
var _v$8 = `fixed rounded border ${props.dashed ? "border-dashed" : "border-solid"} border-gray-500`,
|
|
98
|
+
_v$9 = box().x + "px",
|
|
99
|
+
_v$0 = box().y + "px",
|
|
100
|
+
_v$1 = box().width + "px",
|
|
101
|
+
_v$10 = box().height + "px",
|
|
102
|
+
_v$11 = box().x + 4 + "px",
|
|
103
|
+
_v$12 = box().y + 4 + "px";
|
|
104
|
+
_v$8 !== _p$.e && _$className(_el$5, _p$.e = _v$8);
|
|
105
|
+
_v$9 !== _p$.t && _$setStyleProperty(_el$5, "left", _p$.t = _v$9);
|
|
106
|
+
_v$0 !== _p$.a && _$setStyleProperty(_el$5, "top", _p$.a = _v$0);
|
|
107
|
+
_v$1 !== _p$.o && _$setStyleProperty(_el$5, "width", _p$.o = _v$1);
|
|
108
|
+
_v$10 !== _p$.i && _$setStyleProperty(_el$5, "height", _p$.i = _v$10);
|
|
109
|
+
_v$11 !== _p$.n && _$setStyleProperty(_el$6, "left", _p$.n = _v$11);
|
|
110
|
+
_v$12 !== _p$.s && _$setStyleProperty(_el$6, "top", _p$.s = _v$12);
|
|
103
111
|
return _p$;
|
|
104
112
|
}, {
|
|
105
113
|
e: undefined,
|
|
@@ -107,7 +115,8 @@ export function MaybeOutline(props) {
|
|
|
107
115
|
a: undefined,
|
|
108
116
|
o: undefined,
|
|
109
117
|
i: undefined,
|
|
110
|
-
n: undefined
|
|
118
|
+
n: undefined,
|
|
119
|
+
s: undefined
|
|
111
120
|
});
|
|
112
121
|
return _el$4;
|
|
113
122
|
})());
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { template as _$template } from "solid-js/web";
|
|
2
2
|
import { createComponent as _$createComponent } from "solid-js/web";
|
|
3
|
+
import { className as _$className } from "solid-js/web";
|
|
3
4
|
import { effect as _$effect } from "solid-js/web";
|
|
4
5
|
import { insert as _$insert } from "solid-js/web";
|
|
5
6
|
import { setStyleProperty as _$setStyleProperty } from "solid-js/web";
|
|
6
7
|
import { memo as _$memo } from "solid-js/web";
|
|
7
|
-
var _tmpl$ = /*#__PURE__*/_$template(`<div><div
|
|
8
|
+
var _tmpl$ = /*#__PURE__*/_$template(`<div><div style=z-index:2></div><div class="fixed text-xs font-medium rounded-md"style="z-index:3;box-shadow:0 4px 16px rgba(0, 0, 0, 0.2);font-family:ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, Consolas, monospace;letter-spacing:0.01em;text-overflow:ellipsis;white-space:nowrap">`);
|
|
8
9
|
import { RenderBoxes } from "./RenderBoxes";
|
|
9
10
|
export function Outline(props) {
|
|
10
11
|
const box = () => props.element.thisElement.box;
|
|
@@ -118,20 +119,22 @@ export function Outline(props) {
|
|
|
118
119
|
_$setStyleProperty(_el$3, "overflow", "hidden");
|
|
119
120
|
_$insert(_el$3, () => props.element.thisElement.label);
|
|
120
121
|
_$effect(_p$ => {
|
|
121
|
-
var _v$ =
|
|
122
|
-
_v$2 = box().
|
|
123
|
-
_v$3 = box().
|
|
124
|
-
_v$4 = box().
|
|
125
|
-
_v$5 = box().
|
|
126
|
-
_v$6 = box().
|
|
127
|
-
_v$7 = box().
|
|
128
|
-
|
|
129
|
-
_v$
|
|
130
|
-
_v$
|
|
131
|
-
_v$
|
|
132
|
-
_v$
|
|
133
|
-
_v$
|
|
134
|
-
_v$
|
|
122
|
+
var _v$ = `fixed rounded border ${props.dashed ? "border-dashed" : "border-solid"} border-sky-500`,
|
|
123
|
+
_v$2 = box().x + "px",
|
|
124
|
+
_v$3 = box().y + "px",
|
|
125
|
+
_v$4 = box().width + "px",
|
|
126
|
+
_v$5 = box().height + "px",
|
|
127
|
+
_v$6 = box().x + 4 + "px",
|
|
128
|
+
_v$7 = box().y + 4 + "px",
|
|
129
|
+
_v$8 = box().width - 8 + "px";
|
|
130
|
+
_v$ !== _p$.e && _$className(_el$2, _p$.e = _v$);
|
|
131
|
+
_v$2 !== _p$.t && _$setStyleProperty(_el$2, "left", _p$.t = _v$2);
|
|
132
|
+
_v$3 !== _p$.a && _$setStyleProperty(_el$2, "top", _p$.a = _v$3);
|
|
133
|
+
_v$4 !== _p$.o && _$setStyleProperty(_el$2, "width", _p$.o = _v$4);
|
|
134
|
+
_v$5 !== _p$.i && _$setStyleProperty(_el$2, "height", _p$.i = _v$5);
|
|
135
|
+
_v$6 !== _p$.n && _$setStyleProperty(_el$3, "left", _p$.n = _v$6);
|
|
136
|
+
_v$7 !== _p$.s && _$setStyleProperty(_el$3, "top", _p$.s = _v$7);
|
|
137
|
+
_v$8 !== _p$.h && _$setStyleProperty(_el$3, "max-width", _p$.h = _v$8);
|
|
135
138
|
return _p$;
|
|
136
139
|
}, {
|
|
137
140
|
e: undefined,
|
|
@@ -140,7 +143,8 @@ export function Outline(props) {
|
|
|
140
143
|
o: undefined,
|
|
141
144
|
i: undefined,
|
|
142
145
|
n: undefined,
|
|
143
|
-
s: undefined
|
|
146
|
+
s: undefined,
|
|
147
|
+
h: undefined
|
|
144
148
|
});
|
|
145
149
|
return _el$;
|
|
146
150
|
})();
|
|
@@ -16,15 +16,24 @@ import { isCombinationModifiersPressed } from "../functions/isCombinationModifie
|
|
|
16
16
|
import { MaybeOutline } from "./MaybeOutline";
|
|
17
17
|
import { isLocatorsOwnElement } from "../functions/isLocatorsOwnElement";
|
|
18
18
|
import { Toast } from "./Toast";
|
|
19
|
-
import { collectAncestry, formatAncestryChain } from "../functions/formatAncestryChain";
|
|
19
|
+
import { collectAncestry, formatAncestryChain, truncateAtFirstFile } from "../functions/formatAncestryChain";
|
|
20
20
|
import { enrichAncestryWithSourceMaps } from "../functions/enrichAncestrySourceMaps";
|
|
21
21
|
import { createTreeNode } from "../adapters/createTreeNode";
|
|
22
22
|
import treeIconUrl from "../_generated_tree_icon";
|
|
23
23
|
import { createDejitterRecorder } from "../dejitter/recorder";
|
|
24
24
|
import { RecordingOutline } from "./RecordingOutline";
|
|
25
25
|
import { RecordingResults } from "./RecordingResults";
|
|
26
|
+
const DEJITTER_CONFIG = {
|
|
27
|
+
selector: '[data-treelocator-recording]',
|
|
28
|
+
props: ['opacity', 'transform', 'boundingRect', 'width', 'height'],
|
|
29
|
+
sampleRate: 15,
|
|
30
|
+
maxDuration: 30000,
|
|
31
|
+
idleTimeout: 0,
|
|
32
|
+
mutations: true
|
|
33
|
+
};
|
|
26
34
|
function Runtime(props) {
|
|
27
35
|
const [holdingModKey, setHoldingModKey] = createSignal(false);
|
|
36
|
+
const [holdingShift, setHoldingShift] = createSignal(false);
|
|
28
37
|
const [currentElement, setCurrentElement] = createSignal(null);
|
|
29
38
|
const [toastMessage, setToastMessage] = createSignal(null);
|
|
30
39
|
const [locatorActive, setLocatorActive] = createSignal(false);
|
|
@@ -86,13 +95,16 @@ function Runtime(props) {
|
|
|
86
95
|
}
|
|
87
96
|
function keyUpListener(e) {
|
|
88
97
|
setHoldingModKey(isCombinationModifiersPressed(e));
|
|
98
|
+
setHoldingShift(e.shiftKey);
|
|
89
99
|
}
|
|
90
100
|
function keyDownListener(e) {
|
|
91
101
|
setHoldingModKey(isCombinationModifiersPressed(e, true));
|
|
102
|
+
setHoldingShift(e.shiftKey);
|
|
92
103
|
}
|
|
93
104
|
function mouseMoveListener(e) {
|
|
94
105
|
// Update modifier state from mouse events - more reliable than keydown/keyup
|
|
95
106
|
setHoldingModKey(e.altKey);
|
|
107
|
+
setHoldingShift(e.shiftKey);
|
|
96
108
|
}
|
|
97
109
|
function findElementAtPoint(e) {
|
|
98
110
|
const elementsAtPoint = document.elementsFromPoint(e.clientX, e.clientY);
|
|
@@ -134,14 +146,7 @@ function Runtime(props) {
|
|
|
134
146
|
element.setAttribute('data-treelocator-recording', 'true');
|
|
135
147
|
setRecordedElement(element);
|
|
136
148
|
dejitterInstance = createDejitterRecorder();
|
|
137
|
-
dejitterInstance.configure(
|
|
138
|
-
selector: '[data-treelocator-recording]',
|
|
139
|
-
props: ['opacity', 'transform', 'boundingRect', 'width', 'height'],
|
|
140
|
-
sampleRate: 15,
|
|
141
|
-
maxDuration: 30000,
|
|
142
|
-
idleTimeout: 0,
|
|
143
|
-
mutations: true
|
|
144
|
-
});
|
|
149
|
+
dejitterInstance.configure(DEJITTER_CONFIG);
|
|
145
150
|
dejitterInstance.start();
|
|
146
151
|
startInteractionTracker();
|
|
147
152
|
setRecordingState('recording');
|
|
@@ -251,14 +256,7 @@ function Runtime(props) {
|
|
|
251
256
|
element.setAttribute('data-treelocator-recording', 'true');
|
|
252
257
|
setRecordedElement(element);
|
|
253
258
|
dejitterInstance = createDejitterRecorder();
|
|
254
|
-
dejitterInstance.configure(
|
|
255
|
-
selector: '[data-treelocator-recording]',
|
|
256
|
-
props: ['opacity', 'transform', 'boundingRect', 'width', 'height'],
|
|
257
|
-
sampleRate: 15,
|
|
258
|
-
maxDuration: 30000,
|
|
259
|
-
idleTimeout: 0,
|
|
260
|
-
mutations: true
|
|
261
|
-
});
|
|
259
|
+
dejitterInstance.configure(DEJITTER_CONFIG);
|
|
262
260
|
dejitterInstance.start();
|
|
263
261
|
setRecordingState('recording');
|
|
264
262
|
setReplaying(true);
|
|
@@ -349,6 +347,9 @@ function Runtime(props) {
|
|
|
349
347
|
setRecordedElement(null);
|
|
350
348
|
setViewingPrevious(false);
|
|
351
349
|
setRecordingState('idle');
|
|
350
|
+
try {
|
|
351
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
352
|
+
} catch {}
|
|
352
353
|
}
|
|
353
354
|
function hasPreviousRecording() {
|
|
354
355
|
return loadFromStorage().previous !== null;
|
|
@@ -408,6 +409,7 @@ function Runtime(props) {
|
|
|
408
409
|
}
|
|
409
410
|
function mouseOverListener(e) {
|
|
410
411
|
setHoldingModKey(e.altKey);
|
|
412
|
+
setHoldingShift(e.shiftKey);
|
|
411
413
|
|
|
412
414
|
// Don't update hovered element while recording -- highlight is sticky
|
|
413
415
|
if (recordingState() === 'recording') return;
|
|
@@ -456,7 +458,12 @@ function Runtime(props) {
|
|
|
456
458
|
// Copy ancestry to clipboard on alt+click
|
|
457
459
|
const treeNode = createTreeNode(element, props.adapterId);
|
|
458
460
|
if (treeNode) {
|
|
459
|
-
|
|
461
|
+
let ancestry = collectAncestry(treeNode);
|
|
462
|
+
|
|
463
|
+
// Alt+Shift: keep from bottom up to the first element with a file, discard above
|
|
464
|
+
if (e.shiftKey) {
|
|
465
|
+
ancestry = truncateAtFirstFile(ancestry);
|
|
466
|
+
}
|
|
460
467
|
|
|
461
468
|
// Write immediately with component names (preserves user gesture for clipboard API)
|
|
462
469
|
const formatted = formatAncestryChain(ancestry);
|
|
@@ -513,6 +520,8 @@ function Runtime(props) {
|
|
|
513
520
|
root.addEventListener("scroll", scrollListener);
|
|
514
521
|
}
|
|
515
522
|
onCleanup(() => {
|
|
523
|
+
stopReplay();
|
|
524
|
+
stopInteractionTracker();
|
|
516
525
|
for (const root of roots) {
|
|
517
526
|
root.removeEventListener("keyup", keyUpListener);
|
|
518
527
|
root.removeEventListener("keydown", keyDownListener);
|
|
@@ -540,6 +549,9 @@ function Runtime(props) {
|
|
|
540
549
|
},
|
|
541
550
|
get targets() {
|
|
542
551
|
return props.targets;
|
|
552
|
+
},
|
|
553
|
+
get dashed() {
|
|
554
|
+
return holdingShift();
|
|
543
555
|
}
|
|
544
556
|
}) : null), _$memo(() => _$memo(() => !!(recordingState() === 'recording' && recordedElement()))() ? _$createComponent(RecordingOutline, {
|
|
545
557
|
get element() {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
/**
|
|
3
2
|
* Dejitter — Animation frame recorder & jank detector (vendored)
|
|
4
3
|
*
|
|
@@ -493,11 +492,10 @@ export function createDejitterRecorder() {
|
|
|
493
492
|
}
|
|
494
493
|
return findings;
|
|
495
494
|
}
|
|
496
|
-
function detectShiverFindings(propStats, elements
|
|
495
|
+
function detectShiverFindings(propStats, elements) {
|
|
497
496
|
const findings = [];
|
|
498
497
|
for (const p of propStats.props) {
|
|
499
498
|
if (p.raw < 10) continue;
|
|
500
|
-
if (existingFindings.some(f => f.elem === p.elem && f.prop === p.prop)) continue;
|
|
501
499
|
const timeline = getTimeline(p.elem, p.prop);
|
|
502
500
|
if (timeline.length < 10) continue;
|
|
503
501
|
const numeric = [];
|
|
@@ -539,11 +537,10 @@ export function createDejitterRecorder() {
|
|
|
539
537
|
}
|
|
540
538
|
return findings;
|
|
541
539
|
}
|
|
542
|
-
function detectJumpFindings(propStats, elements
|
|
540
|
+
function detectJumpFindings(propStats, elements) {
|
|
543
541
|
const findings = [];
|
|
544
542
|
for (const p of propStats.props) {
|
|
545
543
|
if (p.raw < 3) continue;
|
|
546
|
-
if (existingFindings.some(f => f.elem === p.elem && f.prop === p.prop)) continue;
|
|
547
544
|
const timeline = getTimeline(p.elem, p.prop);
|
|
548
545
|
if (timeline.length < 3) continue;
|
|
549
546
|
const deltas = [];
|
|
@@ -581,12 +578,11 @@ export function createDejitterRecorder() {
|
|
|
581
578
|
}
|
|
582
579
|
return findings;
|
|
583
580
|
}
|
|
584
|
-
function detectStutterFindings(propStats, elements
|
|
581
|
+
function detectStutterFindings(propStats, elements) {
|
|
585
582
|
const findings = [];
|
|
586
583
|
const st = config.thresholds.stutter;
|
|
587
584
|
for (const p of propStats.props) {
|
|
588
585
|
if (p.raw < 6) continue;
|
|
589
|
-
if (existingFindings.some(f => f.elem === p.elem && f.prop === p.prop)) continue;
|
|
590
586
|
const timeline = getTimeline(p.elem, p.prop);
|
|
591
587
|
if (timeline.length < 6) continue;
|
|
592
588
|
const numeric = [];
|
|
@@ -672,12 +668,11 @@ export function createDejitterRecorder() {
|
|
|
672
668
|
}
|
|
673
669
|
return findings;
|
|
674
670
|
}
|
|
675
|
-
function detectStuckFindings(propStats, elements
|
|
671
|
+
function detectStuckFindings(propStats, elements) {
|
|
676
672
|
const findings = [];
|
|
677
673
|
const sk = config.thresholds.stuck;
|
|
678
674
|
for (const p of propStats.props) {
|
|
679
675
|
if (p.raw < 6) continue;
|
|
680
|
-
if (existingFindings.some(f => f.elem === p.elem && f.prop === p.prop)) continue;
|
|
681
676
|
const timeline = getTimeline(p.elem, p.prop);
|
|
682
677
|
if (timeline.length < 6) continue;
|
|
683
678
|
const numeric = [];
|
|
@@ -770,10 +765,10 @@ export function createDejitterRecorder() {
|
|
|
770
765
|
const propStats = buildPropStats();
|
|
771
766
|
const elements = buildElementMap();
|
|
772
767
|
let findings = detectOutlierFindings(propStats, elements);
|
|
773
|
-
findings = findings.concat(detectShiverFindings(propStats, elements
|
|
774
|
-
findings = findings.concat(detectJumpFindings(propStats, elements
|
|
775
|
-
findings = findings.concat(detectStutterFindings(propStats, elements
|
|
776
|
-
findings = findings.concat(detectStuckFindings(propStats, elements
|
|
768
|
+
findings = findings.concat(detectShiverFindings(propStats, elements));
|
|
769
|
+
findings = findings.concat(detectJumpFindings(propStats, elements));
|
|
770
|
+
findings = findings.concat(detectStutterFindings(propStats, elements));
|
|
771
|
+
findings = findings.concat(detectStuckFindings(propStats, elements));
|
|
777
772
|
findings = deduplicateShivers(findings);
|
|
778
773
|
const sevOrder = {
|
|
779
774
|
high: 0,
|
|
@@ -18,4 +18,10 @@ export interface AncestryItem {
|
|
|
18
18
|
serverComponents?: ServerComponentInfo[];
|
|
19
19
|
}
|
|
20
20
|
export declare function collectAncestry(node: TreeNode): AncestryItem[];
|
|
21
|
+
/**
|
|
22
|
+
* Truncate ancestry from the bottom (clicked element) up to and including
|
|
23
|
+
* the first item that has a filePath. Everything above that is discarded.
|
|
24
|
+
* The ancestry array is bottom-up: index 0 = clicked element, last = root.
|
|
25
|
+
*/
|
|
26
|
+
export declare function truncateAtFirstFile(items: AncestryItem[]): AncestryItem[];
|
|
21
27
|
export declare function formatAncestryChain(items: AncestryItem[]): string;
|
|
@@ -121,6 +121,17 @@ function getInnermostNamedComponent(item) {
|
|
|
121
121
|
}
|
|
122
122
|
return undefined;
|
|
123
123
|
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Truncate ancestry from the bottom (clicked element) up to and including
|
|
127
|
+
* the first item that has a filePath. Everything above that is discarded.
|
|
128
|
+
* The ancestry array is bottom-up: index 0 = clicked element, last = root.
|
|
129
|
+
*/
|
|
130
|
+
export function truncateAtFirstFile(items) {
|
|
131
|
+
const firstWithFile = items.findIndex(item => item.filePath);
|
|
132
|
+
if (firstWithFile === -1) return items;
|
|
133
|
+
return items.slice(0, firstWithFile + 1);
|
|
134
|
+
}
|
|
124
135
|
export function formatAncestryChain(items) {
|
|
125
136
|
if (items.length === 0) {
|
|
126
137
|
return "";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { formatAncestryChain } from "./formatAncestryChain";
|
|
2
|
+
import { formatAncestryChain, truncateAtFirstFile } from "./formatAncestryChain";
|
|
3
3
|
describe("formatAncestryChain", () => {
|
|
4
4
|
it("uses component name only at component boundaries", () => {
|
|
5
5
|
const items = [{
|
|
@@ -239,4 +239,78 @@ describe("formatAncestryChain", () => {
|
|
|
239
239
|
└─ GlassPanel in Sidebar at src/Sidebar.tsx:20`);
|
|
240
240
|
});
|
|
241
241
|
});
|
|
242
|
+
describe("truncateAtFirstFile", () => {
|
|
243
|
+
it("keeps from clicked element up to first ancestor with filePath", () => {
|
|
244
|
+
// Bottom-up: clicked element first, root last
|
|
245
|
+
const items = [{
|
|
246
|
+
elementName: "span",
|
|
247
|
+
componentName: "Button"
|
|
248
|
+
}, {
|
|
249
|
+
elementName: "div",
|
|
250
|
+
componentName: "Card"
|
|
251
|
+
}, {
|
|
252
|
+
elementName: "div",
|
|
253
|
+
componentName: "Layout",
|
|
254
|
+
filePath: "src/Layout.tsx",
|
|
255
|
+
line: 10
|
|
256
|
+
}, {
|
|
257
|
+
elementName: "div",
|
|
258
|
+
componentName: "App",
|
|
259
|
+
filePath: "src/App.tsx",
|
|
260
|
+
line: 1
|
|
261
|
+
}];
|
|
262
|
+
const result = truncateAtFirstFile(items);
|
|
263
|
+
expect(result).toEqual([{
|
|
264
|
+
elementName: "span",
|
|
265
|
+
componentName: "Button"
|
|
266
|
+
}, {
|
|
267
|
+
elementName: "div",
|
|
268
|
+
componentName: "Card"
|
|
269
|
+
}, {
|
|
270
|
+
elementName: "div",
|
|
271
|
+
componentName: "Layout",
|
|
272
|
+
filePath: "src/Layout.tsx",
|
|
273
|
+
line: 10
|
|
274
|
+
}]);
|
|
275
|
+
});
|
|
276
|
+
it("returns just the clicked element when it already has a filePath", () => {
|
|
277
|
+
const items = [{
|
|
278
|
+
elementName: "button",
|
|
279
|
+
componentName: "Button",
|
|
280
|
+
filePath: "src/Button.tsx",
|
|
281
|
+
line: 5
|
|
282
|
+
}, {
|
|
283
|
+
elementName: "div",
|
|
284
|
+
componentName: "Layout",
|
|
285
|
+
filePath: "src/Layout.tsx",
|
|
286
|
+
line: 10
|
|
287
|
+
}, {
|
|
288
|
+
elementName: "div",
|
|
289
|
+
componentName: "App",
|
|
290
|
+
filePath: "src/App.tsx",
|
|
291
|
+
line: 1
|
|
292
|
+
}];
|
|
293
|
+
const result = truncateAtFirstFile(items);
|
|
294
|
+
expect(result).toEqual([{
|
|
295
|
+
elementName: "button",
|
|
296
|
+
componentName: "Button",
|
|
297
|
+
filePath: "src/Button.tsx",
|
|
298
|
+
line: 5
|
|
299
|
+
}]);
|
|
300
|
+
});
|
|
301
|
+
it("returns all items when none have a filePath", () => {
|
|
302
|
+
const items = [{
|
|
303
|
+
elementName: "span",
|
|
304
|
+
componentName: "A"
|
|
305
|
+
}, {
|
|
306
|
+
elementName: "div",
|
|
307
|
+
componentName: "B"
|
|
308
|
+
}];
|
|
309
|
+
const result = truncateAtFirstFile(items);
|
|
310
|
+
expect(result).toEqual(items);
|
|
311
|
+
});
|
|
312
|
+
it("returns empty array for empty input", () => {
|
|
313
|
+
expect(truncateAtFirstFile([])).toEqual([]);
|
|
314
|
+
});
|
|
315
|
+
});
|
|
242
316
|
});
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
// function printDebugOwnerTree(fiber: Fiber): string | null {
|
|
2
|
-
// let current: Fiber | null = fiber || null;
|
|
3
|
-
// let results = [];
|
|
4
|
-
// while (current) {
|
|
5
|
-
// results.push(getUsableName(current));
|
|
6
|
-
// current = current._debugOwner || null;
|
|
7
|
-
// }
|
|
8
|
-
// console.log('DEBUG OWNER: ', results);
|
|
9
|
-
// return null;
|
|
10
|
-
// }
|
|
11
|
-
// function printReturnTree(fiber: Fiber): string | null {
|
|
12
|
-
// let current: Fiber | null = fiber || null;
|
|
13
|
-
// let results = [];
|
|
14
|
-
// while (current) {
|
|
15
|
-
// results.push(getUsableName(current));
|
|
16
|
-
// current = current.return || null;
|
|
17
|
-
// }
|
|
18
|
-
// console.log('RETURN: ', results);
|
|
19
|
-
// return null;
|
|
20
|
-
// }
|
|
21
|
-
|
|
22
1
|
export function getUsableName(fiber) {
|
|
23
2
|
if (!fiber) {
|
|
24
3
|
return "Not found";
|
package/dist/output.css
CHANGED
|
@@ -879,10 +879,6 @@ input:where([type='file']):focus {
|
|
|
879
879
|
left: 0.25rem;
|
|
880
880
|
}
|
|
881
881
|
|
|
882
|
-
.left-1\/2 {
|
|
883
|
-
left: 50%;
|
|
884
|
-
}
|
|
885
|
-
|
|
886
882
|
.left-3 {
|
|
887
883
|
left: 0.75rem;
|
|
888
884
|
}
|
|
@@ -895,10 +891,6 @@ input:where([type='file']):focus {
|
|
|
895
891
|
top: 0.25rem;
|
|
896
892
|
}
|
|
897
893
|
|
|
898
|
-
.top-1\/2 {
|
|
899
|
-
top: 50%;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
894
|
.z-10 {
|
|
903
895
|
z-index: 10;
|
|
904
896
|
}
|
|
@@ -1078,11 +1070,6 @@ input:where([type='file']):focus {
|
|
|
1078
1070
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
1079
1071
|
}
|
|
1080
1072
|
|
|
1081
|
-
.-translate-x-1\/2 {
|
|
1082
|
-
--tw-translate-x: -50%;
|
|
1083
|
-
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
1073
|
.-translate-x-full {
|
|
1087
1074
|
--tw-translate-x: -100%;
|
|
1088
1075
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
@@ -1093,11 +1080,6 @@ input:where([type='file']):focus {
|
|
|
1093
1080
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
1094
1081
|
}
|
|
1095
1082
|
|
|
1096
|
-
.-translate-y-1\/2 {
|
|
1097
|
-
--tw-translate-y: -50%;
|
|
1098
|
-
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
1083
|
.translate-x-full {
|
|
1102
1084
|
--tw-translate-x: 100%;
|
|
1103
1085
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
@@ -1175,6 +1157,12 @@ input:where([type='file']):focus {
|
|
|
1175
1157
|
overflow: scroll;
|
|
1176
1158
|
}
|
|
1177
1159
|
|
|
1160
|
+
.truncate {
|
|
1161
|
+
overflow: hidden;
|
|
1162
|
+
text-overflow: ellipsis;
|
|
1163
|
+
white-space: nowrap;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1178
1166
|
.text-ellipsis {
|
|
1179
1167
|
text-overflow: ellipsis;
|
|
1180
1168
|
}
|
|
@@ -1231,6 +1219,10 @@ input:where([type='file']):focus {
|
|
|
1231
1219
|
border-style: solid;
|
|
1232
1220
|
}
|
|
1233
1221
|
|
|
1222
|
+
.border-dashed {
|
|
1223
|
+
border-style: dashed;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1234
1226
|
.border-amber-500 {
|
|
1235
1227
|
--tw-border-opacity: 1;
|
|
1236
1228
|
border-color: rgb(245 158 11 / var(--tw-border-opacity, 1));
|
|
@@ -1465,11 +1457,6 @@ input:where([type='file']):focus {
|
|
|
1465
1457
|
padding-bottom: 0px;
|
|
1466
1458
|
}
|
|
1467
1459
|
|
|
1468
|
-
.py-0\.5 {
|
|
1469
|
-
padding-top: 0.125rem;
|
|
1470
|
-
padding-bottom: 0.125rem;
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
1460
|
.py-1 {
|
|
1474
1461
|
padding-top: 0.25rem;
|
|
1475
1462
|
padding-bottom: 0.25rem;
|
|
@@ -1803,21 +1790,4 @@ input:where([type='file']):focus {
|
|
|
1803
1790
|
|
|
1804
1791
|
.ease-out {
|
|
1805
1792
|
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
|
1806
|
-
}
|
|
1807
|
-
|
|
1808
|
-
.hover\:bg-white\/30:hover {
|
|
1809
|
-
background-color: rgb(255 255 255 / 0.3);
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
.hover\:text-gray-100:hover {
|
|
1813
|
-
--tw-text-opacity: 1;
|
|
1814
|
-
color: rgb(243 244 246 / var(--tw-text-opacity, 1));
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
.group\/tooltip:hover .group-hover\/tooltip\:visible {
|
|
1818
|
-
visibility: visible;
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
.group\/tooltip:hover .group-hover\/tooltip\:opacity-100 {
|
|
1822
|
-
opacity: 1;
|
|
1823
1793
|
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,35 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Target } from "@locator/shared";
|
|
2
2
|
export type Source = {
|
|
3
3
|
fileName: string;
|
|
4
4
|
lineNumber: number;
|
|
5
5
|
columnNumber?: number;
|
|
6
6
|
projectPath?: string;
|
|
7
7
|
};
|
|
8
|
-
type SimpleElement = {
|
|
9
|
-
type: "element";
|
|
10
|
-
name: string;
|
|
11
|
-
uniqueId: string;
|
|
12
|
-
fiber: Fiber;
|
|
13
|
-
box: SimpleDOMRect | null;
|
|
14
|
-
element: Element | Text;
|
|
15
|
-
children: (SimpleElement | SimpleComponent)[];
|
|
16
|
-
source: Source | null;
|
|
17
|
-
};
|
|
18
|
-
type SimpleComponent = {
|
|
19
|
-
type: "component";
|
|
20
|
-
uniqueId: string;
|
|
21
|
-
name: string;
|
|
22
|
-
fiber: Fiber;
|
|
23
|
-
box: SimpleDOMRect | null;
|
|
24
|
-
children: (SimpleElement | SimpleComponent)[];
|
|
25
|
-
source: Source | null;
|
|
26
|
-
definitionSourceFile: string | null;
|
|
27
|
-
};
|
|
28
|
-
export type SimpleNode = SimpleElement | SimpleComponent;
|
|
29
|
-
export type HighlightedNode = {
|
|
30
|
-
getNode: () => SimpleNode | null;
|
|
31
|
-
setNode: (node: SimpleNode | null) => void;
|
|
32
|
-
};
|
|
33
8
|
export type SimpleDOMRect = {
|
|
34
9
|
height: number;
|
|
35
10
|
width: number;
|
|
@@ -45,9 +20,3 @@ export type LinkProps = {
|
|
|
45
20
|
line: number;
|
|
46
21
|
column: number;
|
|
47
22
|
};
|
|
48
|
-
export type ContextMenuState = {
|
|
49
|
-
target: HTMLElement;
|
|
50
|
-
x: number;
|
|
51
|
-
y: number;
|
|
52
|
-
};
|
|
53
|
-
export {};
|