@treelocator/runtime 0.4.1 → 0.4.6
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 -55
- 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 +0 -1
- package/dist/functions/formatAncestryChain.d.ts +7 -0
- package/dist/functions/formatAncestryChain.js +25 -0
- package/dist/functions/formatAncestryChain.test.js +133 -1
- package/dist/functions/getUsableName.js +0 -21
- package/dist/functions/isCombinationModifiersPressed.js +6 -2
- 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 -55
- 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 +43 -44
- package/src/functions/formatAncestryChain.test.ts +74 -0
- package/src/functions/formatAncestryChain.ts +28 -0
- package/src/functions/getUsableName.ts +0 -21
- package/src/functions/isCombinationModifiersPressed.ts +5 -2
- 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() {
|
|
@@ -18,4 +18,11 @@ export interface AncestryItem {
|
|
|
18
18
|
serverComponents?: ServerComponentInfo[];
|
|
19
19
|
}
|
|
20
20
|
export declare function collectAncestry(node: TreeNode): AncestryItem[];
|
|
21
|
+
/**
|
|
22
|
+
* Truncate ancestry to keep only the local context.
|
|
23
|
+
* - If the clicked element has no filePath: keep up to the first ancestor with a file.
|
|
24
|
+
* - If the clicked element has a filePath: keep up to the first ancestor with a DIFFERENT file.
|
|
25
|
+
* The ancestry array is bottom-up: index 0 = clicked element, last = root.
|
|
26
|
+
*/
|
|
27
|
+
export declare function truncateAtFirstFile(items: AncestryItem[]): AncestryItem[];
|
|
21
28
|
export declare function formatAncestryChain(items: AncestryItem[]): string;
|
|
@@ -121,6 +121,31 @@ function getInnermostNamedComponent(item) {
|
|
|
121
121
|
}
|
|
122
122
|
return undefined;
|
|
123
123
|
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Truncate ancestry to keep only the local context.
|
|
127
|
+
* - If the clicked element has no filePath: keep up to the first ancestor with a file.
|
|
128
|
+
* - If the clicked element has a filePath: keep up to the first ancestor with a DIFFERENT file.
|
|
129
|
+
* The ancestry array is bottom-up: index 0 = clicked element, last = root.
|
|
130
|
+
*/
|
|
131
|
+
export function truncateAtFirstFile(items) {
|
|
132
|
+
if (items.length === 0) return items;
|
|
133
|
+
const clickedFile = items[0]?.filePath;
|
|
134
|
+
if (!clickedFile) {
|
|
135
|
+
// Clicked element has no file: find first ancestor with any file
|
|
136
|
+
const firstWithFile = items.findIndex(item => item.filePath);
|
|
137
|
+
if (firstWithFile === -1) return items;
|
|
138
|
+
return items.slice(0, firstWithFile + 1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Clicked element has a file: find first ancestor with a different file
|
|
142
|
+
for (let i = 1; i < items.length; i++) {
|
|
143
|
+
if (items[i].filePath && items[i].filePath !== clickedFile) {
|
|
144
|
+
return items.slice(0, i + 1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return items;
|
|
148
|
+
}
|
|
124
149
|
export function formatAncestryChain(items) {
|
|
125
150
|
if (items.length === 0) {
|
|
126
151
|
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,136 @@ 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("when clicked element has filePath, keeps up to first different file", () => {
|
|
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
|
+
elementName: "div",
|
|
301
|
+
componentName: "Layout",
|
|
302
|
+
filePath: "src/Layout.tsx",
|
|
303
|
+
line: 10
|
|
304
|
+
}]);
|
|
305
|
+
});
|
|
306
|
+
it("keeps all items in the same file plus first different-file ancestor", () => {
|
|
307
|
+
const items = [{
|
|
308
|
+
elementName: "span",
|
|
309
|
+
componentName: "Label",
|
|
310
|
+
filePath: "src/Button.tsx",
|
|
311
|
+
line: 20
|
|
312
|
+
}, {
|
|
313
|
+
elementName: "button",
|
|
314
|
+
componentName: "Button",
|
|
315
|
+
filePath: "src/Button.tsx",
|
|
316
|
+
line: 5
|
|
317
|
+
}, {
|
|
318
|
+
elementName: "div",
|
|
319
|
+
componentName: "Layout",
|
|
320
|
+
filePath: "src/Layout.tsx",
|
|
321
|
+
line: 10
|
|
322
|
+
}, {
|
|
323
|
+
elementName: "div",
|
|
324
|
+
componentName: "App",
|
|
325
|
+
filePath: "src/App.tsx",
|
|
326
|
+
line: 1
|
|
327
|
+
}];
|
|
328
|
+
const result = truncateAtFirstFile(items);
|
|
329
|
+
expect(result).toEqual([{
|
|
330
|
+
elementName: "span",
|
|
331
|
+
componentName: "Label",
|
|
332
|
+
filePath: "src/Button.tsx",
|
|
333
|
+
line: 20
|
|
334
|
+
}, {
|
|
335
|
+
elementName: "button",
|
|
336
|
+
componentName: "Button",
|
|
337
|
+
filePath: "src/Button.tsx",
|
|
338
|
+
line: 5
|
|
339
|
+
}, {
|
|
340
|
+
elementName: "div",
|
|
341
|
+
componentName: "Layout",
|
|
342
|
+
filePath: "src/Layout.tsx",
|
|
343
|
+
line: 10
|
|
344
|
+
}]);
|
|
345
|
+
});
|
|
346
|
+
it("returns all items when all share the same file", () => {
|
|
347
|
+
const items = [{
|
|
348
|
+
elementName: "span",
|
|
349
|
+
filePath: "src/App.tsx",
|
|
350
|
+
line: 10
|
|
351
|
+
}, {
|
|
352
|
+
elementName: "div",
|
|
353
|
+
filePath: "src/App.tsx",
|
|
354
|
+
line: 5
|
|
355
|
+
}];
|
|
356
|
+
const result = truncateAtFirstFile(items);
|
|
357
|
+
expect(result).toEqual(items);
|
|
358
|
+
});
|
|
359
|
+
it("returns all items when none have a filePath", () => {
|
|
360
|
+
const items = [{
|
|
361
|
+
elementName: "span",
|
|
362
|
+
componentName: "A"
|
|
363
|
+
}, {
|
|
364
|
+
elementName: "div",
|
|
365
|
+
componentName: "B"
|
|
366
|
+
}];
|
|
367
|
+
const result = truncateAtFirstFile(items);
|
|
368
|
+
expect(result).toEqual(items);
|
|
369
|
+
});
|
|
370
|
+
it("returns empty array for empty input", () => {
|
|
371
|
+
expect(truncateAtFirstFile([])).toEqual([]);
|
|
372
|
+
});
|
|
373
|
+
});
|
|
242
374
|
});
|
|
@@ -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";
|
|
@@ -9,8 +9,12 @@ export function getMouseModifiers() {
|
|
|
9
9
|
}
|
|
10
10
|
export function isCombinationModifiersPressed(e, rightClick = false) {
|
|
11
11
|
const modifiers = getMouseModifiers();
|
|
12
|
+
|
|
13
|
+
// Only require shift if it's part of the configured modifiers.
|
|
14
|
+
// Shift is used independently for truncation, so pressing it shouldn't
|
|
15
|
+
// disqualify the activation modifier combo.
|
|
12
16
|
if (rightClick) {
|
|
13
|
-
return e.altKey == !!modifiers.alt && e.metaKey == !!modifiers.meta &&
|
|
17
|
+
return e.altKey == !!modifiers.alt && e.metaKey == !!modifiers.meta && (!modifiers.shift || e.shiftKey);
|
|
14
18
|
}
|
|
15
|
-
return e.altKey == !!modifiers.alt && e.ctrlKey == !!modifiers.ctrl && e.metaKey == !!modifiers.meta &&
|
|
19
|
+
return e.altKey == !!modifiers.alt && e.ctrlKey == !!modifiers.ctrl && e.metaKey == !!modifiers.meta && (!modifiers.shift || e.shiftKey);
|
|
16
20
|
}
|
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
|
}
|