uilint-react 0.1.37 → 0.1.39

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
  import {
3
3
  buildEditorUrl,
4
4
  useUILintContext
5
- } from "./chunk-DEHJKJNT.js";
5
+ } from "./chunk-ILK73X6L.js";
6
6
 
7
7
  // src/components/ui-lint/InspectionPanel.tsx
8
8
  import { useState, useEffect, useCallback, useMemo } from "react";
@@ -199,7 +199,7 @@ function PanelHeader({
199
199
  element,
200
200
  onClose
201
201
  }) {
202
- const componentName = element.componentStack[0]?.name || element.element.tagName.toLowerCase();
202
+ const tagName = element.element.tagName.toLowerCase();
203
203
  const handleOpenInCursor = useCallback(() => {
204
204
  if (element.source) {
205
205
  const url = buildEditorUrl(element.source, "cursor");
@@ -219,11 +219,15 @@ function PanelHeader({
219
219
  },
220
220
  children: [
221
221
  /* @__PURE__ */ jsxs("div", { children: [
222
- /* @__PURE__ */ jsx("div", { style: { fontSize: "14px", fontWeight: 600 }, children: componentName }),
223
- /* @__PURE__ */ jsxs("div", { style: { fontSize: "11px", color: STYLES.textMuted }, children: [
222
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: "14px", fontWeight: 600 }, children: [
224
223
  "<",
225
- element.element.tagName.toLowerCase(),
224
+ tagName,
226
225
  ">"
226
+ ] }),
227
+ element.source && /* @__PURE__ */ jsxs("div", { style: { fontSize: "11px", color: STYLES.textMuted }, children: [
228
+ element.source.fileName.split("/").pop(),
229
+ ":",
230
+ element.source.lineNumber
227
231
  ] })
228
232
  ] }),
229
233
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px" }, children: [
@@ -357,38 +361,6 @@ function InfoTab({ element }) {
357
361
  children: element.source.fileName
358
362
  }
359
363
  ) }),
360
- element.componentStack.length > 0 && /* @__PURE__ */ jsx(Section, { title: "Component Stack", children: /* @__PURE__ */ jsxs(
361
- "div",
362
- {
363
- style: { display: "flex", flexDirection: "column", gap: "4px" },
364
- children: [
365
- element.componentStack.slice(0, 10).map((comp, index) => /* @__PURE__ */ jsx(
366
- ComponentStackItem,
367
- {
368
- component: comp,
369
- index,
370
- isFirst: index === 0
371
- },
372
- index
373
- )),
374
- element.componentStack.length > 10 && /* @__PURE__ */ jsxs(
375
- "div",
376
- {
377
- style: {
378
- fontSize: "11px",
379
- color: STYLES.textDim,
380
- marginTop: "4px"
381
- },
382
- children: [
383
- "...and ",
384
- element.componentStack.length - 10,
385
- " more"
386
- ]
387
- }
388
- )
389
- ]
390
- }
391
- ) }),
392
364
  /* @__PURE__ */ jsxs(Section, { title: "Dimensions", children: [
393
365
  /* @__PURE__ */ jsx(
394
366
  InfoRow,
@@ -867,70 +839,6 @@ function InfoRow({
867
839
  }
868
840
  );
869
841
  }
870
- function ComponentStackItem({
871
- component,
872
- index,
873
- isFirst
874
- }) {
875
- const handleClick = useCallback(() => {
876
- if (component.source) {
877
- const url = buildEditorUrl(component.source, "cursor");
878
- window.open(url, "_blank");
879
- }
880
- }, [component.source]);
881
- return /* @__PURE__ */ jsxs(
882
- "div",
883
- {
884
- style: {
885
- display: "flex",
886
- alignItems: "center",
887
- gap: "8px",
888
- padding: "6px 8px",
889
- marginLeft: index * 8,
890
- backgroundColor: isFirst ? "rgba(59, 130, 246, 0.1)" : "transparent",
891
- borderRadius: "4px",
892
- cursor: component.source ? "pointer" : "default",
893
- transition: "background-color 0.15s"
894
- },
895
- onClick: handleClick,
896
- onMouseEnter: (e) => {
897
- if (component.source) {
898
- e.currentTarget.style.backgroundColor = "rgba(59, 130, 246, 0.15)";
899
- }
900
- },
901
- onMouseLeave: (e) => {
902
- e.currentTarget.style.backgroundColor = isFirst ? "rgba(59, 130, 246, 0.1)" : "transparent";
903
- },
904
- children: [
905
- /* @__PURE__ */ jsx(
906
- "span",
907
- {
908
- style: {
909
- fontSize: "12px",
910
- fontWeight: isFirst ? 600 : 400,
911
- color: isFirst ? STYLES.accent : STYLES.textMuted
912
- },
913
- children: component.name
914
- }
915
- ),
916
- component.source && /* @__PURE__ */ jsxs(
917
- "span",
918
- {
919
- style: {
920
- fontSize: "10px",
921
- color: STYLES.textDim,
922
- fontFamily: STYLES.fontMono
923
- },
924
- children: [
925
- ":",
926
- component.source.lineNumber
927
- ]
928
- }
929
- )
930
- ]
931
- }
932
- );
933
- }
934
842
  function CursorIcon() {
935
843
  return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
936
844
  "path",
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export { AnalysisResult, ConsistencyResult, DOMSnapshot, ElementRole, ElementSna
7
7
  * Types for UILint Source Visualization
8
8
  */
9
9
  /**
10
- * Source location from React Fiber _debugSource
10
+ * Source location from data-loc attribute
11
11
  */
12
12
  interface SourceLocation {
13
13
  fileName: string;
@@ -15,22 +15,20 @@ interface SourceLocation {
15
15
  columnNumber?: number;
16
16
  }
17
17
  /**
18
- * Component information extracted from React Fiber
19
- */
20
- interface ComponentInfo {
21
- name: string;
22
- source: SourceLocation | null;
23
- }
24
- /**
25
- * A scanned DOM element with its React source information
18
+ * A scanned DOM element with its source information
19
+ * Source is always present from data-loc attribute
26
20
  */
27
21
  interface ScannedElement {
22
+ /**
23
+ * Unique per-instance ID derived from data-loc.
24
+ * Format: "loc:path:line:column#occurrence"
25
+ */
28
26
  id: string;
29
27
  element: Element;
30
28
  tagName: string;
31
29
  className: string;
32
- source: SourceLocation | null;
33
- componentStack: ComponentInfo[];
30
+ /** Source location (always present from data-loc) */
31
+ source: SourceLocation;
34
32
  rect: DOMRect;
35
33
  }
36
34
  /**
@@ -87,19 +85,15 @@ interface ElementIssue {
87
85
  */
88
86
  interface LocatorTarget {
89
87
  element: Element;
90
- source: SourceLocation | null;
91
- componentStack: ComponentInfo[];
88
+ source: SourceLocation;
92
89
  rect: DOMRect;
93
- /** Index in the component stack (0 = current element, higher = parent) */
94
- stackIndex: number;
95
90
  }
96
91
  /**
97
92
  * Element being inspected in the sidebar
98
93
  */
99
94
  interface InspectedElement {
100
95
  element: Element;
101
- source: SourceLocation | null;
102
- componentStack: ComponentInfo[];
96
+ source: SourceLocation;
103
97
  rect: DOMRect;
104
98
  /** Optional ID from auto-scan to link to cached results */
105
99
  scannedElementId?: string;
@@ -114,26 +108,20 @@ interface UILintContextValue {
114
108
  altKeyHeld: boolean;
115
109
  /** Current element under cursor when Alt is held */
116
110
  locatorTarget: LocatorTarget | null;
117
- /** Navigate to parent component in locator mode */
118
- locatorGoUp: () => void;
119
- /** Navigate to child component in locator mode */
120
- locatorGoDown: () => void;
121
111
  /** Element currently being inspected in sidebar */
122
112
  inspectedElement: InspectedElement | null;
123
113
  /** Set the element to inspect (opens sidebar) */
124
114
  setInspectedElement: (element: InspectedElement | null) => void;
125
- /** Auto-scan state */
115
+ /** Whether live scanning is enabled */
116
+ liveScanEnabled: boolean;
117
+ /** Auto-scan state (for progress tracking) */
126
118
  autoScanState: AutoScanState;
127
- /** Cache of element issues from auto-scan */
119
+ /** Cache of element issues from scanning */
128
120
  elementIssuesCache: Map<string, ElementIssue>;
129
- /** Start auto-scanning all page elements */
130
- startAutoScan: () => void;
131
- /** Pause the auto-scan */
132
- pauseAutoScan: () => void;
133
- /** Resume the auto-scan */
134
- resumeAutoScan: () => void;
135
- /** Stop and reset the auto-scan */
136
- stopAutoScan: () => void;
121
+ /** Enable live scanning */
122
+ enableLiveScan: () => void;
123
+ /** Disable live scanning */
124
+ disableLiveScan: () => void;
137
125
  }
138
126
  /**
139
127
  * Props for the UILintProvider component
@@ -181,11 +169,22 @@ declare function useUILintContext(): UILintContextValue;
181
169
  declare function UILintProvider({ children, enabled, }: UILintProviderProps): react_jsx_runtime.JSX.Element;
182
170
 
183
171
  /**
184
- * UILint Toolbar - Simple floating button with settings popover
172
+ * UILint Toolbar - Three-segment pill for live scanning
173
+ *
174
+ * Design:
175
+ * ┌────────────────────────────────────────┐
176
+ * │ [👁 Toggle] │ [Issues] │ [...] │
177
+ * └────────────────────────────────────────┘
178
+ * ⌥+Click to inspect
179
+ *
180
+ * Segments:
181
+ * 1. Toggle: Enable/disable live scanning mode
182
+ * 2. Issues: Show issue count, opens results panel
183
+ * 3. Settings: Ellipsis for settings popover
185
184
  */
186
185
 
187
186
  /**
188
- * Main Toolbar Component - Simple floating button with settings popover
187
+ * Main Toolbar Component - Three-segment pill
189
188
  */
190
189
  declare function UILintToolbar(): React$1.ReactPortal | null;
191
190
 
@@ -202,6 +201,8 @@ declare function InspectionPanel(): React$1.ReactPortal | null;
202
201
  /**
203
202
  * Locator Overlay - Shows element info when Alt/Option key is held
204
203
  * Inspired by LocatorJS for a quick "hover to find source" experience
204
+ *
205
+ * Uses data-loc attributes only (no React Fiber).
205
206
  */
206
207
 
207
208
  /**
@@ -210,45 +211,18 @@ declare function InspectionPanel(): React$1.ReactPortal | null;
210
211
  declare function LocatorOverlay(): React$1.ReactPortal | null;
211
212
 
212
213
  /**
213
- * React Fiber inspection utilities for source file visualization
214
+ * DOM utilities for UILint source file visualization
214
215
  *
215
- * In development mode, React attaches debug information to Fiber nodes:
216
- * - _debugSource: { fileName, lineNumber, columnNumber }
217
- * - _debugOwner: The component that rendered this element
218
- * - return: Parent fiber in the tree
216
+ * Uses data-loc attributes injected by the jsx-loc-plugin build transform.
217
+ * This works uniformly for both server and client components in Next.js 15+.
219
218
  */
220
219
 
221
- /** React Fiber type (simplified) */
222
- interface Fiber {
223
- tag: number;
224
- type: string | Function | null;
225
- stateNode: Element | null;
226
- return: Fiber | null;
227
- _debugSource?: {
228
- fileName: string;
229
- lineNumber: number;
230
- columnNumber?: number;
231
- } | null;
232
- _debugOwner?: Fiber | null;
233
- }
234
- /**
235
- * Get React Fiber from a DOM element
236
- * React attaches fiber via __reactFiber$xxx key
237
- */
238
- declare function getFiberFromElement(element: Element): Fiber | null;
239
- /**
240
- * Extract source location from a fiber's _debugSource
241
- */
242
- declare function getDebugSource(fiber: Fiber): SourceLocation | null;
243
- /**
244
- * Get the debug owner fiber (component that rendered this element)
245
- */
246
- declare function getDebugOwner(fiber: Fiber): Fiber | null;
247
220
  /**
248
- * Build a component stack by walking up the fiber tree
249
- * Returns an array from innermost to outermost component
221
+ * Parse source location from data-loc attribute
222
+ * Format: "path/to/file.tsx:line:column" (injected by jsx-loc-plugin)
223
+ * Also supports legacy format: "path/to/file.tsx:line"
250
224
  */
251
- declare function getComponentStack(fiber: Fiber): ComponentInfo[];
225
+ declare function getSourceFromDataLoc(element: Element): SourceLocation | null;
252
226
  /**
253
227
  * Check if a file path is from node_modules
254
228
  */
@@ -258,7 +232,7 @@ declare function isNodeModulesPath(path: string): boolean;
258
232
  */
259
233
  declare function getDisplayName(path: string): string;
260
234
  /**
261
- * Scan the DOM tree and extract React source information
235
+ * Scan the DOM tree and extract elements with data-loc attributes
262
236
  */
263
237
  declare function scanDOMForSources(root?: Element, hideNodeModules?: boolean): ScannedElement[];
264
238
  /**
@@ -366,4 +340,4 @@ declare function isJSDOM(): boolean;
366
340
  */
367
341
  declare function isNode(): boolean;
368
342
 
369
- export { type CachedSource, type ComponentInfo, ConsistencyHighlighter, DATA_UILINT_ID, DEFAULT_SETTINGS, FILE_COLORS, type InspectedElement, InspectionPanel, LocatorOverlay, type LocatorTarget, type ScannedElement, type SourceApiResponse, type SourceFile, type SourceLocation, type UILintContextValue, UILintProvider, type UILintProviderProps, type UILintSettings, UILintToolbar, buildEditorUrl, cleanupDataAttributes, cleanupDataElements, clearSourceCache, createSnapshot, fetchSource, fetchSourceWithContext, getCachedSource, getComponentStack, getDebugOwner, getDebugSource, getDisplayName, getElementById, getElementBySnapshotId, getFiberFromElement, groupBySourceFile, isBrowser, isJSDOM, isNode, isNodeModulesPath, prefetchSources, scanDOM, scanDOMForSources, updateElementRects, useUILintContext };
343
+ export { type CachedSource, ConsistencyHighlighter, DATA_UILINT_ID, DEFAULT_SETTINGS, FILE_COLORS, type InspectedElement, InspectionPanel, LocatorOverlay, type LocatorTarget, type ScannedElement, type SourceApiResponse, type SourceFile, type SourceLocation, type UILintContextValue, UILintProvider, type UILintProviderProps, type UILintSettings, UILintToolbar, buildEditorUrl, cleanupDataAttributes, cleanupDataElements, clearSourceCache, createSnapshot, fetchSource, fetchSourceWithContext, getCachedSource, getDisplayName, getElementById, getElementBySnapshotId, getSourceFromDataLoc, groupBySourceFile, isBrowser, isJSDOM, isNode, isNodeModulesPath, prefetchSources, scanDOM, scanDOMForSources, updateElementRects, useUILintContext };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  UILintToolbar
4
- } from "./chunk-EYWLUDXI.js";
4
+ } from "./chunk-4TLFW7LD.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-ITKEGCAZ.js";
12
+ } from "./chunk-NCNRCF5A.js";
13
13
  import {
14
14
  LocatorOverlay
15
- } from "./chunk-LZX53CPI.js";
15
+ } from "./chunk-7434TUMX.js";
16
16
  import {
17
17
  DATA_UILINT_ID,
18
18
  DEFAULT_SETTINGS,
@@ -20,18 +20,15 @@ import {
20
20
  UILintProvider,
21
21
  buildEditorUrl,
22
22
  cleanupDataAttributes,
23
- getComponentStack,
24
- getDebugOwner,
25
- getDebugSource,
26
23
  getDisplayName,
27
24
  getElementById,
28
- getFiberFromElement,
25
+ getSourceFromDataLoc,
29
26
  groupBySourceFile,
30
27
  isNodeModulesPath,
31
28
  scanDOMForSources,
32
29
  updateElementRects,
33
30
  useUILintContext
34
- } from "./chunk-DEHJKJNT.js";
31
+ } from "./chunk-ILK73X6L.js";
35
32
 
36
33
  // src/consistency/snapshot.ts
37
34
  var DATA_ELEMENTS_ATTR = "data-elements";
@@ -465,13 +462,10 @@ export {
465
462
  fetchSourceWithContext,
466
463
  generateStyleGuideFromStyles as generateStyleGuide,
467
464
  getCachedSource,
468
- getComponentStack,
469
- getDebugOwner,
470
- getDebugSource,
471
465
  getDisplayName,
472
466
  getElementById,
473
467
  getElementBySnapshotId,
474
- getFiberFromElement,
468
+ getSourceFromDataLoc,
475
469
  groupBySourceFile,
476
470
  isBrowser,
477
471
  isJSDOM,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uilint-react",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "React component for AI-powered UI consistency checking",
5
5
  "author": "Peter Suggate",
6
6
  "repository": {
@@ -34,7 +34,7 @@
34
34
  "node": ">=20.0.0"
35
35
  },
36
36
  "dependencies": {
37
- "uilint-core": "^0.1.37",
37
+ "uilint-core": "^0.1.39",
38
38
  "zustand": "^5.0.5"
39
39
  },
40
40
  "peerDependencies": {