uilint-react 0.2.20 → 0.2.22

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.
Files changed (84) hide show
  1. package/dist/components/ui-lint/ElementBadges.d.ts.map +1 -1
  2. package/dist/components/ui-lint/FloatingIcon.d.ts +3 -0
  3. package/dist/components/ui-lint/FloatingIcon.d.ts.map +1 -0
  4. package/dist/components/ui-lint/HeatmapOverlay.d.ts +3 -0
  5. package/dist/components/ui-lint/HeatmapOverlay.d.ts.map +1 -0
  6. package/dist/components/ui-lint/LocatorOverlay.d.ts +0 -4
  7. package/dist/components/ui-lint/LocatorOverlay.d.ts.map +1 -1
  8. package/dist/components/ui-lint/UILintUI.d.ts.map +1 -1
  9. package/dist/components/ui-lint/VisionIssueBadge.d.ts.map +1 -1
  10. package/dist/components/ui-lint/VisionIssuesPanel.d.ts.map +1 -1
  11. package/dist/components/ui-lint/command-palette/CategorySidebar.d.ts +27 -0
  12. package/dist/components/ui-lint/command-palette/CategorySidebar.d.ts.map +1 -0
  13. package/dist/components/ui-lint/command-palette/CommandPalette.d.ts +7 -0
  14. package/dist/components/ui-lint/command-palette/CommandPalette.d.ts.map +1 -0
  15. package/dist/components/ui-lint/command-palette/CommandPaletteInput.d.ts +18 -0
  16. package/dist/components/ui-lint/command-palette/CommandPaletteInput.d.ts.map +1 -0
  17. package/dist/components/ui-lint/command-palette/CommandPaletteItem.d.ts +26 -0
  18. package/dist/components/ui-lint/command-palette/CommandPaletteItem.d.ts.map +1 -0
  19. package/dist/components/ui-lint/command-palette/CommandPaletteResults.d.ts +30 -0
  20. package/dist/components/ui-lint/command-palette/CommandPaletteResults.d.ts.map +1 -0
  21. package/dist/components/ui-lint/command-palette/FilterChips.d.ts +11 -0
  22. package/dist/components/ui-lint/command-palette/FilterChips.d.ts.map +1 -0
  23. package/dist/components/ui-lint/command-palette/RuleToggleItem.d.ts +17 -0
  24. package/dist/components/ui-lint/command-palette/RuleToggleItem.d.ts.map +1 -0
  25. package/dist/components/ui-lint/command-palette/SourceCodePreview.d.ts +17 -0
  26. package/dist/components/ui-lint/command-palette/SourceCodePreview.d.ts.map +1 -0
  27. package/dist/components/ui-lint/{toolbar → command-palette}/icons.d.ts +9 -11
  28. package/dist/components/ui-lint/command-palette/icons.d.ts.map +1 -0
  29. package/dist/components/ui-lint/command-palette/index.d.ts +13 -0
  30. package/dist/components/ui-lint/command-palette/index.d.ts.map +1 -0
  31. package/dist/components/ui-lint/command-palette/types.d.ts +160 -0
  32. package/dist/components/ui-lint/command-palette/types.d.ts.map +1 -0
  33. package/dist/components/ui-lint/command-palette/use-fuzzy-search.d.ts +19 -0
  34. package/dist/components/ui-lint/command-palette/use-fuzzy-search.d.ts.map +1 -0
  35. package/dist/components/ui-lint/command-palette/use-keyboard-navigation.d.ts +10 -0
  36. package/dist/components/ui-lint/command-palette/use-keyboard-navigation.d.ts.map +1 -0
  37. package/dist/components/ui-lint/dom-utils.d.ts +13 -2
  38. package/dist/components/ui-lint/dom-utils.d.ts.map +1 -1
  39. package/dist/components/ui-lint/heatmap-colors.d.ts +24 -0
  40. package/dist/components/ui-lint/heatmap-colors.d.ts.map +1 -0
  41. package/dist/components/ui-lint/hooks/useDevToolEventHandlers.d.ts.map +1 -1
  42. package/dist/components/ui-lint/index.d.ts +3 -3
  43. package/dist/components/ui-lint/index.d.ts.map +1 -1
  44. package/dist/components/ui-lint/store.d.ts +79 -5
  45. package/dist/components/ui-lint/store.d.ts.map +1 -1
  46. package/dist/components/ui-lint/types.d.ts +7 -1
  47. package/dist/components/ui-lint/types.d.ts.map +1 -1
  48. package/dist/components/ui-lint/useDOMObserver.d.ts.map +1 -1
  49. package/dist/devtools.js +101 -95
  50. package/dist/devtools.js.map +1 -1
  51. package/dist/index.d.ts +1 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +6245 -6433
  54. package/dist/index.js.map +1 -1
  55. package/dist/scanner/vision-capture.d.ts +1 -1
  56. package/dist/scanner/vision-capture.d.ts.map +1 -1
  57. package/dist/{vision-capture-l4ZJB8M8.js → vision-capture-8mhdjw77.js} +56 -50
  58. package/dist/vision-capture-8mhdjw77.js.map +1 -0
  59. package/dist/web-component.d.ts.map +1 -1
  60. package/package.json +3 -3
  61. package/dist/components/ui-lint/InspectionPanel.d.ts +0 -6
  62. package/dist/components/ui-lint/InspectionPanel.d.ts.map +0 -1
  63. package/dist/components/ui-lint/ScanResultsPopover.d.ts +0 -6
  64. package/dist/components/ui-lint/ScanResultsPopover.d.ts.map +0 -1
  65. package/dist/components/ui-lint/scan-results/FileTree.d.ts +0 -25
  66. package/dist/components/ui-lint/scan-results/FileTree.d.ts.map +0 -1
  67. package/dist/components/ui-lint/scan-results/IssueRow.d.ts +0 -16
  68. package/dist/components/ui-lint/scan-results/IssueRow.d.ts.map +0 -1
  69. package/dist/components/ui-lint/scan-results/SearchFilter.d.ts +0 -8
  70. package/dist/components/ui-lint/scan-results/SearchFilter.d.ts.map +0 -1
  71. package/dist/components/ui-lint/toolbar/TabbedToolbar.d.ts +0 -2
  72. package/dist/components/ui-lint/toolbar/TabbedToolbar.d.ts.map +0 -1
  73. package/dist/components/ui-lint/toolbar/icons.d.ts.map +0 -1
  74. package/dist/components/ui-lint/toolbar/index.d.ts +0 -3
  75. package/dist/components/ui-lint/toolbar/index.d.ts.map +0 -1
  76. package/dist/components/ui-lint/toolbar/tabs/ConfigureTab.d.ts +0 -2
  77. package/dist/components/ui-lint/toolbar/tabs/ConfigureTab.d.ts.map +0 -1
  78. package/dist/components/ui-lint/toolbar/tabs/ESLintTab.d.ts +0 -2
  79. package/dist/components/ui-lint/toolbar/tabs/ESLintTab.d.ts.map +0 -1
  80. package/dist/components/ui-lint/toolbar/tabs/VisionTab.d.ts +0 -2
  81. package/dist/components/ui-lint/toolbar/tabs/VisionTab.d.ts.map +0 -1
  82. package/dist/components/ui-lint/toolbar/tokens.d.ts +0 -45
  83. package/dist/components/ui-lint/toolbar/tokens.d.ts.map +0 -1
  84. package/dist/vision-capture-l4ZJB8M8.js.map +0 -1
@@ -8,7 +8,7 @@
8
8
  * Element manifest entry for vision analysis
9
9
  */
10
10
  export interface ElementManifest {
11
- /** Unique ID (data-ui-lint-id if present, otherwise generated) */
11
+ /** Unique ID (data-loc if present, otherwise generated) */
12
12
  id: string;
13
13
  /** Visible text content (truncated to 100 chars) */
14
14
  text: string;
@@ -1 +1 @@
1
- {"version":3,"file":"vision-capture.d.ts","sourceRoot":"","sources":["../../src/scanner/vision-capture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,QAAQ,EACJ,SAAS,GACT,WAAW,GACX,OAAO,GACP,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,kBAAkB,GAElB,OAAO,CAAC;IACZ,qBAAqB;IACrB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB;IACvB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,sCAAsC;IACtC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,8BAA8B;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0KD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,GAAE,OAAuB,EAClC,MAAM,CAAC,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC/D,eAAe,EAAE,CAgGnB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,WAAW,EAAE,EACrB,QAAQ,EAAE,eAAe,EAAE,GAC1B,WAAW,EAAE,CA0Bf;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAqCzD;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqDlB;AAyDD;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAKxC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;;;;;;;EASA"}
1
+ {"version":3,"file":"vision-capture.d.ts","sourceRoot":"","sources":["../../src/scanner/vision-capture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,QAAQ,EACJ,SAAS,GACT,WAAW,GACX,OAAO,GACP,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,kBAAkB,GAElB,OAAO,CAAC;IACZ,qBAAqB;IACrB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB;IACvB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,sCAAsC;IACtC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,8BAA8B;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0KD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,GAAE,OAAuB,EAClC,MAAM,CAAC,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC/D,eAAe,EAAE,CA0GnB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,WAAW,EAAE,EACrB,QAAQ,EAAE,eAAe,EAAE,GAC1B,WAAW,EAAE,CA0Bf;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAqCzD;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqDlB;AAyDD;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAKxC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;;;;;;;EASA"}
@@ -1,4 +1,5 @@
1
- const E = /* @__PURE__ */ new Set([
1
+ import { DATA_UILINT_ID as g } from "./index.js";
2
+ const b = /* @__PURE__ */ new Set([
2
3
  "SCRIPT",
3
4
  "STYLE",
4
5
  "SVG",
@@ -12,8 +13,8 @@ const E = /* @__PURE__ */ new Set([
12
13
  "NOSCRIPT",
13
14
  "TEMPLATE",
14
15
  "SLOT"
15
- ]), y = 3, g = 100;
16
- function b(e) {
16
+ ]), T = 3, m = 100;
17
+ function x(e) {
17
18
  const t = e.tagName.toUpperCase(), r = e.getAttribute("role");
18
19
  if (r) return r;
19
20
  switch (t) {
@@ -68,7 +69,7 @@ function b(e) {
68
69
  function S(e) {
69
70
  const t = e.innerText?.trim();
70
71
  if (t)
71
- return t.length > g ? t.slice(0, g) + "…" : t;
72
+ return t.length > m ? t.slice(0, m) + "…" : t;
72
73
  const r = e.getAttribute("aria-label");
73
74
  if (r) return r;
74
75
  const n = e.getAttribute("title");
@@ -78,60 +79,65 @@ function S(e) {
78
79
  const i = e.getAttribute("alt");
79
80
  return i || "";
80
81
  }
81
- function T(e) {
82
+ function R(e) {
82
83
  const t = e, r = window.getComputedStyle(t);
83
84
  if (r.display === "none" || r.visibility === "hidden" || r.opacity === "0") return !1;
84
85
  const n = e.getBoundingClientRect();
85
86
  return !(n.width === 0 && n.height === 0 || n.bottom < -100 || n.top > window.innerHeight + 100 || n.right < -100 || n.left > window.innerWidth + 100);
86
87
  }
87
- function x(e, t) {
88
+ function I(e, t) {
88
89
  const r = e.left + e.width, n = e.top + e.height, o = t.x + t.width, i = t.y + t.height;
89
90
  return !(e.left > o || r < t.x || e.top > i || n < t.y);
90
91
  }
91
- function A(e = document.body, t) {
92
- const r = [], n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), i = window.scrollX || window.pageXOffset || 0, l = window.scrollY || window.pageYOffset || 0, f = e.querySelectorAll("[data-loc]");
93
- for (const c of f) {
94
- if (c.closest("[data-ui-lint]") || E.has(c.tagName) || !T(c)) continue;
95
- const a = c.getBoundingClientRect();
96
- if (t && !x(a, t)) continue;
97
- const s = c.getAttribute("data-loc");
98
- if (!s) continue;
99
- const h = n.get(s) || 0;
100
- n.set(s, h + 1);
101
- let u = o.get(s);
102
- if (u || (u = [], o.set(s, u)), u.length >= y) continue;
103
- const w = S(c), m = c.getAttribute("data-ui-lint-id") || `loc:${s}#${h}`;
92
+ function C(e = document.body, t) {
93
+ const r = [], n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), i = window.scrollX || window.pageXOffset || 0, l = window.scrollY || window.pageYOffset || 0, f = e.querySelectorAll(`[${g}]`);
94
+ for (const s of f) {
95
+ if (s.closest("[data-ui-lint]") || b.has(s.tagName) || !R(s)) continue;
96
+ const c = s.getBoundingClientRect();
97
+ if (t && !I(c, t)) continue;
98
+ let a = s.getAttribute(g);
99
+ if (!a) continue;
100
+ if (a.startsWith("loc:")) {
101
+ a = a.slice(4);
102
+ const w = a.lastIndexOf("#");
103
+ w !== -1 && (a = a.slice(0, w));
104
+ }
105
+ const h = n.get(a) || 0;
106
+ n.set(a, h + 1);
107
+ let u = o.get(a);
108
+ if (u || (u = [], o.set(a, u)), u.length >= T) continue;
109
+ const p = S(s), E = s.getAttribute(g) || `loc:${a}#${h}`;
104
110
  let d;
105
111
  t ? d = {
106
- x: a.x - t.x,
107
- y: a.y - t.y,
108
- width: a.width,
109
- height: a.height
112
+ x: c.x - t.x,
113
+ y: c.y - t.y,
114
+ width: c.width,
115
+ height: c.height
110
116
  } : d = {
111
- x: a.x + i,
112
- y: a.y + l,
113
- width: a.width,
114
- height: a.height
117
+ x: c.x + i,
118
+ y: c.y + l,
119
+ width: c.width,
120
+ height: c.height
115
121
  };
116
- const p = {
117
- id: m,
118
- text: w,
119
- dataLoc: s,
122
+ const y = {
123
+ id: E,
124
+ text: p,
125
+ dataLoc: a,
120
126
  rect: d,
121
- tagName: c.tagName.toLowerCase(),
122
- role: b(c)
127
+ tagName: s.tagName.toLowerCase(),
128
+ role: x(s)
123
129
  };
124
- u.push(p);
130
+ u.push(y);
125
131
  }
126
- for (const [c, a] of o) {
127
- const s = n.get(c) || a.length;
128
- a.forEach((h, u) => {
129
- u === 0 && s > a.length && (h.instanceCount = s), r.push(h);
132
+ for (const [s, c] of o) {
133
+ const a = n.get(s) || c.length;
134
+ c.forEach((h, u) => {
135
+ u === 0 && a > c.length && (h.instanceCount = a), r.push(h);
130
136
  });
131
137
  }
132
138
  return r;
133
139
  }
134
- async function C() {
140
+ async function O() {
135
141
  const e = await import("./index-L3Zm-CoX.js").catch(() => null);
136
142
  if (!e)
137
143
  throw new Error(
@@ -151,7 +157,7 @@ async function C() {
151
157
  );
152
158
  }
153
159
  }
154
- async function L(e) {
160
+ async function N(e) {
155
161
  const t = await import("./index-L3Zm-CoX.js").catch(() => null);
156
162
  if (!t)
157
163
  throw new Error(
@@ -168,7 +174,7 @@ async function L(e) {
168
174
  width: e.width,
169
175
  height: e.height
170
176
  };
171
- return await R(r, i);
177
+ return await A(r, i);
172
178
  } catch (r) {
173
179
  const o = (r instanceof Error ? r : new Error(String(r))).message || "Unknown error", i = /tainted|cross[- ]origin|CORS|security/i.test(o) ? " Hint: this is often caused by cross-origin images/fonts; try removing external images or ensuring they allow CORS." : "";
174
180
  throw new Error(
@@ -176,7 +182,7 @@ async function L(e) {
176
182
  );
177
183
  }
178
184
  }
179
- async function R(e, t) {
185
+ async function A(e, t) {
180
186
  return new Promise((r, n) => {
181
187
  const o = new Image();
182
188
  o.onload = () => {
@@ -213,17 +219,17 @@ async function R(e, t) {
213
219
  }, o.src = e;
214
220
  });
215
221
  }
216
- function I() {
222
+ function P() {
217
223
  return window.location.pathname.replace(/\/+$/, "") || "/";
218
224
  }
219
- function O() {
225
+ function H() {
220
226
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
221
227
  }
222
228
  export {
223
- C as captureScreenshot,
224
- L as captureScreenshotRegion,
225
- A as collectElementManifest,
226
- O as generateTimestamp,
227
- I as getCurrentRoute
229
+ O as captureScreenshot,
230
+ N as captureScreenshotRegion,
231
+ C as collectElementManifest,
232
+ H as generateTimestamp,
233
+ P as getCurrentRoute
228
234
  };
229
- //# sourceMappingURL=vision-capture-l4ZJB8M8.js.map
235
+ //# sourceMappingURL=vision-capture-8mhdjw77.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision-capture-8mhdjw77.js","sources":["../src/scanner/vision-capture.ts"],"sourcesContent":["/**\n * Vision Capture Module\n *\n * Provides screenshot capture and element manifest building for vision-based analysis.\n * Uses html-to-image for DOM-to-image capture.\n */\n\nimport { DATA_UILINT_ID } from \"../components/ui-lint/types\";\n\n/**\n * Element manifest entry for vision analysis\n */\nexport interface ElementManifest {\n /** Unique ID (data-loc if present, otherwise generated) */\n id: string;\n /** Visible text content (truncated to 100 chars) */\n text: string;\n /** data-loc value: \"path:line:column\" */\n dataLoc: string;\n /** Bounding rectangle */\n rect: { x: number; y: number; width: number; height: number };\n /** HTML tag name */\n tagName: string;\n /** Inferred semantic role (button, heading, link, etc.) */\n role?: string;\n /** Total instances with same dataLoc (if deduplicated) */\n instanceCount?: number;\n}\n\n/**\n * Vision analysis issue from the LLM\n */\nexport interface VisionIssue {\n /** Text of the element this issue refers to */\n elementText: string;\n /** Issue description */\n message: string;\n /** Issue category */\n category:\n | \"spacing\"\n | \"alignment\"\n | \"color\"\n | \"typography\"\n | \"layout\"\n | \"contrast\"\n | \"visual-hierarchy\"\n // backward/defensive (older payloads or custom models)\n | \"other\";\n /** Severity level */\n severity: \"error\" | \"warning\" | \"info\";\n /** Matched dataLoc from manifest (filled in after text matching) */\n dataLoc?: string;\n /** Matched element ID (filled in after text matching) */\n elementId?: string;\n}\n\n/**\n * Vision analysis result\n */\nexport interface VisionAnalysisResult {\n /** Route/path that was analyzed */\n route: string;\n /** Timestamp of capture */\n timestamp: number;\n /** Screenshot as base64 data URL */\n screenshotDataUrl?: string;\n /** Element manifest */\n manifest: ElementManifest[];\n /** Issues found by vision analysis */\n issues: VisionIssue[];\n /** Analysis duration in ms */\n analysisTime: number;\n /** Error message if analysis failed */\n error?: string;\n}\n\n/**\n * Tags to skip when collecting manifest\n */\nconst SKIP_TAGS = new Set([\n \"SCRIPT\",\n \"STYLE\",\n \"SVG\",\n \"PATH\",\n \"CIRCLE\",\n \"RECT\",\n \"LINE\",\n \"POLYGON\",\n \"POLYLINE\",\n \"ELLIPSE\",\n \"NOSCRIPT\",\n \"TEMPLATE\",\n \"SLOT\",\n]);\n\n/**\n * Max instances per dataLoc to include in manifest (for deduplication)\n */\nconst MAX_INSTANCES_PER_DATALOC = 3;\n\n/**\n * Max text length for element text\n */\nconst MAX_TEXT_LENGTH = 100;\n\n/**\n * Infer semantic role from element\n */\nfunction inferRole(element: Element): string | undefined {\n const tagName = element.tagName.toUpperCase();\n\n // Check explicit role attribute\n const explicitRole = element.getAttribute(\"role\");\n if (explicitRole) return explicitRole;\n\n // Infer from tag name\n switch (tagName) {\n case \"BUTTON\":\n return \"button\";\n case \"A\":\n return \"link\";\n case \"H1\":\n case \"H2\":\n case \"H3\":\n case \"H4\":\n case \"H5\":\n case \"H6\":\n return \"heading\";\n case \"INPUT\": {\n const type = (element as HTMLInputElement).type;\n if (type === \"submit\" || type === \"button\") return \"button\";\n if (type === \"checkbox\") return \"checkbox\";\n if (type === \"radio\") return \"radio\";\n return \"textbox\";\n }\n case \"TEXTAREA\":\n return \"textbox\";\n case \"SELECT\":\n return \"combobox\";\n case \"IMG\":\n return \"img\";\n case \"NAV\":\n return \"navigation\";\n case \"MAIN\":\n return \"main\";\n case \"HEADER\":\n return \"banner\";\n case \"FOOTER\":\n return \"contentinfo\";\n case \"ASIDE\":\n return \"complementary\";\n case \"ARTICLE\":\n return \"article\";\n case \"SECTION\":\n return \"region\";\n case \"FORM\":\n return \"form\";\n case \"TABLE\":\n return \"table\";\n case \"UL\":\n case \"OL\":\n return \"list\";\n case \"LI\":\n return \"listitem\";\n default:\n return undefined;\n }\n}\n\n/**\n * Get visible text content from element\n */\nfunction getVisibleText(element: Element): string {\n // Try innerText first (respects visibility)\n const innerText = (element as HTMLElement).innerText?.trim();\n if (innerText) {\n return innerText.length > MAX_TEXT_LENGTH\n ? innerText.slice(0, MAX_TEXT_LENGTH) + \"…\"\n : innerText;\n }\n\n // Fallback to aria-label\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n // Fallback to title\n const title = element.getAttribute(\"title\");\n if (title) return title;\n\n // Fallback to placeholder for inputs\n const placeholder = element.getAttribute(\"placeholder\");\n if (placeholder) return placeholder;\n\n // Fallback to alt for images\n const alt = element.getAttribute(\"alt\");\n if (alt) return alt;\n\n return \"\";\n}\n\n/**\n * Check if element is visible\n */\nfunction isElementVisible(element: Element): boolean {\n const htmlEl = element as HTMLElement;\n\n // Check computed style\n const style = window.getComputedStyle(htmlEl);\n if (style.display === \"none\") return false;\n if (style.visibility === \"hidden\") return false;\n if (style.opacity === \"0\") return false;\n\n // Check dimensions\n const rect = element.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) return false;\n\n // Check if in viewport (with some margin)\n if (rect.bottom < -100 || rect.top > window.innerHeight + 100) return false;\n if (rect.right < -100 || rect.left > window.innerWidth + 100) return false;\n\n return true;\n}\n\n/**\n * Check if element rect intersects with a region\n */\nfunction rectIntersectsRegion(\n rect: DOMRect,\n region: { x: number; y: number; width: number; height: number }\n): boolean {\n const rectRight = rect.left + rect.width;\n const rectBottom = rect.top + rect.height;\n const regionRight = region.x + region.width;\n const regionBottom = region.y + region.height;\n\n // Check if rectangles overlap\n return !(\n rect.left > regionRight ||\n rectRight < region.x ||\n rect.top > regionBottom ||\n rectBottom < region.y\n );\n}\n\n/**\n * Collect element manifest from DOM\n *\n * Scans all elements with data-loc attributes and builds a manifest\n * with deduplication for repeated elements (e.g., list items).\n *\n * @param container - Container element to scan (default: document.body)\n * @param region - Optional region to filter elements (only include elements within this region, in viewport coordinates)\n */\nexport function collectElementManifest(\n container: Element = document.body,\n region?: { x: number; y: number; width: number; height: number }\n): ElementManifest[] {\n const manifest: ElementManifest[] = [];\n const dataLocCounts = new Map<string, number>();\n const dataLocInstances = new Map<string, ElementManifest[]>();\n\n // Get scroll offset for converting viewport coords to document coords\n const scrollX = window.scrollX || window.pageXOffset || 0;\n const scrollY = window.scrollY || window.pageYOffset || 0;\n\n // Find all elements with data-loc\n const elements = container.querySelectorAll(`[${DATA_UILINT_ID}]`);\n\n for (const element of elements) {\n // Skip UILint's own elements\n if (element.closest(\"[data-ui-lint]\")) continue;\n\n // Skip certain tag types\n if (SKIP_TAGS.has(element.tagName)) continue;\n\n // Skip hidden elements\n if (!isElementVisible(element)) continue;\n\n // Skip elements outside the region (if region is specified)\n // Region is in viewport coordinates, same as getBoundingClientRect\n const rect = element.getBoundingClientRect();\n if (region && !rectIntersectsRegion(rect, region)) continue;\n\n // Get the data-loc which contains source location\n // Format is either \"path:line:column\" (from jsx plugin) or \"loc:path:line:column#N\" (runtime ID)\n let dataLoc = element.getAttribute(DATA_UILINT_ID);\n if (!dataLoc) continue;\n\n // Normalize runtime ID format to source location format\n if (dataLoc.startsWith(\"loc:\")) {\n dataLoc = dataLoc.slice(4);\n const hashIndex = dataLoc.lastIndexOf(\"#\");\n if (hashIndex !== -1) {\n dataLoc = dataLoc.slice(0, hashIndex);\n }\n }\n\n // Track instance count\n const currentCount = dataLocCounts.get(dataLoc) || 0;\n dataLocCounts.set(dataLoc, currentCount + 1);\n\n // Get or create instances array for this dataLoc\n let instances = dataLocInstances.get(dataLoc);\n if (!instances) {\n instances = [];\n dataLocInstances.set(dataLoc, instances);\n }\n\n // Only collect up to MAX_INSTANCES_PER_DATALOC\n if (instances.length >= MAX_INSTANCES_PER_DATALOC) continue;\n\n const text = getVisibleText(element);\n const id =\n element.getAttribute(DATA_UILINT_ID) || `loc:${dataLoc}#${currentCount}`;\n\n // For region captures, store coordinates relative to the region origin\n // For full page captures, store document coordinates (viewport + scroll)\n let entryRect: { x: number; y: number; width: number; height: number };\n if (region) {\n // Relative to the region's top-left corner\n entryRect = {\n x: rect.x - region.x,\n y: rect.y - region.y,\n width: rect.width,\n height: rect.height,\n };\n } else {\n // Document coordinates for full page\n entryRect = {\n x: rect.x + scrollX,\n y: rect.y + scrollY,\n width: rect.width,\n height: rect.height,\n };\n }\n\n const entry: ElementManifest = {\n id,\n text,\n dataLoc,\n rect: entryRect,\n tagName: element.tagName.toLowerCase(),\n role: inferRole(element),\n };\n\n instances.push(entry);\n }\n\n // Build final manifest with instance counts\n for (const [dataLoc, instances] of dataLocInstances) {\n const totalCount = dataLocCounts.get(dataLoc) || instances.length;\n\n instances.forEach((entry, index) => {\n // Add instance count to first entry if there are more than shown\n if (index === 0 && totalCount > instances.length) {\n entry.instanceCount = totalCount;\n }\n manifest.push(entry);\n });\n }\n\n return manifest;\n}\n\n/**\n * Match vision issues to manifest entries by element text\n *\n * The LLM returns issues with elementText; we need to map back to dataLoc\n */\nexport function matchIssuesToManifest(\n issues: VisionIssue[],\n manifest: ElementManifest[]\n): VisionIssue[] {\n return issues.map((issue) => {\n // Try exact match first\n let match = manifest.find(\n (m) => m.text.toLowerCase() === issue.elementText.toLowerCase()\n );\n\n // Try partial match (text starts with or contains)\n if (!match) {\n match = manifest.find(\n (m) =>\n m.text.toLowerCase().includes(issue.elementText.toLowerCase()) ||\n issue.elementText.toLowerCase().includes(m.text.toLowerCase())\n );\n }\n\n if (match) {\n return {\n ...issue,\n dataLoc: match.dataLoc,\n elementId: match.id,\n };\n }\n\n return issue;\n });\n}\n\n/**\n * Capture screenshot of the current page\n *\n * Uses html-to-image library for DOM-to-image capture.\n * Falls back to canvas if html-to-image is not available.\n */\nexport async function captureScreenshot(): Promise<string> {\n // Try to use html-to-image if available\n const htmlToImage = await import(\"html-to-image\").catch(() => null);\n\n if (!htmlToImage) {\n throw new Error(\n \"Screenshot capture unavailable: `html-to-image` failed to load (check the uilint-react bundle/deps)\"\n );\n }\n\n try {\n const dataUrl = await htmlToImage.toPng(document.body, {\n // Keep file size down for WS transport\n pixelRatio: 1,\n cacheBust: true,\n filter: (node) => {\n // Skip UILint overlay elements\n if (node instanceof Element && node.closest(\"[data-ui-lint]\")) {\n return false;\n }\n return true;\n },\n });\n return dataUrl;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n const msg = err.message || \"Unknown error\";\n\n // Common failure modes: cross-origin images/fonts taint the canvas.\n const hint = /tainted|cross[- ]origin|CORS|security/i.test(msg)\n ? \" Hint: this is often caused by cross-origin images/fonts; try removing external images or ensuring they allow CORS.\"\n : \"\";\n\n throw new Error(\n `Screenshot capture failed (html-to-image): ${msg}.${hint}`\n );\n }\n}\n\n/**\n * Capture screenshot of a specific region of the page\n *\n * @param region - The region to capture (in viewport coordinates)\n */\nexport async function captureScreenshotRegion(region: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): Promise<string> {\n // Try to use html-to-image if available\n const htmlToImage = await import(\"html-to-image\").catch(() => null);\n\n if (!htmlToImage) {\n throw new Error(\n \"Screenshot capture unavailable: `html-to-image` failed to load (check the uilint-react bundle/deps)\"\n );\n }\n\n try {\n // Capture the full page first, then crop to the region\n // This is more reliable than trying to capture a specific element\n const dataUrl = await htmlToImage.toPng(document.body, {\n pixelRatio: 1,\n cacheBust: true,\n filter: (node) => {\n // Skip UILint overlay elements\n if (node instanceof Element && node.closest(\"[data-ui-lint]\")) {\n return false;\n }\n return true;\n },\n });\n\n // The region coordinates are viewport-relative (from mouse events),\n // but html-to-image captures the full document including scrolled content.\n // We need to add the scroll offset to get the correct position in the full document.\n const scrollX = window.scrollX || window.pageXOffset || 0;\n const scrollY = window.scrollY || window.pageYOffset || 0;\n\n const documentRegion = {\n x: region.x + scrollX,\n y: region.y + scrollY,\n width: region.width,\n height: region.height,\n };\n\n // Crop the image to the selected region using canvas\n return await cropImageToRegion(dataUrl, documentRegion);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n const msg = err.message || \"Unknown error\";\n\n // Common failure modes: cross-origin images/fonts taint the canvas.\n const hint = /tainted|cross[- ]origin|CORS|security/i.test(msg)\n ? \" Hint: this is often caused by cross-origin images/fonts; try removing external images or ensuring they allow CORS.\"\n : \"\";\n\n throw new Error(\n `Screenshot capture failed (html-to-image): ${msg}.${hint}`\n );\n }\n}\n\n/**\n * Crop an image data URL to a specific region\n */\nasync function cropImageToRegion(\n dataUrl: string,\n region: { x: number; y: number; width: number; height: number }\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n try {\n // Create a canvas for cropping\n const canvas = document.createElement(\"canvas\");\n canvas.width = region.width;\n canvas.height = region.height;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n\n // Draw the cropped region\n ctx.drawImage(\n img,\n region.x,\n region.y,\n region.width,\n region.height,\n 0,\n 0,\n region.width,\n region.height\n );\n\n // Convert to data URL\n const croppedDataUrl = canvas.toDataURL(\"image/png\");\n resolve(croppedDataUrl);\n } catch (error) {\n reject(\n new Error(\n `Failed to crop image: ${\n error instanceof Error ? error.message : String(error)\n }`\n )\n );\n }\n };\n img.onerror = () => {\n reject(new Error(\"Failed to load image for cropping\"));\n };\n img.src = dataUrl;\n });\n}\n\n/**\n * Get current route from URL\n */\nexport function getCurrentRoute(): string {\n const path = window.location.pathname;\n // Normalize: remove trailing slashes, handle index\n const normalized = path.replace(/\\/+$/, \"\") || \"/\";\n return normalized;\n}\n\n/**\n * Generate filename-safe timestamp\n */\nexport function generateTimestamp(): string {\n return new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\n/**\n * Build vision analysis request payload\n */\nexport function buildVisionAnalysisPayload(options: {\n screenshotDataUrl?: string;\n manifest: ElementManifest[];\n route: string;\n /** Screenshot filename saved under `.uilint/screenshots` (e.g. uilint-...png) */\n screenshotFile?: string;\n}) {\n return {\n type: \"vision:analyze\" as const,\n route: options.route,\n timestamp: Date.now(),\n screenshot: options.screenshotDataUrl,\n screenshotFile: options.screenshotFile,\n manifest: options.manifest,\n };\n}\n"],"names":["SKIP_TAGS","MAX_INSTANCES_PER_DATALOC","MAX_TEXT_LENGTH","inferRole","element","tagName","explicitRole","type","getVisibleText","innerText","ariaLabel","title","placeholder","alt","isElementVisible","htmlEl","style","rect","rectIntersectsRegion","region","rectRight","rectBottom","regionRight","regionBottom","collectElementManifest","container","manifest","dataLocCounts","dataLocInstances","scrollX","scrollY","elements","DATA_UILINT_ID","dataLoc","hashIndex","currentCount","instances","text","id","entryRect","entry","totalCount","index","captureScreenshot","htmlToImage","node","error","msg","hint","captureScreenshotRegion","dataUrl","documentRegion","cropImageToRegion","resolve","reject","img","canvas","ctx","croppedDataUrl","getCurrentRoute","generateTimestamp"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"web-component.d.ts","sourceRoot":"","sources":["../src/web-component.tsx"],"names":[],"mappings":"AA6FA,wBAAgB,2BAA2B,SAqJ1C"}
1
+ {"version":3,"file":"web-component.d.ts","sourceRoot":"","sources":["../src/web-component.tsx"],"names":[],"mappings":"AA+FA,wBAAgB,2BAA2B,SAwJ1C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uilint-react",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
4
4
  "description": "React component for AI-powered UI consistency checking",
5
5
  "author": "Peter Suggate",
6
6
  "repository": {
@@ -47,8 +47,8 @@
47
47
  "react-use-measure": "^2.1.7",
48
48
  "tw-animate-css": "^1.4.0",
49
49
  "zustand": "^5.0.10",
50
- "uilint-core": "0.2.20",
51
- "uilint-eslint": "0.2.20"
50
+ "uilint-core": "0.2.22",
51
+ "uilint-eslint": "0.2.22"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "react": "^19.0.0",
@@ -1,6 +0,0 @@
1
- import { default as React } from 'react';
2
- /**
3
- * Main Inspection Panel Component - Floating Popover
4
- */
5
- export declare function InspectionPanel(): React.ReactPortal | null;
6
- //# sourceMappingURL=InspectionPanel.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"InspectionPanel.d.ts","sourceRoot":"","sources":["../../../src/components/ui-lint/InspectionPanel.tsx"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAMN,MAAM,OAAO,CAAC;AA+Bf;;GAEG;AACH,wBAAgB,eAAe,6BAoX9B"}
@@ -1,6 +0,0 @@
1
- interface ScanResultsPopoverProps {
2
- onClose: () => void;
3
- }
4
- export declare function ScanResultsPopover({ onClose }: ScanResultsPopoverProps): import("react/jsx-runtime").JSX.Element;
5
- export {};
6
- //# sourceMappingURL=ScanResultsPopover.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ScanResultsPopover.d.ts","sourceRoot":"","sources":["../../../src/components/ui-lint/ScanResultsPopover.tsx"],"names":[],"mappings":"AAqDA,UAAU,uBAAuB;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,EAAE,uBAAuB,2CA8VtE"}
@@ -1,25 +0,0 @@
1
- import { ScannedElement, ESLintIssue } from '../types';
2
- export interface FileWithIssues {
3
- path: string;
4
- displayName: string;
5
- disambiguatedName: string;
6
- issueCount: number;
7
- elementsWithIssues: ElementWithIssues[];
8
- fileLevelIssues: ESLintIssue[];
9
- }
10
- export interface ElementWithIssues {
11
- element: ScannedElement;
12
- issueCount: number;
13
- ruleIds: string[];
14
- }
15
- interface FileTreeProps {
16
- files: FileWithIssues[];
17
- expandedFiles: Set<string>;
18
- onToggleFile: (path: string) => void;
19
- onElementHover: (element: ScannedElement | null) => void;
20
- onElementClick: (element: ScannedElement) => void;
21
- onFileLevelIssueClick: (filePath: string, issue: ESLintIssue) => void;
22
- }
23
- export declare function FileTree({ files, expandedFiles, onToggleFile, onElementHover, onElementClick, onFileLevelIssueClick, }: FileTreeProps): import("react/jsx-runtime").JSX.Element | null;
24
- export {};
25
- //# sourceMappingURL=FileTree.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FileTree.d.ts","sourceRoot":"","sources":["../../../../src/components/ui-lint/scan-results/FileTree.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACxC,eAAe,EAAE,WAAW,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,UAAU,aAAa;IACrB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACzD,cAAc,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CACvE;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,qBAAqB,GACtB,EAAE,aAAa,kDAwFf"}
@@ -1,16 +0,0 @@
1
- import { ScannedElement, ESLintIssue } from '../types';
2
- interface IssueRowProps {
3
- element?: ScannedElement;
4
- issue?: ESLintIssue;
5
- issueCount: number;
6
- tagName: string;
7
- lineNumber: number;
8
- columnNumber?: number;
9
- onHover?: (element: ScannedElement | null) => void;
10
- onClick?: (element: ScannedElement) => void;
11
- onFileLevelIssueClick?: () => void;
12
- isFileLevel?: boolean;
13
- }
14
- export declare function IssueRow({ element, issue, issueCount, tagName, lineNumber, columnNumber, onHover, onClick, onFileLevelIssueClick, isFileLevel, }: IssueRowProps): import("react/jsx-runtime").JSX.Element;
15
- export {};
16
- //# sourceMappingURL=IssueRow.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"IssueRow.d.ts","sourceRoot":"","sources":["../../../../src/components/ui-lint/scan-results/IssueRow.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5D,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IACnD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC5C,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,KAAK,EACL,UAAU,EACV,OAAO,EACP,UAAU,EACV,YAAY,EACZ,OAAO,EACP,OAAO,EACP,qBAAqB,EACrB,WAAmB,GACpB,EAAE,aAAa,2CA8Cf"}
@@ -1,8 +0,0 @@
1
- interface SearchFilterProps {
2
- value: string;
3
- onChange: (value: string) => void;
4
- placeholder?: string;
5
- }
6
- export declare function SearchFilter({ value, onChange, placeholder, }: SearchFilterProps): import("react/jsx-runtime").JSX.Element;
7
- export {};
8
- //# sourceMappingURL=SearchFilter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SearchFilter.d.ts","sourceRoot":"","sources":["../../../../src/components/ui-lint/scan-results/SearchFilter.tsx"],"names":[],"mappings":"AAgBA,UAAU,iBAAiB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,WAAwC,GACzC,EAAE,iBAAiB,2CA6CnB"}
@@ -1,2 +0,0 @@
1
- export declare function TabbedToolbar(): import("react/jsx-runtime").JSX.Element;
2
- //# sourceMappingURL=TabbedToolbar.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TabbedToolbar.d.ts","sourceRoot":"","sources":["../../../../src/components/ui-lint/toolbar/TabbedToolbar.tsx"],"names":[],"mappings":"AAWA,wBAAgB,aAAa,4CAwE5B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../../../src/components/ui-lint/toolbar/icons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,SAAU,SAAQ,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC;CAAG;AAE5D;;GAEG;AACH,eAAO,MAAM,KAAK;iBACH,SAAS;sBAgBJ,SAAS;mBAgBZ,SAAS;2BAeD,SAAS;yBAiBX,SAAS;eAenB,SAAS;oBAgBJ,SAAS;kBAmBX,SAAS;oBAmBP,SAAS;kBAgBX,SAAS;qBAgBN,SAAS;kBAgBZ,SAAS;oBAeP,SAAS;kBAiBX,SAAS;qBAiBN,SAAS;uBAeP,SAAS;kBAkBd,SAAS;sBAkBL,SAAS;yBAgBN,SAAS;yBAiBT,SAAS;mBAgBf,SAAS;yBAiBH,SAAS;kBAehB,SAAS;CAgBxB,CAAC"}
@@ -1,3 +0,0 @@
1
- import { default as React } from 'react';
2
- export declare function UILintToolbar(): React.ReactPortal | null;
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui-lint/toolbar/index.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAQxE,wBAAgB,aAAa,6BA6J5B"}
@@ -1,2 +0,0 @@
1
- export declare function ConfigureTab(): import("react/jsx-runtime").JSX.Element;
2
- //# sourceMappingURL=ConfigureTab.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ConfigureTab.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui-lint/toolbar/tabs/ConfigureTab.tsx"],"names":[],"mappings":"AAgFA,wBAAgB,YAAY,4CA+I3B"}
@@ -1,2 +0,0 @@
1
- export declare function ESLintTab(): import("react/jsx-runtime").JSX.Element;
2
- //# sourceMappingURL=ESLintTab.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ESLintTab.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui-lint/toolbar/tabs/ESLintTab.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,SAAS,4CAuOxB"}
@@ -1,2 +0,0 @@
1
- export declare function VisionTab(): import("react/jsx-runtime").JSX.Element;
2
- //# sourceMappingURL=VisionTab.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"VisionTab.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui-lint/toolbar/tabs/VisionTab.tsx"],"names":[],"mappings":"AA6fA,wBAAgB,SAAS,4CA2HxB"}
@@ -1,45 +0,0 @@
1
- /**
2
- * Design tokens for toolbar components
3
- *
4
- * Uses CSS custom properties for theming support.
5
- * Variables are defined in globals.css and support light/dark modes.
6
- */
7
- export declare const TOKENS: {
8
- readonly bgBase: "var(--uilint-backdrop)";
9
- readonly bgElevated: "var(--uilint-background-elevated)";
10
- readonly bgHover: "var(--uilint-hover)";
11
- readonly bgActive: "var(--uilint-active)";
12
- readonly border: "var(--uilint-border)";
13
- readonly borderFocus: "var(--uilint-border-focus)";
14
- readonly textPrimary: "var(--uilint-text-primary)";
15
- readonly textSecondary: "var(--uilint-text-secondary)";
16
- readonly textMuted: "var(--uilint-text-muted)";
17
- readonly textDisabled: "var(--uilint-text-disabled)";
18
- readonly accent: "var(--uilint-accent)";
19
- readonly success: "var(--uilint-success)";
20
- readonly warning: "var(--uilint-warning)";
21
- readonly error: "var(--uilint-error)";
22
- readonly fontFamily: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif";
23
- readonly fontMono: "\"SF Mono\", Monaco, \"Cascadia Code\", monospace";
24
- readonly pillHeight: "40px";
25
- readonly pillRadius: "20px";
26
- readonly buttonMinWidth: "40px";
27
- readonly blur: "blur(16px)";
28
- readonly shadowMd: "var(--uilint-shadow)";
29
- readonly shadowGlow: (color: string) => string;
30
- readonly transitionFast: "150ms cubic-bezier(0.4, 0, 0.2, 1)";
31
- readonly transitionBase: "200ms cubic-bezier(0.4, 0, 0.2, 1)";
32
- readonly transitionSlow: "300ms cubic-bezier(0.4, 0, 0.2, 1)";
33
- };
34
- /**
35
- * Status badge colors using CSS variables
36
- */
37
- export declare const BADGE_COLORS: {
38
- readonly success: "var(--uilint-success)";
39
- readonly successBg: "var(--uilint-success-bg)";
40
- readonly warning: "var(--uilint-warning)";
41
- readonly warningBg: "var(--uilint-warning-bg)";
42
- readonly error: "var(--uilint-error)";
43
- readonly errorBg: "var(--uilint-error-bg)";
44
- };
45
- //# sourceMappingURL=tokens.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/components/ui-lint/toolbar/tokens.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;iCA6BG,MAAM;;;;CAKlB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;CAOf,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"vision-capture-l4ZJB8M8.js","sources":["../src/scanner/vision-capture.ts"],"sourcesContent":["/**\n * Vision Capture Module\n *\n * Provides screenshot capture and element manifest building for vision-based analysis.\n * Uses html-to-image for DOM-to-image capture.\n */\n\n/**\n * Element manifest entry for vision analysis\n */\nexport interface ElementManifest {\n /** Unique ID (data-ui-lint-id if present, otherwise generated) */\n id: string;\n /** Visible text content (truncated to 100 chars) */\n text: string;\n /** data-loc value: \"path:line:column\" */\n dataLoc: string;\n /** Bounding rectangle */\n rect: { x: number; y: number; width: number; height: number };\n /** HTML tag name */\n tagName: string;\n /** Inferred semantic role (button, heading, link, etc.) */\n role?: string;\n /** Total instances with same dataLoc (if deduplicated) */\n instanceCount?: number;\n}\n\n/**\n * Vision analysis issue from the LLM\n */\nexport interface VisionIssue {\n /** Text of the element this issue refers to */\n elementText: string;\n /** Issue description */\n message: string;\n /** Issue category */\n category:\n | \"spacing\"\n | \"alignment\"\n | \"color\"\n | \"typography\"\n | \"layout\"\n | \"contrast\"\n | \"visual-hierarchy\"\n // backward/defensive (older payloads or custom models)\n | \"other\";\n /** Severity level */\n severity: \"error\" | \"warning\" | \"info\";\n /** Matched dataLoc from manifest (filled in after text matching) */\n dataLoc?: string;\n /** Matched element ID (filled in after text matching) */\n elementId?: string;\n}\n\n/**\n * Vision analysis result\n */\nexport interface VisionAnalysisResult {\n /** Route/path that was analyzed */\n route: string;\n /** Timestamp of capture */\n timestamp: number;\n /** Screenshot as base64 data URL */\n screenshotDataUrl?: string;\n /** Element manifest */\n manifest: ElementManifest[];\n /** Issues found by vision analysis */\n issues: VisionIssue[];\n /** Analysis duration in ms */\n analysisTime: number;\n /** Error message if analysis failed */\n error?: string;\n}\n\n/**\n * Tags to skip when collecting manifest\n */\nconst SKIP_TAGS = new Set([\n \"SCRIPT\",\n \"STYLE\",\n \"SVG\",\n \"PATH\",\n \"CIRCLE\",\n \"RECT\",\n \"LINE\",\n \"POLYGON\",\n \"POLYLINE\",\n \"ELLIPSE\",\n \"NOSCRIPT\",\n \"TEMPLATE\",\n \"SLOT\",\n]);\n\n/**\n * Max instances per dataLoc to include in manifest (for deduplication)\n */\nconst MAX_INSTANCES_PER_DATALOC = 3;\n\n/**\n * Max text length for element text\n */\nconst MAX_TEXT_LENGTH = 100;\n\n/**\n * Infer semantic role from element\n */\nfunction inferRole(element: Element): string | undefined {\n const tagName = element.tagName.toUpperCase();\n\n // Check explicit role attribute\n const explicitRole = element.getAttribute(\"role\");\n if (explicitRole) return explicitRole;\n\n // Infer from tag name\n switch (tagName) {\n case \"BUTTON\":\n return \"button\";\n case \"A\":\n return \"link\";\n case \"H1\":\n case \"H2\":\n case \"H3\":\n case \"H4\":\n case \"H5\":\n case \"H6\":\n return \"heading\";\n case \"INPUT\": {\n const type = (element as HTMLInputElement).type;\n if (type === \"submit\" || type === \"button\") return \"button\";\n if (type === \"checkbox\") return \"checkbox\";\n if (type === \"radio\") return \"radio\";\n return \"textbox\";\n }\n case \"TEXTAREA\":\n return \"textbox\";\n case \"SELECT\":\n return \"combobox\";\n case \"IMG\":\n return \"img\";\n case \"NAV\":\n return \"navigation\";\n case \"MAIN\":\n return \"main\";\n case \"HEADER\":\n return \"banner\";\n case \"FOOTER\":\n return \"contentinfo\";\n case \"ASIDE\":\n return \"complementary\";\n case \"ARTICLE\":\n return \"article\";\n case \"SECTION\":\n return \"region\";\n case \"FORM\":\n return \"form\";\n case \"TABLE\":\n return \"table\";\n case \"UL\":\n case \"OL\":\n return \"list\";\n case \"LI\":\n return \"listitem\";\n default:\n return undefined;\n }\n}\n\n/**\n * Get visible text content from element\n */\nfunction getVisibleText(element: Element): string {\n // Try innerText first (respects visibility)\n const innerText = (element as HTMLElement).innerText?.trim();\n if (innerText) {\n return innerText.length > MAX_TEXT_LENGTH\n ? innerText.slice(0, MAX_TEXT_LENGTH) + \"…\"\n : innerText;\n }\n\n // Fallback to aria-label\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n // Fallback to title\n const title = element.getAttribute(\"title\");\n if (title) return title;\n\n // Fallback to placeholder for inputs\n const placeholder = element.getAttribute(\"placeholder\");\n if (placeholder) return placeholder;\n\n // Fallback to alt for images\n const alt = element.getAttribute(\"alt\");\n if (alt) return alt;\n\n return \"\";\n}\n\n/**\n * Check if element is visible\n */\nfunction isElementVisible(element: Element): boolean {\n const htmlEl = element as HTMLElement;\n\n // Check computed style\n const style = window.getComputedStyle(htmlEl);\n if (style.display === \"none\") return false;\n if (style.visibility === \"hidden\") return false;\n if (style.opacity === \"0\") return false;\n\n // Check dimensions\n const rect = element.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) return false;\n\n // Check if in viewport (with some margin)\n if (rect.bottom < -100 || rect.top > window.innerHeight + 100) return false;\n if (rect.right < -100 || rect.left > window.innerWidth + 100) return false;\n\n return true;\n}\n\n/**\n * Check if element rect intersects with a region\n */\nfunction rectIntersectsRegion(\n rect: DOMRect,\n region: { x: number; y: number; width: number; height: number }\n): boolean {\n const rectRight = rect.left + rect.width;\n const rectBottom = rect.top + rect.height;\n const regionRight = region.x + region.width;\n const regionBottom = region.y + region.height;\n\n // Check if rectangles overlap\n return !(\n rect.left > regionRight ||\n rectRight < region.x ||\n rect.top > regionBottom ||\n rectBottom < region.y\n );\n}\n\n/**\n * Collect element manifest from DOM\n *\n * Scans all elements with data-loc attributes and builds a manifest\n * with deduplication for repeated elements (e.g., list items).\n *\n * @param container - Container element to scan (default: document.body)\n * @param region - Optional region to filter elements (only include elements within this region, in viewport coordinates)\n */\nexport function collectElementManifest(\n container: Element = document.body,\n region?: { x: number; y: number; width: number; height: number }\n): ElementManifest[] {\n const manifest: ElementManifest[] = [];\n const dataLocCounts = new Map<string, number>();\n const dataLocInstances = new Map<string, ElementManifest[]>();\n\n // Get scroll offset for converting viewport coords to document coords\n const scrollX = window.scrollX || window.pageXOffset || 0;\n const scrollY = window.scrollY || window.pageYOffset || 0;\n\n // Find all elements with data-loc\n const elements = container.querySelectorAll(\"[data-loc]\");\n\n for (const element of elements) {\n // Skip UILint's own elements\n if (element.closest(\"[data-ui-lint]\")) continue;\n\n // Skip certain tag types\n if (SKIP_TAGS.has(element.tagName)) continue;\n\n // Skip hidden elements\n if (!isElementVisible(element)) continue;\n\n // Skip elements outside the region (if region is specified)\n // Region is in viewport coordinates, same as getBoundingClientRect\n const rect = element.getBoundingClientRect();\n if (region && !rectIntersectsRegion(rect, region)) continue;\n\n const dataLoc = element.getAttribute(\"data-loc\");\n if (!dataLoc) continue;\n\n // Track instance count\n const currentCount = dataLocCounts.get(dataLoc) || 0;\n dataLocCounts.set(dataLoc, currentCount + 1);\n\n // Get or create instances array for this dataLoc\n let instances = dataLocInstances.get(dataLoc);\n if (!instances) {\n instances = [];\n dataLocInstances.set(dataLoc, instances);\n }\n\n // Only collect up to MAX_INSTANCES_PER_DATALOC\n if (instances.length >= MAX_INSTANCES_PER_DATALOC) continue;\n\n const text = getVisibleText(element);\n const id =\n element.getAttribute(\"data-ui-lint-id\") ||\n `loc:${dataLoc}#${currentCount}`;\n\n // For region captures, store coordinates relative to the region origin\n // For full page captures, store document coordinates (viewport + scroll)\n let entryRect: { x: number; y: number; width: number; height: number };\n if (region) {\n // Relative to the region's top-left corner\n entryRect = {\n x: rect.x - region.x,\n y: rect.y - region.y,\n width: rect.width,\n height: rect.height,\n };\n } else {\n // Document coordinates for full page\n entryRect = {\n x: rect.x + scrollX,\n y: rect.y + scrollY,\n width: rect.width,\n height: rect.height,\n };\n }\n\n const entry: ElementManifest = {\n id,\n text,\n dataLoc,\n rect: entryRect,\n tagName: element.tagName.toLowerCase(),\n role: inferRole(element),\n };\n\n instances.push(entry);\n }\n\n // Build final manifest with instance counts\n for (const [dataLoc, instances] of dataLocInstances) {\n const totalCount = dataLocCounts.get(dataLoc) || instances.length;\n\n instances.forEach((entry, index) => {\n // Add instance count to first entry if there are more than shown\n if (index === 0 && totalCount > instances.length) {\n entry.instanceCount = totalCount;\n }\n manifest.push(entry);\n });\n }\n\n return manifest;\n}\n\n/**\n * Match vision issues to manifest entries by element text\n *\n * The LLM returns issues with elementText; we need to map back to dataLoc\n */\nexport function matchIssuesToManifest(\n issues: VisionIssue[],\n manifest: ElementManifest[]\n): VisionIssue[] {\n return issues.map((issue) => {\n // Try exact match first\n let match = manifest.find(\n (m) => m.text.toLowerCase() === issue.elementText.toLowerCase()\n );\n\n // Try partial match (text starts with or contains)\n if (!match) {\n match = manifest.find(\n (m) =>\n m.text.toLowerCase().includes(issue.elementText.toLowerCase()) ||\n issue.elementText.toLowerCase().includes(m.text.toLowerCase())\n );\n }\n\n if (match) {\n return {\n ...issue,\n dataLoc: match.dataLoc,\n elementId: match.id,\n };\n }\n\n return issue;\n });\n}\n\n/**\n * Capture screenshot of the current page\n *\n * Uses html-to-image library for DOM-to-image capture.\n * Falls back to canvas if html-to-image is not available.\n */\nexport async function captureScreenshot(): Promise<string> {\n // Try to use html-to-image if available\n const htmlToImage = await import(\"html-to-image\").catch(() => null);\n\n if (!htmlToImage) {\n throw new Error(\n \"Screenshot capture unavailable: `html-to-image` failed to load (check the uilint-react bundle/deps)\"\n );\n }\n\n try {\n const dataUrl = await htmlToImage.toPng(document.body, {\n // Keep file size down for WS transport\n pixelRatio: 1,\n cacheBust: true,\n filter: (node) => {\n // Skip UILint overlay elements\n if (node instanceof Element && node.closest(\"[data-ui-lint]\")) {\n return false;\n }\n return true;\n },\n });\n return dataUrl;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n const msg = err.message || \"Unknown error\";\n\n // Common failure modes: cross-origin images/fonts taint the canvas.\n const hint = /tainted|cross[- ]origin|CORS|security/i.test(msg)\n ? \" Hint: this is often caused by cross-origin images/fonts; try removing external images or ensuring they allow CORS.\"\n : \"\";\n\n throw new Error(\n `Screenshot capture failed (html-to-image): ${msg}.${hint}`\n );\n }\n}\n\n/**\n * Capture screenshot of a specific region of the page\n *\n * @param region - The region to capture (in viewport coordinates)\n */\nexport async function captureScreenshotRegion(region: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): Promise<string> {\n // Try to use html-to-image if available\n const htmlToImage = await import(\"html-to-image\").catch(() => null);\n\n if (!htmlToImage) {\n throw new Error(\n \"Screenshot capture unavailable: `html-to-image` failed to load (check the uilint-react bundle/deps)\"\n );\n }\n\n try {\n // Capture the full page first, then crop to the region\n // This is more reliable than trying to capture a specific element\n const dataUrl = await htmlToImage.toPng(document.body, {\n pixelRatio: 1,\n cacheBust: true,\n filter: (node) => {\n // Skip UILint overlay elements\n if (node instanceof Element && node.closest(\"[data-ui-lint]\")) {\n return false;\n }\n return true;\n },\n });\n\n // The region coordinates are viewport-relative (from mouse events),\n // but html-to-image captures the full document including scrolled content.\n // We need to add the scroll offset to get the correct position in the full document.\n const scrollX = window.scrollX || window.pageXOffset || 0;\n const scrollY = window.scrollY || window.pageYOffset || 0;\n\n const documentRegion = {\n x: region.x + scrollX,\n y: region.y + scrollY,\n width: region.width,\n height: region.height,\n };\n\n // Crop the image to the selected region using canvas\n return await cropImageToRegion(dataUrl, documentRegion);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n const msg = err.message || \"Unknown error\";\n\n // Common failure modes: cross-origin images/fonts taint the canvas.\n const hint = /tainted|cross[- ]origin|CORS|security/i.test(msg)\n ? \" Hint: this is often caused by cross-origin images/fonts; try removing external images or ensuring they allow CORS.\"\n : \"\";\n\n throw new Error(\n `Screenshot capture failed (html-to-image): ${msg}.${hint}`\n );\n }\n}\n\n/**\n * Crop an image data URL to a specific region\n */\nasync function cropImageToRegion(\n dataUrl: string,\n region: { x: number; y: number; width: number; height: number }\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n try {\n // Create a canvas for cropping\n const canvas = document.createElement(\"canvas\");\n canvas.width = region.width;\n canvas.height = region.height;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n\n // Draw the cropped region\n ctx.drawImage(\n img,\n region.x,\n region.y,\n region.width,\n region.height,\n 0,\n 0,\n region.width,\n region.height\n );\n\n // Convert to data URL\n const croppedDataUrl = canvas.toDataURL(\"image/png\");\n resolve(croppedDataUrl);\n } catch (error) {\n reject(\n new Error(\n `Failed to crop image: ${\n error instanceof Error ? error.message : String(error)\n }`\n )\n );\n }\n };\n img.onerror = () => {\n reject(new Error(\"Failed to load image for cropping\"));\n };\n img.src = dataUrl;\n });\n}\n\n/**\n * Get current route from URL\n */\nexport function getCurrentRoute(): string {\n const path = window.location.pathname;\n // Normalize: remove trailing slashes, handle index\n const normalized = path.replace(/\\/+$/, \"\") || \"/\";\n return normalized;\n}\n\n/**\n * Generate filename-safe timestamp\n */\nexport function generateTimestamp(): string {\n return new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\n/**\n * Build vision analysis request payload\n */\nexport function buildVisionAnalysisPayload(options: {\n screenshotDataUrl?: string;\n manifest: ElementManifest[];\n route: string;\n /** Screenshot filename saved under `.uilint/screenshots` (e.g. uilint-...png) */\n screenshotFile?: string;\n}) {\n return {\n type: \"vision:analyze\" as const,\n route: options.route,\n timestamp: Date.now(),\n screenshot: options.screenshotDataUrl,\n screenshotFile: options.screenshotFile,\n manifest: options.manifest,\n };\n}\n"],"names":["SKIP_TAGS","MAX_INSTANCES_PER_DATALOC","MAX_TEXT_LENGTH","inferRole","element","tagName","explicitRole","type","getVisibleText","innerText","ariaLabel","title","placeholder","alt","isElementVisible","htmlEl","style","rect","rectIntersectsRegion","region","rectRight","rectBottom","regionRight","regionBottom","collectElementManifest","container","manifest","dataLocCounts","dataLocInstances","scrollX","scrollY","elements","dataLoc","currentCount","instances","text","id","entryRect","entry","totalCount","index","captureScreenshot","htmlToImage","node","error","msg","hint","captureScreenshotRegion","dataUrl","documentRegion","cropImageToRegion","resolve","reject","img","canvas","ctx","croppedDataUrl","getCurrentRoute","generateTimestamp"],"mappings":"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"}