uilint-react 0.2.0 → 0.2.3

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
@@ -1,1109 +0,0 @@
1
- "use client";
2
-
3
- // src/components/ui-lint/dom-utils.ts
4
- var DATA_ATTR = "data-ui-lint-id";
5
- var COLORS = [
6
- "#3B82F6",
7
- "#8B5CF6",
8
- "#EC4899",
9
- "#10B981",
10
- "#F59E0B",
11
- "#06B6D4",
12
- "#EF4444",
13
- "#84CC16",
14
- "#6366F1",
15
- "#F97316",
16
- "#14B8A6",
17
- "#A855F7"
18
- ];
19
- var SKIP_TAGS = /* @__PURE__ */ new Set([
20
- "SCRIPT",
21
- "STYLE",
22
- "SVG",
23
- "NOSCRIPT",
24
- "TEMPLATE",
25
- "HEAD",
26
- "META",
27
- "LINK"
28
- ]);
29
- var elementCounter = 0;
30
- function getSourceFromDataLoc(element) {
31
- const loc = element.getAttribute("data-loc");
32
- if (!loc) return null;
33
- const parts = loc.split(":");
34
- if (parts.length < 2) return null;
35
- const lastPart = parts[parts.length - 1];
36
- const secondLastPart = parts[parts.length - 2];
37
- const lastIsNumber = /^\d+$/.test(lastPart);
38
- const secondLastIsNumber = /^\d+$/.test(secondLastPart);
39
- if (lastIsNumber && secondLastIsNumber) {
40
- const columnNumber = parseInt(lastPart, 10);
41
- const lineNumber = parseInt(secondLastPart, 10);
42
- const fileName = parts.slice(0, -2).join(":");
43
- if (isNaN(lineNumber) || isNaN(columnNumber) || !fileName) return null;
44
- return { fileName, lineNumber, columnNumber };
45
- } else if (lastIsNumber) {
46
- const lineNumber = parseInt(lastPart, 10);
47
- const fileName = parts.slice(0, -1).join(":");
48
- if (isNaN(lineNumber) || !fileName) return null;
49
- return { fileName, lineNumber };
50
- }
51
- return null;
52
- }
53
- function isNodeModulesPath(path) {
54
- return path.includes("node_modules");
55
- }
56
- function getDisplayName(path) {
57
- const parts = path.split("/");
58
- return parts[parts.length - 1] || path;
59
- }
60
- function shouldSkipElement(element) {
61
- if (SKIP_TAGS.has(element.tagName.toUpperCase())) return true;
62
- if (element.hasAttribute("data-ui-lint")) return true;
63
- if (element.getAttribute("aria-hidden") === "true") return true;
64
- const styles = window.getComputedStyle(element);
65
- if (styles.display === "none" || styles.visibility === "hidden") return true;
66
- const rect = element.getBoundingClientRect();
67
- if (rect.width === 0 || rect.height === 0) return true;
68
- return false;
69
- }
70
- function scanDOMForSources(root = document.body, hideNodeModules = true) {
71
- const elements = [];
72
- elementCounter = 0;
73
- const occurrenceByDataLoc = /* @__PURE__ */ new Map();
74
- cleanupDataAttributes();
75
- const locElements = root.querySelectorAll("[data-loc]");
76
- for (const el of locElements) {
77
- if (shouldSkipElement(el)) continue;
78
- const source = getSourceFromDataLoc(el);
79
- if (!source) continue;
80
- if (hideNodeModules && isNodeModulesPath(source.fileName)) {
81
- continue;
82
- }
83
- const dataLoc = el.getAttribute("data-loc");
84
- const occurrence = (occurrenceByDataLoc.get(dataLoc) ?? 0) + 1;
85
- occurrenceByDataLoc.set(dataLoc, occurrence);
86
- const id = `loc:${dataLoc}#${occurrence}`;
87
- el.setAttribute(DATA_ATTR, id);
88
- elements.push({
89
- id,
90
- element: el,
91
- tagName: el.tagName.toLowerCase(),
92
- className: typeof el.className === "string" ? el.className : "",
93
- source,
94
- rect: el.getBoundingClientRect()
95
- });
96
- }
97
- return elements;
98
- }
99
- function groupBySourceFile(elements) {
100
- const fileMap = /* @__PURE__ */ new Map();
101
- for (const element of elements) {
102
- if (!element.source) continue;
103
- const path = element.source.fileName;
104
- const existing = fileMap.get(path) || [];
105
- existing.push(element);
106
- fileMap.set(path, existing);
107
- }
108
- const sourceFiles = [];
109
- let colorIndex = 0;
110
- for (const [path, fileElements] of fileMap) {
111
- sourceFiles.push({
112
- path,
113
- displayName: getDisplayName(path),
114
- color: COLORS[colorIndex % COLORS.length],
115
- elements: fileElements
116
- });
117
- colorIndex++;
118
- }
119
- sourceFiles.sort((a, b) => b.elements.length - a.elements.length);
120
- return sourceFiles;
121
- }
122
- function cleanupDataAttributes() {
123
- const elements = document.querySelectorAll(`[${DATA_ATTR}]`);
124
- elements.forEach((el) => el.removeAttribute(DATA_ATTR));
125
- elementCounter = 0;
126
- }
127
- function getElementById(id) {
128
- return document.querySelector(`[${DATA_ATTR}="${id}"]`);
129
- }
130
- function updateElementRects(elements) {
131
- return elements.map((el) => ({
132
- ...el,
133
- rect: el.element.getBoundingClientRect()
134
- }));
135
- }
136
- function buildEditorUrl(source, editor = "cursor", workspaceRoot) {
137
- const { fileName, lineNumber, columnNumber } = source;
138
- const column = columnNumber ?? 1;
139
- let absolutePath = fileName;
140
- if (workspaceRoot && !fileName.startsWith("/")) {
141
- const root = workspaceRoot.endsWith("/") ? workspaceRoot.slice(0, -1) : workspaceRoot;
142
- absolutePath = `${root}/${fileName}`;
143
- }
144
- if (editor === "cursor") {
145
- return `cursor://file/${encodeURIComponent(
146
- absolutePath
147
- )}:${lineNumber}:${column}`;
148
- }
149
- return `vscode://file/${encodeURIComponent(
150
- absolutePath
151
- )}:${lineNumber}:${column}`;
152
- }
153
-
154
- // src/components/ui-lint/types.ts
155
- var FILE_COLORS = [
156
- "#3B82F6",
157
- // blue
158
- "#8B5CF6",
159
- // violet
160
- "#EC4899",
161
- // pink
162
- "#10B981",
163
- // emerald
164
- "#F59E0B",
165
- // amber
166
- "#06B6D4",
167
- // cyan
168
- "#EF4444",
169
- // red
170
- "#84CC16",
171
- // lime
172
- "#6366F1",
173
- // indigo
174
- "#F97316",
175
- // orange
176
- "#14B8A6",
177
- // teal
178
- "#A855F7"
179
- // purple
180
- ];
181
- var DEFAULT_SETTINGS = {
182
- hideNodeModules: true,
183
- autoScanEnabled: false
184
- };
185
- var DEFAULT_AUTO_SCAN_STATE = {
186
- status: "idle",
187
- currentIndex: 0,
188
- totalElements: 0,
189
- elements: []
190
- };
191
- var DATA_UILINT_ID = "data-ui-lint-id";
192
-
193
- // src/components/ui-lint/store.ts
194
- import { create } from "zustand";
195
- function getDataLocFromId(id) {
196
- if (id.startsWith("loc:")) {
197
- const raw = id.slice(4);
198
- return raw.split("#")[0] || null;
199
- }
200
- return null;
201
- }
202
- async function scanFileForIssues(sourceFile, store) {
203
- if (sourceFile.elements.length === 0) {
204
- return { issues: [] };
205
- }
206
- const filePath = sourceFile.path;
207
- let issues = [];
208
- if (store.wsConnected && store.wsConnection) {
209
- try {
210
- issues = await store.requestFileLint(filePath);
211
- console.log("[UILint] ESLint issues:", issues);
212
- } catch (err) {
213
- console.warn("[UILint] WebSocket lint failed:", err);
214
- return { issues: [], error: true };
215
- }
216
- } else {
217
- console.warn("[UILint] WebSocket not connected");
218
- return { issues: [], error: true };
219
- }
220
- return { issues };
221
- }
222
- function distributeIssuesToElements(issues, elements, filePath, updateElementIssue, updateFileIssues, hasError) {
223
- const dataLocToElementIds = /* @__PURE__ */ new Map();
224
- for (const el of elements) {
225
- const dataLoc = getDataLocFromId(el.id);
226
- if (dataLoc) {
227
- const existing = dataLocToElementIds.get(dataLoc);
228
- if (existing) existing.push(el.id);
229
- else dataLocToElementIds.set(dataLoc, [el.id]);
230
- }
231
- }
232
- const issuesByElement = /* @__PURE__ */ new Map();
233
- const unmappedIssues = [];
234
- for (const issue of issues) {
235
- if (issue.dataLoc) {
236
- const elementIds = dataLocToElementIds.get(issue.dataLoc);
237
- if (elementIds && elementIds.length > 0) {
238
- for (const elementId of elementIds) {
239
- const existing = issuesByElement.get(elementId) || [];
240
- existing.push(issue);
241
- issuesByElement.set(elementId, existing);
242
- }
243
- } else {
244
- unmappedIssues.push(issue);
245
- }
246
- } else {
247
- unmappedIssues.push(issue);
248
- }
249
- }
250
- for (const el of elements) {
251
- const elementIssues = issuesByElement.get(el.id) || [];
252
- updateElementIssue(el.id, {
253
- elementId: el.id,
254
- issues: elementIssues,
255
- status: hasError ? "error" : "complete"
256
- });
257
- }
258
- if (unmappedIssues.length > 0) {
259
- updateFileIssues(filePath, unmappedIssues);
260
- } else {
261
- updateFileIssues(filePath, []);
262
- }
263
- }
264
- var DEFAULT_WS_URL = "ws://localhost:9234";
265
- var MAX_RECONNECT_ATTEMPTS = 5;
266
- var RECONNECT_BASE_DELAY = 1e3;
267
- var pendingRequests = /* @__PURE__ */ new Map();
268
- var WS_REQUEST_TIMEOUT_MS = 12e4;
269
- function makeRequestId() {
270
- try {
271
- const c = globalThis.crypto;
272
- if (c?.randomUUID) return c.randomUUID();
273
- } catch {
274
- }
275
- return `req_${Date.now()}_${Math.random().toString(16).slice(2)}`;
276
- }
277
- var useUILintStore = create()((set, get) => ({
278
- // ============ Settings ============
279
- settings: DEFAULT_SETTINGS,
280
- updateSettings: (partial) => set((state) => ({
281
- settings: { ...state.settings, ...partial }
282
- })),
283
- // ============ Locator Mode ============
284
- altKeyHeld: false,
285
- setAltKeyHeld: (held) => set({ altKeyHeld: held }),
286
- locatorTarget: null,
287
- setLocatorTarget: (target) => set({ locatorTarget: target }),
288
- // ============ Inspection ============
289
- inspectedElement: null,
290
- setInspectedElement: (el) => set({ inspectedElement: el }),
291
- // ============ Live Scanning ============
292
- liveScanEnabled: false,
293
- autoScanState: DEFAULT_AUTO_SCAN_STATE,
294
- elementIssuesCache: /* @__PURE__ */ new Map(),
295
- fileIssuesCache: /* @__PURE__ */ new Map(),
296
- scanLock: false,
297
- _setScanState: (partial) => set((state) => ({
298
- autoScanState: { ...state.autoScanState, ...partial }
299
- })),
300
- updateElementIssue: (id, issue) => set((state) => {
301
- const newCache = new Map(state.elementIssuesCache);
302
- newCache.set(id, issue);
303
- return { elementIssuesCache: newCache };
304
- }),
305
- updateFileIssues: (filePath, issues) => set((state) => {
306
- const newCache = new Map(state.fileIssuesCache);
307
- if (issues.length > 0) {
308
- newCache.set(filePath, issues);
309
- } else {
310
- newCache.delete(filePath);
311
- }
312
- return { fileIssuesCache: newCache };
313
- }),
314
- enableLiveScan: async (hideNodeModules) => {
315
- const state = get();
316
- if (state.scanLock) {
317
- console.warn("UILint: Scan already in progress");
318
- return;
319
- }
320
- set({
321
- liveScanEnabled: true,
322
- scanLock: true
323
- });
324
- const elements = scanDOMForSources(document.body, hideNodeModules);
325
- const initialCache = /* @__PURE__ */ new Map();
326
- for (const el of elements) {
327
- initialCache.set(el.id, {
328
- elementId: el.id,
329
- issues: [],
330
- status: "pending"
331
- });
332
- }
333
- set({
334
- elementIssuesCache: initialCache,
335
- autoScanState: {
336
- status: "scanning",
337
- currentIndex: 0,
338
- totalElements: elements.length,
339
- elements
340
- }
341
- });
342
- await get()._runScanLoop(elements);
343
- },
344
- disableLiveScan: () => {
345
- set({
346
- liveScanEnabled: false,
347
- scanLock: false,
348
- autoScanState: DEFAULT_AUTO_SCAN_STATE,
349
- elementIssuesCache: /* @__PURE__ */ new Map(),
350
- fileIssuesCache: /* @__PURE__ */ new Map()
351
- });
352
- },
353
- scanNewElements: async (newElements) => {
354
- const state = get();
355
- if (!state.liveScanEnabled) return;
356
- if (newElements.length === 0) return;
357
- set((s) => {
358
- const newCache = new Map(s.elementIssuesCache);
359
- for (const el of newElements) {
360
- newCache.set(el.id, {
361
- elementId: el.id,
362
- issues: [],
363
- status: "pending"
364
- });
365
- }
366
- return {
367
- elementIssuesCache: newCache,
368
- autoScanState: {
369
- ...s.autoScanState,
370
- elements: [...s.autoScanState.elements, ...newElements],
371
- totalElements: s.autoScanState.totalElements + newElements.length
372
- }
373
- };
374
- });
375
- const sourceFiles = groupBySourceFile(newElements);
376
- for (const sourceFile of sourceFiles) {
377
- for (const el of sourceFile.elements) {
378
- get().updateElementIssue(el.id, {
379
- elementId: el.id,
380
- issues: [],
381
- status: "scanning"
382
- });
383
- }
384
- await new Promise((resolve) => requestAnimationFrame(resolve));
385
- const { issues, error } = await scanFileForIssues(sourceFile, get());
386
- distributeIssuesToElements(
387
- issues,
388
- sourceFile.elements,
389
- sourceFile.path,
390
- get().updateElementIssue,
391
- get().updateFileIssues,
392
- error ?? false
393
- );
394
- if (get().wsConnected && get().wsConnection) {
395
- get().subscribeToFile(sourceFile.path);
396
- }
397
- await new Promise((resolve) => requestAnimationFrame(resolve));
398
- }
399
- },
400
- _runScanLoop: async (elements) => {
401
- const sourceFiles = groupBySourceFile(elements);
402
- let processedElements = 0;
403
- for (const sourceFile of sourceFiles) {
404
- if (!get().liveScanEnabled) {
405
- set({
406
- scanLock: false,
407
- autoScanState: DEFAULT_AUTO_SCAN_STATE
408
- });
409
- return;
410
- }
411
- get()._setScanState({ currentIndex: processedElements });
412
- for (const el of sourceFile.elements) {
413
- get().updateElementIssue(el.id, {
414
- elementId: el.id,
415
- issues: [],
416
- status: "scanning"
417
- });
418
- }
419
- await new Promise((resolve) => requestAnimationFrame(resolve));
420
- const { issues, error } = await scanFileForIssues(sourceFile, get());
421
- distributeIssuesToElements(
422
- issues,
423
- sourceFile.elements,
424
- sourceFile.path,
425
- get().updateElementIssue,
426
- get().updateFileIssues,
427
- error ?? false
428
- );
429
- if (get().wsConnected && get().wsConnection) {
430
- get().subscribeToFile(sourceFile.path);
431
- }
432
- processedElements += sourceFile.elements.length;
433
- await new Promise((resolve) => requestAnimationFrame(resolve));
434
- }
435
- set({
436
- scanLock: false,
437
- autoScanState: {
438
- ...get().autoScanState,
439
- status: "complete",
440
- currentIndex: elements.length
441
- }
442
- });
443
- },
444
- // ============ DOM Observer ============
445
- removeStaleResults: (elementIds) => set((state) => {
446
- const newCache = new Map(state.elementIssuesCache);
447
- const newElements = state.autoScanState.elements.filter(
448
- (el) => !elementIds.includes(el.id)
449
- );
450
- for (const id of elementIds) {
451
- newCache.delete(id);
452
- }
453
- return {
454
- elementIssuesCache: newCache,
455
- autoScanState: {
456
- ...state.autoScanState,
457
- elements: newElements,
458
- totalElements: newElements.length
459
- }
460
- };
461
- }),
462
- // ============ File/Element Selection ============
463
- hoveredFilePath: null,
464
- selectedFilePath: null,
465
- selectedElementId: null,
466
- hoveredElementId: null,
467
- setHoveredFilePath: (path) => set({ hoveredFilePath: path }),
468
- setSelectedFilePath: (path) => set({ selectedFilePath: path }),
469
- setSelectedElementId: (id) => set({ selectedElementId: id }),
470
- setHoveredElementId: (id) => set({ hoveredElementId: id }),
471
- // ============ WebSocket ============
472
- wsConnection: null,
473
- wsConnected: false,
474
- wsUrl: DEFAULT_WS_URL,
475
- wsReconnectAttempts: 0,
476
- eslintIssuesCache: /* @__PURE__ */ new Map(),
477
- wsProgressPhase: /* @__PURE__ */ new Map(),
478
- wsLastActivity: null,
479
- wsRecentResults: [],
480
- workspaceRoot: null,
481
- appRoot: null,
482
- serverCwd: null,
483
- connectWebSocket: (url) => {
484
- const targetUrl = url || get().wsUrl;
485
- const existing = get().wsConnection;
486
- if (existing && existing.readyState !== WebSocket.CLOSED) {
487
- existing.close();
488
- }
489
- if (typeof WebSocket === "undefined") {
490
- console.warn("[UILint] WebSocket not available in this environment");
491
- return;
492
- }
493
- try {
494
- const ws = new WebSocket(targetUrl);
495
- ws.onopen = () => {
496
- console.log("[UILint] WebSocket connected to", targetUrl);
497
- set({
498
- wsConnected: true,
499
- wsReconnectAttempts: 0,
500
- wsUrl: targetUrl
501
- });
502
- };
503
- ws.onclose = () => {
504
- console.log("[UILint] WebSocket disconnected");
505
- set({ wsConnected: false, wsConnection: null });
506
- const attempts = get().wsReconnectAttempts;
507
- if (attempts < MAX_RECONNECT_ATTEMPTS) {
508
- const delay = RECONNECT_BASE_DELAY * Math.pow(2, attempts);
509
- console.log(
510
- `[UILint] Reconnecting in ${delay}ms (attempt ${attempts + 1})`
511
- );
512
- setTimeout(() => {
513
- set({ wsReconnectAttempts: attempts + 1 });
514
- get()._reconnectWebSocket();
515
- }, delay);
516
- }
517
- };
518
- ws.onerror = (error) => {
519
- console.error("[UILint] WebSocket error:", error);
520
- };
521
- ws.onmessage = (event) => {
522
- try {
523
- const data = JSON.parse(event.data);
524
- get()._handleWsMessage(data);
525
- } catch (err) {
526
- console.error("[UILint] Failed to parse WebSocket message:", err);
527
- }
528
- };
529
- set({ wsConnection: ws, wsUrl: targetUrl });
530
- } catch (err) {
531
- console.error("[UILint] Failed to create WebSocket:", err);
532
- }
533
- },
534
- disconnectWebSocket: () => {
535
- const ws = get().wsConnection;
536
- if (ws) {
537
- ws.close();
538
- set({
539
- wsConnection: null,
540
- wsConnected: false,
541
- wsReconnectAttempts: MAX_RECONNECT_ATTEMPTS
542
- });
543
- }
544
- },
545
- requestFileLint: async (filePath) => {
546
- const { wsConnection, wsConnected, eslintIssuesCache } = get();
547
- const cached = eslintIssuesCache.get(filePath);
548
- if (cached) {
549
- console.log("[UILint] using cached issues for", filePath);
550
- return cached;
551
- }
552
- if (!wsConnected || !wsConnection) {
553
- console.log("[UILint] WebSocket not connected, using HTTP fallback");
554
- return [];
555
- }
556
- return new Promise((resolve, reject) => {
557
- const requestId = makeRequestId();
558
- pendingRequests.set(requestId, { resolve, reject });
559
- const message = {
560
- type: "lint:file",
561
- filePath,
562
- requestId
563
- };
564
- wsConnection.send(JSON.stringify(message));
565
- setTimeout(() => {
566
- if (pendingRequests.has(requestId)) {
567
- pendingRequests.delete(requestId);
568
- reject(new Error("Request timed out"));
569
- }
570
- }, WS_REQUEST_TIMEOUT_MS);
571
- });
572
- },
573
- requestElementLint: async (filePath, dataLoc) => {
574
- const { wsConnection, wsConnected } = get();
575
- if (!wsConnected || !wsConnection) {
576
- console.log("[UILint] WebSocket not connected, using HTTP fallback");
577
- return [];
578
- }
579
- return new Promise((resolve, reject) => {
580
- const requestId = makeRequestId();
581
- pendingRequests.set(requestId, { resolve, reject });
582
- const message = {
583
- type: "lint:element",
584
- filePath,
585
- dataLoc,
586
- requestId
587
- };
588
- wsConnection.send(JSON.stringify(message));
589
- setTimeout(() => {
590
- if (pendingRequests.has(requestId)) {
591
- pendingRequests.delete(requestId);
592
- reject(new Error("Request timed out"));
593
- }
594
- }, WS_REQUEST_TIMEOUT_MS);
595
- });
596
- },
597
- subscribeToFile: (filePath) => {
598
- const { wsConnection, wsConnected } = get();
599
- if (!wsConnected || !wsConnection) return;
600
- const message = { type: "subscribe:file", filePath };
601
- wsConnection.send(JSON.stringify(message));
602
- },
603
- invalidateCache: (filePath) => {
604
- const { wsConnection, wsConnected } = get();
605
- if (filePath) {
606
- set((state) => {
607
- const next = new Map(state.eslintIssuesCache);
608
- next.delete(filePath);
609
- return { eslintIssuesCache: next };
610
- });
611
- } else {
612
- set({ eslintIssuesCache: /* @__PURE__ */ new Map() });
613
- }
614
- if (wsConnected && wsConnection) {
615
- const message = {
616
- type: "cache:invalidate",
617
- filePath
618
- };
619
- wsConnection.send(JSON.stringify(message));
620
- }
621
- },
622
- _handleWsMessage: (data) => {
623
- switch (data.type) {
624
- case "lint:result": {
625
- const { filePath, issues, requestId } = data;
626
- set((state2) => {
627
- const next = new Map(state2.eslintIssuesCache);
628
- next.set(filePath, issues);
629
- return { eslintIssuesCache: next };
630
- });
631
- const state = get();
632
- if (state.liveScanEnabled) {
633
- const sourceFiles = groupBySourceFile(state.autoScanState.elements);
634
- const sf = sourceFiles.find((s) => s.path === filePath);
635
- if (sf) {
636
- distributeIssuesToElements(
637
- issues,
638
- sf.elements,
639
- filePath,
640
- state.updateElementIssue,
641
- state.updateFileIssues,
642
- false
643
- );
644
- } else {
645
- const unmappedIssues = issues.filter((i) => !i.dataLoc);
646
- if (unmappedIssues.length > 0) {
647
- state.updateFileIssues(filePath, unmappedIssues);
648
- }
649
- }
650
- }
651
- set((state2) => {
652
- const next = new Map(state2.wsProgressPhase);
653
- next.delete(filePath);
654
- return { wsProgressPhase: next };
655
- });
656
- set((state2) => {
657
- const next = [
658
- { filePath, issueCount: issues.length, updatedAt: Date.now() },
659
- ...state2.wsRecentResults.filter((r) => r.filePath !== filePath)
660
- ].slice(0, 8);
661
- return { wsRecentResults: next };
662
- });
663
- set({
664
- wsLastActivity: {
665
- filePath,
666
- phase: `Done (${issues.length} issues)`,
667
- updatedAt: Date.now()
668
- }
669
- });
670
- if (requestId) {
671
- const pending = pendingRequests.get(requestId);
672
- if (pending) {
673
- pending.resolve(issues);
674
- pendingRequests.delete(requestId);
675
- }
676
- }
677
- break;
678
- }
679
- case "lint:progress": {
680
- const { filePath, phase } = data;
681
- set((state) => {
682
- const next = new Map(state.wsProgressPhase);
683
- next.set(filePath, phase);
684
- return {
685
- wsProgressPhase: next,
686
- wsLastActivity: { filePath, phase, updatedAt: Date.now() }
687
- };
688
- });
689
- break;
690
- }
691
- case "file:changed": {
692
- const { filePath } = data;
693
- set((state2) => {
694
- const next = new Map(state2.eslintIssuesCache);
695
- next.delete(filePath);
696
- return { eslintIssuesCache: next };
697
- });
698
- const state = get();
699
- if (state.liveScanEnabled) {
700
- const sourceFiles = groupBySourceFile(state.autoScanState.elements);
701
- const sf = sourceFiles.find((s) => s.path === filePath);
702
- if (sf) {
703
- for (const el of sf.elements) {
704
- const existing = state.elementIssuesCache.get(el.id);
705
- state.updateElementIssue(el.id, {
706
- elementId: el.id,
707
- issues: existing?.issues || [],
708
- status: "scanning"
709
- });
710
- }
711
- state.requestFileLint(filePath).catch(() => {
712
- for (const el of sf.elements) {
713
- const existing = state.elementIssuesCache.get(el.id);
714
- state.updateElementIssue(el.id, {
715
- elementId: el.id,
716
- issues: existing?.issues || [],
717
- status: "error"
718
- });
719
- }
720
- });
721
- }
722
- }
723
- break;
724
- }
725
- case "workspace:info": {
726
- const { appRoot, workspaceRoot, serverCwd } = data;
727
- console.log("[UILint] Received workspace info:", {
728
- appRoot,
729
- workspaceRoot,
730
- serverCwd
731
- });
732
- set({ appRoot, workspaceRoot, serverCwd });
733
- break;
734
- }
735
- }
736
- },
737
- _reconnectWebSocket: () => {
738
- const { wsUrl } = get();
739
- get().connectWebSocket(wsUrl);
740
- }
741
- }));
742
-
743
- // src/components/ui-lint/UILintProvider.tsx
744
- import {
745
- createContext,
746
- useContext,
747
- useState,
748
- useEffect as useEffect2,
749
- useCallback as useCallback2,
750
- useMemo
751
- } from "react";
752
-
753
- // src/components/ui-lint/useDOMObserver.ts
754
- import { useEffect, useRef, useCallback } from "react";
755
- var RECONCILE_DEBOUNCE_MS = 100;
756
- function useDOMObserver(enabled = true) {
757
- const observerRef = useRef(null);
758
- const reconcileTimeoutRef = useRef(
759
- null
760
- );
761
- const knownElementIdsRef = useRef(/* @__PURE__ */ new Set());
762
- const liveScanEnabled = useUILintStore((s) => s.liveScanEnabled);
763
- const settings = useUILintStore((s) => s.settings);
764
- const autoScanState = useUILintStore((s) => s.autoScanState);
765
- const removeStaleResults = useUILintStore(
766
- (s) => s.removeStaleResults
767
- );
768
- const scanNewElements = useUILintStore((s) => s.scanNewElements);
769
- useEffect(() => {
770
- if (autoScanState.elements.length > 0) {
771
- const ids = new Set(autoScanState.elements.map((el) => el.id));
772
- knownElementIdsRef.current = ids;
773
- }
774
- }, [autoScanState.elements]);
775
- const reconcileElements = useCallback(() => {
776
- if (!liveScanEnabled) return;
777
- const currentElements = scanDOMForSources(
778
- document.body,
779
- settings.hideNodeModules
780
- );
781
- const currentIds = new Set(currentElements.map((el) => el.id));
782
- const knownIds = knownElementIdsRef.current;
783
- const newElements = [];
784
- for (const el of currentElements) {
785
- if (!knownIds.has(el.id)) {
786
- newElements.push(el);
787
- }
788
- }
789
- const removedElementIds = [];
790
- for (const id of knownIds) {
791
- if (!currentIds.has(id)) {
792
- removedElementIds.push(id);
793
- }
794
- }
795
- knownElementIdsRef.current = currentIds;
796
- if (newElements.length > 0) {
797
- scanNewElements(newElements);
798
- }
799
- if (removedElementIds.length > 0) {
800
- removeStaleResults(removedElementIds);
801
- }
802
- }, [
803
- liveScanEnabled,
804
- settings.hideNodeModules,
805
- scanNewElements,
806
- removeStaleResults
807
- ]);
808
- const debouncedReconcile = useCallback(() => {
809
- if (reconcileTimeoutRef.current) {
810
- clearTimeout(reconcileTimeoutRef.current);
811
- }
812
- reconcileTimeoutRef.current = setTimeout(() => {
813
- reconcileElements();
814
- reconcileTimeoutRef.current = null;
815
- }, RECONCILE_DEBOUNCE_MS);
816
- }, [reconcileElements]);
817
- useEffect(() => {
818
- if (!enabled) return;
819
- if (typeof window === "undefined") return;
820
- const observer = new MutationObserver((mutations) => {
821
- let hasRelevantChanges = false;
822
- for (const mutation of mutations) {
823
- for (const node of mutation.addedNodes) {
824
- if (node instanceof Element) {
825
- if (node.hasAttribute("data-loc")) {
826
- hasRelevantChanges = true;
827
- break;
828
- }
829
- if (node.querySelector("[data-loc]")) {
830
- hasRelevantChanges = true;
831
- break;
832
- }
833
- }
834
- }
835
- if (hasRelevantChanges) break;
836
- for (const node of mutation.removedNodes) {
837
- if (node instanceof Element) {
838
- if (node.hasAttribute("data-loc") || node.querySelector("[data-loc]")) {
839
- hasRelevantChanges = true;
840
- break;
841
- }
842
- }
843
- }
844
- if (hasRelevantChanges) break;
845
- }
846
- if (hasRelevantChanges) {
847
- debouncedReconcile();
848
- }
849
- });
850
- observer.observe(document.body, {
851
- childList: true,
852
- subtree: true
853
- });
854
- observerRef.current = observer;
855
- return () => {
856
- observer.disconnect();
857
- observerRef.current = null;
858
- if (reconcileTimeoutRef.current) {
859
- clearTimeout(reconcileTimeoutRef.current);
860
- reconcileTimeoutRef.current = null;
861
- }
862
- };
863
- }, [enabled, debouncedReconcile]);
864
- }
865
-
866
- // src/components/ui-lint/UILintProvider.tsx
867
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
868
- var UILintContext = createContext(null);
869
- function useUILintContext() {
870
- const context = useContext(UILintContext);
871
- if (!context) {
872
- throw new Error("useUILintContext must be used within a UILintProvider");
873
- }
874
- return context;
875
- }
876
- function isBrowser() {
877
- return typeof window !== "undefined";
878
- }
879
- function UILintProvider({
880
- children,
881
- enabled = true
882
- }) {
883
- const [isMounted, setIsMounted] = useState(false);
884
- const settings = useUILintStore((s) => s.settings);
885
- const updateSettings = useUILintStore((s) => s.updateSettings);
886
- const altKeyHeld = useUILintStore((s) => s.altKeyHeld);
887
- const setAltKeyHeld = useUILintStore((s) => s.setAltKeyHeld);
888
- const locatorTarget = useUILintStore((s) => s.locatorTarget);
889
- const setLocatorTarget = useUILintStore(
890
- (s) => s.setLocatorTarget
891
- );
892
- const inspectedElement = useUILintStore(
893
- (s) => s.inspectedElement
894
- );
895
- const setInspectedElement = useUILintStore(
896
- (s) => s.setInspectedElement
897
- );
898
- const liveScanEnabled = useUILintStore((s) => s.liveScanEnabled);
899
- const autoScanState = useUILintStore((s) => s.autoScanState);
900
- const elementIssuesCache = useUILintStore(
901
- (s) => s.elementIssuesCache
902
- );
903
- const storeEnableLiveScan = useUILintStore(
904
- (s) => s.enableLiveScan
905
- );
906
- const disableLiveScan = useUILintStore((s) => s.disableLiveScan);
907
- const connectWebSocket = useUILintStore(
908
- (s) => s.connectWebSocket
909
- );
910
- const disconnectWebSocket = useUILintStore(
911
- (s) => s.disconnectWebSocket
912
- );
913
- useDOMObserver(enabled && isMounted);
914
- const getLocatorTargetFromElement = useCallback2(
915
- (element) => {
916
- if (element.closest("[data-ui-lint]")) return null;
917
- const source = getSourceFromDataLoc(element);
918
- if (!source) return null;
919
- if (settings.hideNodeModules && isNodeModulesPath(source.fileName)) {
920
- return null;
921
- }
922
- return {
923
- element,
924
- source,
925
- rect: element.getBoundingClientRect()
926
- };
927
- },
928
- [settings.hideNodeModules]
929
- );
930
- const handleMouseMove = useCallback2(
931
- (e) => {
932
- if (!altKeyHeld) return;
933
- const elementAtPoint = document.elementFromPoint(e.clientX, e.clientY);
934
- if (!elementAtPoint) {
935
- setLocatorTarget(null);
936
- return;
937
- }
938
- let current = elementAtPoint;
939
- while (current) {
940
- const target = getLocatorTargetFromElement(current);
941
- if (target) {
942
- setLocatorTarget(target);
943
- return;
944
- }
945
- current = current.parentElement;
946
- }
947
- setLocatorTarget(null);
948
- },
949
- [altKeyHeld, getLocatorTargetFromElement, setLocatorTarget]
950
- );
951
- const handleLocatorClick = useCallback2(
952
- (e) => {
953
- if (!altKeyHeld || !locatorTarget) return;
954
- const targetEl = e.target;
955
- if (targetEl?.closest?.("[data-ui-lint]")) return;
956
- e.preventDefault();
957
- e.stopPropagation();
958
- setInspectedElement({
959
- element: locatorTarget.element,
960
- source: locatorTarget.source,
961
- rect: locatorTarget.rect
962
- });
963
- setLocatorTarget(null);
964
- },
965
- [altKeyHeld, locatorTarget, setInspectedElement, setLocatorTarget]
966
- );
967
- useEffect2(() => {
968
- if (!isBrowser() || !enabled) return;
969
- const handleKeyDown = (e) => {
970
- if (e.key === "Alt") {
971
- setAltKeyHeld(true);
972
- }
973
- };
974
- const handleKeyUp = (e) => {
975
- if (e.key === "Alt") {
976
- setAltKeyHeld(false);
977
- setLocatorTarget(null);
978
- }
979
- };
980
- const handleBlur = () => {
981
- setAltKeyHeld(false);
982
- setLocatorTarget(null);
983
- };
984
- window.addEventListener("keydown", handleKeyDown);
985
- window.addEventListener("keyup", handleKeyUp);
986
- window.addEventListener("blur", handleBlur);
987
- return () => {
988
- window.removeEventListener("keydown", handleKeyDown);
989
- window.removeEventListener("keyup", handleKeyUp);
990
- window.removeEventListener("blur", handleBlur);
991
- };
992
- }, [enabled, setAltKeyHeld, setLocatorTarget]);
993
- useEffect2(() => {
994
- if (!isBrowser() || !enabled) return;
995
- if (!altKeyHeld) return;
996
- window.addEventListener("mousemove", handleMouseMove);
997
- window.addEventListener("click", handleLocatorClick, true);
998
- return () => {
999
- window.removeEventListener("mousemove", handleMouseMove);
1000
- window.removeEventListener("click", handleLocatorClick, true);
1001
- };
1002
- }, [enabled, altKeyHeld, handleMouseMove, handleLocatorClick]);
1003
- useEffect2(() => {
1004
- if (!isBrowser() || !enabled) return;
1005
- const handleKeyDown = (e) => {
1006
- if (e.key === "Escape" && inspectedElement) {
1007
- setInspectedElement(null);
1008
- }
1009
- };
1010
- window.addEventListener("keydown", handleKeyDown);
1011
- return () => window.removeEventListener("keydown", handleKeyDown);
1012
- }, [enabled, inspectedElement, setInspectedElement]);
1013
- useEffect2(() => {
1014
- setIsMounted(true);
1015
- }, []);
1016
- useEffect2(() => {
1017
- if (!isBrowser() || !enabled) return;
1018
- if (!isMounted) return;
1019
- connectWebSocket();
1020
- return () => {
1021
- disconnectWebSocket();
1022
- };
1023
- }, [enabled, isMounted, connectWebSocket, disconnectWebSocket]);
1024
- const enableLiveScan = useCallback2(() => {
1025
- storeEnableLiveScan(settings.hideNodeModules);
1026
- }, [storeEnableLiveScan, settings.hideNodeModules]);
1027
- const contextValue = useMemo(
1028
- () => ({
1029
- settings,
1030
- updateSettings,
1031
- altKeyHeld,
1032
- locatorTarget,
1033
- inspectedElement,
1034
- setInspectedElement,
1035
- liveScanEnabled,
1036
- autoScanState,
1037
- elementIssuesCache,
1038
- enableLiveScan,
1039
- disableLiveScan
1040
- }),
1041
- [
1042
- settings,
1043
- updateSettings,
1044
- altKeyHeld,
1045
- locatorTarget,
1046
- inspectedElement,
1047
- setInspectedElement,
1048
- liveScanEnabled,
1049
- autoScanState,
1050
- elementIssuesCache,
1051
- enableLiveScan,
1052
- disableLiveScan
1053
- ]
1054
- );
1055
- const shouldRenderUI = enabled && isMounted;
1056
- return /* @__PURE__ */ jsxs(UILintContext.Provider, { value: contextValue, children: [
1057
- children,
1058
- shouldRenderUI && /* @__PURE__ */ jsx(UILintUI, {})
1059
- ] });
1060
- }
1061
- function UILintUI() {
1062
- const { altKeyHeld, inspectedElement, liveScanEnabled } = useUILintContext();
1063
- const [components, setComponents] = useState(null);
1064
- useEffect2(() => {
1065
- Promise.all([
1066
- import("./UILintToolbar-CLVXQHCZ.js"),
1067
- import("./InspectionPanel-NXSE7CMH.js"),
1068
- import("./LocatorOverlay-3TKK74BD.js"),
1069
- import("./ElementBadges-2CTPMJ6L.js")
1070
- ]).then(([toolbar, panel, locator, badges]) => {
1071
- setComponents({
1072
- Toolbar: toolbar.UILintToolbar,
1073
- Panel: panel.InspectionPanel,
1074
- LocatorOverlay: locator.LocatorOverlay,
1075
- InspectedHighlight: locator.InspectedElementHighlight,
1076
- ElementBadges: badges.ElementBadges
1077
- });
1078
- });
1079
- }, []);
1080
- if (!components) return null;
1081
- const { Toolbar, Panel, LocatorOverlay, InspectedHighlight, ElementBadges } = components;
1082
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1083
- /* @__PURE__ */ jsx(Toolbar, {}),
1084
- (altKeyHeld || inspectedElement) && /* @__PURE__ */ jsx(LocatorOverlay, {}),
1085
- liveScanEnabled && /* @__PURE__ */ jsx(ElementBadges, {}),
1086
- inspectedElement && /* @__PURE__ */ jsxs(Fragment, { children: [
1087
- /* @__PURE__ */ jsx(InspectedHighlight, {}),
1088
- /* @__PURE__ */ jsx(Panel, {})
1089
- ] })
1090
- ] });
1091
- }
1092
-
1093
- export {
1094
- getSourceFromDataLoc,
1095
- isNodeModulesPath,
1096
- getDisplayName,
1097
- scanDOMForSources,
1098
- groupBySourceFile,
1099
- cleanupDataAttributes,
1100
- getElementById,
1101
- updateElementRects,
1102
- buildEditorUrl,
1103
- FILE_COLORS,
1104
- DEFAULT_SETTINGS,
1105
- DATA_UILINT_ID,
1106
- useUILintStore,
1107
- useUILintContext,
1108
- UILintProvider
1109
- };