uilint-react 0.2.1 → 0.2.4

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 (120) hide show
  1. package/dist/DevTool.d.ts +11 -0
  2. package/dist/DevTool.d.ts.map +1 -0
  3. package/dist/ElementBadges-BNRIjtRW.js +672 -0
  4. package/dist/ElementBadges-BNRIjtRW.js.map +1 -0
  5. package/dist/VisionIssueBadge-C6vfwtec.js +154 -0
  6. package/dist/VisionIssueBadge-C6vfwtec.js.map +1 -0
  7. package/dist/components/Highlighter.d.ts +6 -0
  8. package/dist/components/Highlighter.d.ts.map +1 -0
  9. package/dist/components/ui-lint/ElementBadges.d.ts +6 -0
  10. package/dist/components/ui-lint/ElementBadges.d.ts.map +1 -0
  11. package/dist/components/ui-lint/InspectionPanel.d.ts +6 -0
  12. package/dist/components/ui-lint/InspectionPanel.d.ts.map +1 -0
  13. package/dist/components/ui-lint/LocatorOverlay.d.ts +14 -0
  14. package/dist/components/ui-lint/LocatorOverlay.d.ts.map +1 -0
  15. package/dist/components/ui-lint/RegionSelector.d.ts +18 -0
  16. package/dist/components/ui-lint/RegionSelector.d.ts.map +1 -0
  17. package/dist/components/ui-lint/ScanResultsPopover.d.ts +6 -0
  18. package/dist/components/ui-lint/ScanResultsPopover.d.ts.map +1 -0
  19. package/dist/components/ui-lint/ScreenshotViewer.d.ts +11 -0
  20. package/dist/components/ui-lint/ScreenshotViewer.d.ts.map +1 -0
  21. package/dist/components/ui-lint/UILintProvider.d.ts +11 -0
  22. package/dist/components/ui-lint/UILintProvider.d.ts.map +1 -0
  23. package/dist/components/ui-lint/VisionIssueBadge.d.ts +6 -0
  24. package/dist/components/ui-lint/VisionIssueBadge.d.ts.map +1 -0
  25. package/dist/components/ui-lint/VisionIssuesPanel.d.ts +12 -0
  26. package/dist/components/ui-lint/VisionIssuesPanel.d.ts.map +1 -0
  27. package/dist/components/ui-lint/badge-layout.d.ts +91 -0
  28. package/dist/components/ui-lint/badge-layout.d.ts.map +1 -0
  29. package/dist/components/ui-lint/code-formatting.d.ts +14 -0
  30. package/dist/components/ui-lint/code-formatting.d.ts.map +1 -0
  31. package/dist/components/ui-lint/dom-utils.d.ts +43 -0
  32. package/dist/components/ui-lint/dom-utils.d.ts.map +1 -0
  33. package/dist/components/ui-lint/index.d.ts +22 -0
  34. package/dist/components/ui-lint/index.d.ts.map +1 -0
  35. package/dist/components/ui-lint/inspection-panel-positioning.d.ts +41 -0
  36. package/dist/components/ui-lint/inspection-panel-positioning.d.ts.map +1 -0
  37. package/dist/components/ui-lint/portal-host.d.ts +3 -0
  38. package/dist/components/ui-lint/portal-host.d.ts.map +1 -0
  39. package/dist/components/ui-lint/scan-results/FileTree.d.ts +25 -0
  40. package/dist/components/ui-lint/scan-results/FileTree.d.ts.map +1 -0
  41. package/dist/components/ui-lint/scan-results/IssueRow.d.ts +16 -0
  42. package/dist/components/ui-lint/scan-results/IssueRow.d.ts.map +1 -0
  43. package/dist/components/ui-lint/scan-results/SearchFilter.d.ts +8 -0
  44. package/dist/components/ui-lint/scan-results/SearchFilter.d.ts.map +1 -0
  45. package/dist/components/ui-lint/source-fetcher.d.ts +40 -0
  46. package/dist/components/ui-lint/source-fetcher.d.ts.map +1 -0
  47. package/dist/components/ui-lint/store.d.ts +194 -0
  48. package/dist/components/ui-lint/store.d.ts.map +1 -0
  49. package/dist/components/ui-lint/toolbar/TabbedToolbar.d.ts +2 -0
  50. package/dist/components/ui-lint/toolbar/TabbedToolbar.d.ts.map +1 -0
  51. package/dist/components/ui-lint/toolbar/icons.d.ts +29 -0
  52. package/dist/components/ui-lint/toolbar/icons.d.ts.map +1 -0
  53. package/dist/components/ui-lint/toolbar/index.d.ts +3 -0
  54. package/dist/components/ui-lint/toolbar/index.d.ts.map +1 -0
  55. package/dist/components/ui-lint/toolbar/tabs/ConfigureTab.d.ts +2 -0
  56. package/dist/components/ui-lint/toolbar/tabs/ConfigureTab.d.ts.map +1 -0
  57. package/dist/components/ui-lint/toolbar/tabs/ESLintTab.d.ts +2 -0
  58. package/dist/components/ui-lint/toolbar/tabs/ESLintTab.d.ts.map +1 -0
  59. package/dist/components/ui-lint/toolbar/tabs/VisionTab.d.ts +2 -0
  60. package/dist/components/ui-lint/toolbar/tabs/VisionTab.d.ts.map +1 -0
  61. package/dist/components/ui-lint/toolbar/tokens.d.ts +45 -0
  62. package/dist/components/ui-lint/toolbar/tokens.d.ts.map +1 -0
  63. package/dist/components/ui-lint/types.d.ts +170 -0
  64. package/dist/components/ui-lint/types.d.ts.map +1 -0
  65. package/dist/components/ui-lint/useDOMObserver.d.ts +11 -0
  66. package/dist/components/ui-lint/useDOMObserver.d.ts.map +1 -0
  67. package/dist/components/ui-lint/visibility-utils.d.ts +41 -0
  68. package/dist/components/ui-lint/visibility-utils.d.ts.map +1 -0
  69. package/dist/consistency/highlights.d.ts +14 -0
  70. package/dist/consistency/highlights.d.ts.map +1 -0
  71. package/dist/consistency/index.d.ts +7 -0
  72. package/dist/consistency/index.d.ts.map +1 -0
  73. package/dist/consistency/snapshot.d.ts +14 -0
  74. package/dist/consistency/snapshot.d.ts.map +1 -0
  75. package/dist/consistency/types.d.ts +5 -0
  76. package/dist/consistency/types.d.ts.map +1 -0
  77. package/dist/devtools.d.ts +8 -0
  78. package/dist/devtools.d.ts.map +1 -0
  79. package/dist/devtools.js +259 -0
  80. package/dist/devtools.js.map +1 -0
  81. package/dist/environment-DVxa60C6.js +26 -0
  82. package/dist/environment-DVxa60C6.js.map +1 -0
  83. package/dist/index-BGzkrD0y.js +12304 -0
  84. package/dist/index-BGzkrD0y.js.map +1 -0
  85. package/dist/index-L3Zm-CoX.js +513 -0
  86. package/dist/index-L3Zm-CoX.js.map +1 -0
  87. package/dist/index.d.ts +13 -339
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +43 -482
  90. package/dist/index.js.map +1 -0
  91. package/dist/lib/utils.d.ts +11 -0
  92. package/dist/lib/utils.d.ts.map +1 -0
  93. package/dist/node.d.ts +7 -38
  94. package/dist/node.d.ts.map +1 -0
  95. package/dist/node.js +32 -79
  96. package/dist/node.js.map +1 -0
  97. package/dist/scanner/dom-scanner.d.ts +7 -0
  98. package/dist/scanner/dom-scanner.d.ts.map +1 -0
  99. package/dist/scanner/environment.d.ts +16 -0
  100. package/dist/scanner/environment.d.ts.map +1 -0
  101. package/dist/scanner/jsdom-adapter.d.ts +30 -0
  102. package/dist/scanner/jsdom-adapter.d.ts.map +1 -0
  103. package/dist/scanner/vision-capture.d.ts +131 -0
  104. package/dist/scanner/vision-capture.d.ts.map +1 -0
  105. package/dist/styles/inject-styles.d.ts +2 -0
  106. package/dist/styles/inject-styles.d.ts.map +1 -0
  107. package/dist/vision-capture-EIrYA_XF.js +216 -0
  108. package/dist/vision-capture-EIrYA_XF.js.map +1 -0
  109. package/dist/web-component.d.ts +2 -0
  110. package/dist/web-component.d.ts.map +1 -0
  111. package/package.json +36 -7
  112. package/dist/ElementBadges-2CTPMJ6L.js +0 -825
  113. package/dist/InspectionPanel-NXSE7CMH.js +0 -10
  114. package/dist/LocatorOverlay-3TKK74BD.js +0 -11
  115. package/dist/UILintToolbar-CLVXQHCZ.js +0 -10
  116. package/dist/chunk-552GIJIQ.js +0 -1109
  117. package/dist/chunk-K46BWHFU.js +0 -271
  118. package/dist/chunk-KKNPFLL2.js +0 -1853
  119. package/dist/chunk-S4IWHBOQ.js +0 -178
  120. package/dist/chunk-Z4AAGFIN.js +0 -933
package/dist/node.js CHANGED
@@ -1,55 +1,21 @@
1
- // src/scanner/jsdom-adapter.ts
2
- import { OllamaClient, createStyleSummary as createStyleSummary2 } from "uilint-core";
3
-
4
- // src/scanner/dom-scanner.ts
5
- import {
6
- extractStylesFromDOM,
7
- createStyleSummary,
8
- serializeStyles,
9
- truncateHTML
10
- } from "uilint-core";
11
- function scanDOM(root) {
12
- const targetRoot = root || document.body;
13
- const styles = extractStylesFromDOM(targetRoot);
14
- const html = targetRoot instanceof Element ? targetRoot.outerHTML : targetRoot.body?.outerHTML || "";
15
- return {
16
- html: truncateHTML(html, 5e4),
17
- styles,
18
- elementCount: targetRoot.querySelectorAll("*").length,
19
- timestamp: Date.now()
20
- };
21
- }
22
-
23
- // src/scanner/environment.ts
24
- function isBrowser() {
25
- return typeof window !== "undefined" && typeof window.document !== "undefined";
26
- }
27
- function isJSDOM() {
28
- if (!isBrowser()) return false;
29
- const userAgent = window.navigator?.userAgent || "";
30
- return userAgent.includes("jsdom");
31
- }
32
-
33
- // src/scanner/jsdom-adapter.ts
34
- var JSDOMAdapter = class {
1
+ import { OllamaClient as i, createStyleSummary as o } from "uilint-core";
2
+ import { s as r } from "./environment-DVxa60C6.js";
3
+ import { a as f } from "./environment-DVxa60C6.js";
4
+ class l {
35
5
  styleGuideContent = null;
36
6
  styleGuidePath;
37
7
  client;
38
- constructor(styleGuidePath = ".uilint/styleguide.md") {
39
- this.styleGuidePath = styleGuidePath;
40
- this.client = new OllamaClient();
8
+ constructor(e = ".uilint/styleguide.md") {
9
+ this.styleGuidePath = e, this.client = new i();
41
10
  }
42
11
  /**
43
12
  * Loads the style guide from the filesystem (Node.js only)
44
13
  */
45
14
  async loadStyleGuide() {
46
- if (typeof process === "undefined") return null;
15
+ if (typeof process > "u") return null;
47
16
  try {
48
- const fs = await import("fs/promises");
49
- const path = await import("path");
50
- const fullPath = path.resolve(process.cwd(), this.styleGuidePath);
51
- this.styleGuideContent = await fs.readFile(fullPath, "utf-8");
52
- return this.styleGuideContent;
17
+ const e = await import("fs/promises"), n = (await import("path")).resolve(process.cwd(), this.styleGuidePath);
18
+ return this.styleGuideContent = await e.readFile(n, "utf-8"), this.styleGuideContent;
53
19
  } catch {
54
20
  return null;
55
21
  }
@@ -57,52 +23,39 @@ var JSDOMAdapter = class {
57
23
  /**
58
24
  * Analyzes the current DOM and returns issues
59
25
  */
60
- async analyze(root) {
61
- const startTime = Date.now();
62
- const snapshot = scanDOM(root);
63
- const styleSummary = createStyleSummary2(snapshot.styles);
64
- if (!this.styleGuideContent) {
65
- await this.loadStyleGuide();
66
- }
67
- const result = await this.client.analyzeStyles(
68
- styleSummary,
69
- this.styleGuideContent
70
- );
71
- return {
72
- issues: result.issues,
73
- analysisTime: Date.now() - startTime
26
+ async analyze(e) {
27
+ const t = Date.now(), n = r(e), a = o(n.styles);
28
+ return this.styleGuideContent || await this.loadStyleGuide(), {
29
+ issues: (await this.client.analyzeStyles(
30
+ a,
31
+ this.styleGuideContent
32
+ )).issues,
33
+ analysisTime: Date.now() - t
74
34
  };
75
35
  }
76
36
  /**
77
37
  * Outputs issues to console.warn (for test visibility)
78
38
  */
79
- outputWarnings(issues) {
80
- if (issues.length === 0) {
39
+ outputWarnings(e) {
40
+ if (e.length === 0) {
81
41
  console.warn("[UILint] No UI consistency issues found");
82
42
  return;
83
43
  }
84
- issues.forEach((issue) => {
85
- const parts = [`\u26A0\uFE0F [UILint] ${issue.message}`];
86
- if (issue.currentValue && issue.expectedValue) {
87
- parts.push(
88
- `Current: ${issue.currentValue}, Expected: ${issue.expectedValue}`
89
- );
90
- }
91
- if (issue.suggestion) {
92
- parts.push(`Suggestion: ${issue.suggestion}`);
93
- }
94
- console.warn(parts.join(" | "));
44
+ e.forEach((t) => {
45
+ const n = [`⚠️ [UILint] ${t.message}`];
46
+ t.currentValue && t.expectedValue && n.push(
47
+ `Current: ${t.currentValue}, Expected: ${t.expectedValue}`
48
+ ), t.suggestion && n.push(`Suggestion: ${t.suggestion}`), console.warn(n.join(" | "));
95
49
  });
96
50
  }
97
- };
98
- async function runUILintInTest(root) {
99
- const adapter = new JSDOMAdapter();
100
- const result = await adapter.analyze(root);
101
- adapter.outputWarnings(result.issues);
102
- return result.issues;
51
+ }
52
+ async function d(s) {
53
+ const e = new l(), t = await e.analyze(s);
54
+ return e.outputWarnings(t.issues), t.issues;
103
55
  }
104
56
  export {
105
- JSDOMAdapter,
106
- isJSDOM,
107
- runUILintInTest
57
+ l as JSDOMAdapter,
58
+ f as isJSDOM,
59
+ d as runUILintInTest
108
60
  };
61
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sources":["../src/scanner/jsdom-adapter.ts"],"sourcesContent":["/**\n * Adapter for running UILint in JSDOM/Node.js test environments\n * Uses uilint-core for analysis\n */\n\nimport { OllamaClient, createStyleSummary } from \"uilint-core\";\nimport type { UILintIssue, AnalysisResult } from \"uilint-core\";\nimport { scanDOM } from \"./dom-scanner.js\";\nimport { isJSDOM } from \"./environment.js\";\n\n/**\n * Adapter for running UILint in JSDOM/Node.js test environments\n * Calls Ollama directly and outputs to console.warn()\n */\nexport class JSDOMAdapter {\n private styleGuideContent: string | null = null;\n private styleGuidePath: string;\n private client: InstanceType<typeof OllamaClient>;\n\n constructor(styleGuidePath: string = \".uilint/styleguide.md\") {\n this.styleGuidePath = styleGuidePath;\n this.client = new OllamaClient();\n }\n\n /**\n * Loads the style guide from the filesystem (Node.js only)\n */\n async loadStyleGuide(): Promise<string | null> {\n if (typeof process === \"undefined\") return null;\n\n try {\n // Dynamic import for Node.js fs module\n const fs = await import(\"fs/promises\");\n const path = await import(\"path\");\n\n const fullPath = path.resolve(process.cwd(), this.styleGuidePath);\n this.styleGuideContent = await fs.readFile(fullPath, \"utf-8\");\n return this.styleGuideContent;\n } catch {\n // Style guide doesn't exist yet\n return null;\n }\n }\n\n /**\n * Analyzes the current DOM and returns issues\n */\n async analyze(root?: Element | Document): Promise<AnalysisResult> {\n const startTime = Date.now();\n\n // Scan the DOM\n const snapshot = scanDOM(root);\n const styleSummary = createStyleSummary(snapshot.styles);\n\n // Load style guide if not already loaded\n if (!this.styleGuideContent) {\n await this.loadStyleGuide();\n }\n\n // Call Ollama via the core client\n const result = await this.client.analyzeStyles(\n styleSummary,\n this.styleGuideContent\n );\n\n return {\n issues: result.issues,\n analysisTime: Date.now() - startTime,\n };\n }\n\n /**\n * Outputs issues to console.warn (for test visibility)\n */\n outputWarnings(issues: UILintIssue[]): void {\n if (issues.length === 0) {\n console.warn(\"[UILint] No UI consistency issues found\");\n return;\n }\n\n issues.forEach((issue) => {\n const parts = [`⚠️ [UILint] ${issue.message}`];\n\n if (issue.currentValue && issue.expectedValue) {\n parts.push(\n `Current: ${issue.currentValue}, Expected: ${issue.expectedValue}`\n );\n }\n\n if (issue.suggestion) {\n parts.push(`Suggestion: ${issue.suggestion}`);\n }\n\n console.warn(parts.join(\" | \"));\n });\n }\n}\n\n/**\n * Convenience function for running UILint in tests\n */\nexport async function runUILintInTest(\n root?: Element | Document\n): Promise<UILintIssue[]> {\n const adapter = new JSDOMAdapter();\n const result = await adapter.analyze(root);\n adapter.outputWarnings(result.issues);\n return result.issues;\n}\n\n// Re-export for backwards compatibility\nexport { isJSDOM };\n"],"names":["JSDOMAdapter","styleGuidePath","OllamaClient","fs","fullPath","root","startTime","snapshot","scanDOM","styleSummary","createStyleSummary","issues","issue","parts","runUILintInTest","adapter","result"],"mappings":";;;AAcO,MAAMA,EAAa;AAAA,EAChB,oBAAmC;AAAA,EACnC;AAAA,EACA;AAAA,EAER,YAAYC,IAAyB,yBAAyB;AAC5D,SAAK,iBAAiBA,GACtB,KAAK,SAAS,IAAIC,EAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAyC;AAC7C,QAAI,OAAO,UAAY,IAAa,QAAO;AAE3C,QAAI;AAEF,YAAMC,IAAK,MAAM,OAAO,aAAa,GAG/BC,KAFO,MAAM,OAAO,MAAM,GAEV,QAAQ,QAAQ,IAAA,GAAO,KAAK,cAAc;AAChE,kBAAK,oBAAoB,MAAMD,EAAG,SAASC,GAAU,OAAO,GACrD,KAAK;AAAA,IACd,QAAQ;AAEN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQC,GAAoD;AAChE,UAAMC,IAAY,KAAK,IAAA,GAGjBC,IAAWC,EAAQH,CAAI,GACvBI,IAAeC,EAAmBH,EAAS,MAAM;AAGvD,WAAK,KAAK,qBACR,MAAM,KAAK,eAAA,GASN;AAAA,MACL,SANa,MAAM,KAAK,OAAO;AAAA,QAC/BE;AAAA,QACA,KAAK;AAAA,MAAA,GAIU;AAAA,MACf,cAAc,KAAK,QAAQH;AAAA,IAAA;AAAA,EAE/B;AAAA;AAAA;AAAA;AAAA,EAKA,eAAeK,GAA6B;AAC1C,QAAIA,EAAO,WAAW,GAAG;AACvB,cAAQ,KAAK,yCAAyC;AACtD;AAAA,IACF;AAEA,IAAAA,EAAO,QAAQ,CAACC,MAAU;AACxB,YAAMC,IAAQ,CAAC,eAAeD,EAAM,OAAO,EAAE;AAE7C,MAAIA,EAAM,gBAAgBA,EAAM,iBAC9BC,EAAM;AAAA,QACJ,YAAYD,EAAM,YAAY,eAAeA,EAAM,aAAa;AAAA,MAAA,GAIhEA,EAAM,cACRC,EAAM,KAAK,eAAeD,EAAM,UAAU,EAAE,GAG9C,QAAQ,KAAKC,EAAM,KAAK,KAAK,CAAC;AAAA,IAChC,CAAC;AAAA,EACH;AACF;AAKA,eAAsBC,EACpBT,GACwB;AACxB,QAAMU,IAAU,IAAIf,EAAA,GACdgB,IAAS,MAAMD,EAAQ,QAAQV,CAAI;AACzC,SAAAU,EAAQ,eAAeC,EAAO,MAAM,GAC7BA,EAAO;AAChB;"}
@@ -0,0 +1,7 @@
1
+ import { createStyleSummary, serializeStyles, DOMSnapshot } from 'uilint-core';
2
+ export { createStyleSummary, serializeStyles };
3
+ /**
4
+ * Scans the DOM and extracts a snapshot of all styles
5
+ */
6
+ export declare function scanDOM(root?: Element | Document): DOMSnapshot;
7
+ //# sourceMappingURL=dom-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom-scanner.d.ts","sourceRoot":"","sources":["../../src/scanner/dom-scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEL,kBAAkB,EAClB,eAAe,EAEf,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,CAAC;AAE/C;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,CAe9D"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Environment detection utilities
3
+ */
4
+ /**
5
+ * Checks if we're running in a browser environment
6
+ */
7
+ export declare function isBrowser(): boolean;
8
+ /**
9
+ * Checks if we're running in a JSDOM environment (tests)
10
+ */
11
+ export declare function isJSDOM(): boolean;
12
+ /**
13
+ * Checks if we're running in Node.js
14
+ */
15
+ export declare function isNode(): boolean;
16
+ //# sourceMappingURL=environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/scanner/environment.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAInC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,OAAO,CAKjC;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,OAAO,CAMhC"}
@@ -0,0 +1,30 @@
1
+ import { UILintIssue, AnalysisResult } from 'uilint-core';
2
+ import { isJSDOM } from './environment.js';
3
+ /**
4
+ * Adapter for running UILint in JSDOM/Node.js test environments
5
+ * Calls Ollama directly and outputs to console.warn()
6
+ */
7
+ export declare class JSDOMAdapter {
8
+ private styleGuideContent;
9
+ private styleGuidePath;
10
+ private client;
11
+ constructor(styleGuidePath?: string);
12
+ /**
13
+ * Loads the style guide from the filesystem (Node.js only)
14
+ */
15
+ loadStyleGuide(): Promise<string | null>;
16
+ /**
17
+ * Analyzes the current DOM and returns issues
18
+ */
19
+ analyze(root?: Element | Document): Promise<AnalysisResult>;
20
+ /**
21
+ * Outputs issues to console.warn (for test visibility)
22
+ */
23
+ outputWarnings(issues: UILintIssue[]): void;
24
+ }
25
+ /**
26
+ * Convenience function for running UILint in tests
27
+ */
28
+ export declare function runUILintInTest(root?: Element | Document): Promise<UILintIssue[]>;
29
+ export { isJSDOM };
30
+ //# sourceMappingURL=jsdom-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsdom-adapter.d.ts","sourceRoot":"","sources":["../../src/scanner/jsdom-adapter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAoC;gBAEtC,cAAc,GAAE,MAAgC;IAK5D;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAiB9C;;OAEG;IACG,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;IAwBjE;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI;CAsB5C;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GACxB,OAAO,CAAC,WAAW,EAAE,CAAC,CAKxB;AAGD,OAAO,EAAE,OAAO,EAAE,CAAC"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Vision Capture Module
3
+ *
4
+ * Provides screenshot capture and element manifest building for vision-based analysis.
5
+ * Uses html-to-image for DOM-to-image capture.
6
+ */
7
+ /**
8
+ * Element manifest entry for vision analysis
9
+ */
10
+ export interface ElementManifest {
11
+ /** Unique ID (data-ui-lint-id if present, otherwise generated) */
12
+ id: string;
13
+ /** Visible text content (truncated to 100 chars) */
14
+ text: string;
15
+ /** data-loc value: "path:line:column" */
16
+ dataLoc: string;
17
+ /** Bounding rectangle */
18
+ rect: {
19
+ x: number;
20
+ y: number;
21
+ width: number;
22
+ height: number;
23
+ };
24
+ /** HTML tag name */
25
+ tagName: string;
26
+ /** Inferred semantic role (button, heading, link, etc.) */
27
+ role?: string;
28
+ /** Total instances with same dataLoc (if deduplicated) */
29
+ instanceCount?: number;
30
+ }
31
+ /**
32
+ * Vision analysis issue from the LLM
33
+ */
34
+ export interface VisionIssue {
35
+ /** Text of the element this issue refers to */
36
+ elementText: string;
37
+ /** Issue description */
38
+ message: string;
39
+ /** Issue category */
40
+ category: "spacing" | "alignment" | "color" | "typography" | "layout" | "contrast" | "visual-hierarchy" | "other";
41
+ /** Severity level */
42
+ severity: "error" | "warning" | "info";
43
+ /** Matched dataLoc from manifest (filled in after text matching) */
44
+ dataLoc?: string;
45
+ /** Matched element ID (filled in after text matching) */
46
+ elementId?: string;
47
+ }
48
+ /**
49
+ * Vision analysis result
50
+ */
51
+ export interface VisionAnalysisResult {
52
+ /** Route/path that was analyzed */
53
+ route: string;
54
+ /** Timestamp of capture */
55
+ timestamp: number;
56
+ /** Screenshot as base64 data URL */
57
+ screenshotDataUrl?: string;
58
+ /** Element manifest */
59
+ manifest: ElementManifest[];
60
+ /** Issues found by vision analysis */
61
+ issues: VisionIssue[];
62
+ /** Analysis duration in ms */
63
+ analysisTime: number;
64
+ /** Error message if analysis failed */
65
+ error?: string;
66
+ }
67
+ /**
68
+ * Collect element manifest from DOM
69
+ *
70
+ * Scans all elements with data-loc attributes and builds a manifest
71
+ * with deduplication for repeated elements (e.g., list items).
72
+ *
73
+ * @param container - Container element to scan (default: document.body)
74
+ * @param region - Optional region to filter elements (only include elements within this region)
75
+ */
76
+ export declare function collectElementManifest(container?: Element, region?: {
77
+ x: number;
78
+ y: number;
79
+ width: number;
80
+ height: number;
81
+ }): ElementManifest[];
82
+ /**
83
+ * Match vision issues to manifest entries by element text
84
+ *
85
+ * The LLM returns issues with elementText; we need to map back to dataLoc
86
+ */
87
+ export declare function matchIssuesToManifest(issues: VisionIssue[], manifest: ElementManifest[]): VisionIssue[];
88
+ /**
89
+ * Capture screenshot of the current page
90
+ *
91
+ * Uses html-to-image library for DOM-to-image capture.
92
+ * Falls back to canvas if html-to-image is not available.
93
+ */
94
+ export declare function captureScreenshot(): Promise<string>;
95
+ /**
96
+ * Capture screenshot of a specific region of the page
97
+ *
98
+ * @param region - The region to capture (in viewport coordinates)
99
+ */
100
+ export declare function captureScreenshotRegion(region: {
101
+ x: number;
102
+ y: number;
103
+ width: number;
104
+ height: number;
105
+ }): Promise<string>;
106
+ /**
107
+ * Get current route from URL
108
+ */
109
+ export declare function getCurrentRoute(): string;
110
+ /**
111
+ * Generate filename-safe timestamp
112
+ */
113
+ export declare function generateTimestamp(): string;
114
+ /**
115
+ * Build vision analysis request payload
116
+ */
117
+ export declare function buildVisionAnalysisPayload(options: {
118
+ screenshotDataUrl?: string;
119
+ manifest: ElementManifest[];
120
+ route: string;
121
+ /** Screenshot filename saved under `.uilint/screenshots` (e.g. uilint-...png) */
122
+ screenshotFile?: string;
123
+ }): {
124
+ type: "vision:analyze";
125
+ route: string;
126
+ timestamp: number;
127
+ screenshot: string | undefined;
128
+ screenshotFile: string | undefined;
129
+ manifest: ElementManifest[];
130
+ };
131
+ //# sourceMappingURL=vision-capture.d.ts.map
@@ -0,0 +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,CA2EnB;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,CAwClB;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"}
@@ -0,0 +1,2 @@
1
+ export declare function injectDevToolStyles(cssText: string): void;
2
+ //# sourceMappingURL=inject-styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject-styles.d.ts","sourceRoot":"","sources":["../../src/styles/inject-styles.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,QAUlD"}
@@ -0,0 +1,216 @@
1
+ const d = /* @__PURE__ */ new Set([
2
+ "SCRIPT",
3
+ "STYLE",
4
+ "SVG",
5
+ "PATH",
6
+ "CIRCLE",
7
+ "RECT",
8
+ "LINE",
9
+ "POLYGON",
10
+ "POLYLINE",
11
+ "ELLIPSE",
12
+ "NOSCRIPT",
13
+ "TEMPLATE",
14
+ "SLOT"
15
+ ]), m = 3, h = 100;
16
+ function w(n) {
17
+ const t = n.tagName.toUpperCase(), r = n.getAttribute("role");
18
+ if (r) return r;
19
+ switch (t) {
20
+ case "BUTTON":
21
+ return "button";
22
+ case "A":
23
+ return "link";
24
+ case "H1":
25
+ case "H2":
26
+ case "H3":
27
+ case "H4":
28
+ case "H5":
29
+ case "H6":
30
+ return "heading";
31
+ case "INPUT": {
32
+ const e = n.type;
33
+ return e === "submit" || e === "button" ? "button" : e === "checkbox" ? "checkbox" : e === "radio" ? "radio" : "textbox";
34
+ }
35
+ case "TEXTAREA":
36
+ return "textbox";
37
+ case "SELECT":
38
+ return "combobox";
39
+ case "IMG":
40
+ return "img";
41
+ case "NAV":
42
+ return "navigation";
43
+ case "MAIN":
44
+ return "main";
45
+ case "HEADER":
46
+ return "banner";
47
+ case "FOOTER":
48
+ return "contentinfo";
49
+ case "ASIDE":
50
+ return "complementary";
51
+ case "ARTICLE":
52
+ return "article";
53
+ case "SECTION":
54
+ return "region";
55
+ case "FORM":
56
+ return "form";
57
+ case "TABLE":
58
+ return "table";
59
+ case "UL":
60
+ case "OL":
61
+ return "list";
62
+ case "LI":
63
+ return "listitem";
64
+ default:
65
+ return;
66
+ }
67
+ }
68
+ function p(n) {
69
+ const t = n.innerText?.trim();
70
+ if (t)
71
+ return t.length > h ? t.slice(0, h) + "…" : t;
72
+ const r = n.getAttribute("aria-label");
73
+ if (r) return r;
74
+ const e = n.getAttribute("title");
75
+ if (e) return e;
76
+ const o = n.getAttribute("placeholder");
77
+ if (o) return o;
78
+ const i = n.getAttribute("alt");
79
+ return i || "";
80
+ }
81
+ function E(n) {
82
+ const t = n, r = window.getComputedStyle(t);
83
+ if (r.display === "none" || r.visibility === "hidden" || r.opacity === "0") return !1;
84
+ const e = n.getBoundingClientRect();
85
+ return !(e.width === 0 && e.height === 0 || e.bottom < -100 || e.top > window.innerHeight + 100 || e.right < -100 || e.left > window.innerWidth + 100);
86
+ }
87
+ function b(n, t) {
88
+ const r = n.left + n.width, e = n.top + n.height, o = t.x + t.width, i = t.y + t.height;
89
+ return !(n.left > o || r < t.x || n.top > i || e < t.y);
90
+ }
91
+ function T(n = document.body, t) {
92
+ const r = [], e = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), i = n.querySelectorAll("[data-loc]");
93
+ for (const a of i) {
94
+ if (a.closest("[data-ui-lint]") || d.has(a.tagName) || !E(a)) continue;
95
+ const c = a.getBoundingClientRect();
96
+ if (t && !b(c, t)) continue;
97
+ const s = a.getAttribute("data-loc");
98
+ if (!s) continue;
99
+ const l = e.get(s) || 0;
100
+ e.set(s, l + 1);
101
+ let u = o.get(s);
102
+ if (u || (u = [], o.set(s, u)), u.length >= m) continue;
103
+ const g = p(a), f = {
104
+ id: a.getAttribute("data-ui-lint-id") || `loc:${s}#${l}`,
105
+ text: g,
106
+ dataLoc: s,
107
+ rect: {
108
+ x: c.x,
109
+ y: c.y,
110
+ width: c.width,
111
+ height: c.height
112
+ },
113
+ tagName: a.tagName.toLowerCase(),
114
+ role: w(a)
115
+ };
116
+ u.push(f);
117
+ }
118
+ for (const [a, c] of o) {
119
+ const s = e.get(a) || c.length;
120
+ c.forEach((l, u) => {
121
+ u === 0 && s > c.length && (l.instanceCount = s), r.push(l);
122
+ });
123
+ }
124
+ return r;
125
+ }
126
+ async function R() {
127
+ const n = await import("./index-L3Zm-CoX.js").catch(() => null);
128
+ if (!n)
129
+ throw new Error(
130
+ "Screenshot capture unavailable: `html-to-image` failed to load (check the uilint-react bundle/deps)"
131
+ );
132
+ try {
133
+ return await n.toPng(document.body, {
134
+ // Keep file size down for WS transport
135
+ pixelRatio: 1,
136
+ cacheBust: !0,
137
+ filter: (r) => !(r instanceof Element && r.closest("[data-ui-lint]"))
138
+ });
139
+ } catch (t) {
140
+ const e = (t instanceof Error ? t : new Error(String(t))).message || "Unknown error", o = /tainted|cross[- ]origin|CORS|security/i.test(e) ? " Hint: this is often caused by cross-origin images/fonts; try removing external images or ensuring they allow CORS." : "";
141
+ throw new Error(
142
+ `Screenshot capture failed (html-to-image): ${e}.${o}`
143
+ );
144
+ }
145
+ }
146
+ async function x(n) {
147
+ const t = await import("./index-L3Zm-CoX.js").catch(() => null);
148
+ if (!t)
149
+ throw new Error(
150
+ "Screenshot capture unavailable: `html-to-image` failed to load (check the uilint-react bundle/deps)"
151
+ );
152
+ try {
153
+ const r = await t.toPng(document.body, {
154
+ pixelRatio: 1,
155
+ cacheBust: !0,
156
+ filter: (e) => !(e instanceof Element && e.closest("[data-ui-lint]"))
157
+ });
158
+ return await y(r, n);
159
+ } catch (r) {
160
+ 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." : "";
161
+ throw new Error(
162
+ `Screenshot capture failed (html-to-image): ${o}.${i}`
163
+ );
164
+ }
165
+ }
166
+ async function y(n, t) {
167
+ return new Promise((r, e) => {
168
+ const o = new Image();
169
+ o.onload = () => {
170
+ try {
171
+ const i = document.createElement("canvas");
172
+ i.width = t.width, i.height = t.height;
173
+ const a = i.getContext("2d");
174
+ if (!a) {
175
+ e(new Error("Failed to get canvas context"));
176
+ return;
177
+ }
178
+ a.drawImage(
179
+ o,
180
+ t.x,
181
+ t.y,
182
+ t.width,
183
+ t.height,
184
+ 0,
185
+ 0,
186
+ t.width,
187
+ t.height
188
+ );
189
+ const c = i.toDataURL("image/png");
190
+ r(c);
191
+ } catch (i) {
192
+ e(
193
+ new Error(
194
+ `Failed to crop image: ${i instanceof Error ? i.message : String(i)}`
195
+ )
196
+ );
197
+ }
198
+ }, o.onerror = () => {
199
+ e(new Error("Failed to load image for cropping"));
200
+ }, o.src = n;
201
+ });
202
+ }
203
+ function A() {
204
+ return window.location.pathname.replace(/\/+$/, "") || "/";
205
+ }
206
+ function C() {
207
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
208
+ }
209
+ export {
210
+ R as captureScreenshot,
211
+ x as captureScreenshotRegion,
212
+ T as collectElementManifest,
213
+ C as generateTimestamp,
214
+ A as getCurrentRoute
215
+ };
216
+ //# sourceMappingURL=vision-capture-EIrYA_XF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision-capture-EIrYA_XF.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)\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 // 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 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 const entry: ElementManifest = {\n id,\n text,\n dataLoc,\n rect: {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height,\n },\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 // Crop the image to the selected region using canvas\n return await cropImageToRegion(dataUrl, region);\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","elements","dataLoc","currentCount","instances","text","entry","totalCount","index","captureScreenshot","htmlToImage","node","error","msg","hint","captureScreenshotRegion","dataUrl","cropImageToRegion","resolve","reject","img","canvas","ctx","croppedDataUrl","getCurrentRoute","generateTimestamp"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function defineUILintDevtoolsElement(): void;
2
+ //# sourceMappingURL=web-component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-component.d.ts","sourceRoot":"","sources":["../src/web-component.tsx"],"names":[],"mappings":"AA6FA,wBAAgB,2BAA2B,SAqJ1C"}