uilint-react 0.1.31 → 0.1.33
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.
- package/dist/{ElementBadges-BSTXYVB7.js → ElementBadges-TBAUB3KM.js} +1 -1
- package/dist/{InspectionPanel-MUUDJ2BK.js → InspectionPanel-JNLWBL4D.js} +2 -2
- package/dist/{LocatorOverlay-XJH5TE2J.js → LocatorOverlay-6SAH7LN2.js} +2 -2
- package/dist/{UILintToolbar-RD6BT3LL.js → UILintToolbar-WNV5RS2L.js} +2 -2
- package/dist/{chunk-GLJBGFZK.js → chunk-HTNIKCEM.js} +219 -2
- package/dist/{chunk-XAAIRXB4.js → chunk-JBBUE3Y5.js} +197 -695
- package/dist/{chunk-M3H56XIZ.js → chunk-MO4NS6EG.js} +296 -59
- package/dist/{chunk-OV5BPTNA.js → chunk-Z6PWYQGW.js} +1 -1
- package/dist/index.d.ts +12 -33
- package/dist/index.js +6 -84
- package/package.json +2 -2
|
@@ -270,43 +270,25 @@ function getDataLocFromId(id) {
|
|
|
270
270
|
}
|
|
271
271
|
return null;
|
|
272
272
|
}
|
|
273
|
-
async function scanFileForIssues(sourceFile) {
|
|
273
|
+
async function scanFileForIssues(sourceFile, store) {
|
|
274
274
|
if (sourceFile.elements.length === 0) {
|
|
275
275
|
return { issues: [] };
|
|
276
276
|
}
|
|
277
277
|
const filePath = sourceFile.path;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
278
|
+
let issues = [];
|
|
279
|
+
if (store.wsConnected && store.wsConnection) {
|
|
280
|
+
try {
|
|
281
|
+
issues = await store.requestFileLint(filePath);
|
|
282
|
+
console.log("[UILint] ESLint issues:", issues);
|
|
283
|
+
} catch (err) {
|
|
284
|
+
console.warn("[UILint] WebSocket lint failed:", err);
|
|
283
285
|
return { issues: [], error: true };
|
|
284
286
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
for (const el of sourceFile.elements) {
|
|
288
|
-
const dataLoc = getDataLocFromId(el.id);
|
|
289
|
-
if (dataLoc) {
|
|
290
|
-
dataLocs.push(dataLoc);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
const analyzeResponse = await fetch("/api/.uilint/analyze", {
|
|
294
|
-
method: "POST",
|
|
295
|
-
headers: { "Content-Type": "application/json" },
|
|
296
|
-
body: JSON.stringify({
|
|
297
|
-
sourceCode: sourceData.content,
|
|
298
|
-
filePath: sourceData.relativePath || filePath,
|
|
299
|
-
dataLocs
|
|
300
|
-
})
|
|
301
|
-
});
|
|
302
|
-
if (!analyzeResponse.ok) {
|
|
303
|
-
return { issues: [], error: true };
|
|
304
|
-
}
|
|
305
|
-
const result = await analyzeResponse.json();
|
|
306
|
-
return { issues: result.issues || [] };
|
|
307
|
-
} catch {
|
|
287
|
+
} else {
|
|
288
|
+
console.warn("[UILint] WebSocket not connected");
|
|
308
289
|
return { issues: [], error: true };
|
|
309
290
|
}
|
|
291
|
+
return { issues };
|
|
310
292
|
}
|
|
311
293
|
function distributeIssuesToElements(issues, elements, updateElementIssue, hasError) {
|
|
312
294
|
const dataLocToElementId = /* @__PURE__ */ new Map();
|
|
@@ -336,6 +318,19 @@ function distributeIssuesToElements(issues, elements, updateElementIssue, hasErr
|
|
|
336
318
|
});
|
|
337
319
|
}
|
|
338
320
|
}
|
|
321
|
+
var DEFAULT_WS_URL = "ws://localhost:9234";
|
|
322
|
+
var MAX_RECONNECT_ATTEMPTS = 5;
|
|
323
|
+
var RECONNECT_BASE_DELAY = 1e3;
|
|
324
|
+
var pendingRequests = /* @__PURE__ */ new Map();
|
|
325
|
+
var WS_REQUEST_TIMEOUT_MS = 12e4;
|
|
326
|
+
function makeRequestId() {
|
|
327
|
+
try {
|
|
328
|
+
const c = globalThis.crypto;
|
|
329
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
330
|
+
} catch {
|
|
331
|
+
}
|
|
332
|
+
return `req_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
333
|
+
}
|
|
339
334
|
var useUILintStore = create()((set, get) => ({
|
|
340
335
|
// ============ Settings ============
|
|
341
336
|
settings: DEFAULT_SETTINGS,
|
|
@@ -362,31 +357,6 @@ var useUILintStore = create()((set, get) => ({
|
|
|
362
357
|
// ============ Inspection ============
|
|
363
358
|
inspectedElement: null,
|
|
364
359
|
setInspectedElement: (el) => set({ inspectedElement: el }),
|
|
365
|
-
// ============ Manual Scan (InspectionPanel) ============
|
|
366
|
-
manualScanCache: /* @__PURE__ */ new Map(),
|
|
367
|
-
upsertManualScan: (key, patch) => set((state) => {
|
|
368
|
-
const next = new Map(state.manualScanCache);
|
|
369
|
-
const existing = next.get(key);
|
|
370
|
-
const base = existing ?? {
|
|
371
|
-
key,
|
|
372
|
-
status: "idle",
|
|
373
|
-
issues: [],
|
|
374
|
-
updatedAt: Date.now()
|
|
375
|
-
};
|
|
376
|
-
next.set(key, {
|
|
377
|
-
...base,
|
|
378
|
-
...patch,
|
|
379
|
-
key,
|
|
380
|
-
updatedAt: Date.now()
|
|
381
|
-
});
|
|
382
|
-
return { manualScanCache: next };
|
|
383
|
-
}),
|
|
384
|
-
clearManualScan: (key) => set((state) => {
|
|
385
|
-
if (!state.manualScanCache.has(key)) return state;
|
|
386
|
-
const next = new Map(state.manualScanCache);
|
|
387
|
-
next.delete(key);
|
|
388
|
-
return { manualScanCache: next };
|
|
389
|
-
}),
|
|
390
360
|
// ============ Auto-Scan ============
|
|
391
361
|
autoScanState: DEFAULT_AUTO_SCAN_STATE,
|
|
392
362
|
elementIssuesCache: /* @__PURE__ */ new Map(),
|
|
@@ -492,13 +462,16 @@ var useUILintStore = create()((set, get) => ({
|
|
|
492
462
|
});
|
|
493
463
|
}
|
|
494
464
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
495
|
-
const { issues, error } = await scanFileForIssues(sourceFile);
|
|
465
|
+
const { issues, error } = await scanFileForIssues(sourceFile, get());
|
|
496
466
|
distributeIssuesToElements(
|
|
497
467
|
issues,
|
|
498
468
|
sourceFile.elements,
|
|
499
469
|
get().updateElementIssue,
|
|
500
470
|
error ?? false
|
|
501
471
|
);
|
|
472
|
+
if (get().wsConnected && get().wsConnection) {
|
|
473
|
+
get().subscribeToFile(sourceFile.path);
|
|
474
|
+
}
|
|
502
475
|
processedElements += sourceFile.elements.length;
|
|
503
476
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
504
477
|
}
|
|
@@ -510,6 +483,256 @@ var useUILintStore = create()((set, get) => ({
|
|
|
510
483
|
currentIndex: elements.length
|
|
511
484
|
}
|
|
512
485
|
});
|
|
486
|
+
},
|
|
487
|
+
// ============ WebSocket ============
|
|
488
|
+
wsConnection: null,
|
|
489
|
+
wsConnected: false,
|
|
490
|
+
wsUrl: DEFAULT_WS_URL,
|
|
491
|
+
wsReconnectAttempts: 0,
|
|
492
|
+
eslintIssuesCache: /* @__PURE__ */ new Map(),
|
|
493
|
+
wsProgressPhase: /* @__PURE__ */ new Map(),
|
|
494
|
+
wsLastActivity: null,
|
|
495
|
+
wsRecentResults: [],
|
|
496
|
+
connectWebSocket: (url) => {
|
|
497
|
+
const targetUrl = url || get().wsUrl;
|
|
498
|
+
const existing = get().wsConnection;
|
|
499
|
+
if (existing && existing.readyState !== WebSocket.CLOSED) {
|
|
500
|
+
existing.close();
|
|
501
|
+
}
|
|
502
|
+
if (typeof WebSocket === "undefined") {
|
|
503
|
+
console.warn("[UILint] WebSocket not available in this environment");
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
try {
|
|
507
|
+
const ws = new WebSocket(targetUrl);
|
|
508
|
+
ws.onopen = () => {
|
|
509
|
+
console.log("[UILint] WebSocket connected to", targetUrl);
|
|
510
|
+
set({
|
|
511
|
+
wsConnected: true,
|
|
512
|
+
wsReconnectAttempts: 0,
|
|
513
|
+
wsUrl: targetUrl
|
|
514
|
+
});
|
|
515
|
+
};
|
|
516
|
+
ws.onclose = () => {
|
|
517
|
+
console.log("[UILint] WebSocket disconnected");
|
|
518
|
+
set({ wsConnected: false, wsConnection: null });
|
|
519
|
+
const attempts = get().wsReconnectAttempts;
|
|
520
|
+
if (attempts < MAX_RECONNECT_ATTEMPTS) {
|
|
521
|
+
const delay = RECONNECT_BASE_DELAY * Math.pow(2, attempts);
|
|
522
|
+
console.log(
|
|
523
|
+
`[UILint] Reconnecting in ${delay}ms (attempt ${attempts + 1})`
|
|
524
|
+
);
|
|
525
|
+
setTimeout(() => {
|
|
526
|
+
set({ wsReconnectAttempts: attempts + 1 });
|
|
527
|
+
get()._reconnectWebSocket();
|
|
528
|
+
}, delay);
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
ws.onerror = (error) => {
|
|
532
|
+
console.error("[UILint] WebSocket error:", error);
|
|
533
|
+
};
|
|
534
|
+
ws.onmessage = (event) => {
|
|
535
|
+
try {
|
|
536
|
+
const data = JSON.parse(event.data);
|
|
537
|
+
get()._handleWsMessage(data);
|
|
538
|
+
} catch (err) {
|
|
539
|
+
console.error("[UILint] Failed to parse WebSocket message:", err);
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
set({ wsConnection: ws, wsUrl: targetUrl });
|
|
543
|
+
} catch (err) {
|
|
544
|
+
console.error("[UILint] Failed to create WebSocket:", err);
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
disconnectWebSocket: () => {
|
|
548
|
+
const ws = get().wsConnection;
|
|
549
|
+
if (ws) {
|
|
550
|
+
ws.close();
|
|
551
|
+
set({
|
|
552
|
+
wsConnection: null,
|
|
553
|
+
wsConnected: false,
|
|
554
|
+
wsReconnectAttempts: MAX_RECONNECT_ATTEMPTS
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
requestFileLint: async (filePath) => {
|
|
559
|
+
const { wsConnection, wsConnected, eslintIssuesCache } = get();
|
|
560
|
+
const cached = eslintIssuesCache.get(filePath);
|
|
561
|
+
if (cached) {
|
|
562
|
+
console.log("[UILint] using cached issues for", filePath);
|
|
563
|
+
return cached;
|
|
564
|
+
}
|
|
565
|
+
if (!wsConnected || !wsConnection) {
|
|
566
|
+
console.log("[UILint] WebSocket not connected, using HTTP fallback");
|
|
567
|
+
return [];
|
|
568
|
+
}
|
|
569
|
+
return new Promise((resolve, reject) => {
|
|
570
|
+
const requestId = makeRequestId();
|
|
571
|
+
pendingRequests.set(requestId, { resolve, reject });
|
|
572
|
+
const message = {
|
|
573
|
+
type: "lint:file",
|
|
574
|
+
filePath,
|
|
575
|
+
requestId
|
|
576
|
+
};
|
|
577
|
+
wsConnection.send(JSON.stringify(message));
|
|
578
|
+
setTimeout(() => {
|
|
579
|
+
if (pendingRequests.has(requestId)) {
|
|
580
|
+
pendingRequests.delete(requestId);
|
|
581
|
+
reject(new Error("Request timed out"));
|
|
582
|
+
}
|
|
583
|
+
}, WS_REQUEST_TIMEOUT_MS);
|
|
584
|
+
});
|
|
585
|
+
},
|
|
586
|
+
requestElementLint: async (filePath, dataLoc) => {
|
|
587
|
+
const { wsConnection, wsConnected } = get();
|
|
588
|
+
if (!wsConnected || !wsConnection) {
|
|
589
|
+
console.log("[UILint] WebSocket not connected, using HTTP fallback");
|
|
590
|
+
return [];
|
|
591
|
+
}
|
|
592
|
+
return new Promise((resolve, reject) => {
|
|
593
|
+
const requestId = makeRequestId();
|
|
594
|
+
pendingRequests.set(requestId, { resolve, reject });
|
|
595
|
+
const message = {
|
|
596
|
+
type: "lint:element",
|
|
597
|
+
filePath,
|
|
598
|
+
dataLoc,
|
|
599
|
+
requestId
|
|
600
|
+
};
|
|
601
|
+
wsConnection.send(JSON.stringify(message));
|
|
602
|
+
setTimeout(() => {
|
|
603
|
+
if (pendingRequests.has(requestId)) {
|
|
604
|
+
pendingRequests.delete(requestId);
|
|
605
|
+
reject(new Error("Request timed out"));
|
|
606
|
+
}
|
|
607
|
+
}, WS_REQUEST_TIMEOUT_MS);
|
|
608
|
+
});
|
|
609
|
+
},
|
|
610
|
+
subscribeToFile: (filePath) => {
|
|
611
|
+
const { wsConnection, wsConnected } = get();
|
|
612
|
+
if (!wsConnected || !wsConnection) return;
|
|
613
|
+
const message = { type: "subscribe:file", filePath };
|
|
614
|
+
wsConnection.send(JSON.stringify(message));
|
|
615
|
+
},
|
|
616
|
+
invalidateCache: (filePath) => {
|
|
617
|
+
const { wsConnection, wsConnected } = get();
|
|
618
|
+
if (filePath) {
|
|
619
|
+
set((state) => {
|
|
620
|
+
const next = new Map(state.eslintIssuesCache);
|
|
621
|
+
next.delete(filePath);
|
|
622
|
+
return { eslintIssuesCache: next };
|
|
623
|
+
});
|
|
624
|
+
} else {
|
|
625
|
+
set({ eslintIssuesCache: /* @__PURE__ */ new Map() });
|
|
626
|
+
}
|
|
627
|
+
if (wsConnected && wsConnection) {
|
|
628
|
+
const message = {
|
|
629
|
+
type: "cache:invalidate",
|
|
630
|
+
filePath
|
|
631
|
+
};
|
|
632
|
+
wsConnection.send(JSON.stringify(message));
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
_handleWsMessage: (data) => {
|
|
636
|
+
switch (data.type) {
|
|
637
|
+
case "lint:result": {
|
|
638
|
+
const { filePath, issues, requestId } = data;
|
|
639
|
+
set((state2) => {
|
|
640
|
+
const next = new Map(state2.eslintIssuesCache);
|
|
641
|
+
next.set(filePath, issues);
|
|
642
|
+
return { eslintIssuesCache: next };
|
|
643
|
+
});
|
|
644
|
+
const state = get();
|
|
645
|
+
if (state.autoScanState.status !== "idle") {
|
|
646
|
+
const sourceFiles = groupBySourceFile(state.autoScanState.elements);
|
|
647
|
+
const sf = sourceFiles.find((s) => s.path === filePath);
|
|
648
|
+
if (sf) {
|
|
649
|
+
distributeIssuesToElements(
|
|
650
|
+
issues,
|
|
651
|
+
sf.elements,
|
|
652
|
+
state.updateElementIssue,
|
|
653
|
+
false
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
set((state2) => {
|
|
658
|
+
const next = new Map(state2.wsProgressPhase);
|
|
659
|
+
next.delete(filePath);
|
|
660
|
+
return { wsProgressPhase: next };
|
|
661
|
+
});
|
|
662
|
+
set((state2) => {
|
|
663
|
+
const next = [
|
|
664
|
+
{ filePath, issueCount: issues.length, updatedAt: Date.now() },
|
|
665
|
+
...state2.wsRecentResults.filter((r) => r.filePath !== filePath)
|
|
666
|
+
].slice(0, 8);
|
|
667
|
+
return { wsRecentResults: next };
|
|
668
|
+
});
|
|
669
|
+
set({
|
|
670
|
+
wsLastActivity: {
|
|
671
|
+
filePath,
|
|
672
|
+
phase: `Done (${issues.length} issues)`,
|
|
673
|
+
updatedAt: Date.now()
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
if (requestId) {
|
|
677
|
+
const pending = pendingRequests.get(requestId);
|
|
678
|
+
if (pending) {
|
|
679
|
+
pending.resolve(issues);
|
|
680
|
+
pendingRequests.delete(requestId);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
case "lint:progress": {
|
|
686
|
+
const { filePath, phase } = data;
|
|
687
|
+
set((state) => {
|
|
688
|
+
const next = new Map(state.wsProgressPhase);
|
|
689
|
+
next.set(filePath, phase);
|
|
690
|
+
return {
|
|
691
|
+
wsProgressPhase: next,
|
|
692
|
+
wsLastActivity: { filePath, phase, updatedAt: Date.now() }
|
|
693
|
+
};
|
|
694
|
+
});
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
case "file:changed": {
|
|
698
|
+
const { filePath } = data;
|
|
699
|
+
set((state2) => {
|
|
700
|
+
const next = new Map(state2.eslintIssuesCache);
|
|
701
|
+
next.delete(filePath);
|
|
702
|
+
return { eslintIssuesCache: next };
|
|
703
|
+
});
|
|
704
|
+
const state = get();
|
|
705
|
+
if (state.autoScanState.status !== "idle") {
|
|
706
|
+
const sourceFiles = groupBySourceFile(state.autoScanState.elements);
|
|
707
|
+
const sf = sourceFiles.find((s) => s.path === filePath);
|
|
708
|
+
if (sf) {
|
|
709
|
+
for (const el of sf.elements) {
|
|
710
|
+
const existing = state.elementIssuesCache.get(el.id);
|
|
711
|
+
state.updateElementIssue(el.id, {
|
|
712
|
+
elementId: el.id,
|
|
713
|
+
issues: existing?.issues || [],
|
|
714
|
+
status: "scanning"
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
state.requestFileLint(filePath).catch(() => {
|
|
718
|
+
for (const el of sf.elements) {
|
|
719
|
+
const existing = state.elementIssuesCache.get(el.id);
|
|
720
|
+
state.updateElementIssue(el.id, {
|
|
721
|
+
elementId: el.id,
|
|
722
|
+
issues: existing?.issues || [],
|
|
723
|
+
status: "error"
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
_reconnectWebSocket: () => {
|
|
734
|
+
const { wsUrl } = get();
|
|
735
|
+
get().connectWebSocket(wsUrl);
|
|
513
736
|
}
|
|
514
737
|
}));
|
|
515
738
|
function useEffectiveLocatorTarget() {
|
|
@@ -579,6 +802,12 @@ function UILintProvider({
|
|
|
579
802
|
const pauseAutoScan = useUILintStore((s) => s.pauseAutoScan);
|
|
580
803
|
const resumeAutoScan = useUILintStore((s) => s.resumeAutoScan);
|
|
581
804
|
const stopAutoScan = useUILintStore((s) => s.stopAutoScan);
|
|
805
|
+
const connectWebSocket = useUILintStore(
|
|
806
|
+
(s) => s.connectWebSocket
|
|
807
|
+
);
|
|
808
|
+
const disconnectWebSocket = useUILintStore(
|
|
809
|
+
(s) => s.disconnectWebSocket
|
|
810
|
+
);
|
|
582
811
|
const effectiveLocatorTarget = useEffectiveLocatorTarget();
|
|
583
812
|
const getLocatorTargetFromElement = useCallback(
|
|
584
813
|
(element) => {
|
|
@@ -748,6 +977,14 @@ function UILintProvider({
|
|
|
748
977
|
useEffect(() => {
|
|
749
978
|
setIsMounted(true);
|
|
750
979
|
}, []);
|
|
980
|
+
useEffect(() => {
|
|
981
|
+
if (!isBrowser() || !enabled) return;
|
|
982
|
+
if (!isMounted) return;
|
|
983
|
+
connectWebSocket();
|
|
984
|
+
return () => {
|
|
985
|
+
disconnectWebSocket();
|
|
986
|
+
};
|
|
987
|
+
}, [enabled, isMounted, connectWebSocket, disconnectWebSocket]);
|
|
751
988
|
const wrappedStartAutoScan = useCallback(() => {
|
|
752
989
|
startAutoScan(settings.hideNodeModules);
|
|
753
990
|
}, [startAutoScan, settings.hideNodeModules]);
|
|
@@ -796,10 +1033,10 @@ function UILintUI() {
|
|
|
796
1033
|
const [components, setComponents] = useState(null);
|
|
797
1034
|
useEffect(() => {
|
|
798
1035
|
Promise.all([
|
|
799
|
-
import("./UILintToolbar-
|
|
800
|
-
import("./InspectionPanel-
|
|
801
|
-
import("./LocatorOverlay-
|
|
802
|
-
import("./ElementBadges-
|
|
1036
|
+
import("./UILintToolbar-WNV5RS2L.js"),
|
|
1037
|
+
import("./InspectionPanel-JNLWBL4D.js"),
|
|
1038
|
+
import("./LocatorOverlay-6SAH7LN2.js"),
|
|
1039
|
+
import("./ElementBadges-TBAUB3KM.js")
|
|
803
1040
|
]).then(([toolbar, panel, locator, badges]) => {
|
|
804
1041
|
setComponents({
|
|
805
1042
|
Toolbar: toolbar.UILintToolbar,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import React$1 from 'react';
|
|
3
|
-
import { GroupedSnapshot, Violation, DOMSnapshot
|
|
3
|
+
import { GroupedSnapshot, Violation, DOMSnapshot } from 'uilint-core';
|
|
4
4
|
export { AnalysisResult, ConsistencyResult, DOMSnapshot, ElementRole, ElementSnapshot, ExtractedStyles, GroupedSnapshot, SerializedStyles, StyleGuide, StyleSnapshot, UILintIssue, Violation, ViolationCategory, ViolationSeverity, createEmptyStyleGuide, createStyleSummary, extractStylesFromDOM, generateStyleGuideFromStyles as generateStyleGuide, mergeStyleGuides, parseStyleGuide, serializeStyles } from 'uilint-core';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -59,14 +59,18 @@ interface AutoScanState {
|
|
|
59
59
|
elements: ScannedElement[];
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* ESLint issue from WebSocket server (uilint serve)
|
|
63
63
|
*/
|
|
64
|
-
interface
|
|
64
|
+
interface ESLintIssue {
|
|
65
65
|
/** Line number in source file */
|
|
66
|
-
line
|
|
66
|
+
line: number;
|
|
67
|
+
/** Column number */
|
|
68
|
+
column?: number;
|
|
67
69
|
/** Issue description */
|
|
68
70
|
message: string;
|
|
69
|
-
/**
|
|
71
|
+
/** ESLint rule ID (e.g., "uilint/semantic", "uilint/no-arbitrary-tailwind") */
|
|
72
|
+
ruleId?: string;
|
|
73
|
+
/** data-loc value to match to DOM element */
|
|
70
74
|
dataLoc?: string;
|
|
71
75
|
}
|
|
72
76
|
/**
|
|
@@ -74,7 +78,8 @@ interface ScanIssue {
|
|
|
74
78
|
*/
|
|
75
79
|
interface ElementIssue {
|
|
76
80
|
elementId: string;
|
|
77
|
-
|
|
81
|
+
/** ESLint rule violations from uilint-eslint (including semantic rule) */
|
|
82
|
+
issues: ESLintIssue[];
|
|
78
83
|
status: "pending" | "scanning" | "complete" | "error";
|
|
79
84
|
}
|
|
80
85
|
/**
|
|
@@ -361,30 +366,4 @@ declare function isJSDOM(): boolean;
|
|
|
361
366
|
*/
|
|
362
367
|
declare function isNode(): boolean;
|
|
363
368
|
|
|
364
|
-
|
|
365
|
-
* LLM client for browser environment
|
|
366
|
-
* Uses uilint-core for prompts and wraps API calls
|
|
367
|
-
*/
|
|
368
|
-
|
|
369
|
-
interface LLMClientOptions {
|
|
370
|
-
apiEndpoint?: string;
|
|
371
|
-
model?: string;
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Client for communicating with the LLM via API route (browser environment)
|
|
375
|
-
*/
|
|
376
|
-
declare class LLMClient {
|
|
377
|
-
private apiEndpoint;
|
|
378
|
-
private model;
|
|
379
|
-
constructor(options?: LLMClientOptions);
|
|
380
|
-
/**
|
|
381
|
-
* Analyzes extracted styles and returns issues
|
|
382
|
-
*/
|
|
383
|
-
analyze(styles: ExtractedStyles, styleGuide: string | null): Promise<AnalysisResult>;
|
|
384
|
-
/**
|
|
385
|
-
* Generates a style guide from detected styles
|
|
386
|
-
*/
|
|
387
|
-
generateStyleGuide(styles: ExtractedStyles): Promise<string | null>;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
export { type CachedSource, type ComponentInfo, ConsistencyHighlighter, DATA_UILINT_ID, DEFAULT_SETTINGS, FILE_COLORS, type InspectedElement, InspectionPanel, LLMClient, LocatorOverlay, type LocatorTarget, type ScannedElement, type SourceApiResponse, type SourceFile, type SourceLocation, type UILintContextValue, UILintProvider, type UILintProviderProps, type UILintSettings, UILintToolbar, buildEditorUrl, cleanupDataAttributes, cleanupDataElements, clearSourceCache, createSnapshot, fetchSource, fetchSourceWithContext, getCachedSource, getComponentStack, getDebugOwner, getDebugSource, getDisplayName, getElementById, getElementBySnapshotId, getFiberFromElement, groupBySourceFile, isBrowser, isJSDOM, isNode, isNodeModulesPath, prefetchSources, scanDOM, scanDOMForSources, updateElementRects, useUILintContext };
|
|
369
|
+
export { type CachedSource, type ComponentInfo, ConsistencyHighlighter, DATA_UILINT_ID, DEFAULT_SETTINGS, FILE_COLORS, type InspectedElement, InspectionPanel, LocatorOverlay, type LocatorTarget, type ScannedElement, type SourceApiResponse, type SourceFile, type SourceLocation, type UILintContextValue, UILintProvider, type UILintProviderProps, type UILintSettings, UILintToolbar, buildEditorUrl, cleanupDataAttributes, cleanupDataElements, clearSourceCache, createSnapshot, fetchSource, fetchSourceWithContext, getCachedSource, getComponentStack, getDebugOwner, getDebugSource, getDisplayName, getElementById, getElementBySnapshotId, getFiberFromElement, groupBySourceFile, isBrowser, isJSDOM, isNode, isNodeModulesPath, prefetchSources, scanDOM, scanDOMForSources, updateElementRects, useUILintContext };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
UILintToolbar
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HTNIKCEM.js";
|
|
5
5
|
import {
|
|
6
6
|
InspectionPanel,
|
|
7
7
|
clearSourceCache,
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
fetchSourceWithContext,
|
|
10
10
|
getCachedSource,
|
|
11
11
|
prefetchSources
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JBBUE3Y5.js";
|
|
13
13
|
import {
|
|
14
14
|
LocatorOverlay
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-Z6PWYQGW.js";
|
|
16
16
|
import {
|
|
17
17
|
DATA_UILINT_ID,
|
|
18
18
|
DEFAULT_SETTINGS,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
scanDOMForSources,
|
|
32
32
|
updateElementRects,
|
|
33
33
|
useUILintContext
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-MO4NS6EG.js";
|
|
35
35
|
|
|
36
36
|
// src/consistency/snapshot.ts
|
|
37
37
|
var DATA_ELEMENTS_ATTR = "data-elements";
|
|
@@ -439,85 +439,8 @@ function isNode() {
|
|
|
439
439
|
import {
|
|
440
440
|
extractStylesFromDOM as extractStylesFromDOM2,
|
|
441
441
|
serializeStyles as serializeStyles2,
|
|
442
|
-
createStyleSummary as
|
|
442
|
+
createStyleSummary as createStyleSummary2
|
|
443
443
|
} from "uilint-core";
|
|
444
|
-
|
|
445
|
-
// src/analyzer/llm-client.ts
|
|
446
|
-
import {
|
|
447
|
-
createStyleSummary as createStyleSummary2,
|
|
448
|
-
buildAnalysisPrompt,
|
|
449
|
-
buildStyleGuidePrompt,
|
|
450
|
-
UILINT_DEFAULT_OLLAMA_MODEL
|
|
451
|
-
} from "uilint-core";
|
|
452
|
-
var DEFAULT_API_ENDPOINT = "/api/.uilint/analyze";
|
|
453
|
-
var LLMClient = class {
|
|
454
|
-
apiEndpoint;
|
|
455
|
-
model;
|
|
456
|
-
constructor(options = {}) {
|
|
457
|
-
this.apiEndpoint = options.apiEndpoint || DEFAULT_API_ENDPOINT;
|
|
458
|
-
this.model = options.model || UILINT_DEFAULT_OLLAMA_MODEL;
|
|
459
|
-
}
|
|
460
|
-
/**
|
|
461
|
-
* Analyzes extracted styles and returns issues
|
|
462
|
-
*/
|
|
463
|
-
async analyze(styles, styleGuide) {
|
|
464
|
-
const startTime = Date.now();
|
|
465
|
-
const styleSummary = createStyleSummary2(styles);
|
|
466
|
-
try {
|
|
467
|
-
const response = await fetch(this.apiEndpoint, {
|
|
468
|
-
method: "POST",
|
|
469
|
-
headers: { "Content-Type": "application/json" },
|
|
470
|
-
body: JSON.stringify({
|
|
471
|
-
styleSummary,
|
|
472
|
-
styleGuide,
|
|
473
|
-
model: this.model
|
|
474
|
-
})
|
|
475
|
-
});
|
|
476
|
-
if (!response.ok) {
|
|
477
|
-
throw new Error(`API request failed: ${response.status}`);
|
|
478
|
-
}
|
|
479
|
-
const data = await response.json();
|
|
480
|
-
return {
|
|
481
|
-
issues: data.issues || [],
|
|
482
|
-
suggestedStyleGuide: data.suggestedStyleGuide,
|
|
483
|
-
analysisTime: Date.now() - startTime
|
|
484
|
-
};
|
|
485
|
-
} catch (error) {
|
|
486
|
-
console.error("[UILint] Analysis failed:", error);
|
|
487
|
-
return {
|
|
488
|
-
issues: [],
|
|
489
|
-
analysisTime: Date.now() - startTime
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
/**
|
|
494
|
-
* Generates a style guide from detected styles
|
|
495
|
-
*/
|
|
496
|
-
async generateStyleGuide(styles) {
|
|
497
|
-
const styleSummary = createStyleSummary2(styles);
|
|
498
|
-
try {
|
|
499
|
-
const response = await fetch(this.apiEndpoint, {
|
|
500
|
-
method: "POST",
|
|
501
|
-
headers: { "Content-Type": "application/json" },
|
|
502
|
-
body: JSON.stringify({
|
|
503
|
-
styleSummary,
|
|
504
|
-
generateGuide: true,
|
|
505
|
-
model: this.model
|
|
506
|
-
})
|
|
507
|
-
});
|
|
508
|
-
if (!response.ok) {
|
|
509
|
-
throw new Error(`API request failed: ${response.status}`);
|
|
510
|
-
}
|
|
511
|
-
const data = await response.json();
|
|
512
|
-
return data.styleGuide || null;
|
|
513
|
-
} catch (error) {
|
|
514
|
-
console.error("[UILint] Style guide generation failed:", error);
|
|
515
|
-
return null;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
// src/index.ts
|
|
521
444
|
import { parseStyleGuide } from "uilint-core";
|
|
522
445
|
import { generateStyleGuideFromStyles } from "uilint-core";
|
|
523
446
|
import { createEmptyStyleGuide, mergeStyleGuides } from "uilint-core";
|
|
@@ -527,7 +450,6 @@ export {
|
|
|
527
450
|
DEFAULT_SETTINGS,
|
|
528
451
|
FILE_COLORS,
|
|
529
452
|
InspectionPanel,
|
|
530
|
-
LLMClient,
|
|
531
453
|
LocatorOverlay,
|
|
532
454
|
UILintProvider,
|
|
533
455
|
UILintToolbar,
|
|
@@ -537,7 +459,7 @@ export {
|
|
|
537
459
|
clearSourceCache,
|
|
538
460
|
createEmptyStyleGuide,
|
|
539
461
|
createSnapshot,
|
|
540
|
-
|
|
462
|
+
createStyleSummary2 as createStyleSummary,
|
|
541
463
|
extractStylesFromDOM2 as extractStylesFromDOM,
|
|
542
464
|
fetchSource,
|
|
543
465
|
fetchSourceWithContext,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uilint-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"description": "React component for AI-powered UI consistency checking",
|
|
5
5
|
"author": "Peter Suggate",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"node": ">=20.0.0"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"uilint-core": "^0.1.
|
|
37
|
+
"uilint-core": "^0.1.33",
|
|
38
38
|
"zustand": "^5.0.5"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|