uilint-react 0.1.25 → 0.1.27

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.
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
  import {
4
4
  useUILintContext
5
- } from "./chunk-45MPASAN.js";
5
+ } from "./chunk-5VJ2Q2QW.js";
6
6
 
7
7
  // src/components/ui-lint/ElementBadges.tsx
8
8
  import React, { useState, useEffect, useCallback, useMemo } from "react";
@@ -2,8 +2,8 @@
2
2
  "use client";
3
3
  import {
4
4
  InspectionPanel
5
- } from "./chunk-SIVHTQ2P.js";
6
- import "./chunk-45MPASAN.js";
5
+ } from "./chunk-QYRESGFG.js";
6
+ import "./chunk-5VJ2Q2QW.js";
7
7
  export {
8
8
  InspectionPanel
9
9
  };
@@ -3,8 +3,8 @@
3
3
  import {
4
4
  InspectedElementHighlight,
5
5
  LocatorOverlay
6
- } from "./chunk-RA27RIJ2.js";
7
- import "./chunk-45MPASAN.js";
6
+ } from "./chunk-XLIDEQXH.js";
7
+ import "./chunk-5VJ2Q2QW.js";
8
8
  export {
9
9
  InspectedElementHighlight,
10
10
  LocatorOverlay
@@ -2,8 +2,8 @@
2
2
  "use client";
3
3
  import {
4
4
  UILintToolbar
5
- } from "./chunk-ORYG2TNM.js";
6
- import "./chunk-45MPASAN.js";
5
+ } from "./chunk-7X5HN55P.js";
6
+ import "./chunk-5VJ2Q2QW.js";
7
7
  export {
8
8
  UILintToolbar
9
9
  };
@@ -130,17 +130,18 @@ function scanDOMForSources(root = document.body, hideNodeModules = true) {
130
130
  let node = walker.currentNode;
131
131
  while (node) {
132
132
  if (node instanceof Element) {
133
- let source = getSourceFromDataLoc(node);
133
+ let source = null;
134
134
  let componentStack = [];
135
- if (!source) {
136
- const fiber = getFiberFromElement(node);
137
- if (fiber) {
138
- source = getDebugSource(fiber);
139
- if (!source && fiber._debugOwner) {
140
- source = getDebugSource(fiber._debugOwner);
141
- }
142
- componentStack = getComponentStack(fiber);
135
+ const fiber = getFiberFromElement(node);
136
+ if (fiber) {
137
+ source = getDebugSource(fiber);
138
+ if (!source && fiber._debugOwner) {
139
+ source = getDebugSource(fiber._debugOwner);
143
140
  }
141
+ componentStack = getComponentStack(fiber);
142
+ }
143
+ if (!source) {
144
+ source = getSourceFromDataLoc(node);
144
145
  }
145
146
  if (hideNodeModules && source && isNodeModulesPath(source.fileName)) {
146
147
  const appSource = componentStack.find(
@@ -261,67 +262,78 @@ var DEFAULT_AUTO_SCAN_STATE = {
261
262
  };
262
263
  var DATA_UILINT_ID = "data-ui-lint-id";
263
264
 
264
- // src/components/ui-lint/UILintProvider.tsx
265
- import {
266
- createContext,
267
- useContext,
268
- useState,
269
- useEffect,
270
- useCallback,
271
- useMemo
272
- } from "react";
273
-
274
265
  // src/components/ui-lint/store.ts
275
266
  import { create } from "zustand";
276
- async function scanElementForIssues(element) {
277
- if (!element.source) {
278
- return {
279
- elementId: element.id,
280
- issues: [],
281
- status: "complete"
282
- };
267
+ function getDataLocFromId(id) {
268
+ if (id.startsWith("loc:")) {
269
+ return id.slice(4);
283
270
  }
271
+ return null;
272
+ }
273
+ async function scanFileForIssues(sourceFile) {
274
+ if (sourceFile.elements.length === 0) {
275
+ return { issues: [] };
276
+ }
277
+ const filePath = sourceFile.path;
284
278
  try {
285
279
  const sourceResponse = await fetch(
286
- `/api/.uilint/source?path=${encodeURIComponent(element.source.fileName)}`
280
+ `/api/.uilint/source?path=${encodeURIComponent(filePath)}`
287
281
  );
288
282
  if (!sourceResponse.ok) {
289
- return {
290
- elementId: element.id,
291
- issues: [],
292
- status: "error"
293
- };
283
+ return { issues: [], error: true };
294
284
  }
295
285
  const sourceData = await sourceResponse.json();
286
+ const dataLocs = [];
287
+ for (const el of sourceFile.elements) {
288
+ const dataLoc = getDataLocFromId(el.id);
289
+ if (dataLoc) {
290
+ dataLocs.push(dataLoc);
291
+ }
292
+ }
296
293
  const analyzeResponse = await fetch("/api/.uilint/analyze", {
297
294
  method: "POST",
298
295
  headers: { "Content-Type": "application/json" },
299
296
  body: JSON.stringify({
300
297
  sourceCode: sourceData.content,
301
- filePath: sourceData.relativePath || element.source.fileName,
302
- componentName: element.componentStack[0]?.name || element.tagName,
303
- componentLine: element.source.lineNumber
298
+ filePath: sourceData.relativePath || filePath,
299
+ dataLocs
304
300
  })
305
301
  });
306
302
  if (!analyzeResponse.ok) {
307
- return {
308
- elementId: element.id,
309
- issues: [],
310
- status: "error"
311
- };
303
+ return { issues: [], error: true };
312
304
  }
313
305
  const result = await analyzeResponse.json();
314
- return {
315
- elementId: element.id,
316
- issues: result.issues || [],
317
- status: "complete"
318
- };
306
+ return { issues: result.issues || [] };
319
307
  } catch {
320
- return {
321
- elementId: element.id,
322
- issues: [],
323
- status: "error"
324
- };
308
+ return { issues: [], error: true };
309
+ }
310
+ }
311
+ function distributeIssuesToElements(issues, elements, updateElementIssue, hasError) {
312
+ const dataLocToElementId = /* @__PURE__ */ new Map();
313
+ for (const el of elements) {
314
+ const dataLoc = getDataLocFromId(el.id);
315
+ if (dataLoc) {
316
+ dataLocToElementId.set(dataLoc, el.id);
317
+ }
318
+ }
319
+ const issuesByElement = /* @__PURE__ */ new Map();
320
+ for (const issue of issues) {
321
+ if (issue.dataLoc) {
322
+ const elementId = dataLocToElementId.get(issue.dataLoc);
323
+ if (elementId) {
324
+ const existing = issuesByElement.get(elementId) || [];
325
+ existing.push(issue);
326
+ issuesByElement.set(elementId, existing);
327
+ }
328
+ }
329
+ }
330
+ for (const el of elements) {
331
+ const elementIssues = issuesByElement.get(el.id) || [];
332
+ updateElementIssue(el.id, {
333
+ elementId: el.id,
334
+ issues: elementIssues,
335
+ status: hasError ? "error" : "complete"
336
+ });
325
337
  }
326
338
  }
327
339
  var useUILintStore = create()((set, get) => ({
@@ -350,6 +362,31 @@ var useUILintStore = create()((set, get) => ({
350
362
  // ============ Inspection ============
351
363
  inspectedElement: null,
352
364
  setInspectedElement: (el) => set({ inspectedElement: el }),
365
+ // ============ Manual Scan (InspectionPanel) ============
366
+ manualScanCache: /* @__PURE__ */ new Map(),
367
+ upsertManualScan: (key, patch) => set((state) => {
368
+ const next = new Map(state.manualScanCache);
369
+ const existing = next.get(key);
370
+ const base = existing ?? {
371
+ key,
372
+ status: "idle",
373
+ issues: [],
374
+ updatedAt: Date.now()
375
+ };
376
+ next.set(key, {
377
+ ...base,
378
+ ...patch,
379
+ key,
380
+ updatedAt: Date.now()
381
+ });
382
+ return { manualScanCache: next };
383
+ }),
384
+ clearManualScan: (key) => set((state) => {
385
+ if (!state.manualScanCache.has(key)) return state;
386
+ const next = new Map(state.manualScanCache);
387
+ next.delete(key);
388
+ return { manualScanCache: next };
389
+ }),
353
390
  // ============ Auto-Scan ============
354
391
  autoScanState: DEFAULT_AUTO_SCAN_STATE,
355
392
  elementIssuesCache: /* @__PURE__ */ new Map(),
@@ -419,7 +456,16 @@ var useUILintStore = create()((set, get) => ({
419
456
  });
420
457
  },
421
458
  _runScanLoop: async (elements, startIndex) => {
422
- for (let i = startIndex; i < elements.length; i++) {
459
+ const sourceFiles = groupBySourceFile(elements);
460
+ let processedElements = 0;
461
+ let skipElements = startIndex;
462
+ for (const sourceFile of sourceFiles) {
463
+ if (skipElements >= sourceFile.elements.length) {
464
+ skipElements -= sourceFile.elements.length;
465
+ processedElements += sourceFile.elements.length;
466
+ continue;
467
+ }
468
+ skipElements = 0;
423
469
  if (get().scanAborted) {
424
470
  set({
425
471
  scanLock: false,
@@ -437,16 +483,23 @@ var useUILintStore = create()((set, get) => ({
437
483
  return;
438
484
  }
439
485
  }
440
- const element = elements[i];
441
- get()._setScanState({ currentIndex: i });
442
- get().updateElementIssue(element.id, {
443
- elementId: element.id,
444
- issues: [],
445
- status: "scanning"
446
- });
486
+ get()._setScanState({ currentIndex: processedElements });
487
+ for (const el of sourceFile.elements) {
488
+ get().updateElementIssue(el.id, {
489
+ elementId: el.id,
490
+ issues: [],
491
+ status: "scanning"
492
+ });
493
+ }
447
494
  await new Promise((resolve) => requestAnimationFrame(resolve));
448
- const result = await scanElementForIssues(element);
449
- get().updateElementIssue(element.id, result);
495
+ const { issues, error } = await scanFileForIssues(sourceFile);
496
+ distributeIssuesToElements(
497
+ issues,
498
+ sourceFile.elements,
499
+ get().updateElementIssue,
500
+ error ?? false
501
+ );
502
+ processedElements += sourceFile.elements.length;
450
503
  await new Promise((resolve) => requestAnimationFrame(resolve));
451
504
  }
452
505
  set({
@@ -472,6 +525,14 @@ function useEffectiveLocatorTarget() {
472
525
  }
473
526
 
474
527
  // src/components/ui-lint/UILintProvider.tsx
528
+ import {
529
+ createContext,
530
+ useContext,
531
+ useState,
532
+ useEffect,
533
+ useCallback,
534
+ useMemo
535
+ } from "react";
475
536
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
476
537
  var UILintContext = createContext(null);
477
538
  function useUILintContext() {
@@ -522,17 +583,18 @@ function UILintProvider({
522
583
  const getLocatorTargetFromElement = useCallback(
523
584
  (element) => {
524
585
  if (element.closest("[data-ui-lint]")) return null;
525
- let source = getSourceFromDataLoc(element);
586
+ let source = null;
526
587
  let componentStack = [];
527
- if (!source) {
528
- const fiber = getFiberFromElement(element);
529
- if (fiber) {
530
- source = getDebugSource(fiber);
531
- if (!source && fiber._debugOwner) {
532
- source = getDebugSource(fiber._debugOwner);
533
- }
534
- componentStack = getComponentStack(fiber);
588
+ const fiber = getFiberFromElement(element);
589
+ if (fiber) {
590
+ source = getDebugSource(fiber);
591
+ if (!source && fiber._debugOwner) {
592
+ source = getDebugSource(fiber._debugOwner);
535
593
  }
594
+ componentStack = getComponentStack(fiber);
595
+ }
596
+ if (!source) {
597
+ source = getSourceFromDataLoc(element);
536
598
  }
537
599
  if (!source && componentStack.length === 0) return null;
538
600
  if (settings.hideNodeModules && source && isNodeModulesPath(source.fileName)) {
@@ -583,7 +645,9 @@ function UILintProvider({
583
645
  );
584
646
  const handleLocatorClick = useCallback(
585
647
  (e) => {
586
- if (!altKeyHeld || !effectiveLocatorTarget) return;
648
+ if (!altKeyHeld && !inspectedElement || !effectiveLocatorTarget) return;
649
+ const targetEl = e.target;
650
+ if (targetEl?.closest?.("[data-ui-lint]")) return;
587
651
  e.preventDefault();
588
652
  e.stopPropagation();
589
653
  let source = effectiveLocatorTarget.source;
@@ -599,16 +663,15 @@ function UILintProvider({
599
663
  componentStack: effectiveLocatorTarget.componentStack,
600
664
  rect: effectiveLocatorTarget.rect
601
665
  });
602
- setAltKeyHeld(false);
603
666
  setLocatorTarget(null);
604
667
  setLocatorStackIndex(0);
605
668
  },
606
669
  [
607
670
  altKeyHeld,
608
671
  effectiveLocatorTarget,
672
+ inspectedElement,
609
673
  locatorStackIndex,
610
674
  setInspectedElement,
611
- setAltKeyHeld,
612
675
  setLocatorTarget,
613
676
  setLocatorStackIndex
614
677
  ]
@@ -646,9 +709,7 @@ function UILintProvider({
646
709
  if (!isBrowser() || !enabled) return;
647
710
  if (!altKeyHeld && !inspectedElement) return;
648
711
  window.addEventListener("mousemove", handleMouseMove);
649
- if (altKeyHeld) {
650
- window.addEventListener("click", handleLocatorClick, true);
651
- }
712
+ window.addEventListener("click", handleLocatorClick, true);
652
713
  return () => {
653
714
  window.removeEventListener("mousemove", handleMouseMove);
654
715
  window.removeEventListener("click", handleLocatorClick, true);
@@ -735,10 +796,10 @@ function UILintUI() {
735
796
  const [components, setComponents] = useState(null);
736
797
  useEffect(() => {
737
798
  Promise.all([
738
- import("./UILintToolbar-57XAWTGK.js"),
739
- import("./InspectionPanel-FRJB6CJ6.js"),
740
- import("./LocatorOverlay-O4XZCAPC.js"),
741
- import("./ElementBadges-2WRRHFLI.js")
799
+ import("./UILintToolbar-GMZ6YSI2.js"),
800
+ import("./InspectionPanel-4OWY4FVY.js"),
801
+ import("./LocatorOverlay-JJDOKNOS.js"),
802
+ import("./ElementBadges-HFQNIIO2.js")
742
803
  ]).then(([toolbar, panel, locator, badges]) => {
743
804
  setComponents({
744
805
  Toolbar: toolbar.UILintToolbar,
@@ -779,6 +840,7 @@ export {
779
840
  FILE_COLORS,
780
841
  DEFAULT_SETTINGS,
781
842
  DATA_UILINT_ID,
843
+ useUILintStore,
782
844
  useUILintContext,
783
845
  UILintProvider
784
846
  };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  useUILintContext
4
- } from "./chunk-45MPASAN.js";
4
+ } from "./chunk-5VJ2Q2QW.js";
5
5
 
6
6
  // src/components/ui-lint/UILintToolbar.tsx
7
7
  import { useState, useRef, useEffect } from "react";