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.
@@ -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
- try {
279
- const sourceResponse = await fetch(
280
- `/api/.uilint/source?path=${encodeURIComponent(filePath)}`
281
- );
282
- if (!sourceResponse.ok) {
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
- const sourceData = await sourceResponse.json();
286
- const dataLocs = [];
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-RD6BT3LL.js"),
800
- import("./InspectionPanel-MUUDJ2BK.js"),
801
- import("./LocatorOverlay-XJH5TE2J.js"),
802
- import("./ElementBadges-BSTXYVB7.js")
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,
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  useUILintContext
4
- } from "./chunk-M3H56XIZ.js";
4
+ } from "./chunk-MO4NS6EG.js";
5
5
 
6
6
  // src/components/ui-lint/LocatorOverlay.tsx
7
7
  import { useState, useEffect, useMemo } from "react";
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, ExtractedStyles, AnalysisResult } from 'uilint-core';
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
- * A single issue found during scanning
62
+ * ESLint issue from WebSocket server (uilint serve)
63
63
  */
64
- interface ScanIssue {
64
+ interface ESLintIssue {
65
65
  /** Line number in source file */
66
- line?: number;
66
+ line: number;
67
+ /** Column number */
68
+ column?: number;
67
69
  /** Issue description */
68
70
  message: string;
69
- /** data-loc value to match to DOM element (format: path:line:column) */
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
- issues: ScanIssue[];
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-GLJBGFZK.js";
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-XAAIRXB4.js";
12
+ } from "./chunk-JBBUE3Y5.js";
13
13
  import {
14
14
  LocatorOverlay
15
- } from "./chunk-OV5BPTNA.js";
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-M3H56XIZ.js";
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 createStyleSummary3
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
- createStyleSummary3 as createStyleSummary,
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.31",
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.31",
37
+ "uilint-core": "^0.1.33",
38
38
  "zustand": "^5.0.5"
39
39
  },
40
40
  "peerDependencies": {