uilint-react 0.1.38 → 0.1.40
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/{ElementBadges-J2ELN2PU.js → ElementBadges-2I25HN6W.js} +48 -15
- package/dist/{InspectionPanel-7N56XBWA.js → InspectionPanel-4LOWGHW7.js} +2 -2
- package/dist/{LocatorOverlay-RDDLASGB.js → LocatorOverlay-ERRFPXKK.js} +2 -2
- package/dist/{UILintToolbar-OQY2V7Q7.js → UILintToolbar-MJH7RUZK.js} +2 -2
- package/dist/chunk-2VRWAMW7.js +927 -0
- package/dist/chunk-C6NUU5MF.js +1628 -0
- package/dist/{chunk-PU6XPNPN.js → chunk-LAL3JTAA.js} +161 -136
- package/dist/{chunk-V4273T5B.js → chunk-UD6HPLEZ.js} +1 -1
- package/dist/index.d.ts +32 -24
- package/dist/index.js +4 -4
- package/package.json +2 -2
- package/dist/chunk-GZOQ6QWC.js +0 -908
- package/dist/chunk-WEBVLQL5.js +0 -911
|
@@ -70,6 +70,7 @@ function shouldSkipElement(element) {
|
|
|
70
70
|
function scanDOMForSources(root = document.body, hideNodeModules = true) {
|
|
71
71
|
const elements = [];
|
|
72
72
|
elementCounter = 0;
|
|
73
|
+
const occurrenceByDataLoc = /* @__PURE__ */ new Map();
|
|
73
74
|
cleanupDataAttributes();
|
|
74
75
|
const locElements = root.querySelectorAll("[data-loc]");
|
|
75
76
|
for (const el of locElements) {
|
|
@@ -80,7 +81,9 @@ function scanDOMForSources(root = document.body, hideNodeModules = true) {
|
|
|
80
81
|
continue;
|
|
81
82
|
}
|
|
82
83
|
const dataLoc = el.getAttribute("data-loc");
|
|
83
|
-
const
|
|
84
|
+
const occurrence = (occurrenceByDataLoc.get(dataLoc) ?? 0) + 1;
|
|
85
|
+
occurrenceByDataLoc.set(dataLoc, occurrence);
|
|
86
|
+
const id = `loc:${dataLoc}#${occurrence}`;
|
|
84
87
|
el.setAttribute(DATA_ATTR, id);
|
|
85
88
|
elements.push({
|
|
86
89
|
id,
|
|
@@ -130,16 +133,21 @@ function updateElementRects(elements) {
|
|
|
130
133
|
rect: el.element.getBoundingClientRect()
|
|
131
134
|
}));
|
|
132
135
|
}
|
|
133
|
-
function buildEditorUrl(source, editor = "cursor") {
|
|
136
|
+
function buildEditorUrl(source, editor = "cursor", workspaceRoot) {
|
|
134
137
|
const { fileName, lineNumber, columnNumber } = source;
|
|
135
138
|
const column = columnNumber ?? 1;
|
|
139
|
+
let absolutePath = fileName;
|
|
140
|
+
if (workspaceRoot && !fileName.startsWith("/")) {
|
|
141
|
+
const root = workspaceRoot.endsWith("/") ? workspaceRoot.slice(0, -1) : workspaceRoot;
|
|
142
|
+
absolutePath = `${root}/${fileName}`;
|
|
143
|
+
}
|
|
136
144
|
if (editor === "cursor") {
|
|
137
145
|
return `cursor://file/${encodeURIComponent(
|
|
138
|
-
|
|
146
|
+
absolutePath
|
|
139
147
|
)}:${lineNumber}:${column}`;
|
|
140
148
|
}
|
|
141
149
|
return `vscode://file/${encodeURIComponent(
|
|
142
|
-
|
|
150
|
+
absolutePath
|
|
143
151
|
)}:${lineNumber}:${column}`;
|
|
144
152
|
}
|
|
145
153
|
|
|
@@ -186,7 +194,8 @@ var DATA_UILINT_ID = "data-ui-lint-id";
|
|
|
186
194
|
import { create } from "zustand";
|
|
187
195
|
function getDataLocFromId(id) {
|
|
188
196
|
if (id.startsWith("loc:")) {
|
|
189
|
-
|
|
197
|
+
const raw = id.slice(4);
|
|
198
|
+
return raw.split("#")[0] || null;
|
|
190
199
|
}
|
|
191
200
|
return null;
|
|
192
201
|
}
|
|
@@ -211,21 +220,25 @@ async function scanFileForIssues(sourceFile, store) {
|
|
|
211
220
|
return { issues };
|
|
212
221
|
}
|
|
213
222
|
function distributeIssuesToElements(issues, elements, updateElementIssue, hasError) {
|
|
214
|
-
const
|
|
223
|
+
const dataLocToElementIds = /* @__PURE__ */ new Map();
|
|
215
224
|
for (const el of elements) {
|
|
216
225
|
const dataLoc = getDataLocFromId(el.id);
|
|
217
226
|
if (dataLoc) {
|
|
218
|
-
|
|
227
|
+
const existing = dataLocToElementIds.get(dataLoc);
|
|
228
|
+
if (existing) existing.push(el.id);
|
|
229
|
+
else dataLocToElementIds.set(dataLoc, [el.id]);
|
|
219
230
|
}
|
|
220
231
|
}
|
|
221
232
|
const issuesByElement = /* @__PURE__ */ new Map();
|
|
222
233
|
for (const issue of issues) {
|
|
223
234
|
if (issue.dataLoc) {
|
|
224
|
-
const
|
|
225
|
-
if (
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
235
|
+
const elementIds = dataLocToElementIds.get(issue.dataLoc);
|
|
236
|
+
if (elementIds) {
|
|
237
|
+
for (const elementId of elementIds) {
|
|
238
|
+
const existing = issuesByElement.get(elementId) || [];
|
|
239
|
+
existing.push(issue);
|
|
240
|
+
issuesByElement.set(elementId, existing);
|
|
241
|
+
}
|
|
229
242
|
}
|
|
230
243
|
}
|
|
231
244
|
}
|
|
@@ -265,12 +278,11 @@ var useUILintStore = create()((set, get) => ({
|
|
|
265
278
|
// ============ Inspection ============
|
|
266
279
|
inspectedElement: null,
|
|
267
280
|
setInspectedElement: (el) => set({ inspectedElement: el }),
|
|
268
|
-
// ============
|
|
281
|
+
// ============ Live Scanning ============
|
|
282
|
+
liveScanEnabled: false,
|
|
269
283
|
autoScanState: DEFAULT_AUTO_SCAN_STATE,
|
|
270
284
|
elementIssuesCache: /* @__PURE__ */ new Map(),
|
|
271
285
|
scanLock: false,
|
|
272
|
-
scanPaused: false,
|
|
273
|
-
scanAborted: false,
|
|
274
286
|
_setScanState: (partial) => set((state) => ({
|
|
275
287
|
autoScanState: { ...state.autoScanState, ...partial }
|
|
276
288
|
})),
|
|
@@ -279,17 +291,15 @@ var useUILintStore = create()((set, get) => ({
|
|
|
279
291
|
newCache.set(id, issue);
|
|
280
292
|
return { elementIssuesCache: newCache };
|
|
281
293
|
}),
|
|
282
|
-
|
|
294
|
+
enableLiveScan: async (hideNodeModules) => {
|
|
283
295
|
const state = get();
|
|
284
296
|
if (state.scanLock) {
|
|
285
297
|
console.warn("UILint: Scan already in progress");
|
|
286
298
|
return;
|
|
287
299
|
}
|
|
288
300
|
set({
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
scanAborted: false,
|
|
292
|
-
pendingNewElements: 0
|
|
301
|
+
liveScanEnabled: true,
|
|
302
|
+
scanLock: true
|
|
293
303
|
});
|
|
294
304
|
const elements = scanDOMForSources(document.body, hideNodeModules);
|
|
295
305
|
const initialCache = /* @__PURE__ */ new Map();
|
|
@@ -309,60 +319,72 @@ var useUILintStore = create()((set, get) => ({
|
|
|
309
319
|
elements
|
|
310
320
|
}
|
|
311
321
|
});
|
|
312
|
-
await get()._runScanLoop(elements
|
|
313
|
-
},
|
|
314
|
-
pauseAutoScan: () => {
|
|
315
|
-
set({ scanPaused: true });
|
|
316
|
-
get()._setScanState({ status: "paused" });
|
|
322
|
+
await get()._runScanLoop(elements);
|
|
317
323
|
},
|
|
318
|
-
|
|
319
|
-
const state = get();
|
|
320
|
-
if (state.autoScanState.status !== "paused") return;
|
|
321
|
-
set({ scanPaused: false });
|
|
322
|
-
get()._setScanState({ status: "scanning" });
|
|
323
|
-
get()._runScanLoop(
|
|
324
|
-
state.autoScanState.elements,
|
|
325
|
-
state.autoScanState.currentIndex
|
|
326
|
-
);
|
|
327
|
-
},
|
|
328
|
-
stopAutoScan: () => {
|
|
324
|
+
disableLiveScan: () => {
|
|
329
325
|
set({
|
|
330
|
-
|
|
331
|
-
scanPaused: false,
|
|
326
|
+
liveScanEnabled: false,
|
|
332
327
|
scanLock: false,
|
|
333
328
|
autoScanState: DEFAULT_AUTO_SCAN_STATE,
|
|
334
|
-
elementIssuesCache: /* @__PURE__ */ new Map()
|
|
335
|
-
pendingNewElements: 0
|
|
329
|
+
elementIssuesCache: /* @__PURE__ */ new Map()
|
|
336
330
|
});
|
|
337
331
|
},
|
|
338
|
-
|
|
332
|
+
scanNewElements: async (newElements) => {
|
|
333
|
+
const state = get();
|
|
334
|
+
if (!state.liveScanEnabled) return;
|
|
335
|
+
if (newElements.length === 0) return;
|
|
336
|
+
set((s) => {
|
|
337
|
+
const newCache = new Map(s.elementIssuesCache);
|
|
338
|
+
for (const el of newElements) {
|
|
339
|
+
newCache.set(el.id, {
|
|
340
|
+
elementId: el.id,
|
|
341
|
+
issues: [],
|
|
342
|
+
status: "pending"
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return {
|
|
346
|
+
elementIssuesCache: newCache,
|
|
347
|
+
autoScanState: {
|
|
348
|
+
...s.autoScanState,
|
|
349
|
+
elements: [...s.autoScanState.elements, ...newElements],
|
|
350
|
+
totalElements: s.autoScanState.totalElements + newElements.length
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
});
|
|
354
|
+
const sourceFiles = groupBySourceFile(newElements);
|
|
355
|
+
for (const sourceFile of sourceFiles) {
|
|
356
|
+
for (const el of sourceFile.elements) {
|
|
357
|
+
get().updateElementIssue(el.id, {
|
|
358
|
+
elementId: el.id,
|
|
359
|
+
issues: [],
|
|
360
|
+
status: "scanning"
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
364
|
+
const { issues, error } = await scanFileForIssues(sourceFile, get());
|
|
365
|
+
distributeIssuesToElements(
|
|
366
|
+
issues,
|
|
367
|
+
sourceFile.elements,
|
|
368
|
+
get().updateElementIssue,
|
|
369
|
+
error ?? false
|
|
370
|
+
);
|
|
371
|
+
if (get().wsConnected && get().wsConnection) {
|
|
372
|
+
get().subscribeToFile(sourceFile.path);
|
|
373
|
+
}
|
|
374
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
_runScanLoop: async (elements) => {
|
|
339
378
|
const sourceFiles = groupBySourceFile(elements);
|
|
340
379
|
let processedElements = 0;
|
|
341
|
-
let skipElements = startIndex;
|
|
342
380
|
for (const sourceFile of sourceFiles) {
|
|
343
|
-
if (
|
|
344
|
-
skipElements -= sourceFile.elements.length;
|
|
345
|
-
processedElements += sourceFile.elements.length;
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
skipElements = 0;
|
|
349
|
-
if (get().scanAborted) {
|
|
381
|
+
if (!get().liveScanEnabled) {
|
|
350
382
|
set({
|
|
351
383
|
scanLock: false,
|
|
352
|
-
autoScanState:
|
|
384
|
+
autoScanState: DEFAULT_AUTO_SCAN_STATE
|
|
353
385
|
});
|
|
354
386
|
return;
|
|
355
387
|
}
|
|
356
|
-
while (get().scanPaused) {
|
|
357
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
358
|
-
if (get().scanAborted) {
|
|
359
|
-
set({
|
|
360
|
-
scanLock: false,
|
|
361
|
-
autoScanState: { ...get().autoScanState, status: "idle" }
|
|
362
|
-
});
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
388
|
get()._setScanState({ currentIndex: processedElements });
|
|
367
389
|
for (const el of sourceFile.elements) {
|
|
368
390
|
get().updateElementIssue(el.id, {
|
|
@@ -394,10 +416,7 @@ var useUILintStore = create()((set, get) => ({
|
|
|
394
416
|
}
|
|
395
417
|
});
|
|
396
418
|
},
|
|
397
|
-
// ============
|
|
398
|
-
pendingNewElements: 0,
|
|
399
|
-
setPendingNewElements: (count) => set({ pendingNewElements: count }),
|
|
400
|
-
clearPendingNewElements: () => set({ pendingNewElements: 0 }),
|
|
419
|
+
// ============ DOM Observer ============
|
|
401
420
|
removeStaleResults: (elementIds) => set((state) => {
|
|
402
421
|
const newCache = new Map(state.elementIssuesCache);
|
|
403
422
|
const newElements = state.autoScanState.elements.filter(
|
|
@@ -415,6 +434,15 @@ var useUILintStore = create()((set, get) => ({
|
|
|
415
434
|
}
|
|
416
435
|
};
|
|
417
436
|
}),
|
|
437
|
+
// ============ File/Element Selection ============
|
|
438
|
+
hoveredFilePath: null,
|
|
439
|
+
selectedFilePath: null,
|
|
440
|
+
selectedElementId: null,
|
|
441
|
+
hoveredElementId: null,
|
|
442
|
+
setHoveredFilePath: (path) => set({ hoveredFilePath: path }),
|
|
443
|
+
setSelectedFilePath: (path) => set({ selectedFilePath: path }),
|
|
444
|
+
setSelectedElementId: (id) => set({ selectedElementId: id }),
|
|
445
|
+
setHoveredElementId: (id) => set({ hoveredElementId: id }),
|
|
418
446
|
// ============ WebSocket ============
|
|
419
447
|
wsConnection: null,
|
|
420
448
|
wsConnected: false,
|
|
@@ -424,6 +452,9 @@ var useUILintStore = create()((set, get) => ({
|
|
|
424
452
|
wsProgressPhase: /* @__PURE__ */ new Map(),
|
|
425
453
|
wsLastActivity: null,
|
|
426
454
|
wsRecentResults: [],
|
|
455
|
+
workspaceRoot: null,
|
|
456
|
+
appRoot: null,
|
|
457
|
+
serverCwd: null,
|
|
427
458
|
connectWebSocket: (url) => {
|
|
428
459
|
const targetUrl = url || get().wsUrl;
|
|
429
460
|
const existing = get().wsConnection;
|
|
@@ -573,7 +604,7 @@ var useUILintStore = create()((set, get) => ({
|
|
|
573
604
|
return { eslintIssuesCache: next };
|
|
574
605
|
});
|
|
575
606
|
const state = get();
|
|
576
|
-
if (state.
|
|
607
|
+
if (state.liveScanEnabled) {
|
|
577
608
|
const sourceFiles = groupBySourceFile(state.autoScanState.elements);
|
|
578
609
|
const sf = sourceFiles.find((s) => s.path === filePath);
|
|
579
610
|
if (sf) {
|
|
@@ -633,7 +664,7 @@ var useUILintStore = create()((set, get) => ({
|
|
|
633
664
|
return { eslintIssuesCache: next };
|
|
634
665
|
});
|
|
635
666
|
const state = get();
|
|
636
|
-
if (state.
|
|
667
|
+
if (state.liveScanEnabled) {
|
|
637
668
|
const sourceFiles = groupBySourceFile(state.autoScanState.elements);
|
|
638
669
|
const sf = sourceFiles.find((s) => s.path === filePath);
|
|
639
670
|
if (sf) {
|
|
@@ -659,6 +690,16 @@ var useUILintStore = create()((set, get) => ({
|
|
|
659
690
|
}
|
|
660
691
|
break;
|
|
661
692
|
}
|
|
693
|
+
case "workspace:info": {
|
|
694
|
+
const { appRoot, workspaceRoot, serverCwd } = data;
|
|
695
|
+
console.log("[UILint] Received workspace info:", {
|
|
696
|
+
appRoot,
|
|
697
|
+
workspaceRoot,
|
|
698
|
+
serverCwd
|
|
699
|
+
});
|
|
700
|
+
set({ appRoot, workspaceRoot, serverCwd });
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
662
703
|
}
|
|
663
704
|
},
|
|
664
705
|
_reconnectWebSocket: () => {
|
|
@@ -685,50 +726,53 @@ function useDOMObserver(enabled = true) {
|
|
|
685
726
|
const reconcileTimeoutRef = useRef(
|
|
686
727
|
null
|
|
687
728
|
);
|
|
688
|
-
const
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
);
|
|
729
|
+
const knownElementIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
730
|
+
const liveScanEnabled = useUILintStore((s) => s.liveScanEnabled);
|
|
731
|
+
const settings = useUILintStore((s) => s.settings);
|
|
732
|
+
const autoScanState = useUILintStore((s) => s.autoScanState);
|
|
692
733
|
const removeStaleResults = useUILintStore(
|
|
693
734
|
(s) => s.removeStaleResults
|
|
694
735
|
);
|
|
695
|
-
const
|
|
736
|
+
const scanNewElements = useUILintStore((s) => s.scanNewElements);
|
|
696
737
|
useEffect(() => {
|
|
697
|
-
if (autoScanState.
|
|
738
|
+
if (autoScanState.elements.length > 0) {
|
|
698
739
|
const ids = new Set(autoScanState.elements.map((el) => el.id));
|
|
699
|
-
|
|
740
|
+
knownElementIdsRef.current = ids;
|
|
700
741
|
}
|
|
701
|
-
}, [autoScanState.
|
|
742
|
+
}, [autoScanState.elements]);
|
|
702
743
|
const reconcileElements = useCallback(() => {
|
|
703
|
-
|
|
704
|
-
const
|
|
744
|
+
if (!liveScanEnabled) return;
|
|
745
|
+
const currentElements = scanDOMForSources(
|
|
746
|
+
document.body,
|
|
747
|
+
settings.hideNodeModules
|
|
748
|
+
);
|
|
749
|
+
const currentIds = new Set(currentElements.map((el) => el.id));
|
|
750
|
+
const knownIds = knownElementIdsRef.current;
|
|
751
|
+
const newElements = [];
|
|
705
752
|
for (const el of currentElements) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
currentIds.add(`loc:${dataLoc}`);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
const lastScanIds = lastScanElementIdsRef.current;
|
|
712
|
-
if (lastScanIds.size === 0) return;
|
|
713
|
-
const newElementIds = [];
|
|
714
|
-
for (const id of currentIds) {
|
|
715
|
-
if (!lastScanIds.has(id)) {
|
|
716
|
-
newElementIds.push(id);
|
|
753
|
+
if (!knownIds.has(el.id)) {
|
|
754
|
+
newElements.push(el);
|
|
717
755
|
}
|
|
718
756
|
}
|
|
719
757
|
const removedElementIds = [];
|
|
720
|
-
for (const id of
|
|
758
|
+
for (const id of knownIds) {
|
|
721
759
|
if (!currentIds.has(id)) {
|
|
722
760
|
removedElementIds.push(id);
|
|
723
761
|
}
|
|
724
762
|
}
|
|
725
|
-
|
|
726
|
-
|
|
763
|
+
knownElementIdsRef.current = currentIds;
|
|
764
|
+
if (newElements.length > 0) {
|
|
765
|
+
scanNewElements(newElements);
|
|
727
766
|
}
|
|
728
767
|
if (removedElementIds.length > 0) {
|
|
729
768
|
removeStaleResults(removedElementIds);
|
|
730
769
|
}
|
|
731
|
-
}, [
|
|
770
|
+
}, [
|
|
771
|
+
liveScanEnabled,
|
|
772
|
+
settings.hideNodeModules,
|
|
773
|
+
scanNewElements,
|
|
774
|
+
removeStaleResults
|
|
775
|
+
]);
|
|
732
776
|
const debouncedReconcile = useCallback(() => {
|
|
733
777
|
if (reconcileTimeoutRef.current) {
|
|
734
778
|
clearTimeout(reconcileTimeoutRef.current);
|
|
@@ -819,14 +863,15 @@ function UILintProvider({
|
|
|
819
863
|
const setInspectedElement = useUILintStore(
|
|
820
864
|
(s) => s.setInspectedElement
|
|
821
865
|
);
|
|
866
|
+
const liveScanEnabled = useUILintStore((s) => s.liveScanEnabled);
|
|
822
867
|
const autoScanState = useUILintStore((s) => s.autoScanState);
|
|
823
868
|
const elementIssuesCache = useUILintStore(
|
|
824
869
|
(s) => s.elementIssuesCache
|
|
825
870
|
);
|
|
826
|
-
const
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
const
|
|
871
|
+
const storeEnableLiveScan = useUILintStore(
|
|
872
|
+
(s) => s.enableLiveScan
|
|
873
|
+
);
|
|
874
|
+
const disableLiveScan = useUILintStore((s) => s.disableLiveScan);
|
|
830
875
|
const connectWebSocket = useUILintStore(
|
|
831
876
|
(s) => s.connectWebSocket
|
|
832
877
|
);
|
|
@@ -852,7 +897,7 @@ function UILintProvider({
|
|
|
852
897
|
);
|
|
853
898
|
const handleMouseMove = useCallback2(
|
|
854
899
|
(e) => {
|
|
855
|
-
if (!altKeyHeld
|
|
900
|
+
if (!altKeyHeld) return;
|
|
856
901
|
const elementAtPoint = document.elementFromPoint(e.clientX, e.clientY);
|
|
857
902
|
if (!elementAtPoint) {
|
|
858
903
|
setLocatorTarget(null);
|
|
@@ -869,16 +914,11 @@ function UILintProvider({
|
|
|
869
914
|
}
|
|
870
915
|
setLocatorTarget(null);
|
|
871
916
|
},
|
|
872
|
-
[
|
|
873
|
-
altKeyHeld,
|
|
874
|
-
inspectedElement,
|
|
875
|
-
getLocatorTargetFromElement,
|
|
876
|
-
setLocatorTarget
|
|
877
|
-
]
|
|
917
|
+
[altKeyHeld, getLocatorTargetFromElement, setLocatorTarget]
|
|
878
918
|
);
|
|
879
919
|
const handleLocatorClick = useCallback2(
|
|
880
920
|
(e) => {
|
|
881
|
-
if (!altKeyHeld
|
|
921
|
+
if (!altKeyHeld || !locatorTarget) return;
|
|
882
922
|
const targetEl = e.target;
|
|
883
923
|
if (targetEl?.closest?.("[data-ui-lint]")) return;
|
|
884
924
|
e.preventDefault();
|
|
@@ -890,13 +930,7 @@ function UILintProvider({
|
|
|
890
930
|
});
|
|
891
931
|
setLocatorTarget(null);
|
|
892
932
|
},
|
|
893
|
-
[
|
|
894
|
-
altKeyHeld,
|
|
895
|
-
locatorTarget,
|
|
896
|
-
inspectedElement,
|
|
897
|
-
setInspectedElement,
|
|
898
|
-
setLocatorTarget
|
|
899
|
-
]
|
|
933
|
+
[altKeyHeld, locatorTarget, setInspectedElement, setLocatorTarget]
|
|
900
934
|
);
|
|
901
935
|
useEffect2(() => {
|
|
902
936
|
if (!isBrowser() || !enabled) return;
|
|
@@ -926,20 +960,14 @@ function UILintProvider({
|
|
|
926
960
|
}, [enabled, setAltKeyHeld, setLocatorTarget]);
|
|
927
961
|
useEffect2(() => {
|
|
928
962
|
if (!isBrowser() || !enabled) return;
|
|
929
|
-
if (!altKeyHeld
|
|
963
|
+
if (!altKeyHeld) return;
|
|
930
964
|
window.addEventListener("mousemove", handleMouseMove);
|
|
931
965
|
window.addEventListener("click", handleLocatorClick, true);
|
|
932
966
|
return () => {
|
|
933
967
|
window.removeEventListener("mousemove", handleMouseMove);
|
|
934
968
|
window.removeEventListener("click", handleLocatorClick, true);
|
|
935
969
|
};
|
|
936
|
-
}, [
|
|
937
|
-
enabled,
|
|
938
|
-
altKeyHeld,
|
|
939
|
-
inspectedElement,
|
|
940
|
-
handleMouseMove,
|
|
941
|
-
handleLocatorClick
|
|
942
|
-
]);
|
|
970
|
+
}, [enabled, altKeyHeld, handleMouseMove, handleLocatorClick]);
|
|
943
971
|
useEffect2(() => {
|
|
944
972
|
if (!isBrowser() || !enabled) return;
|
|
945
973
|
const handleKeyDown = (e) => {
|
|
@@ -961,9 +989,9 @@ function UILintProvider({
|
|
|
961
989
|
disconnectWebSocket();
|
|
962
990
|
};
|
|
963
991
|
}, [enabled, isMounted, connectWebSocket, disconnectWebSocket]);
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
}, [
|
|
992
|
+
const enableLiveScan = useCallback2(() => {
|
|
993
|
+
storeEnableLiveScan(settings.hideNodeModules);
|
|
994
|
+
}, [storeEnableLiveScan, settings.hideNodeModules]);
|
|
967
995
|
const contextValue = useMemo(
|
|
968
996
|
() => ({
|
|
969
997
|
settings,
|
|
@@ -972,12 +1000,11 @@ function UILintProvider({
|
|
|
972
1000
|
locatorTarget,
|
|
973
1001
|
inspectedElement,
|
|
974
1002
|
setInspectedElement,
|
|
1003
|
+
liveScanEnabled,
|
|
975
1004
|
autoScanState,
|
|
976
1005
|
elementIssuesCache,
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
resumeAutoScan,
|
|
980
|
-
stopAutoScan
|
|
1006
|
+
enableLiveScan,
|
|
1007
|
+
disableLiveScan
|
|
981
1008
|
}),
|
|
982
1009
|
[
|
|
983
1010
|
settings,
|
|
@@ -986,12 +1013,11 @@ function UILintProvider({
|
|
|
986
1013
|
locatorTarget,
|
|
987
1014
|
inspectedElement,
|
|
988
1015
|
setInspectedElement,
|
|
1016
|
+
liveScanEnabled,
|
|
989
1017
|
autoScanState,
|
|
990
1018
|
elementIssuesCache,
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
resumeAutoScan,
|
|
994
|
-
stopAutoScan
|
|
1019
|
+
enableLiveScan,
|
|
1020
|
+
disableLiveScan
|
|
995
1021
|
]
|
|
996
1022
|
);
|
|
997
1023
|
const shouldRenderUI = enabled && isMounted;
|
|
@@ -1001,14 +1027,14 @@ function UILintProvider({
|
|
|
1001
1027
|
] });
|
|
1002
1028
|
}
|
|
1003
1029
|
function UILintUI() {
|
|
1004
|
-
const { altKeyHeld, inspectedElement,
|
|
1030
|
+
const { altKeyHeld, inspectedElement, liveScanEnabled } = useUILintContext();
|
|
1005
1031
|
const [components, setComponents] = useState(null);
|
|
1006
1032
|
useEffect2(() => {
|
|
1007
1033
|
Promise.all([
|
|
1008
|
-
import("./UILintToolbar-
|
|
1009
|
-
import("./InspectionPanel-
|
|
1010
|
-
import("./LocatorOverlay-
|
|
1011
|
-
import("./ElementBadges-
|
|
1034
|
+
import("./UILintToolbar-MJH7RUZK.js"),
|
|
1035
|
+
import("./InspectionPanel-4LOWGHW7.js"),
|
|
1036
|
+
import("./LocatorOverlay-ERRFPXKK.js"),
|
|
1037
|
+
import("./ElementBadges-2I25HN6W.js")
|
|
1012
1038
|
]).then(([toolbar, panel, locator, badges]) => {
|
|
1013
1039
|
setComponents({
|
|
1014
1040
|
Toolbar: toolbar.UILintToolbar,
|
|
@@ -1021,11 +1047,10 @@ function UILintUI() {
|
|
|
1021
1047
|
}, []);
|
|
1022
1048
|
if (!components) return null;
|
|
1023
1049
|
const { Toolbar, Panel, LocatorOverlay, InspectedHighlight, ElementBadges } = components;
|
|
1024
|
-
const showBadges = autoScanState.status !== "idle";
|
|
1025
1050
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1026
1051
|
/* @__PURE__ */ jsx(Toolbar, {}),
|
|
1027
1052
|
(altKeyHeld || inspectedElement) && /* @__PURE__ */ jsx(LocatorOverlay, {}),
|
|
1028
|
-
|
|
1053
|
+
liveScanEnabled && /* @__PURE__ */ jsx(ElementBadges, {}),
|
|
1029
1054
|
inspectedElement && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1030
1055
|
/* @__PURE__ */ jsx(InspectedHighlight, {}),
|
|
1031
1056
|
/* @__PURE__ */ jsx(Panel, {})
|
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,10 @@ interface SourceLocation {
|
|
|
19
19
|
* Source is always present from data-loc attribute
|
|
20
20
|
*/
|
|
21
21
|
interface ScannedElement {
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Unique per-instance ID derived from data-loc.
|
|
24
|
+
* Format: "loc:path:line:column#occurrence"
|
|
25
|
+
*/
|
|
23
26
|
id: string;
|
|
24
27
|
element: Element;
|
|
25
28
|
tagName: string;
|
|
@@ -109,18 +112,16 @@ interface UILintContextValue {
|
|
|
109
112
|
inspectedElement: InspectedElement | null;
|
|
110
113
|
/** Set the element to inspect (opens sidebar) */
|
|
111
114
|
setInspectedElement: (element: InspectedElement | null) => void;
|
|
112
|
-
/**
|
|
115
|
+
/** Whether live scanning is enabled */
|
|
116
|
+
liveScanEnabled: boolean;
|
|
117
|
+
/** Auto-scan state (for progress tracking) */
|
|
113
118
|
autoScanState: AutoScanState;
|
|
114
|
-
/** Cache of element issues from
|
|
119
|
+
/** Cache of element issues from scanning */
|
|
115
120
|
elementIssuesCache: Map<string, ElementIssue>;
|
|
116
|
-
/**
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
|
|
120
|
-
/** Resume the auto-scan */
|
|
121
|
-
resumeAutoScan: () => void;
|
|
122
|
-
/** Stop and reset the auto-scan */
|
|
123
|
-
stopAutoScan: () => void;
|
|
121
|
+
/** Enable live scanning */
|
|
122
|
+
enableLiveScan: () => void;
|
|
123
|
+
/** Disable live scanning */
|
|
124
|
+
disableLiveScan: () => void;
|
|
124
125
|
}
|
|
125
126
|
/**
|
|
126
127
|
* Props for the UILintProvider component
|
|
@@ -168,27 +169,31 @@ declare function useUILintContext(): UILintContextValue;
|
|
|
168
169
|
declare function UILintProvider({ children, enabled, }: UILintProviderProps): react_jsx_runtime.JSX.Element;
|
|
169
170
|
|
|
170
171
|
/**
|
|
171
|
-
* UILint Toolbar -
|
|
172
|
+
* UILint Toolbar - Improved UX Version
|
|
172
173
|
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
174
|
+
* Key improvements:
|
|
175
|
+
* - Clear visual hierarchy: Primary (toggle) → Secondary (issues) → Tertiary (settings)
|
|
176
|
+
* - Contextual hints that only show when relevant
|
|
177
|
+
* - CSS-based hover/focus states (no inline handlers)
|
|
178
|
+
* - Full keyboard navigation with visible focus rings
|
|
179
|
+
* - Smooth animations for all state changes
|
|
180
|
+
* - Better disabled state communication
|
|
181
|
+
* - Expanded panel that doesn't conflict with settings
|
|
182
|
+
* - Touch-friendly targets (min 44px)
|
|
183
|
+
* - ARIA labels and semantic markup
|
|
178
184
|
*/
|
|
179
185
|
|
|
180
|
-
/**
|
|
181
|
-
* Main Toolbar Component - Segmented pill with Scan + Settings
|
|
182
|
-
*/
|
|
183
186
|
declare function UILintToolbar(): React$1.ReactPortal | null;
|
|
184
187
|
|
|
185
188
|
/**
|
|
186
|
-
* Inspection Panel -
|
|
187
|
-
*
|
|
189
|
+
* Inspection Panel - Lightweight floating popover showing element issues
|
|
190
|
+
* with inline code previews and "Open in Cursor" actions
|
|
191
|
+
*
|
|
192
|
+
* Positions near the clicked badge, avoiding overlap with the element itself.
|
|
188
193
|
*/
|
|
189
194
|
|
|
190
195
|
/**
|
|
191
|
-
* Main Inspection Panel Component
|
|
196
|
+
* Main Inspection Panel Component - Floating Popover
|
|
192
197
|
*/
|
|
193
198
|
declare function InspectionPanel(): React$1.ReactPortal | null;
|
|
194
199
|
|
|
@@ -247,8 +252,11 @@ declare function getElementById(id: string): Element | null;
|
|
|
247
252
|
declare function updateElementRects(elements: ScannedElement[]): ScannedElement[];
|
|
248
253
|
/**
|
|
249
254
|
* Build an "Open in Editor" URL
|
|
255
|
+
* @param source - The source location (fileName may be relative or absolute)
|
|
256
|
+
* @param editor - The editor to open in (cursor or vscode)
|
|
257
|
+
* @param workspaceRoot - Optional workspace root to prepend for relative paths
|
|
250
258
|
*/
|
|
251
|
-
declare function buildEditorUrl(source: SourceLocation, editor?: "cursor" | "vscode"): string;
|
|
259
|
+
declare function buildEditorUrl(source: SourceLocation, editor?: "cursor" | "vscode", workspaceRoot?: string | null): string;
|
|
252
260
|
|
|
253
261
|
/**
|
|
254
262
|
* Client for fetching source code from the dev API
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
UILintToolbar
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-C6NUU5MF.js";
|
|
5
5
|
import {
|
|
6
6
|
InspectionPanel,
|
|
7
7
|
clearSourceCache,
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
fetchSourceWithContext,
|
|
10
10
|
getCachedSource,
|
|
11
11
|
prefetchSources
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-2VRWAMW7.js";
|
|
13
13
|
import {
|
|
14
14
|
LocatorOverlay
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-UD6HPLEZ.js";
|
|
16
16
|
import {
|
|
17
17
|
DATA_UILINT_ID,
|
|
18
18
|
DEFAULT_SETTINGS,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
scanDOMForSources,
|
|
29
29
|
updateElementRects,
|
|
30
30
|
useUILintContext
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-LAL3JTAA.js";
|
|
32
32
|
|
|
33
33
|
// src/consistency/snapshot.ts
|
|
34
34
|
var DATA_ELEMENTS_ATTR = "data-elements";
|