uilint-core 0.2.147 → 0.2.149

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.
@@ -1795,4 +1795,4 @@ export {
1795
1795
  createOperationActions,
1796
1796
  createOperationMessageHandlers
1797
1797
  };
1798
- //# sourceMappingURL=chunk-J4LUG7RV.js.map
1798
+ //# sourceMappingURL=chunk-OGU2FLMN.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ollama/prompts.ts","../src/ollama/defaults.ts","../src/ollama/client.ts","../src/format/issues.ts","../src/tailwind/class-tokens.ts","../src/scanner/style-extractor.ts","../src/styleguide/schema.ts","../src/styleguide/parser.ts","../src/styleguide/generator.ts","../src/consistency/prompts.ts","../src/consistency/analyzer.ts","../src/logger.ts","../src/plugin/registry.ts","../src/plugin/types.ts","../src/plugin/operation.ts"],"sourcesContent":["/**\n * LLM prompt builders for UILint analysis\n */\n\n/**\n * Builds a prompt for style analysis\n */\nexport function buildAnalysisPrompt(\n styleSummary: string,\n styleGuide: string | null\n): string {\n const guideSection = styleGuide\n ? `## Current Style Guide\\n${styleGuide}\\n\\n`\n : \"## No Style Guide Found\\nAnalyze the styles and identify inconsistencies.\\n\\n\";\n\n return `You are a UI consistency analyzer. Analyze the following extracted styles and identify UI consistency violations.\n\n${guideSection}\n\n${styleSummary}\n\nRespond with JSON ONLY. Return a single JSON object containing an \"issues\" array. Each issue should have:\n- id: unique string identifier\n- type: one of \"color\", \"typography\", \"spacing\", \"component\", \"responsive\", \"accessibility\"\n- message: human-readable description of the issue\n- currentValue: the problematic value found\n- expectedValue: what it should be (if known from style guide)\n\nIMPORTANT:\n- Output ONLY violations. Do NOT include fix instructions, remediation steps, or \"suggestions\".\n- Do NOT include extra top-level keys. Only return {\"issues\":[...]}.\n- Keep messages short and neutral.\n\nFocus on:\n1. Similar but not identical colors (e.g., #3B82F6 vs #3575E2)\n2. Inconsistent font sizes or weights\n3. Spacing values that don't follow a consistent scale (e.g., 4px base unit)\n4. Mixed border-radius values\n5. If utility/Tailwind classes are present in the summary, treat them as the styling surface area and flag inconsistent utility usage (e.g., mixing px-4 and px-5 for the same component type)\n\nBe minimal. Only report significant inconsistencies.\n\nExample response:\n{\n \"issues\": [\n {\n ...issue fields...\n }\n ]\n}`;\n}\n\nexport interface BuildSourceAnalysisPromptOptions {\n /**\n * Optional filename/path for extra context in the prompt.\n */\n filePath?: string;\n /**\n * Optional hint about language (e.g. tsx, jsx, html).\n */\n languageHint?: string;\n /**\n * Optional additional context (e.g., Tailwind tokens, extracted utilities).\n * Keep this concise; it's appended verbatim.\n */\n extraContext?: string;\n}\n\n/**\n * Builds a prompt for analyzing a raw source file/snippet (TSX/JSX/etc) directly,\n * without attempting to parse it as HTML/DOM.\n */\nexport function buildSourceAnalysisPrompt(\n source: string,\n styleGuide: string | null,\n options: BuildSourceAnalysisPromptOptions = {}\n): string {\n const guideSection = styleGuide\n ? `## Current Style Guide\\n${styleGuide}\\n\\n`\n : \"## No Style Guide Found\\nAnalyze the UI code and identify inconsistencies.\\n\\n\";\n\n const metaLines: string[] = [];\n if (options.filePath) metaLines.push(`- filePath: ${options.filePath}`);\n if (options.languageHint)\n metaLines.push(`- languageHint: ${options.languageHint}`);\n const metaSection =\n metaLines.length > 0\n ? `## Source Metadata\\n${metaLines.join(\"\\n\")}\\n\\n`\n : \"\";\n\n const extra =\n options.extraContext && options.extraContext.trim()\n ? `## Additional Context\\n${options.extraContext.trim()}\\n\\n`\n : \"\";\n\n return `You are a UI consistency analyzer. Analyze the following UI source code and identify UI consistency violations.\n\n${guideSection}${metaSection}${extra}## Source Code (raw)\n${source}\n\nRespond with JSON ONLY. Return a single JSON object containing an \"issues\" array. Each issue should have:\n- id: unique string identifier\n- type: one of \"color\", \"typography\", \"spacing\", \"component\", \"responsive\", \"accessibility\"\n- message: human-readable description of the issue\n- currentValue: the problematic value found\n- expectedValue: what it should be (if known from style guide)\n\nIMPORTANT:\n- Output ONLY violations. Do NOT include fix instructions, remediation steps, or \"suggestions\".\n- Do NOT include extra top-level keys. Only return {\"issues\":[...]}.\n- Keep messages short and neutral.\n\nFocus on:\n1. Inconsistent Tailwind/utility classes (e.g., mixing px-4 and px-5 for the same component type)\n2. Inconsistent component variants (e.g., button sizes/radii/typography drift)\n3. Hardcoded colors/spacing/typography that should use tokens/scale\n4. Accessibility issues visible from code (e.g., missing labels, low-contrast combos if obvious)\n\nBe minimal. Only report significant inconsistencies.`;\n}\n\nexport interface BuildSourceScanPromptOptions {\n /**\n * Display filename/path for context in the prompt.\n */\n filePath?: string;\n /**\n * Optional focus target for manual scan UX.\n */\n componentName?: string;\n componentLine?: number;\n /**\n * If true, the scan is for the selected element + children (UI text only).\n */\n includeChildren?: boolean;\n /**\n * Optional list of data-loc values (format: \"path:line:column\") that the model MUST\n * restrict issues to (so UI can highlight exactly).\n */\n dataLocs?: string[];\n}\n\n/**\n * Builds a prompt for scanning a source file/snippet for issues that can be mapped\n * back to rendered DOM elements via `data-loc`.\n *\n * Response schema (JSON):\n * { \"issues\": [{ \"line\"?: number, \"message\": string, \"dataLoc\"?: string }] }\n */\nexport function buildSourceScanPrompt(\n sourceCode: string,\n styleGuide: string | null,\n options: BuildSourceScanPromptOptions = {}\n): string {\n const fileLabel = options.filePath || \"component.tsx\";\n const dataLocList = Array.isArray(options.dataLocs) ? options.dataLocs : [];\n\n const focusSection =\n options.componentName && options.componentName.trim()\n ? `## Focus\n\nFocus on the \"${options.componentName.trim()}\" component${\n typeof options.componentLine === \"number\"\n ? ` (near line ${options.componentLine})`\n : \"\"\n }.\n\n`\n : \"\";\n\n const scopeText =\n options.includeChildren === true\n ? \"Scope: selected element + children.\"\n : \"Scope: selected element only.\";\n\n const dataLocSection =\n dataLocList.length > 0\n ? `## Element Locations (data-loc values)\n\nThe following elements are rendered on the page from this file. Each data-loc has format \"path:line:column\".\nWhen reporting issues, include the matching dataLoc value so we can highlight the specific element.\n\n\\`\\`\\`\n${dataLocList.join(\"\\n\")}\n\\`\\`\\`\n\n`\n : \"\";\n\n const guideSection = styleGuide\n ? `## Style Guide\n\n${styleGuide}\n\n`\n : \"\";\n\n return `You are a UI code reviewer. Analyze the following React/TypeScript UI code for style consistency issues.\n\n${guideSection}${focusSection}${scopeText}\n\n${dataLocSection}## Source Code (${fileLabel})\n\n\\`\\`\\`tsx\n${sourceCode}\n\\`\\`\\`\n\n## Task\n\nIdentify any style inconsistencies, violations of best practices, or deviations from the style guide.\nFor each issue, provide:\n- line: the line number in the source file\n- message: a clear description of the issue\n- dataLoc: the matching data-loc value from the list above (if applicable, to identify the specific element)\n\n## Critical Requirements for dataLoc\n\n- If \"Element Locations (data-loc values)\" are provided, ONLY report issues that correspond to one of those elements.\n- When you include a dataLoc, it MUST be copied verbatim from the list (no shortening paths, no normalization).\n- If you cannot match an issue to a provided dataLoc, omit that issue from the response.\n\nRespond with JSON:\n\\`\\`\\`json\n{\n \"issues\": [\n { \"line\": 12, \"message\": \"Color #3575E2 should be #3B82F6 (primary blue from styleguide)\", \"dataLoc\": \"app/page.tsx:12:5\" }\n ]\n}\n\\`\\`\\`\n\nIf no issues are found, respond with:\n\\`\\`\\`json\n{ \"issues\": [] }\n\\`\\`\\``;\n}\n\n/**\n * Builds a prompt for style guide generation\n */\nexport function buildStyleGuidePrompt(styleSummary: string): string {\n return `You are a design system expert. Based on the following detected styles, generate a clean, organized style guide in Markdown format.\n\n${styleSummary}\n\nGenerate a style guide with these sections:\n1. Colors - List the main colors with semantic names (Primary, Secondary, etc.)\n2. Typography - Font families, sizes, and weights\n3. Spacing - Base unit and common spacing values\n4. Components - Common component patterns\n5. Tailwind (if utility classes are present) - list commonly used utilities and any relevant theme tokens\n\nUse this format:\n# UI Style Guide\n\n## Colors\n- **Primary**: #HEXCODE (usage description)\n- **Secondary**: #HEXCODE (usage description)\n...\n\n## Typography\n- **Font Family**: FontName\n- **Font Sizes**: list of sizes\n- **Font Weights**: list of weights\n\n## Spacing\n- **Base unit**: Xpx\n- **Common values**: list of values\n\n## Components\n- **Buttons**: styles\n- **Cards**: styles\n...\n\nBe concise and focus on the most used values.`;\n}\n","/**\n * Single source of truth for Ollama defaults used across the monorepo.\n */\n\n/**\n * Default Ollama model used when none is provided.\n *\n * Note: This should match the model we recommend users `ollama pull`.\n */\nexport const UILINT_DEFAULT_OLLAMA_MODEL = \"qwen3-vl:8b-instruct\";\n","/**\n * Ollama API client for LLM interactions\n */\n\nimport type {\n UILintIssue,\n AnalysisResult,\n OllamaClientOptions,\n StreamProgressCallback,\n LLMInstrumentationCallbacks,\n} from \"../types.js\";\nimport {\n buildAnalysisPrompt,\n buildSourceAnalysisPrompt,\n buildStyleGuidePrompt,\n type BuildSourceAnalysisPromptOptions,\n} from \"./prompts.js\";\nimport { UILINT_DEFAULT_OLLAMA_MODEL } from \"./defaults.js\";\n\nconst DEFAULT_BASE_URL = \"http://localhost:11434\";\nconst DEFAULT_TIMEOUT = 120000;\n\nexport class OllamaClient {\n private baseUrl: string;\n private model: string;\n private timeout: number;\n private instrumentation?: LLMInstrumentationCallbacks;\n\n constructor(options: OllamaClientOptions = {}) {\n this.baseUrl = options.baseUrl || DEFAULT_BASE_URL;\n this.model = options.model || UILINT_DEFAULT_OLLAMA_MODEL;\n this.timeout = options.timeout || DEFAULT_TIMEOUT;\n this.instrumentation = options.instrumentation;\n }\n\n /**\n * Low-level completion API for custom prompts (used by installers/tools).\n *\n * When `json` is true, Ollama is requested to emit JSON (best-effort).\n */\n async complete(\n prompt: string,\n options: {\n json?: boolean;\n stream?: boolean;\n onProgress?: StreamProgressCallback;\n } = {}\n ): Promise<string> {\n const jsonFormat = options.json ?? false;\n if (options.stream && options.onProgress) {\n return await this.generateStreaming(\n prompt,\n jsonFormat,\n options.onProgress\n );\n }\n return await this.generate(prompt, jsonFormat);\n }\n\n /**\n * Analyzes styles and returns issues\n */\n async analyzeStyles(\n styleSummary: string,\n styleGuide: string | null,\n onProgress?: StreamProgressCallback\n ): Promise<AnalysisResult> {\n const startTime = Date.now();\n const prompt = buildAnalysisPrompt(styleSummary, styleGuide);\n\n try {\n const response = onProgress\n ? await this.generateStreaming(\n prompt,\n true,\n onProgress,\n \"analyze-styles\"\n )\n : await this.generate(prompt, true, \"analyze-styles\");\n const issues = this.parseIssuesResponse(response);\n\n return {\n issues,\n analysisTime: Date.now() - startTime,\n };\n } catch (error) {\n console.error(\"[UILint] Analysis failed:\", error);\n return {\n issues: [],\n analysisTime: Date.now() - startTime,\n };\n }\n }\n\n /**\n * Analyzes a raw source file/snippet (TSX/JSX/etc) directly and returns issues.\n * This bypasses HTML/DOM parsing entirely.\n */\n async analyzeSource(\n source: string,\n styleGuide: string | null,\n onProgress?: StreamProgressCallback,\n options: BuildSourceAnalysisPromptOptions = {}\n ): Promise<AnalysisResult> {\n const startTime = Date.now();\n const prompt = buildSourceAnalysisPrompt(source, styleGuide, options);\n\n try {\n const response = onProgress\n ? await this.generateStreaming(\n prompt,\n true,\n onProgress,\n \"analyze-source\"\n )\n : await this.generate(prompt, true, \"analyze-source\");\n const issues = this.parseIssuesResponse(response);\n\n return {\n issues,\n analysisTime: Date.now() - startTime,\n };\n } catch (error) {\n console.error(\"[UILint] Analysis failed:\", error);\n return {\n issues: [],\n analysisTime: Date.now() - startTime,\n };\n }\n }\n\n /**\n * Generates a style guide from detected styles\n */\n async generateStyleGuide(styleSummary: string): Promise<string | null> {\n const prompt = buildStyleGuidePrompt(styleSummary);\n\n try {\n const response = await this.generate(\n prompt,\n false,\n \"generate-styleguide\"\n );\n return response;\n } catch (error) {\n console.error(\"[UILint] Style guide generation failed:\", error);\n return null;\n }\n }\n\n /**\n * Core generate method that calls Ollama API\n */\n private async generate(\n prompt: string,\n jsonFormat: boolean = true,\n operationName: string = \"ollama-generate\"\n ): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Start instrumentation span if available\n const span = this.instrumentation?.onGenerationStart?.({\n name: operationName,\n model: this.model,\n prompt,\n metadata: { jsonFormat, stream: false },\n });\n\n try {\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: this.model,\n prompt,\n stream: false,\n ...(jsonFormat && { format: \"json\" }),\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const error = `Ollama API error: ${response.status}`;\n span?.end(\"\", { error });\n throw new Error(error);\n }\n\n const data = await response.json();\n const output = data.response || \"\";\n\n // End instrumentation span with output and usage\n span?.end(output, {\n promptTokens: data.prompt_eval_count,\n completionTokens: data.eval_count,\n totalTokens:\n (data.prompt_eval_count || 0) + (data.eval_count || 0) || undefined,\n });\n\n return output;\n } catch (error) {\n span?.end(\"\", { error: String(error) });\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Streaming generate method that calls Ollama API with streaming\n * and reports progress via callback\n */\n private async generateStreaming(\n prompt: string,\n jsonFormat: boolean = true,\n onProgress: StreamProgressCallback,\n operationName: string = \"ollama-generate-stream\"\n ): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Start instrumentation span if available\n const span = this.instrumentation?.onGenerationStart?.({\n name: operationName,\n model: this.model,\n prompt,\n metadata: { jsonFormat, stream: true },\n });\n\n // Track token counts from streaming response\n let promptTokens: number | undefined;\n let completionTokens: number | undefined;\n\n try {\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: this.model,\n prompt,\n stream: true,\n ...(jsonFormat && { format: \"json\" }),\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const error = `Ollama API error: ${response.status}`;\n span?.end(\"\", { error });\n throw new Error(error);\n }\n\n if (!response.body) {\n const error = \"No response body for streaming\";\n span?.end(\"\", { error });\n throw new Error(error);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let fullResponse = \"\";\n let lastLineEmitted = \"\";\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete JSON lines from the buffer\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const chunk = JSON.parse(line);\n const delta: string = chunk.response || \"\";\n\n if (delta) {\n fullResponse += delta;\n // Get the latest line from the response for progress display\n const responseLines = fullResponse.split(\"\\n\");\n const latestLine =\n responseLines[responseLines.length - 1] ||\n responseLines[responseLines.length - 2] ||\n \"\";\n lastLineEmitted = latestLine.trim();\n onProgress(lastLineEmitted, fullResponse, delta);\n }\n // Capture token counts from final chunk\n if (chunk.done) {\n promptTokens = chunk.prompt_eval_count;\n completionTokens = chunk.eval_count;\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n\n // Process any remaining content in buffer\n if (buffer.trim()) {\n try {\n const chunk = JSON.parse(buffer);\n const delta: string = chunk.response || \"\";\n if (delta) {\n fullResponse += delta;\n }\n if (chunk.done) {\n promptTokens = chunk.prompt_eval_count;\n completionTokens = chunk.eval_count;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n\n // End instrumentation span with output and usage\n span?.end(fullResponse, {\n promptTokens,\n completionTokens,\n totalTokens: (promptTokens || 0) + (completionTokens || 0) || undefined,\n });\n\n return fullResponse;\n } catch (error) {\n span?.end(\"\", { error: String(error) });\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Parses issues from LLM response\n */\n private parseIssuesResponse(response: string): UILintIssue[] {\n try {\n const parsed = JSON.parse(response);\n return parsed.issues || [];\n } catch {\n console.warn(\"[UILint] Failed to parse LLM response as JSON\");\n return [];\n }\n }\n\n /**\n * Checks if Ollama is available\n */\n async isAvailable(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/api/tags`, {\n method: \"GET\",\n signal: AbortSignal.timeout(5000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /**\n * Gets the current model\n */\n getModel(): string {\n return this.model;\n }\n\n /**\n * Sets the model\n */\n setModel(model: string): void {\n this.model = model;\n }\n\n /**\n * Sets instrumentation callbacks for observability.\n * This allows configuring instrumentation after construction.\n */\n setInstrumentation(\n instrumentation: LLMInstrumentationCallbacks | undefined\n ): void {\n this.instrumentation = instrumentation;\n }\n\n /**\n * Returns true if instrumentation is currently configured.\n */\n hasInstrumentation(): boolean {\n return !!this.instrumentation;\n }\n}\n\n// Default singleton instance\nlet defaultClient: OllamaClient | null = null;\n\nexport function getOllamaClient(options?: OllamaClientOptions): OllamaClient {\n if (!defaultClient || options) {\n defaultClient = new OllamaClient(options);\n }\n return defaultClient;\n}\n","import type { UILintIssue } from \"../types.js\";\n\nexport interface FormatViolationsOptions {\n /**\n * Optional context label (e.g., filename) to include as a single header line.\n * Keep this empty for the most minimal output.\n */\n context?: string;\n /**\n * Include the trailing \"consult the style guide\" message.\n * Defaults to true.\n */\n includeFooter?: boolean;\n /**\n * Override the footer message.\n */\n footerMessage?: string;\n}\n\nconst DEFAULT_FOOTER = \"Consult the style guide for guidance.\";\n\n/**\n * Ensures issues are safe/minimal to print or serialize:\n * - drops `suggestion` (we only want violations)\n * - trims string fields\n */\nexport function sanitizeIssues(issues: UILintIssue[]): UILintIssue[] {\n return issues.map((issue) => ({\n id: String(issue.id ?? \"\").trim(),\n type: issue.type,\n message: String(issue.message ?? \"\").trim(),\n element: issue.element ? String(issue.element).trim() : undefined,\n selector: issue.selector ? String(issue.selector).trim() : undefined,\n currentValue: issue.currentValue ? String(issue.currentValue).trim() : undefined,\n expectedValue: issue.expectedValue\n ? String(issue.expectedValue).trim()\n : undefined,\n // Intentionally omit `suggestion`\n }));\n}\n\n/**\n * Minimal human-readable rendering of violations.\n * Intended for CLI/MCP text output.\n */\nexport function formatViolationsText(\n issues: UILintIssue[],\n options: FormatViolationsOptions = {}\n): string {\n const { context, includeFooter = true, footerMessage = DEFAULT_FOOTER } =\n options;\n\n if (!issues || issues.length === 0) {\n return \"No violations found.\";\n }\n\n const lines: string[] = [];\n\n if (context && context.trim()) {\n lines.push(`Violations in ${context.trim()}:`);\n lines.push(\"\");\n }\n\n const sanitized = sanitizeIssues(issues);\n sanitized.forEach((issue, i) => {\n lines.push(`${i + 1}. [${issue.type}] ${issue.message}`);\n if (issue.currentValue && issue.expectedValue) {\n lines.push(` ${issue.currentValue} → ${issue.expectedValue}`);\n } else if (issue.currentValue) {\n lines.push(` ${issue.currentValue}`);\n }\n lines.push(\"\");\n });\n\n // remove trailing blank line\n while (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop();\n\n if (includeFooter) {\n lines.push(\"\");\n lines.push(footerMessage);\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Tailwind / utility-class token extraction helpers.\n *\n * Notes:\n * - Works on HTML (class=\"...\") and TSX-ish input (className=\"...\") via regex.\n * - Variant parsing is bracket-aware so arbitrary values like bg-[color:var(--x)]\n * don't get split incorrectly on \":\".\n */\n\nexport interface ClassTokenCounts {\n /**\n * Base utilities with variants stripped.\n * Example: \"sm:hover:bg-gray-50\" => utility \"bg-gray-50\"\n */\n utilities: Map<string, number>;\n /**\n * Individual variant prefixes.\n * Example: \"sm:hover:bg-gray-50\" => variants \"sm\", \"hover\"\n */\n variants: Map<string, number>;\n}\n\nexport interface ExtractClassTokenOptions {\n /**\n * Maximum number of tokens to process (guardrail for very large inputs).\n */\n maxTokens?: number;\n}\n\nexport function extractClassTokensFromHtml(\n html: string,\n options: ExtractClassTokenOptions = {}\n): ClassTokenCounts {\n const { maxTokens = 20000 } = options;\n\n const utilities = new Map<string, number>();\n const variants = new Map<string, number>();\n\n if (!html) return { utilities, variants };\n\n // Match both HTML and JSX-ish attributes.\n // - class=\"...\"\n // - className=\"...\"\n const attrPattern = /\\b(?:class|className)\\s*=\\s*[\"']([^\"']+)[\"']/g;\n\n let tokenBudget = maxTokens;\n let match: RegExpExecArray | null;\n while ((match = attrPattern.exec(html)) && tokenBudget > 0) {\n const raw = match[1];\n if (!raw) continue;\n\n const tokens = raw.split(/\\s+/g).filter(Boolean);\n for (const token of tokens) {\n if (tokenBudget-- <= 0) break;\n\n const { base, variantList } = splitVariants(token);\n const normalizedBase = normalizeUtility(base);\n if (!normalizedBase) continue;\n\n increment(utilities, normalizedBase);\n for (const v of variantList) increment(variants, v);\n }\n }\n\n return { utilities, variants };\n}\n\nexport function topEntries(\n map: Map<string, number>,\n limit: number\n): Array<{ token: string; count: number }> {\n return [...map.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, limit)\n .map(([token, count]) => ({ token, count }));\n}\n\nfunction increment(map: Map<string, number>, key: string): void {\n map.set(key, (map.get(key) || 0) + 1);\n}\n\nfunction normalizeUtility(token: string): string | null {\n const t = token.trim();\n if (!t) return null;\n\n // Strip important modifier\n const noImportant = t.startsWith(\"!\") ? t.slice(1) : t;\n\n // Ignore obviously non-class tokens\n if (!noImportant || noImportant === \"{\" || noImportant === \"}\") return null;\n\n return noImportant;\n}\n\n/**\n * Splits a class token into variants and base utility.\n * Bracket-aware to avoid splitting arbitrary values on \":\".\n *\n * Examples:\n * - \"sm:hover:bg-gray-50\" => variants [\"sm\",\"hover\"], base \"bg-gray-50\"\n * - \"bg-[color:var(--x)]\" => variants [], base \"bg-[color:var(--x)]\"\n * - \"sm:bg-[color:var(--x)]\" => variants [\"sm\"], base \"bg-[color:var(--x)]\"\n */\nfunction splitVariants(token: string): { base: string; variantList: string[] } {\n const parts: string[] = [];\n let buf = \"\";\n let bracketDepth = 0;\n\n for (let i = 0; i < token.length; i++) {\n const ch = token[i];\n if (ch === \"[\") bracketDepth++;\n if (ch === \"]\" && bracketDepth > 0) bracketDepth--;\n\n if (ch === \":\" && bracketDepth === 0) {\n parts.push(buf);\n buf = \"\";\n continue;\n }\n\n buf += ch;\n }\n parts.push(buf);\n\n if (parts.length <= 1) return { base: token, variantList: [] };\n\n const base = parts[parts.length - 1] || \"\";\n const variantList = parts\n .slice(0, -1)\n .map((v) => v.trim())\n .filter(Boolean);\n\n return { base, variantList };\n}\n","/**\n * Style extraction from DOM elements\n */\n\nimport type {\n ExtractedStyles,\n SerializedStyles,\n TailwindThemeTokens,\n} from \"../types.js\";\nimport {\n extractClassTokensFromHtml,\n topEntries,\n} from \"../tailwind/class-tokens.js\";\n\n/**\n * Extracts all computed styles from elements in the document\n * Works in both browser and JSDOM environments\n */\nexport function extractStyles(\n root: Element | Document,\n getComputedStyle: (el: Element) => CSSStyleDeclaration\n): ExtractedStyles {\n const styles: ExtractedStyles = {\n colors: new Map(),\n fontSizes: new Map(),\n fontFamilies: new Map(),\n fontWeights: new Map(),\n spacing: new Map(),\n borderRadius: new Map(),\n };\n\n const elements = root.querySelectorAll(\"*\");\n\n elements.forEach((element) => {\n // nodeType === 1 means Element node (works in both browser and JSDOM)\n if (element.nodeType !== 1) return;\n\n const computed = getComputedStyle(element);\n\n // Extract colors\n extractColor(computed.color, styles.colors);\n extractColor(computed.backgroundColor, styles.colors);\n extractColor(computed.borderColor, styles.colors);\n\n // Extract typography\n incrementMap(styles.fontSizes, computed.fontSize);\n incrementMap(styles.fontFamilies, normalizeFontFamily(computed.fontFamily));\n incrementMap(styles.fontWeights, computed.fontWeight);\n\n // Extract spacing\n extractSpacing(computed.margin, styles.spacing);\n extractSpacing(computed.padding, styles.spacing);\n incrementMap(styles.spacing, computed.gap);\n\n // Extract border radius\n incrementMap(styles.borderRadius, computed.borderRadius);\n });\n\n return styles;\n}\n\n/**\n * Extracts styles from browser DOM (uses window.getComputedStyle)\n */\nexport function extractStylesFromDOM(\n root?: Element | Document\n): ExtractedStyles {\n const targetRoot = root || document.body;\n return extractStyles(targetRoot, (el) => window.getComputedStyle(el));\n}\n\nfunction extractColor(color: string, map: Map<string, number>): void {\n if (!color || color === \"transparent\" || color === \"rgba(0, 0, 0, 0)\") return;\n\n // Normalize to hex\n const hex = rgbToHex(color);\n if (hex) {\n incrementMap(map, hex);\n }\n}\n\nfunction extractSpacing(value: string, map: Map<string, number>): void {\n if (!value || value === \"0px\") return;\n\n // Split compound values (e.g., \"10px 20px 10px 20px\")\n const values = value.split(\" \").filter((v) => v && v !== \"0px\");\n values.forEach((v) => incrementMap(map, v));\n}\n\nfunction incrementMap(map: Map<string, number>, value: string): void {\n if (!value || value === \"normal\" || value === \"auto\") return;\n map.set(value, (map.get(value) || 0) + 1);\n}\n\nfunction normalizeFontFamily(fontFamily: string): string {\n // Get the primary font (first in the stack)\n const primary = fontFamily.split(\",\")[0].trim();\n return primary.replace(/['\"]/g, \"\");\n}\n\nfunction rgbToHex(rgb: string): string | null {\n // Handle hex values\n if (rgb.startsWith(\"#\")) return rgb.toUpperCase();\n\n // Handle rgb/rgba values\n const match = rgb.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n if (!match) return null;\n\n const [, r, g, b] = match;\n const toHex = (n: string) => parseInt(n).toString(16).padStart(2, \"0\");\n\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\n}\n\n/**\n * Converts ExtractedStyles maps to plain objects for serialization\n */\nexport function serializeStyles(styles: ExtractedStyles): SerializedStyles {\n return {\n colors: Object.fromEntries(styles.colors),\n fontSizes: Object.fromEntries(styles.fontSizes),\n fontFamilies: Object.fromEntries(styles.fontFamilies),\n fontWeights: Object.fromEntries(styles.fontWeights),\n spacing: Object.fromEntries(styles.spacing),\n borderRadius: Object.fromEntries(styles.borderRadius),\n };\n}\n\n/**\n * Converts SerializedStyles back to ExtractedStyles\n */\nexport function deserializeStyles(\n serialized: SerializedStyles\n): ExtractedStyles {\n return {\n colors: new Map(Object.entries(serialized.colors)),\n fontSizes: new Map(Object.entries(serialized.fontSizes)),\n fontFamilies: new Map(Object.entries(serialized.fontFamilies)),\n fontWeights: new Map(Object.entries(serialized.fontWeights)),\n spacing: new Map(Object.entries(serialized.spacing)),\n borderRadius: new Map(Object.entries(serialized.borderRadius)),\n };\n}\n\n/**\n * Creates a summary of extracted styles for LLM analysis\n */\nexport function createStyleSummary(\n styles: ExtractedStyles,\n options: CreateStyleSummaryOptions = {}\n): string {\n return createStyleSummaryWithOptions(styles, options);\n}\n\nexport interface CreateStyleSummaryOptions {\n /**\n * Optional HTML/TSX-ish string used to extract utility classes (Tailwind etc).\n */\n html?: string;\n /**\n * Optional Tailwind theme tokens (typically from tailwind.config.*).\n */\n tailwindTheme?: TailwindThemeTokens | null;\n}\n\n/**\n * Creates a summary of extracted styles for LLM analysis.\n * Accepts optional Tailwind context to make Tailwind-heavy projects analyzable\n * even when computed styles are sparse (e.g., JSDOM without loaded CSS).\n */\nexport function createStyleSummaryWithOptions(\n styles: ExtractedStyles,\n options: CreateStyleSummaryOptions = {}\n): string {\n const lines: string[] = [];\n\n lines.push(\"## Detected Styles Summary\\n\");\n\n // Colors\n lines.push(\"### Colors\");\n const sortedColors = [...styles.colors.entries()].sort((a, b) => b[1] - a[1]);\n sortedColors.slice(0, 20).forEach(([color, count]) => {\n lines.push(`- ${color}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Font sizes\n lines.push(\"### Font Sizes\");\n const sortedFontSizes = [...styles.fontSizes.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedFontSizes.forEach(([size, count]) => {\n lines.push(`- ${size}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Font families\n lines.push(\"### Font Families\");\n const sortedFontFamilies = [...styles.fontFamilies.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedFontFamilies.forEach(([family, count]) => {\n lines.push(`- ${family}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Font weights\n lines.push(\"### Font Weights\");\n const sortedFontWeights = [...styles.fontWeights.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedFontWeights.forEach(([weight, count]) => {\n lines.push(`- ${weight}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Spacing\n lines.push(\"### Spacing Values\");\n const sortedSpacing = [...styles.spacing.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedSpacing.slice(0, 15).forEach(([value, count]) => {\n lines.push(`- ${value}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Border radius\n lines.push(\"### Border Radius\");\n const sortedBorderRadius = [...styles.borderRadius.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedBorderRadius.forEach(([value, count]) => {\n lines.push(`- ${value}: ${count} occurrences`);\n });\n\n // Tailwind / utility classes (optional)\n if (options.html) {\n const tokens = extractClassTokensFromHtml(options.html);\n const topUtilities = topEntries(tokens.utilities, 40);\n const topVariants = topEntries(tokens.variants, 15);\n\n lines.push(\"\");\n lines.push(\"### Utility Classes (from markup)\");\n if (topUtilities.length === 0) {\n lines.push(\"- (none detected)\");\n } else {\n topUtilities.forEach(({ token, count }) => {\n lines.push(`- ${token}: ${count} occurrences`);\n });\n }\n\n if (topVariants.length > 0) {\n lines.push(\"\");\n lines.push(\"### Common Variants\");\n topVariants.forEach(({ token, count }) => {\n lines.push(`- ${token}: ${count} occurrences`);\n });\n }\n }\n\n // Tailwind theme tokens (optional)\n if (options.tailwindTheme) {\n const tt = options.tailwindTheme;\n lines.push(\"\");\n lines.push(\"### Tailwind Theme Tokens (from config)\");\n lines.push(`- configPath: ${tt.configPath}`);\n lines.push(`- colors: ${tt.colors.length}`);\n lines.push(`- spacingKeys: ${tt.spacingKeys.length}`);\n lines.push(`- borderRadiusKeys: ${tt.borderRadiusKeys.length}`);\n lines.push(`- fontFamilyKeys: ${tt.fontFamilyKeys.length}`);\n lines.push(`- fontSizeKeys: ${tt.fontSizeKeys.length}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Truncates HTML to a maximum length\n */\nexport function truncateHTML(html: string, maxLength: number = 50000): string {\n if (html.length <= maxLength) return html;\n return html.slice(0, maxLength) + \"<!-- truncated -->\";\n}\n","/**\n * Style guide schema and utilities\n */\n\nimport type { StyleGuide, ColorRule, TypographyRule, SpacingRule, ComponentRule } from \"../types.js\";\n\n/**\n * Creates an empty style guide structure\n */\nexport function createEmptyStyleGuide(): StyleGuide {\n return {\n colors: [],\n typography: [],\n spacing: [],\n components: [],\n };\n}\n\n/**\n * Validates a style guide object\n */\nexport function validateStyleGuide(guide: unknown): guide is StyleGuide {\n if (!guide || typeof guide !== \"object\") return false;\n\n const g = guide as Record<string, unknown>;\n\n return (\n Array.isArray(g.colors) &&\n Array.isArray(g.typography) &&\n Array.isArray(g.spacing) &&\n Array.isArray(g.components)\n );\n}\n\n/**\n * Merges detected styles into an existing style guide\n */\nexport function mergeStyleGuides(\n existing: StyleGuide,\n detected: Partial<StyleGuide>\n): StyleGuide {\n return {\n colors: mergeColorRules(existing.colors, detected.colors || []),\n typography: mergeTypographyRules(existing.typography, detected.typography || []),\n spacing: mergeSpacingRules(existing.spacing, detected.spacing || []),\n components: mergeComponentRules(existing.components, detected.components || []),\n };\n}\n\nfunction mergeColorRules(existing: ColorRule[], detected: ColorRule[]): ColorRule[] {\n const merged = [...existing];\n detected.forEach((rule) => {\n const existingIndex = merged.findIndex(\n (e) => e.name === rule.name || e.value === rule.value\n );\n if (existingIndex === -1) {\n merged.push(rule);\n }\n });\n return merged;\n}\n\nfunction mergeTypographyRules(existing: TypographyRule[], detected: TypographyRule[]): TypographyRule[] {\n const merged = [...existing];\n detected.forEach((rule) => {\n const existingIndex = merged.findIndex((e) => e.element === rule.element);\n if (existingIndex === -1) {\n merged.push(rule);\n }\n });\n return merged;\n}\n\nfunction mergeSpacingRules(existing: SpacingRule[], detected: SpacingRule[]): SpacingRule[] {\n const merged = [...existing];\n detected.forEach((rule) => {\n const existingIndex = merged.findIndex(\n (e) => e.name === rule.name || e.value === rule.value\n );\n if (existingIndex === -1) {\n merged.push(rule);\n }\n });\n return merged;\n}\n\nfunction mergeComponentRules(existing: ComponentRule[], detected: ComponentRule[]): ComponentRule[] {\n const merged = [...existing];\n detected.forEach((rule) => {\n const existingIndex = merged.findIndex((e) => e.name === rule.name);\n if (existingIndex === -1) {\n merged.push(rule);\n }\n });\n return merged;\n}\n\n/**\n * Creates a color rule\n */\nexport function createColorRule(\n name: string,\n value: string,\n usage: string = \"\"\n): ColorRule {\n return { name, value: value.toUpperCase(), usage };\n}\n\n/**\n * Creates a typography rule\n */\nexport function createTypographyRule(\n element: string,\n options: Partial<Omit<TypographyRule, \"element\">> = {}\n): TypographyRule {\n return { element, ...options };\n}\n\n/**\n * Creates a spacing rule\n */\nexport function createSpacingRule(name: string, value: string): SpacingRule {\n return { name, value };\n}\n\n/**\n * Creates a component rule\n */\nexport function createComponentRule(name: string, styles: string[]): ComponentRule {\n return { name, styles };\n}\n\n","/**\n * Parse Markdown style guides into structured data\n */\n\nimport type {\n StyleGuide,\n ColorRule,\n TypographyRule,\n SpacingRule,\n ComponentRule,\n ExtractedStyleValues,\n} from \"../types.js\";\nimport { createEmptyStyleGuide } from \"./schema.js\";\n\n/**\n * Parses a Markdown style guide into a structured object\n */\nexport function parseStyleGuide(markdown: string): StyleGuide {\n const guide = createEmptyStyleGuide();\n const sections = splitIntoSections(markdown);\n\n sections.forEach(({ title, content }) => {\n const lowerTitle = title.toLowerCase();\n\n if (lowerTitle.includes(\"color\")) {\n guide.colors = parseColorSection(content);\n } else if (\n lowerTitle.includes(\"typography\") ||\n lowerTitle.includes(\"font\")\n ) {\n guide.typography = parseTypographySection(content);\n } else if (lowerTitle.includes(\"spacing\")) {\n guide.spacing = parseSpacingSection(content);\n } else if (lowerTitle.includes(\"component\")) {\n guide.components = parseComponentSection(content);\n }\n });\n\n return guide;\n}\n\ninterface Section {\n title: string;\n content: string;\n}\n\nfunction splitIntoSections(markdown: string): Section[] {\n const sections: Section[] = [];\n const lines = markdown.split(\"\\n\");\n\n let currentTitle = \"\";\n let currentContent: string[] = [];\n\n lines.forEach((line) => {\n const headerMatch = line.match(/^##\\s+(.+)$/);\n\n if (headerMatch) {\n if (currentTitle) {\n sections.push({\n title: currentTitle,\n content: currentContent.join(\"\\n\"),\n });\n }\n currentTitle = headerMatch[1];\n currentContent = [];\n } else {\n currentContent.push(line);\n }\n });\n\n if (currentTitle) {\n sections.push({\n title: currentTitle,\n content: currentContent.join(\"\\n\"),\n });\n }\n\n return sections;\n}\n\nfunction parseColorSection(content: string): ColorRule[] {\n const colors: ColorRule[] = [];\n const lines = content.split(\"\\n\");\n\n lines.forEach((line) => {\n // Match patterns like: - **Primary**: #3B82F6 (used in buttons)\n const match = line.match(\n /[-*]\\s*\\*?\\*?([^*:]+)\\*?\\*?:\\s*(#[A-Fa-f0-9]{6})\\s*(?:\\(([^)]+)\\))?/\n );\n\n if (match) {\n colors.push({\n name: match[1].trim(),\n value: match[2].toUpperCase(),\n usage: match[3] || \"\",\n });\n }\n });\n\n return colors;\n}\n\nfunction parseTypographySection(content: string): TypographyRule[] {\n const typography: TypographyRule[] = [];\n const lines = content.split(\"\\n\");\n\n lines.forEach((line) => {\n // Match patterns like: - **Headings**: font-family: \"Inter\", font-size: 24px\n const elementMatch = line.match(/[-*]\\s*\\*?\\*?([^*:]+)\\*?\\*?:\\s*(.+)/);\n\n if (elementMatch) {\n const rule: TypographyRule = {\n element: elementMatch[1].trim(),\n };\n\n const props = elementMatch[2];\n\n const fontFamilyMatch = props.match(/font-family:\\s*\"?([^\",]+)\"?/);\n if (fontFamilyMatch) rule.fontFamily = fontFamilyMatch[1].trim();\n\n const fontSizeMatch = props.match(/font-size:\\s*(\\d+px)/);\n if (fontSizeMatch) rule.fontSize = fontSizeMatch[1];\n\n const fontWeightMatch = props.match(/font-weight:\\s*(\\d+)/);\n if (fontWeightMatch) rule.fontWeight = fontWeightMatch[1];\n\n const lineHeightMatch = props.match(/line-height:\\s*([\\d.]+)/);\n if (lineHeightMatch) rule.lineHeight = lineHeightMatch[1];\n\n typography.push(rule);\n }\n });\n\n return typography;\n}\n\nfunction parseSpacingSection(content: string): SpacingRule[] {\n const spacing: SpacingRule[] = [];\n const lines = content.split(\"\\n\");\n\n lines.forEach((line) => {\n // Match patterns like: - **Base unit**: 4px\n const match = line.match(/[-*]\\s*\\*?\\*?([^*:]+)\\*?\\*?:\\s*(.+)/);\n\n if (match) {\n spacing.push({\n name: match[1].trim(),\n value: match[2].trim(),\n });\n }\n });\n\n return spacing;\n}\n\nfunction parseComponentSection(content: string): ComponentRule[] {\n const components: ComponentRule[] = [];\n const lines = content.split(\"\\n\");\n\n lines.forEach((line) => {\n // Match patterns like: - **Buttons**: rounded-lg, px-4 py-2\n const match = line.match(/[-*]\\s*\\*?\\*?([^*:]+)\\*?\\*?:\\s*(.+)/);\n\n if (match) {\n components.push({\n name: match[1].trim(),\n styles: match[2].split(\",\").map((s) => s.trim()),\n });\n }\n });\n\n return components;\n}\n\n/**\n * Parses sections from a Markdown style guide (simpler format)\n */\nexport function parseStyleGuideSections(\n content: string\n): Record<string, string> {\n const sections: Record<string, string> = {};\n const lines = content.split(\"\\n\");\n\n let currentSection = \"intro\";\n let currentContent: string[] = [];\n\n for (const line of lines) {\n const headerMatch = line.match(/^##\\s+(.+)$/);\n\n if (headerMatch) {\n if (currentContent.length > 0) {\n sections[currentSection.toLowerCase()] = currentContent\n .join(\"\\n\")\n .trim();\n }\n\n currentSection = headerMatch[1];\n currentContent = [];\n } else {\n currentContent.push(line);\n }\n }\n\n if (currentContent.length > 0) {\n sections[currentSection.toLowerCase()] = currentContent.join(\"\\n\").trim();\n }\n\n return sections;\n}\n\n/**\n * Extracts specific values from the style guide\n */\nexport function extractStyleValues(content: string): ExtractedStyleValues {\n const result: ExtractedStyleValues = {\n colors: [],\n fontSizes: [],\n fontFamilies: [],\n spacing: [],\n borderRadius: [],\n };\n\n // Extract hex colors\n const colorMatches = content.matchAll(/#[A-Fa-f0-9]{6}\\b/g);\n for (const match of colorMatches) {\n if (!result.colors.includes(match[0].toUpperCase())) {\n result.colors.push(match[0].toUpperCase());\n }\n }\n\n // Extract font sizes (e.g., 16px, 1.5rem)\n const fontSizeMatches = content.matchAll(/\\b(\\d+(?:\\.\\d+)?(?:px|rem|em))\\b/g);\n for (const match of fontSizeMatches) {\n if (!result.fontSizes.includes(match[1])) {\n result.fontSizes.push(match[1]);\n }\n }\n\n // Extract font families (quoted strings in font context)\n const fontFamilyMatches = content.matchAll(\n /font-family:\\s*[\"']?([^\"',\\n]+)/gi\n );\n for (const match of fontFamilyMatches) {\n const family = match[1].trim();\n if (!result.fontFamilies.includes(family)) {\n result.fontFamilies.push(family);\n }\n }\n\n return result;\n}\n\nexport interface TailwindAllowlist {\n allowAnyColor: boolean;\n allowStandardSpacing: boolean;\n allowedTailwindColors: Set<string>;\n allowedUtilities: Set<string>;\n allowedSpacingKeys: Set<string>;\n allowedBorderRadiusKeys: Set<string>;\n allowedFontSizeKeys: Set<string>;\n allowedFontFamilyKeys: Set<string>;\n}\n\n/**\n * Extract Tailwind / utility-class allowlist configuration from a style guide.\n *\n * Expected formats:\n * - A JSON code block inside a \"## Tailwind\" section (preferred; produced by UILint)\n * - Fallback: inline backticked utilities within the Tailwind section\n */\nexport function extractTailwindAllowlist(content: string): TailwindAllowlist {\n const empty: TailwindAllowlist = {\n allowAnyColor: false,\n allowStandardSpacing: false,\n allowedTailwindColors: new Set(),\n allowedUtilities: new Set(),\n allowedSpacingKeys: new Set(),\n allowedBorderRadiusKeys: new Set(),\n allowedFontSizeKeys: new Set(),\n allowedFontFamilyKeys: new Set(),\n };\n\n // Only look for allowlist details in the Tailwind section.\n const sections = parseStyleGuideSections(content);\n const tailwindSection =\n sections[\"tailwind\"] ??\n // defensive: some styleguides use different casing/spacing\n sections[\"tailwind utilities\"] ??\n \"\";\n\n if (!tailwindSection) return empty;\n\n const parsed = tryParseFirstJsonCodeBlock(tailwindSection);\n if (parsed && typeof parsed === \"object\") {\n const parsedObj = parsed as Record<string, unknown>;\n const allowAnyColor = Boolean(parsedObj.allowAnyColor);\n const allowStandardSpacing = Boolean(parsedObj.allowStandardSpacing);\n\n const allowedUtilitiesArr = Array.isArray(parsedObj.allowedUtilities)\n ? (parsedObj.allowedUtilities as unknown[]).filter(\n (u): u is string => typeof u === \"string\"\n )\n : [];\n\n const themeTokens = (parsedObj.themeTokens ?? {}) as Record<string, unknown>;\n const themeColors = Array.isArray(themeTokens.colors)\n ? (themeTokens.colors as unknown[]).filter(\n (c): c is string => typeof c === \"string\"\n )\n : [];\n const spacingKeys = Array.isArray(themeTokens.spacingKeys)\n ? (themeTokens.spacingKeys as unknown[]).filter(\n (k): k is string => typeof k === \"string\"\n )\n : [];\n const borderRadiusKeys = Array.isArray(themeTokens.borderRadiusKeys)\n ? (themeTokens.borderRadiusKeys as unknown[]).filter(\n (k): k is string => typeof k === \"string\"\n )\n : [];\n const fontFamilyKeys = Array.isArray(themeTokens.fontFamilyKeys)\n ? (themeTokens.fontFamilyKeys as unknown[]).filter(\n (k): k is string => typeof k === \"string\"\n )\n : [];\n const fontSizeKeys = Array.isArray(themeTokens.fontSizeKeys)\n ? (themeTokens.fontSizeKeys as unknown[]).filter(\n (k): k is string => typeof k === \"string\"\n )\n : [];\n\n const allowedTailwindColors = new Set<string>();\n for (const c of themeColors) {\n const raw = c.trim();\n if (!raw) continue;\n if (raw.toLowerCase().startsWith(\"tailwind:\")) {\n allowedTailwindColors.add(raw.toLowerCase());\n continue;\n }\n const m = raw.match(/^([a-zA-Z]+)-(\\d{2,3})$/);\n if (m) {\n allowedTailwindColors.add(`tailwind:${m[1].toLowerCase()}-${m[2]}`);\n }\n }\n\n return {\n allowAnyColor,\n allowStandardSpacing,\n allowedTailwindColors,\n allowedUtilities: new Set(\n allowedUtilitiesArr.map((s) => s.trim()).filter(Boolean)\n ),\n allowedSpacingKeys: new Set(\n spacingKeys.map((s) => s.trim()).filter(Boolean)\n ),\n allowedBorderRadiusKeys: new Set(\n borderRadiusKeys.map((s) => s.trim()).filter(Boolean)\n ),\n allowedFontSizeKeys: new Set(\n fontSizeKeys.map((s) => s.trim()).filter(Boolean)\n ),\n allowedFontFamilyKeys: new Set(\n fontFamilyKeys.map((s) => s.trim()).filter(Boolean)\n ),\n };\n }\n\n // Fallback: harvest backticked utilities from markdown.\n const backticked: string[] = [];\n for (const m of tailwindSection.matchAll(/`([^`]+)`/g)) {\n backticked.push(m[1]);\n }\n\n return {\n ...empty,\n allowedUtilities: new Set(\n backticked\n .flatMap((s) => s.split(/[,\\s]+/g))\n .map((s) => s.trim())\n .filter(Boolean)\n ),\n };\n}\n\nfunction tryParseFirstJsonCodeBlock(section: string): unknown | null {\n // Prefer ```json fenced blocks, but fall back to any fenced block.\n const jsonBlocks = [...section.matchAll(/```json\\s*([\\s\\S]*?)```/gi)];\n const anyBlocks = [...section.matchAll(/```\\s*([\\s\\S]*?)```/g)];\n\n const candidates = (jsonBlocks.length ? jsonBlocks : anyBlocks).map(\n (m) => m[1]\n );\n for (const raw of candidates) {\n const trimmed = raw.trim();\n if (!trimmed) continue;\n try {\n return JSON.parse(trimmed);\n } catch {\n continue;\n }\n }\n return null;\n}\n","/**\n * Generate Markdown style guides from extracted styles\n */\n\nimport type {\n ExtractedStyles,\n StyleGuide,\n TailwindThemeTokens,\n} from \"../types.js\";\n\n/**\n * Generates a Markdown style guide from extracted styles\n */\nexport interface GenerateStyleGuideOptions {\n /**\n * Optional HTML/TSX-ish string used to extract utility classes (Tailwind etc).\n */\n html?: string;\n /**\n * Optional Tailwind theme tokens (typically from tailwind.config.*).\n */\n tailwindTheme?: TailwindThemeTokens | null;\n}\n\nexport function generateStyleGuideFromStyles(\n styles: ExtractedStyles,\n options: GenerateStyleGuideOptions = {}\n): string {\n // NOTE: Style guide auto-generation has been removed.\n // UILint now requires an explicit, user-owned style guide file (typically\n // `.uilint/styleguide.md`) to avoid silently producing/overwriting rules.\n void styles;\n void options;\n throw new Error(\n 'Style guide auto-generation has been removed. Create \".uilint/styleguide.md\" at your workspace root (recommended: run \"/genstyleguide\" in Cursor).'\n );\n}\n\n/**\n * Converts a StyleGuide object back to Markdown\n */\nexport function styleGuideToMarkdown(guide: StyleGuide): string {\n const lines: string[] = [];\n\n lines.push(\"# UI Style Guide\");\n lines.push(\"\");\n\n // Colors\n lines.push(\"## Colors\");\n guide.colors.forEach((color) => {\n const usage = color.usage ? ` (${color.usage})` : \"\";\n lines.push(`- **${color.name}**: ${color.value}${usage}`);\n });\n lines.push(\"\");\n\n // Typography\n lines.push(\"## Typography\");\n guide.typography.forEach((typo) => {\n const props: string[] = [];\n if (typo.fontFamily) props.push(`font-family: \"${typo.fontFamily}\"`);\n if (typo.fontSize) props.push(`font-size: ${typo.fontSize}`);\n if (typo.fontWeight) props.push(`font-weight: ${typo.fontWeight}`);\n if (typo.lineHeight) props.push(`line-height: ${typo.lineHeight}`);\n lines.push(`- **${typo.element}**: ${props.join(\", \")}`);\n });\n lines.push(\"\");\n\n // Spacing\n lines.push(\"## Spacing\");\n guide.spacing.forEach((space) => {\n lines.push(`- **${space.name}**: ${space.value}`);\n });\n lines.push(\"\");\n\n // Components\n lines.push(\"## Components\");\n guide.components.forEach((comp) => {\n lines.push(`- **${comp.name}**: ${comp.styles.join(\", \")}`);\n });\n\n return lines.join(\"\\n\");\n}\n","/**\n * LLM prompt builders for UI consistency analysis\n */\n\nimport type {\n ElementSnapshot,\n GroupedSnapshot,\n} from \"./types.js\";\n\n/**\n * Formats an element for inclusion in the prompt (minimal data for LLM)\n */\nfunction formatElement(el: ElementSnapshot): string {\n const parts = [\n `id: ${el.id}`,\n `text: \"${el.text}\"`,\n el.component ? `component: ${el.component}` : null,\n `context: ${el.context || \"root\"}`,\n ].filter(Boolean);\n\n // Only include non-empty style values\n const styleEntries = Object.entries(el.styles).filter(\n ([, v]) => v && v !== \"0px\" && v !== \"none\" && v !== \"normal\"\n );\n if (styleEntries.length > 0) {\n const styleStr = styleEntries.map(([k, v]) => `${k}: ${v}`).join(\", \");\n parts.push(`styles: { ${styleStr} }`);\n }\n\n if (el.rect.width > 0 || el.rect.height > 0) {\n parts.push(`size: ${Math.round(el.rect.width)}x${Math.round(el.rect.height)}`);\n }\n\n return ` { ${parts.join(\", \")} }`;\n}\n\n/**\n * Formats a group of elements for the prompt\n */\nfunction formatGroup(\n groupName: string,\n elements: ElementSnapshot[]\n): string | null {\n if (elements.length < 2) return null;\n\n const lines = [`## ${groupName} (${elements.length} elements)`];\n elements.forEach((el) => {\n lines.push(formatElement(el));\n });\n return lines.join(\"\\n\");\n}\n\n/**\n * Builds a single prompt for analyzing ALL element groups\n * This reduces LLM calls to 1 for better performance\n */\nexport function buildConsistencyPrompt(snapshot: GroupedSnapshot): string {\n const groupSections: string[] = [];\n\n // Format each group that has 2+ elements\n const groups: Array<{ name: string; key: keyof GroupedSnapshot }> = [\n { name: \"Buttons\", key: \"buttons\" },\n { name: \"Headings\", key: \"headings\" },\n { name: \"Cards\", key: \"cards\" },\n { name: \"Links\", key: \"links\" },\n { name: \"Inputs\", key: \"inputs\" },\n { name: \"Containers\", key: \"containers\" },\n ];\n\n for (const { name, key } of groups) {\n const section = formatGroup(name, snapshot[key]);\n if (section) {\n groupSections.push(section);\n }\n }\n\n if (groupSections.length === 0) {\n return \"No element groups with 2+ elements found for consistency analysis.\";\n }\n\n return `You are a UI consistency analyzer. Your task is to find visual inconsistencies between similar UI elements that SHOULD match but DON'T.\n\n# Instructions\n\nAnalyze the following groups of UI elements. Within each group, elements should generally have consistent styling unless they're intentionally different (e.g., primary vs secondary buttons).\n\n## What to FLAG (violations):\n- Padding/spacing differences between similar elements (e.g., one button has 12px padding, another has 16px)\n- Font size or weight inconsistencies within same element types\n- Unintentional color variations (e.g., slightly different blues: #3B82F6 vs #3575E2)\n- Border radius mismatches (e.g., one card has 8px radius, another has 12px)\n- Shadow inconsistencies between similar components\n- Heading hierarchy issues (h1 should be larger than h2, h2 larger than h3)\n\n## What to NOT FLAG:\n- Intentional variations (primary vs secondary buttons, different heading levels)\n- Elements in different contexts that reasonably differ (header vs footer)\n- Sub-2px differences (likely rounding or subpixel rendering)\n- Different element types that shouldn't match\n\n# Element Groups\n\n${groupSections.join(\"\\n\\n\")}\n\n# Response Format\n\nRespond with JSON ONLY. Return a single JSON object with a \"violations\" array.\n\nEach violation should have:\n- elementIds: array of element IDs involved, e.g. [\"el-3\", \"el-7\"]\n- category: one of \"spacing\", \"color\", \"typography\", \"sizing\", \"borders\", \"shadows\"\n- severity: one of \"error\" (major inconsistency), \"warning\" (minor but noticeable), \"info\" (subtle)\n- message: short human-readable description\n- details: { property: the CSS property, values: array of differing values found, suggestion?: optional fix }\n\nExample response:\n{\n \"violations\": [\n {\n \"elementIds\": [\"el-3\", \"el-7\"],\n \"category\": \"spacing\",\n \"severity\": \"warning\",\n \"message\": \"Inconsistent padding on buttons\",\n \"details\": {\n \"property\": \"padding\",\n \"values\": [\"12px 24px\", \"16px 32px\"],\n \"suggestion\": \"Use consistent padding of 12px 24px\"\n }\n }\n ]\n}\n\nBe minimal. Only report significant inconsistencies. If no violations found, return {\"violations\": []}.`;\n}\n\n/**\n * Counts total elements across all groups\n */\nexport function countElements(snapshot: GroupedSnapshot): number {\n return (\n snapshot.buttons.length +\n snapshot.headings.length +\n snapshot.cards.length +\n snapshot.links.length +\n snapshot.inputs.length +\n snapshot.containers.length\n );\n}\n\n/**\n * Checks if a snapshot has any groups worth analyzing (2+ elements)\n */\nexport function hasAnalyzableGroups(snapshot: GroupedSnapshot): boolean {\n return (\n snapshot.buttons.length >= 2 ||\n snapshot.headings.length >= 2 ||\n snapshot.cards.length >= 2 ||\n snapshot.links.length >= 2 ||\n snapshot.inputs.length >= 2 ||\n snapshot.containers.length >= 2\n );\n}\n","/**\n * Consistency analysis logic - shared between CLI and API routes\n */\n\nimport type {\n GroupedSnapshot,\n Violation,\n ConsistencyResult,\n ViolationCategory,\n ViolationSeverity,\n} from \"./types.js\";\nimport {\n buildConsistencyPrompt,\n countElements,\n hasAnalyzableGroups,\n} from \"./prompts.js\";\nimport { OllamaClient } from \"../ollama/client.js\";\n\nconst VALID_CATEGORIES: ViolationCategory[] = [\n \"spacing\",\n \"color\",\n \"typography\",\n \"sizing\",\n \"borders\",\n \"shadows\",\n];\n\nconst VALID_SEVERITIES: ViolationSeverity[] = [\"error\", \"warning\", \"info\"];\n\n/**\n * Parses and validates a GroupedSnapshot from JSON string\n */\nexport function parseGroupedSnapshot(json: string): GroupedSnapshot | null {\n try {\n const parsed = JSON.parse(json);\n\n // Validate structure\n if (!parsed || typeof parsed !== \"object\") return null;\n\n const result: GroupedSnapshot = {\n buttons: Array.isArray(parsed.buttons) ? parsed.buttons : [],\n headings: Array.isArray(parsed.headings) ? parsed.headings : [],\n cards: Array.isArray(parsed.cards) ? parsed.cards : [],\n links: Array.isArray(parsed.links) ? parsed.links : [],\n inputs: Array.isArray(parsed.inputs) ? parsed.inputs : [],\n containers: Array.isArray(parsed.containers) ? parsed.containers : [],\n };\n\n return result;\n } catch {\n return null;\n }\n}\n\n/**\n * Parses violations from LLM response with defensive handling\n */\nexport function parseViolationsResponse(response: string): Violation[] {\n try {\n // Try direct JSON parse first\n const parsed = JSON.parse(response);\n if (Array.isArray(parsed.violations)) {\n return validateViolations(parsed.violations);\n }\n return [];\n } catch {\n // Try to extract JSON from response using regex\n const jsonMatch = response.match(/\\{[\\s\\S]*\"violations\"[\\s\\S]*\\}/);\n if (jsonMatch) {\n try {\n const parsed = JSON.parse(jsonMatch[0]);\n if (Array.isArray(parsed.violations)) {\n return validateViolations(parsed.violations);\n }\n } catch {\n // Fallback failed\n }\n }\n return [];\n }\n}\n\n/**\n * Validates and filters violations to ensure correct structure\n */\nexport function validateViolations(violations: unknown[]): Violation[] {\n return violations\n .filter((v): v is Violation => {\n if (!v || typeof v !== \"object\") return false;\n const obj = v as Record<string, unknown>;\n\n // Required fields\n if (!Array.isArray(obj.elementIds)) return false;\n if (typeof obj.category !== \"string\") return false;\n if (typeof obj.severity !== \"string\") return false;\n if (typeof obj.message !== \"string\") return false;\n if (!obj.details || typeof obj.details !== \"object\") return false;\n\n // Validate category\n if (!VALID_CATEGORIES.includes(obj.category as ViolationCategory))\n return false;\n\n // Validate severity\n if (!VALID_SEVERITIES.includes(obj.severity as ViolationSeverity))\n return false;\n\n return true;\n })\n .map((v) => ({\n elementIds: v.elementIds,\n category: v.category,\n severity: v.severity,\n message: v.message,\n details: {\n property:\n typeof v.details.property === \"string\" ? v.details.property : \"\",\n values: Array.isArray(v.details.values) ? v.details.values : [],\n suggestion:\n typeof v.details.suggestion === \"string\"\n ? v.details.suggestion\n : undefined,\n },\n }));\n}\n\nexport interface AnalyzeConsistencyOptions {\n /** Ollama model to use */\n model?: string;\n /** Ollama base URL */\n baseUrl?: string;\n}\n\n/**\n * Analyzes a grouped snapshot for UI consistency violations\n * This is the main entry point for consistency analysis\n */\nexport async function analyzeConsistency(\n snapshot: GroupedSnapshot,\n options: AnalyzeConsistencyOptions = {}\n): Promise<ConsistencyResult> {\n const startTime = Date.now();\n const elementCount = countElements(snapshot);\n\n // Check if there are analyzable groups\n if (!hasAnalyzableGroups(snapshot)) {\n return {\n violations: [],\n elementCount,\n analysisTime: Date.now() - startTime,\n };\n }\n\n // Build prompt and call LLM\n const prompt = buildConsistencyPrompt(snapshot);\n const client = new OllamaClient({\n model: options.model,\n baseUrl: options.baseUrl,\n });\n\n const response = await client.complete(prompt, { json: true });\n\n // Parse violations\n const violations = parseViolationsResponse(response);\n\n return {\n violations,\n elementCount,\n analysisTime: Date.now() - startTime,\n };\n}\n\n/**\n * Formats violations for plain text output\n */\nexport function formatConsistencyViolations(violations: Violation[]): string {\n if (violations.length === 0) {\n return \"✓ No consistency issues found.\";\n }\n\n const lines: string[] = [\n `Found ${violations.length} consistency issue(s):\\n`,\n ];\n\n const severityIcons: Record<ViolationSeverity, string> = {\n error: \"✖\",\n warning: \"⚠\",\n info: \"ℹ\",\n };\n\n violations.forEach((v, i) => {\n const icon = severityIcons[v.severity] || \"•\";\n\n lines.push(`${i + 1}. ${icon} [${v.category}] ${v.message}`);\n lines.push(` Elements: ${v.elementIds.join(\", \")}`);\n\n if (v.details.property) {\n lines.push(` Property: ${v.details.property}`);\n }\n if (v.details.values.length > 0) {\n lines.push(` Values: ${v.details.values.join(\" vs \")}`);\n }\n if (v.details.suggestion) {\n lines.push(` Suggestion: ${v.details.suggestion}`);\n }\n lines.push(\"\");\n });\n\n return lines.join(\"\\n\");\n}\n","/**\n * Shared logger for UILint packages\n * Outputs styled messages to stderr to avoid interfering with stdout\n */\n\nimport pc from \"picocolors\";\n\nconst PREFIX = pc.cyan(\"[uilint]\");\n\n// ============================================================================\n// Test Environment Detection\n// ============================================================================\n\n/**\n * Check if we're running in a test environment.\n * Vitest sets VITEST=true when running tests.\n */\nfunction isTestEnvironment(): boolean {\n return (\n typeof process !== \"undefined\" &&\n (process.env.VITEST === \"true\" || process.env.NODE_ENV === \"test\")\n );\n}\n\n// Cache the result since it won't change during runtime\nconst IS_TEST = isTestEnvironment();\n\n// ============================================================================\n// Test-Aware Logging (silent during tests)\n// ============================================================================\n\n/**\n * Log a message to the console (silent during tests).\n * Use this for development/debugging logs that shouldn't appear in test output.\n */\nexport function devLog(...args: unknown[]): void {\n if (!IS_TEST) {\n console.log(...args);\n }\n}\n\n/**\n * Log a warning to the console (silent during tests).\n * Use this for development warnings that shouldn't appear in test output.\n */\nexport function devWarn(...args: unknown[]): void {\n if (!IS_TEST) {\n console.warn(...args);\n }\n}\n\n/**\n * Log an error to the console (silent during tests).\n * Use this for development errors that shouldn't appear in test output.\n */\nexport function devError(...args: unknown[]): void {\n if (!IS_TEST) {\n console.error(...args);\n }\n}\n\n/**\n * Log an info message to stderr\n */\nexport function logInfo(message: string): void {\n console.error(`${PREFIX} ${pc.blue(\"ℹ\")} ${message}`);\n}\n\n/**\n * Log a success message to stderr\n */\nexport function logSuccess(message: string): void {\n console.error(`${PREFIX} ${pc.green(\"✓\")} ${message}`);\n}\n\n/**\n * Log a warning message to stderr\n */\nexport function logWarning(message: string): void {\n console.error(`${PREFIX} ${pc.yellow(\"⚠\")} ${message}`);\n}\n\n/**\n * Log an error message to stderr\n */\nexport function logError(message: string): void {\n console.error(`${PREFIX} ${pc.red(\"✗\")} ${message}`);\n}\n\n/**\n * Log a debug message to stderr (dimmed)\n */\nexport function logDebug(message: string): void {\n console.error(`${PREFIX} ${pc.dim(message)}`);\n}\n\n/**\n * Create a progress logger that updates the same line\n * Returns methods to update and finish the progress\n */\nexport function createProgress(initialMessage: string) {\n let lastLength = 0;\n\n const write = (message: string) => {\n // Clear the previous line if needed\n if (lastLength > 0) {\n process.stderr.write(\"\\r\" + \" \".repeat(lastLength) + \"\\r\");\n }\n const line = `${PREFIX} ${pc.magenta(\"⟳\")} ${message}`;\n process.stderr.write(line);\n lastLength = line.length;\n };\n\n write(initialMessage);\n\n return {\n update: (message: string) => {\n write(message);\n },\n succeed: (message: string) => {\n if (lastLength > 0) {\n process.stderr.write(\"\\r\" + \" \".repeat(lastLength) + \"\\r\");\n }\n console.error(`${PREFIX} ${pc.green(\"✓\")} ${message}`);\n lastLength = 0;\n },\n fail: (message: string) => {\n if (lastLength > 0) {\n process.stderr.write(\"\\r\" + \" \".repeat(lastLength) + \"\\r\");\n }\n console.error(`${PREFIX} ${pc.red(\"✗\")} ${message}`);\n lastLength = 0;\n },\n };\n}\n\n// Re-export picocolors for consistent styling\nexport { pc };\n","/**\n * Plugin Registry\n *\n * Global registry for plugins to register themselves with.\n * Plugins call pluginRegistry.register() on import (side effect)\n * or explicitly via direct call.\n *\n * The host (uilint-react, CLI) reads from this registry to\n * discover and initialize plugins.\n */\n\nimport type { PluginWithHandlers } from \"./context.js\";\n\n/**\n * Error thrown when plugin registration fails.\n */\nexport class PluginRegistrationError extends Error {\n constructor(\n message: string,\n public readonly pluginId?: string\n ) {\n super(message);\n this.name = \"PluginRegistrationError\";\n }\n}\n\n/**\n * Event types for registry changes.\n */\nexport type PluginRegistryEvent =\n | { type: \"registered\"; plugin: PluginWithHandlers }\n | { type: \"unregistered\"; pluginId: string };\n\n/**\n * Listener for registry events.\n */\nexport type PluginRegistryListener = (event: PluginRegistryEvent) => void;\n\n/**\n * Plugin Registry\n *\n * Singleton registry for plugin registration and discovery.\n */\nexport class PluginRegistry {\n private plugins = new Map<string, PluginWithHandlers>();\n private registrationOrder: string[] = [];\n private listeners = new Set<PluginRegistryListener>();\n\n /**\n * Register a plugin.\n *\n * @param plugin - Plugin definition with handlers\n * @throws PluginRegistrationError if plugin with same ID already registered\n */\n register<TState>(plugin: PluginWithHandlers<TState>): void {\n // Validate required fields\n if (!plugin.id) {\n throw new PluginRegistrationError(\"Plugin must have an id\");\n }\n if (!plugin.name) {\n throw new PluginRegistrationError(\n `Plugin \"${plugin.id}\" must have a name`,\n plugin.id\n );\n }\n if (!plugin.version) {\n throw new PluginRegistrationError(\n `Plugin \"${plugin.id}\" must have a version`,\n plugin.id\n );\n }\n if (!plugin.state) {\n throw new PluginRegistrationError(\n `Plugin \"${plugin.id}\" must have a state definition`,\n plugin.id\n );\n }\n\n // Check for duplicate registration\n if (this.plugins.has(plugin.id)) {\n throw new PluginRegistrationError(\n `Plugin \"${plugin.id}\" is already registered`,\n plugin.id\n );\n }\n\n // Register the plugin\n this.plugins.set(plugin.id, plugin as PluginWithHandlers);\n this.registrationOrder.push(plugin.id);\n\n // Notify listeners\n this.notifyListeners({ type: \"registered\", plugin: plugin as PluginWithHandlers });\n }\n\n /**\n * Unregister a plugin.\n *\n * @param id - Plugin ID to unregister\n * @returns true if plugin was unregistered, false if not found\n */\n unregister(id: string): boolean {\n if (!this.plugins.has(id)) {\n return false;\n }\n\n this.plugins.delete(id);\n this.registrationOrder = this.registrationOrder.filter((pid) => pid !== id);\n\n // Notify listeners\n this.notifyListeners({ type: \"unregistered\", pluginId: id });\n\n return true;\n }\n\n /**\n * Get a plugin by ID.\n *\n * @param id - Plugin ID\n * @returns Plugin definition or undefined if not found\n */\n get<TState = unknown>(id: string): PluginWithHandlers<TState> | undefined {\n return this.plugins.get(id) as PluginWithHandlers<TState> | undefined;\n }\n\n /**\n * Check if a plugin is registered.\n *\n * @param id - Plugin ID\n * @returns true if registered\n */\n has(id: string): boolean {\n return this.plugins.has(id);\n }\n\n /**\n * Get all registered plugins in registration order.\n *\n * @returns Array of plugin definitions\n */\n getAll(): PluginWithHandlers[] {\n return this.registrationOrder.map((id) => this.plugins.get(id)!);\n }\n\n /**\n * Get all plugin IDs in registration order.\n *\n * @returns Array of plugin IDs\n */\n getIds(): string[] {\n return [...this.registrationOrder];\n }\n\n /**\n * Get count of registered plugins.\n */\n get size(): number {\n return this.plugins.size;\n }\n\n /**\n * Subscribe to registry changes.\n *\n * @param listener - Callback for registry events\n * @returns Unsubscribe function\n */\n subscribe(listener: PluginRegistryListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Clear all registered plugins.\n * Useful for testing.\n */\n clear(): void {\n const ids = [...this.registrationOrder];\n for (const id of ids) {\n this.unregister(id);\n }\n }\n\n /**\n * Get plugins sorted by dependencies.\n *\n * Plugins are sorted so that dependencies come before dependents.\n * Throws if there are circular dependencies.\n *\n * @returns Sorted array of plugins\n * @throws Error if circular dependency detected\n */\n getSortedByDependencies(): PluginWithHandlers[] {\n const sorted: PluginWithHandlers[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n\n const visit = (id: string) => {\n if (visited.has(id)) return;\n if (visiting.has(id)) {\n throw new Error(`Circular dependency detected involving plugin \"${id}\"`);\n }\n\n const plugin = this.plugins.get(id);\n if (!plugin) return;\n\n visiting.add(id);\n\n // Visit dependencies first\n for (const depId of plugin.dependencies || []) {\n if (!this.plugins.has(depId)) {\n throw new Error(\n `Plugin \"${id}\" depends on \"${depId}\" which is not registered`\n );\n }\n visit(depId);\n }\n\n visiting.delete(id);\n visited.add(id);\n sorted.push(plugin);\n };\n\n // Visit all plugins\n for (const id of this.registrationOrder) {\n visit(id);\n }\n\n return sorted;\n }\n\n /**\n * Notify all listeners of an event.\n */\n private notifyListeners(event: PluginRegistryEvent): void {\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (error) {\n console.error(\"Plugin registry listener error:\", error);\n }\n }\n }\n}\n\n/**\n * Global plugin registry singleton.\n *\n * Plugins import this and call register() to add themselves:\n *\n * ```typescript\n * import { pluginRegistry } from \"uilint-core\";\n * import { myPlugin } from \"./plugin\";\n *\n * pluginRegistry.register(myPlugin);\n * ```\n */\nexport const pluginRegistry = new PluginRegistry();\n","/**\n * Plugin System Types\n *\n * These types define the contract between plugin packages and the host\n * (uilint-react, uilint CLI). Plugin packages export PluginDefinitions,\n * hosts interpret and render them.\n *\n * NO REACT CODE - This is pure TypeScript.\n */\n\n// =============================================================================\n// DATA BINDING\n// =============================================================================\n\n/**\n * Reference to a value in the panel's data context.\n * Uses dot notation: \"issue.sourceLocation.filePath\"\n */\nexport interface DataBinding {\n binding: string;\n}\n\n/**\n * Reference to a computed expression.\n * Evaluated at runtime: \"screenshots.length === 0\"\n */\nexport interface ExpressionBinding {\n expression: string;\n}\n\n/**\n * Type guard for DataBinding\n */\nexport function isDataBinding(value: unknown): value is DataBinding {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"binding\" in value &&\n typeof (value as DataBinding).binding === \"string\"\n );\n}\n\n/**\n * Type guard for ExpressionBinding\n */\nexport function isExpressionBinding(value: unknown): value is ExpressionBinding {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"expression\" in value &&\n typeof (value as ExpressionBinding).expression === \"string\"\n );\n}\n\n/**\n * Dynamic value that can be a static value or a binding.\n */\nexport type DynamicValue<T> = T | DataBinding;\n\n/**\n * Conditional value based on a binding.\n */\nexport interface ConditionalValue<T> {\n condition: DataBinding;\n true: T;\n false: T;\n}\n\n/**\n * Type guard for ConditionalValue\n */\nexport function isConditionalValue<T>(value: unknown): value is ConditionalValue<T> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"condition\" in value &&\n \"true\" in value &&\n \"false\" in value\n );\n}\n\n// =============================================================================\n// ICONS\n// =============================================================================\n\n/**\n * Icon identifiers - the host maps these to actual icon components.\n * Any lucide-react icon name is accepted; the listed names provide autocompletion.\n */\nexport type IconName =\n // UI Navigation\n | \"target\"\n | \"link\"\n | \"external-link\"\n | \"chevron-right\"\n | \"chevron-down\"\n | \"chevron-up\"\n | \"x\"\n | \"check\"\n | \"alert-triangle\"\n | \"alert-circle\"\n | \"info\"\n | \"help-circle\"\n // Analysis\n | \"eye\"\n | \"camera\"\n | \"crop\"\n | \"scan\"\n | \"search\"\n | \"filter\"\n | \"brain\"\n | \"sparkles\"\n // Code\n | \"code\"\n | \"file\"\n | \"file-code\"\n | \"folder\"\n | \"git-branch\"\n | \"copy\"\n | \"clipboard\"\n // Content\n | \"book\"\n | \"book-open\"\n | \"bookmark\"\n // Actions\n | \"play\"\n | \"pause\"\n | \"refresh\"\n | \"trash\"\n | \"edit\"\n | \"settings\"\n | \"plus\"\n | \"minus\"\n // Status\n | \"loader\"\n | \"check-circle\"\n | \"x-circle\"\n | \"clock\"\n // Allow any string for forward compatibility with lucide-react icons\n | (string & {});\n\n// =============================================================================\n// ACTIONS\n// =============================================================================\n\n/**\n * Reference to an action handler defined by the plugin.\n */\nexport interface ActionReference {\n /** Action type - resolved by plugin's action handlers */\n type: string;\n /** Static payload to pass to the handler */\n payload?: Record<string, unknown>;\n /** Dynamic payload from data bindings (binding path -> payload key) */\n payloadBindings?: Record<string, string>;\n}\n\n// =============================================================================\n// FETCH CONFIGURATION\n// =============================================================================\n\n/**\n * Configuration for fetching dynamic data.\n */\nexport interface FetchConfig {\n /** Type of fetch operation */\n type: \"source-code\" | \"file-content\";\n /** Parameters for the fetch */\n params: {\n /** File path (can be a binding) */\n filePath?: DataBinding;\n /** Line number to center on */\n line?: DataBinding;\n /** Lines of context above the target */\n contextAbove?: number;\n /** Lines of context below the target */\n contextBelow?: number;\n };\n}\n\n// =============================================================================\n// PANEL SECTIONS\n// =============================================================================\n\n/**\n * Header section with optional icon.\n */\nexport interface HeaderSection {\n type: \"header\";\n /** Icon to display */\n icon?: IconName;\n /** Header text (static or bound) */\n text: DynamicValue<string>;\n /** Subtitle text */\n subtitle?: DynamicValue<string>;\n /** Whether header sticks to top when scrolling */\n sticky?: boolean;\n}\n\n/**\n * Code viewer with syntax highlighting.\n */\nexport interface CodeViewerSection {\n type: \"code-viewer\";\n /** Section label */\n label?: string;\n /** Icon for the section header */\n icon?: IconName;\n /** Code content (binding or fetch config) */\n code: DataBinding | { fetch: FetchConfig };\n /** Location information for navigation */\n location?: DataBinding;\n /** Starting line number */\n startLine?: DynamicValue<number>;\n /** Lines to highlight (array of line numbers) */\n highlightLines?: DataBinding;\n /** Enable diff-style highlighting */\n diffHighlighting?: boolean;\n /** Max height before scrolling (px) */\n maxHeight?: number;\n /** Action to execute on code navigation */\n onNavigate?: ActionReference;\n}\n\n/**\n * Configuration for one side of a code comparison.\n */\nexport interface CodePanelConfig {\n /** Panel label */\n label: string;\n /** Icon for the panel */\n icon?: IconName;\n /** Code content */\n code: DataBinding | { fetch: FetchConfig };\n /** Location information */\n location?: DataBinding;\n}\n\n/**\n * Side-by-side or stacked code comparison.\n */\nexport interface CodeComparisonSection {\n type: \"code-comparison\";\n /** Layout mode */\n mode: \"stacked\" | \"side-by-side\";\n /** Source code panel (left/top) */\n source: CodePanelConfig;\n /** Target code panel (right/bottom) */\n target: CodePanelConfig;\n /** Whether to compute and show diff highlighting */\n computeDiff?: boolean;\n /** Max height for each panel (px) */\n maxHeight?: number;\n}\n\n/**\n * Badge/pill display for status, severity, etc.\n */\nexport interface BadgeSection {\n type: \"badge\";\n /** Badge variant determines styling. Plugins can define custom variants. */\n variant: string;\n /** Value to display */\n value: DataBinding;\n /** Optional label before value */\n label?: string;\n /** Center the badge horizontally */\n centered?: boolean;\n}\n\n/**\n * Plain text content.\n */\nexport interface TextSection {\n type: \"text\";\n /** Text content */\n content: DynamicValue<string>;\n /** Text variant for styling */\n variant?: \"body\" | \"caption\" | \"muted\" | \"error\" | \"success\";\n}\n\n/**\n * Action button configuration.\n */\nexport interface ActionButton {\n /** Unique button ID */\n id: string;\n /** Button label (static, bound, or conditional) */\n label: DynamicValue<string> | ConditionalValue<string>;\n /** Button icon */\n icon?: IconName;\n /** Button variant for styling */\n variant?: \"primary\" | \"secondary\" | \"ghost\" | \"danger\";\n /** Action to execute on click */\n action: ActionReference;\n /** Disabled state (binding to boolean) */\n disabled?: DataBinding;\n /** Visibility (binding to boolean) */\n visible?: DataBinding;\n}\n\n/**\n * Action buttons section.\n */\nexport interface ActionsSection {\n type: \"actions\";\n /** Layout direction */\n direction?: \"row\" | \"column\";\n /** Buttons to render */\n actions: ActionButton[];\n}\n\n/**\n * Visual divider.\n */\nexport interface DividerSection {\n type: \"divider\";\n /** Spacing around divider */\n spacing?: \"small\" | \"medium\" | \"large\";\n}\n\n/**\n * Conditional rendering section.\n */\nexport interface ConditionalSection {\n type: \"conditional\";\n /** Condition to evaluate */\n condition: DataBinding | ExpressionBinding;\n /** Sections to render when condition is true */\n then: PanelSection[];\n /** Sections to render when condition is false */\n else?: PanelSection[];\n}\n\n/**\n * List section for rendering arrays of items.\n */\nexport interface ListSection {\n type: \"list\";\n /** Binding to array of items */\n items: DataBinding;\n /** Layout for each item (uses \"item\" prefix for bindings) */\n itemLayout: PanelSection[];\n /** Message when list is empty */\n emptyMessage?: string;\n}\n\n/**\n * Image display section.\n */\nexport interface ImageSection {\n type: \"image\";\n /** Image source (data URL or URL) */\n src: DataBinding;\n /** Alt text for accessibility */\n alt?: string;\n /** Max height (px) */\n maxHeight?: number;\n /** Region to highlight on the image */\n highlightRegion?: DataBinding;\n}\n\n/**\n * Card display for list items.\n */\nexport interface CardSection {\n type: \"card\";\n /** Thumbnail image source */\n thumbnail?: DataBinding;\n /** Card title */\n title: DynamicValue<string>;\n /** Card subtitle */\n subtitle?: DynamicValue<string>;\n /** Badge to display on card */\n badge?: {\n variant: \"severity\" | \"status\" | \"count\";\n value: DataBinding;\n label?: string;\n };\n /** Action on card click */\n onClick?: ActionReference;\n}\n\n/**\n * Progress indicator section.\n */\nexport interface ProgressSection {\n type: \"progress\";\n /** Current value (0-100 or binding) */\n value: DynamicValue<number>;\n /** Label to show */\n label?: DynamicValue<string>;\n /** Whether progress is indeterminate */\n indeterminate?: boolean;\n}\n\n/**\n * Union of all section types.\n */\nexport type PanelSection =\n | HeaderSection\n | CodeViewerSection\n | CodeComparisonSection\n | BadgeSection\n | TextSection\n | ActionsSection\n | DividerSection\n | ConditionalSection\n | ListSection\n | ImageSection\n | CardSection\n | ProgressSection;\n\n// =============================================================================\n// PANEL DEFINITION\n// =============================================================================\n\n/**\n * Loading state configuration.\n */\nexport interface LoadingConfig {\n /** Condition that indicates loading */\n when: DataBinding;\n /** Message to show while loading */\n message?: string;\n /** Sub-message for additional context */\n submessage?: string;\n}\n\n/**\n * Empty state configuration.\n */\nexport interface EmptyConfig {\n /** Condition that indicates empty state */\n when: DataBinding | ExpressionBinding;\n /** Message to show */\n message: string;\n /** Sub-message for additional context */\n submessage?: string;\n /** Icon to display */\n icon?: IconName;\n}\n\n/**\n * Complete panel definition.\n */\nexport interface PanelDefinition {\n /** Unique panel ID */\n id: string;\n /** Panel title (static or bound) */\n title: DynamicValue<string>;\n /** Priority for ordering (higher = earlier) */\n priority?: number;\n /** Panel layout - array of sections */\n layout: PanelSection[];\n /** Loading state configuration */\n loading?: LoadingConfig;\n /** Empty state configuration */\n empty?: EmptyConfig;\n}\n\n// =============================================================================\n// COMMANDS\n// =============================================================================\n\n/**\n * Command palette command definition.\n */\nexport interface CommandDefinition {\n /** Unique command ID (convention: \"pluginId:action-name\") */\n id: string;\n /** Command title shown in palette */\n title: string;\n /** Keywords for search */\n keywords: string[];\n /** Category for grouping */\n category: string;\n /** Subtitle/description */\n subtitle?: string;\n /** Icon to display */\n icon?: IconName;\n /** Keyboard shortcut (e.g., \"Cmd+Shift+C\") */\n shortcut?: string;\n /** Action to execute */\n action: ActionReference;\n /** Availability condition */\n isAvailable?: DataBinding | ExpressionBinding;\n /** Hide from \"All\" category (only show in plugin's category) */\n hideFromAllCategory?: boolean;\n}\n\n// =============================================================================\n// TOOLBAR\n// =============================================================================\n\n/**\n * Single toolbar action.\n */\nexport interface ToolbarActionDefinition {\n /** Unique action ID */\n id: string;\n /** Icon to display */\n icon: IconName;\n /** Tooltip text */\n tooltip: string;\n /** Action to execute */\n action: ActionReference;\n /** Visibility condition */\n isVisible?: DataBinding | ExpressionBinding;\n /** Enabled condition */\n isEnabled?: DataBinding | ExpressionBinding;\n}\n\n/**\n * Toolbar action group (dropdown).\n */\nexport interface ToolbarGroupDefinition {\n /** Unique group ID */\n id: string;\n /** Icon for the group button */\n icon: IconName;\n /** Tooltip for the group */\n tooltip: string;\n /** Priority for ordering (higher = earlier) */\n priority?: number;\n /** Group visibility condition */\n isVisible?: DataBinding | ExpressionBinding;\n /** Actions in this group */\n actions: ToolbarActionDefinition[];\n}\n\n// =============================================================================\n// RULES\n// =============================================================================\n\n/**\n * Rule option field schema for UI generation.\n */\nexport interface RuleOptionField {\n /** Option key */\n key: string;\n /** Display label */\n label: string;\n /** Field type */\n type: \"text\" | \"number\" | \"boolean\" | \"select\";\n /** Default value */\n defaultValue?: unknown;\n /** Options for select type */\n options?: Array<{ value: string; label: string }>;\n /** Field description */\n description?: string;\n /** Placeholder text */\n placeholder?: string;\n}\n\n/**\n * Rule option schema for generating settings UI.\n */\nexport interface RuleOptionSchema {\n fields: RuleOptionField[];\n}\n\n/**\n * Requirement for a rule to function.\n */\nexport interface RuleRequirement {\n /** Type of requirement. Plugins define their own requirement types. */\n type: string;\n /** Human-readable description */\n description: string;\n /** Hint for how to set up */\n setupHint: string;\n}\n\n/**\n * ESLint rule definition with UI metadata.\n */\nexport interface RuleDefinition {\n /** Rule ID */\n id: string;\n /** Human-readable name */\n name: string;\n /** Description */\n description: string;\n /** Category for grouping */\n category: string;\n /** Icon */\n icon?: IconName;\n /** Default severity */\n defaultSeverity: \"error\" | \"warn\" | \"off\";\n /** Whether enabled by default */\n defaultEnabled: boolean;\n /** Heatmap color for this rule's issues */\n heatmapColor?: string;\n /** Custom inspector panel ID to open for this rule's issues */\n customInspectorPanel?: string;\n /** Requirements for the rule to work */\n requirements?: RuleRequirement[];\n /** Default options */\n defaultOptions?: Record<string, unknown>[];\n /** Option schema for settings UI */\n optionSchema?: RuleOptionSchema;\n /** Documentation (markdown) */\n docs?: string;\n}\n\n// =============================================================================\n// ISSUE AGGREGATION\n// =============================================================================\n\n/**\n * Issue contributed by a plugin.\n */\nexport interface PluginIssue {\n /** Unique issue ID */\n id: string;\n /** Issue message */\n message: string;\n /** Severity level */\n severity: \"error\" | \"warning\" | \"info\";\n /** Associated rule ID */\n ruleId: string;\n /** Issue category */\n category?: string;\n /** Additional data for inspector */\n data?: Record<string, unknown>;\n}\n\n/**\n * Issue contribution from a plugin.\n */\nexport interface IssueContribution {\n /** Plugin that contributed these issues */\n pluginId: string;\n /** Issues keyed by dataLoc */\n issues: Map<string, PluginIssue[]>;\n}\n\n// =============================================================================\n// STATE MANAGEMENT\n// =============================================================================\n\n/**\n * Computed value definition.\n */\nexport type ComputedValue<TState, TResult> = (state: TState) => TResult;\n\n/**\n * Persistence configuration for plugin state.\n */\nexport interface PersistConfig<TState> {\n /** Storage key */\n key: string;\n /** Keys to include (if specified, only these are persisted) */\n include?: (keyof TState)[];\n /** Keys to exclude from persistence */\n exclude?: (keyof TState)[];\n}\n\n/**\n * State definition for a plugin.\n */\nexport interface StateDefinition<TState> {\n /** Initial state */\n initialState: TState;\n /** Computed values derived from state */\n computed?: Record<string, ComputedValue<TState, unknown>>;\n /** Persistence configuration */\n persist?: PersistConfig<TState>;\n}\n\n// =============================================================================\n// PLUGIN DEFINITION\n// =============================================================================\n\n/**\n * Complete plugin definition.\n *\n * This is what analysis packages export. The host (uilint-react, CLI)\n * imports these and wires everything up.\n *\n * NO REACT - Pure TypeScript configuration.\n */\nexport interface PluginDefinition<TState = unknown> {\n // === Metadata ===\n /** Unique plugin ID */\n id: string;\n /** Human-readable name */\n name: string;\n /** Semantic version */\n version: string;\n /** Description */\n description?: string;\n /** Icon */\n icon?: IconName;\n /** Plugin IDs this depends on */\n dependencies?: string[];\n\n // === State ===\n /** State definition */\n state: StateDefinition<TState>;\n\n // === UI Contributions (Declarative) ===\n /** Command palette commands */\n commands?: CommandDefinition[];\n /** Toolbar action groups */\n toolbarGroups?: ToolbarGroupDefinition[];\n /** Inspector panels */\n panels?: PanelDefinition[];\n\n // === Rules ===\n /** Rule definitions */\n rules?: RuleDefinition[];\n /** Rule categories this plugin handles */\n handlesRuleCategories?: string[];\n\n // === Browser Actions ===\n /** Browser-side actions this plugin needs. Plugins register handlers for these. */\n browserActions?: string[];\n}\n","/**\n * Operation Lifecycle Framework\n *\n * Shared types and factory functions for plugins that perform long-running\n * server-mediated operations (indexing, analysis, etc.).\n *\n * Plugins compose these into their own state/actions/messages rather than\n * inheriting from a base class.\n *\n * NO REACT CODE - Pure TypeScript.\n */\n\nimport type { ActionHandlers, MessageHandlers, PluginContext } from \"./context.js\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/**\n * Status lifecycle for a long-running plugin operation.\n *\n * - \"idle\" -- nothing has run yet (initial state)\n * - \"active\" -- operation is in progress\n * - \"complete\" -- finished successfully\n * - \"error\" -- finished with an error\n */\nexport type OperationStatus = \"idle\" | \"active\" | \"complete\" | \"error\";\n\n/**\n * Progress tracking for a long-running operation.\n */\nexport interface OperationProgress {\n current: number;\n total: number;\n /** Optional human-readable context (e.g., file path, phase name) */\n message?: string;\n}\n\n/**\n * State shape for a long-running operation.\n *\n * Plugins embed this inside their own state interface:\n * ```typescript\n * interface MyPluginState {\n * indexing: OperationState<IndexStats>;\n * // ...other plugin-specific fields\n * }\n * ```\n *\n * @template TStats Type of the completion payload (e.g., IndexStats)\n */\nexport interface OperationState<TStats = unknown> {\n /** Current operation status */\n status: OperationStatus;\n /** Progress info (null when idle or complete) */\n progress: OperationProgress | null;\n /** Completion stats from the last successful run */\n stats: TStats | null;\n /** Error message from the last failed run */\n lastError: string | null;\n}\n\n// =============================================================================\n// INITIAL STATE FACTORY\n// =============================================================================\n\n/**\n * Creates default initial state for an operation.\n *\n * @example\n * ```typescript\n * const initialState: MyState = {\n * indexing: createOperationInitialState<IndexStats>(),\n * };\n * ```\n */\nexport function createOperationInitialState<\n TStats = unknown,\n>(): OperationState<TStats> {\n return {\n status: \"idle\",\n progress: null,\n stats: null,\n lastError: null,\n };\n}\n\n// =============================================================================\n// COMPUTED VALUES FACTORY\n// =============================================================================\n\n/**\n * Creates standard computed values for an operation.\n *\n * Returns four computed selectors that can be spread (and optionally renamed)\n * into a plugin's `StateDefinition.computed`:\n *\n * - `isReady` -- status is \"complete\"\n * - `isActive` -- status is \"active\"\n * - `hasError` -- status is \"error\" or lastError is non-null\n * - `progressPercent` -- 0-100 number derived from progress\n *\n * @param getOp Extracts the OperationState from the full plugin state\n *\n * @example\n * ```typescript\n * const opComputed = createOperationComputed<MyState>((s) => s.indexing);\n * const stateDefinition = {\n * computed: {\n * isIndexReady: opComputed.isReady,\n * isIndexing: opComputed.isActive,\n * hasError: opComputed.hasError,\n * progressPercent: opComputed.progressPercent,\n * },\n * };\n * ```\n */\nexport function createOperationComputed<TState>(\n getOp: (state: TState) => OperationState\n) {\n return {\n isReady: (state: TState) => getOp(state).status === \"complete\",\n isActive: (state: TState) => getOp(state).status === \"active\",\n hasError: (state: TState) => {\n const op = getOp(state);\n return op.status === \"error\" || op.lastError !== null;\n },\n progressPercent: (state: TState) => {\n const op = getOp(state);\n if (!op.progress) return 0;\n if (op.progress.total === 0) return 0;\n return Math.round(\n (op.progress.current / op.progress.total) * 100\n );\n },\n };\n}\n\n// =============================================================================\n// ACTION HANDLERS FACTORY\n// =============================================================================\n\n/**\n * Configuration for creating operation action handlers.\n */\nexport interface OperationActionConfig<TState, TStats> {\n /** Extract the OperationState from the full plugin state */\n getOp: (state: TState) => OperationState<TStats>;\n /** Produce a partial state update that sets the operation state */\n setOp: (op: OperationState<TStats>) => Partial<TState>;\n}\n\n/**\n * Creates the four standard lifecycle action handlers for an operation.\n *\n * Returns handlers keyed as:\n * - `\"handle-{prefix}-start\"`\n * - `\"handle-{prefix}-progress\"`\n * - `\"handle-{prefix}-complete\"`\n * - `\"handle-{prefix}-error\"`\n *\n * These can be spread into a plugin's `ActionHandlers` alongside\n * any plugin-specific actions.\n *\n * @param prefix Action name prefix (e.g., \"indexing\", \"analysis\")\n * @param config How to read/write the operation state from plugin state\n *\n * @example\n * ```typescript\n * export const myActions: ActionHandlers<MyState> = {\n * ...createOperationActions<MyState, IndexStats>(\"indexing\", {\n * getOp: (s) => s.indexing,\n * setOp: (op) => ({ indexing: op }),\n * }),\n * // plugin-specific actions\n * \"start-indexing\": (ctx) => { ... },\n * };\n * ```\n */\nexport function createOperationActions<TState, TStats>(\n prefix: string,\n config: OperationActionConfig<TState, TStats>\n): ActionHandlers<TState> {\n const { getOp, setOp } = config;\n\n return {\n [`handle-${prefix}-start`]: (ctx: PluginContext<TState>) => {\n const current = getOp(ctx.getState());\n ctx.setState(\n setOp({\n ...current,\n status: \"active\",\n progress: { current: 0, total: 0 },\n lastError: null,\n })\n );\n },\n\n [`handle-${prefix}-progress`]: (\n ctx: PluginContext<TState>,\n payload: unknown\n ) => {\n const current = getOp(ctx.getState());\n ctx.setState(\n setOp({\n ...current,\n status: \"active\",\n progress: payload as OperationProgress,\n })\n );\n },\n\n [`handle-${prefix}-complete`]: (\n ctx: PluginContext<TState>,\n payload: unknown\n ) => {\n const current = getOp(ctx.getState());\n ctx.setState(\n setOp({\n ...current,\n status: \"complete\",\n progress: null,\n stats: payload as TStats,\n lastError: null,\n })\n );\n },\n\n [`handle-${prefix}-error`]: (\n ctx: PluginContext<TState>,\n payload: unknown\n ) => {\n const current = getOp(ctx.getState());\n const { error } = payload as { error: string };\n ctx.setState(\n setOp({\n ...current,\n status: \"error\",\n progress: null,\n lastError: error,\n })\n );\n },\n };\n}\n\n// =============================================================================\n// MESSAGE HANDLERS FACTORY\n// =============================================================================\n\n/**\n * Configuration for mapping WebSocket messages to operation actions.\n */\nexport interface OperationMessageConfig {\n /** Prefix for action dispatch (e.g., \"indexing\", \"analysis\") */\n actionPrefix: string;\n /** WS message type for start (optional -- some operations don't have one) */\n startMessage?: string;\n /** WS message type for progress */\n progressMessage: string;\n /** WS message type for completion */\n completeMessage: string;\n /** WS message type for error */\n errorMessage: string;\n /** Extract progress payload from the raw WS message */\n extractProgress?: (message: unknown) => OperationProgress;\n /** Extract completion stats from the raw WS message */\n extractStats?: (message: unknown) => unknown;\n /** Extract error string from the raw WS message */\n extractError?: (message: unknown) => string;\n}\n\n/**\n * Creates WebSocket message handlers for a standard operation lifecycle.\n *\n * Each handler dispatches to the corresponding `handle-{prefix}-*` action.\n *\n * @example\n * ```typescript\n * export const myMessages: MessageHandlers<MyState> = {\n * ...createOperationMessageHandlers<MyState>({\n * actionPrefix: \"indexing\",\n * startMessage: \"duplicates:indexing:start\",\n * progressMessage: \"duplicates:indexing:progress\",\n * completeMessage: \"duplicates:indexing:complete\",\n * errorMessage: \"duplicates:indexing:error\",\n * extractProgress: (msg) => ({\n * current: (msg as any).current ?? 0,\n * total: (msg as any).total ?? 0,\n * message: (msg as any).message,\n * }),\n * }),\n * };\n * ```\n */\nexport function createOperationMessageHandlers<TState>(\n config: OperationMessageConfig\n): MessageHandlers<TState> {\n const handlers: MessageHandlers<TState> = {};\n const { actionPrefix } = config;\n\n if (config.startMessage) {\n handlers[config.startMessage] = (ctx: PluginContext<TState>) => {\n ctx.dispatch(`handle-${actionPrefix}-start`);\n };\n }\n\n handlers[config.progressMessage] = (\n ctx: PluginContext<TState>,\n message: unknown\n ) => {\n const progress = config.extractProgress\n ? config.extractProgress(message)\n : (message as OperationProgress);\n ctx.dispatch(`handle-${actionPrefix}-progress`, progress);\n };\n\n handlers[config.completeMessage] = (\n ctx: PluginContext<TState>,\n message: unknown\n ) => {\n const stats = config.extractStats\n ? config.extractStats(message)\n : message;\n ctx.dispatch(`handle-${actionPrefix}-complete`, stats);\n };\n\n handlers[config.errorMessage] = (\n ctx: PluginContext<TState>,\n message: unknown\n ) => {\n const error = config.extractError\n ? config.extractError(message)\n : (message as { error: string }).error;\n ctx.dispatch(`handle-${actionPrefix}-error`, { error });\n };\n\n return handlers;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/ollama/prompts.ts","../src/ollama/defaults.ts","../src/ollama/client.ts","../src/format/issues.ts","../src/tailwind/class-tokens.ts","../src/scanner/style-extractor.ts","../src/styleguide/schema.ts","../src/styleguide/parser.ts","../src/styleguide/generator.ts","../src/consistency/prompts.ts","../src/consistency/analyzer.ts","../src/logger.ts","../src/plugin/registry.ts","../src/plugin/types.ts","../src/plugin/operation.ts"],"sourcesContent":["/**\n * LLM prompt builders for UILint analysis\n */\n\n/**\n * Builds a prompt for style analysis\n */\nexport function buildAnalysisPrompt(\n styleSummary: string,\n styleGuide: string | null\n): string {\n const guideSection = styleGuide\n ? `## Current Style Guide\\n${styleGuide}\\n\\n`\n : \"## No Style Guide Found\\nAnalyze the styles and identify inconsistencies.\\n\\n\";\n\n return `You are a UI consistency analyzer. Analyze the following extracted styles and identify UI consistency violations.\n\n${guideSection}\n\n${styleSummary}\n\nRespond with JSON ONLY. Return a single JSON object containing an \"issues\" array. Each issue should have:\n- id: unique string identifier\n- type: one of \"color\", \"typography\", \"spacing\", \"component\", \"responsive\", \"accessibility\"\n- message: human-readable description of the issue\n- currentValue: the problematic value found\n- expectedValue: what it should be (if known from style guide)\n\nIMPORTANT:\n- Output ONLY violations. Do NOT include fix instructions, remediation steps, or \"suggestions\".\n- Do NOT include extra top-level keys. Only return {\"issues\":[...]}.\n- Keep messages short and neutral.\n\nFocus on:\n1. Similar but not identical colors (e.g., #3B82F6 vs #3575E2)\n2. Inconsistent font sizes or weights\n3. Spacing values that don't follow a consistent scale (e.g., 4px base unit)\n4. Mixed border-radius values\n5. If utility/Tailwind classes are present in the summary, treat them as the styling surface area and flag inconsistent utility usage (e.g., mixing px-4 and px-5 for the same component type)\n\nBe minimal. Only report significant inconsistencies.\n\nExample response:\n{\n \"issues\": [\n {\n ...issue fields...\n }\n ]\n}`;\n}\n\nexport interface BuildSourceAnalysisPromptOptions {\n /**\n * Optional filename/path for extra context in the prompt.\n */\n filePath?: string;\n /**\n * Optional hint about language (e.g. tsx, jsx, html).\n */\n languageHint?: string;\n /**\n * Optional additional context (e.g., Tailwind tokens, extracted utilities).\n * Keep this concise; it's appended verbatim.\n */\n extraContext?: string;\n}\n\n/**\n * Builds a prompt for analyzing a raw source file/snippet (TSX/JSX/etc) directly,\n * without attempting to parse it as HTML/DOM.\n */\nexport function buildSourceAnalysisPrompt(\n source: string,\n styleGuide: string | null,\n options: BuildSourceAnalysisPromptOptions = {}\n): string {\n const guideSection = styleGuide\n ? `## Current Style Guide\\n${styleGuide}\\n\\n`\n : \"## No Style Guide Found\\nAnalyze the UI code and identify inconsistencies.\\n\\n\";\n\n const metaLines: string[] = [];\n if (options.filePath) metaLines.push(`- filePath: ${options.filePath}`);\n if (options.languageHint)\n metaLines.push(`- languageHint: ${options.languageHint}`);\n const metaSection =\n metaLines.length > 0\n ? `## Source Metadata\\n${metaLines.join(\"\\n\")}\\n\\n`\n : \"\";\n\n const extra =\n options.extraContext && options.extraContext.trim()\n ? `## Additional Context\\n${options.extraContext.trim()}\\n\\n`\n : \"\";\n\n return `You are a UI consistency analyzer. Analyze the following UI source code and identify UI consistency violations.\n\n${guideSection}${metaSection}${extra}## Source Code (raw)\n${source}\n\nRespond with JSON ONLY. Return a single JSON object containing an \"issues\" array. Each issue should have:\n- id: unique string identifier\n- type: one of \"color\", \"typography\", \"spacing\", \"component\", \"responsive\", \"accessibility\"\n- message: human-readable description of the issue\n- currentValue: the problematic value found\n- expectedValue: what it should be (if known from style guide)\n\nIMPORTANT:\n- Output ONLY violations. Do NOT include fix instructions, remediation steps, or \"suggestions\".\n- Do NOT include extra top-level keys. Only return {\"issues\":[...]}.\n- Keep messages short and neutral.\n\nFocus on:\n1. Inconsistent Tailwind/utility classes (e.g., mixing px-4 and px-5 for the same component type)\n2. Inconsistent component variants (e.g., button sizes/radii/typography drift)\n3. Hardcoded colors/spacing/typography that should use tokens/scale\n4. Accessibility issues visible from code (e.g., missing labels, low-contrast combos if obvious)\n\nBe minimal. Only report significant inconsistencies.`;\n}\n\nexport interface BuildSourceScanPromptOptions {\n /**\n * Display filename/path for context in the prompt.\n */\n filePath?: string;\n /**\n * Optional focus target for manual scan UX.\n */\n componentName?: string;\n componentLine?: number;\n /**\n * If true, the scan is for the selected element + children (UI text only).\n */\n includeChildren?: boolean;\n /**\n * Optional list of data-loc values (format: \"path:line:column\") that the model MUST\n * restrict issues to (so UI can highlight exactly).\n */\n dataLocs?: string[];\n}\n\n/**\n * Builds a prompt for scanning a source file/snippet for issues that can be mapped\n * back to rendered DOM elements via `data-loc`.\n *\n * Response schema (JSON):\n * { \"issues\": [{ \"line\"?: number, \"message\": string, \"dataLoc\"?: string }] }\n */\nexport function buildSourceScanPrompt(\n sourceCode: string,\n styleGuide: string | null,\n options: BuildSourceScanPromptOptions = {}\n): string {\n const fileLabel = options.filePath || \"component.tsx\";\n const dataLocList = Array.isArray(options.dataLocs) ? options.dataLocs : [];\n\n const focusSection =\n options.componentName && options.componentName.trim()\n ? `## Focus\n\nFocus on the \"${options.componentName.trim()}\" component${\n typeof options.componentLine === \"number\"\n ? ` (near line ${options.componentLine})`\n : \"\"\n }.\n\n`\n : \"\";\n\n const scopeText =\n options.includeChildren === true\n ? \"Scope: selected element + children.\"\n : \"Scope: selected element only.\";\n\n const dataLocSection =\n dataLocList.length > 0\n ? `## Element Locations (data-loc values)\n\nThe following elements are rendered on the page from this file. Each data-loc has format \"path:line:column\".\nWhen reporting issues, include the matching dataLoc value so we can highlight the specific element.\n\n\\`\\`\\`\n${dataLocList.join(\"\\n\")}\n\\`\\`\\`\n\n`\n : \"\";\n\n const guideSection = styleGuide\n ? `## Style Guide\n\n${styleGuide}\n\n`\n : \"\";\n\n return `You are a UI code reviewer. Analyze the following React/TypeScript UI code for style consistency issues.\n\n${guideSection}${focusSection}${scopeText}\n\n${dataLocSection}## Source Code (${fileLabel})\n\n\\`\\`\\`tsx\n${sourceCode}\n\\`\\`\\`\n\n## Task\n\nIdentify any style inconsistencies, violations of best practices, or deviations from the style guide.\nFor each issue, provide:\n- line: the line number in the source file\n- message: a clear description of the issue\n- dataLoc: the matching data-loc value from the list above (if applicable, to identify the specific element)\n\n## Critical Requirements for dataLoc\n\n- If \"Element Locations (data-loc values)\" are provided, ONLY report issues that correspond to one of those elements.\n- When you include a dataLoc, it MUST be copied verbatim from the list (no shortening paths, no normalization).\n- If you cannot match an issue to a provided dataLoc, omit that issue from the response.\n\nRespond with JSON:\n\\`\\`\\`json\n{\n \"issues\": [\n { \"line\": 12, \"message\": \"Color #3575E2 should be #3B82F6 (primary blue from styleguide)\", \"dataLoc\": \"app/page.tsx:12:5\" }\n ]\n}\n\\`\\`\\`\n\nIf no issues are found, respond with:\n\\`\\`\\`json\n{ \"issues\": [] }\n\\`\\`\\``;\n}\n\n/**\n * Builds a prompt for style guide generation\n */\nexport function buildStyleGuidePrompt(styleSummary: string): string {\n return `You are a design system expert. Based on the following detected styles, generate a clean, organized style guide in Markdown format.\n\n${styleSummary}\n\nGenerate a style guide with these sections:\n1. Colors - List the main colors with semantic names (Primary, Secondary, etc.)\n2. Typography - Font families, sizes, and weights\n3. Spacing - Base unit and common spacing values\n4. Components - Common component patterns\n5. Tailwind (if utility classes are present) - list commonly used utilities and any relevant theme tokens\n\nUse this format:\n# UI Style Guide\n\n## Colors\n- **Primary**: #HEXCODE (usage description)\n- **Secondary**: #HEXCODE (usage description)\n...\n\n## Typography\n- **Font Family**: FontName\n- **Font Sizes**: list of sizes\n- **Font Weights**: list of weights\n\n## Spacing\n- **Base unit**: Xpx\n- **Common values**: list of values\n\n## Components\n- **Buttons**: styles\n- **Cards**: styles\n...\n\nBe concise and focus on the most used values.`;\n}\n","/**\n * Single source of truth for Ollama defaults used across the monorepo.\n */\n\n/**\n * Default Ollama model used when none is provided.\n *\n * Note: This should match the model we recommend users `ollama pull`.\n */\nexport const UILINT_DEFAULT_OLLAMA_MODEL = \"qwen3-vl:8b-instruct\";\n","/**\n * Ollama API client for LLM interactions\n */\n\nimport type {\n UILintIssue,\n AnalysisResult,\n OllamaClientOptions,\n StreamProgressCallback,\n LLMInstrumentationCallbacks,\n} from \"../types.js\";\nimport {\n buildAnalysisPrompt,\n buildSourceAnalysisPrompt,\n buildStyleGuidePrompt,\n type BuildSourceAnalysisPromptOptions,\n} from \"./prompts.js\";\nimport { UILINT_DEFAULT_OLLAMA_MODEL } from \"./defaults.js\";\n\nconst DEFAULT_BASE_URL = \"http://localhost:11434\";\nconst DEFAULT_TIMEOUT = 120000;\n\nexport class OllamaClient {\n private baseUrl: string;\n private model: string;\n private timeout: number;\n private instrumentation?: LLMInstrumentationCallbacks;\n\n constructor(options: OllamaClientOptions = {}) {\n this.baseUrl = options.baseUrl || DEFAULT_BASE_URL;\n this.model = options.model || UILINT_DEFAULT_OLLAMA_MODEL;\n this.timeout = options.timeout || DEFAULT_TIMEOUT;\n this.instrumentation = options.instrumentation;\n }\n\n /**\n * Low-level completion API for custom prompts (used by installers/tools).\n *\n * When `json` is true, Ollama is requested to emit JSON (best-effort).\n */\n async complete(\n prompt: string,\n options: {\n json?: boolean;\n stream?: boolean;\n onProgress?: StreamProgressCallback;\n } = {}\n ): Promise<string> {\n const jsonFormat = options.json ?? false;\n if (options.stream && options.onProgress) {\n return await this.generateStreaming(\n prompt,\n jsonFormat,\n options.onProgress\n );\n }\n return await this.generate(prompt, jsonFormat);\n }\n\n /**\n * Analyzes styles and returns issues\n */\n async analyzeStyles(\n styleSummary: string,\n styleGuide: string | null,\n onProgress?: StreamProgressCallback\n ): Promise<AnalysisResult> {\n const startTime = Date.now();\n const prompt = buildAnalysisPrompt(styleSummary, styleGuide);\n\n try {\n const response = onProgress\n ? await this.generateStreaming(\n prompt,\n true,\n onProgress,\n \"analyze-styles\"\n )\n : await this.generate(prompt, true, \"analyze-styles\");\n const issues = this.parseIssuesResponse(response);\n\n return {\n issues,\n analysisTime: Date.now() - startTime,\n };\n } catch (error) {\n console.error(\"[UILint] Analysis failed:\", error);\n return {\n issues: [],\n analysisTime: Date.now() - startTime,\n };\n }\n }\n\n /**\n * Analyzes a raw source file/snippet (TSX/JSX/etc) directly and returns issues.\n * This bypasses HTML/DOM parsing entirely.\n */\n async analyzeSource(\n source: string,\n styleGuide: string | null,\n onProgress?: StreamProgressCallback,\n options: BuildSourceAnalysisPromptOptions = {}\n ): Promise<AnalysisResult> {\n const startTime = Date.now();\n const prompt = buildSourceAnalysisPrompt(source, styleGuide, options);\n\n try {\n const response = onProgress\n ? await this.generateStreaming(\n prompt,\n true,\n onProgress,\n \"analyze-source\"\n )\n : await this.generate(prompt, true, \"analyze-source\");\n const issues = this.parseIssuesResponse(response);\n\n return {\n issues,\n analysisTime: Date.now() - startTime,\n };\n } catch (error) {\n console.error(\"[UILint] Analysis failed:\", error);\n return {\n issues: [],\n analysisTime: Date.now() - startTime,\n };\n }\n }\n\n /**\n * Generates a style guide from detected styles\n */\n async generateStyleGuide(styleSummary: string): Promise<string | null> {\n const prompt = buildStyleGuidePrompt(styleSummary);\n\n try {\n const response = await this.generate(\n prompt,\n false,\n \"generate-styleguide\"\n );\n return response;\n } catch (error) {\n console.error(\"[UILint] Style guide generation failed:\", error);\n return null;\n }\n }\n\n /**\n * Core generate method that calls Ollama API\n */\n private async generate(\n prompt: string,\n jsonFormat: boolean = true,\n operationName: string = \"ollama-generate\"\n ): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Start instrumentation span if available\n const span = this.instrumentation?.onGenerationStart?.({\n name: operationName,\n model: this.model,\n prompt,\n metadata: { jsonFormat, stream: false },\n });\n\n try {\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: this.model,\n prompt,\n stream: false,\n ...(jsonFormat && { format: \"json\" }),\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const error = `Ollama API error: ${response.status}`;\n span?.end(\"\", { error });\n throw new Error(error);\n }\n\n const data = await response.json();\n const output = data.response || \"\";\n\n // End instrumentation span with output and usage\n span?.end(output, {\n promptTokens: data.prompt_eval_count,\n completionTokens: data.eval_count,\n totalTokens:\n (data.prompt_eval_count || 0) + (data.eval_count || 0) || undefined,\n });\n\n return output;\n } catch (error) {\n span?.end(\"\", { error: String(error) });\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Streaming generate method that calls Ollama API with streaming\n * and reports progress via callback\n */\n private async generateStreaming(\n prompt: string,\n jsonFormat: boolean = true,\n onProgress: StreamProgressCallback,\n operationName: string = \"ollama-generate-stream\"\n ): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Start instrumentation span if available\n const span = this.instrumentation?.onGenerationStart?.({\n name: operationName,\n model: this.model,\n prompt,\n metadata: { jsonFormat, stream: true },\n });\n\n // Track token counts from streaming response\n let promptTokens: number | undefined;\n let completionTokens: number | undefined;\n\n try {\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: this.model,\n prompt,\n stream: true,\n ...(jsonFormat && { format: \"json\" }),\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const error = `Ollama API error: ${response.status}`;\n span?.end(\"\", { error });\n throw new Error(error);\n }\n\n if (!response.body) {\n const error = \"No response body for streaming\";\n span?.end(\"\", { error });\n throw new Error(error);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let fullResponse = \"\";\n let lastLineEmitted = \"\";\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete JSON lines from the buffer\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const chunk = JSON.parse(line);\n const delta: string = chunk.response || \"\";\n\n if (delta) {\n fullResponse += delta;\n // Get the latest line from the response for progress display\n const responseLines = fullResponse.split(\"\\n\");\n const latestLine =\n responseLines[responseLines.length - 1] ||\n responseLines[responseLines.length - 2] ||\n \"\";\n lastLineEmitted = latestLine.trim();\n onProgress(lastLineEmitted, fullResponse, delta);\n }\n // Capture token counts from final chunk\n if (chunk.done) {\n promptTokens = chunk.prompt_eval_count;\n completionTokens = chunk.eval_count;\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n\n // Process any remaining content in buffer\n if (buffer.trim()) {\n try {\n const chunk = JSON.parse(buffer);\n const delta: string = chunk.response || \"\";\n if (delta) {\n fullResponse += delta;\n }\n if (chunk.done) {\n promptTokens = chunk.prompt_eval_count;\n completionTokens = chunk.eval_count;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n\n // End instrumentation span with output and usage\n span?.end(fullResponse, {\n promptTokens,\n completionTokens,\n totalTokens: (promptTokens || 0) + (completionTokens || 0) || undefined,\n });\n\n return fullResponse;\n } catch (error) {\n span?.end(\"\", { error: String(error) });\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Parses issues from LLM response\n */\n private parseIssuesResponse(response: string): UILintIssue[] {\n try {\n const parsed = JSON.parse(response);\n return parsed.issues || [];\n } catch {\n console.warn(\"[UILint] Failed to parse LLM response as JSON\");\n return [];\n }\n }\n\n /**\n * Checks if Ollama is available\n */\n async isAvailable(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/api/tags`, {\n method: \"GET\",\n signal: AbortSignal.timeout(5000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /**\n * Gets the current model\n */\n getModel(): string {\n return this.model;\n }\n\n /**\n * Sets the model\n */\n setModel(model: string): void {\n this.model = model;\n }\n\n /**\n * Sets instrumentation callbacks for observability.\n * This allows configuring instrumentation after construction.\n */\n setInstrumentation(\n instrumentation: LLMInstrumentationCallbacks | undefined\n ): void {\n this.instrumentation = instrumentation;\n }\n\n /**\n * Returns true if instrumentation is currently configured.\n */\n hasInstrumentation(): boolean {\n return !!this.instrumentation;\n }\n}\n\n// Default singleton instance\nlet defaultClient: OllamaClient | null = null;\n\nexport function getOllamaClient(options?: OllamaClientOptions): OllamaClient {\n if (!defaultClient || options) {\n defaultClient = new OllamaClient(options);\n }\n return defaultClient;\n}\n","import type { UILintIssue } from \"../types.js\";\n\nexport interface FormatViolationsOptions {\n /**\n * Optional context label (e.g., filename) to include as a single header line.\n * Keep this empty for the most minimal output.\n */\n context?: string;\n /**\n * Include the trailing \"consult the style guide\" message.\n * Defaults to true.\n */\n includeFooter?: boolean;\n /**\n * Override the footer message.\n */\n footerMessage?: string;\n}\n\nconst DEFAULT_FOOTER = \"Consult the style guide for guidance.\";\n\n/**\n * Ensures issues are safe/minimal to print or serialize:\n * - drops `suggestion` (we only want violations)\n * - trims string fields\n */\nexport function sanitizeIssues(issues: UILintIssue[]): UILintIssue[] {\n return issues.map((issue) => ({\n id: String(issue.id ?? \"\").trim(),\n type: issue.type,\n message: String(issue.message ?? \"\").trim(),\n element: issue.element ? String(issue.element).trim() : undefined,\n selector: issue.selector ? String(issue.selector).trim() : undefined,\n currentValue: issue.currentValue ? String(issue.currentValue).trim() : undefined,\n expectedValue: issue.expectedValue\n ? String(issue.expectedValue).trim()\n : undefined,\n // Intentionally omit `suggestion`\n }));\n}\n\n/**\n * Minimal human-readable rendering of violations.\n * Intended for CLI/MCP text output.\n */\nexport function formatViolationsText(\n issues: UILintIssue[],\n options: FormatViolationsOptions = {}\n): string {\n const { context, includeFooter = true, footerMessage = DEFAULT_FOOTER } =\n options;\n\n if (!issues || issues.length === 0) {\n return \"No violations found.\";\n }\n\n const lines: string[] = [];\n\n if (context && context.trim()) {\n lines.push(`Violations in ${context.trim()}:`);\n lines.push(\"\");\n }\n\n const sanitized = sanitizeIssues(issues);\n sanitized.forEach((issue, i) => {\n lines.push(`${i + 1}. [${issue.type}] ${issue.message}`);\n if (issue.currentValue && issue.expectedValue) {\n lines.push(` ${issue.currentValue} → ${issue.expectedValue}`);\n } else if (issue.currentValue) {\n lines.push(` ${issue.currentValue}`);\n }\n lines.push(\"\");\n });\n\n // remove trailing blank line\n while (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop();\n\n if (includeFooter) {\n lines.push(\"\");\n lines.push(footerMessage);\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Tailwind / utility-class token extraction helpers.\n *\n * Notes:\n * - Works on HTML (class=\"...\") and TSX-ish input (className=\"...\") via regex.\n * - Variant parsing is bracket-aware so arbitrary values like bg-[color:var(--x)]\n * don't get split incorrectly on \":\".\n */\n\nexport interface ClassTokenCounts {\n /**\n * Base utilities with variants stripped.\n * Example: \"sm:hover:bg-gray-50\" => utility \"bg-gray-50\"\n */\n utilities: Map<string, number>;\n /**\n * Individual variant prefixes.\n * Example: \"sm:hover:bg-gray-50\" => variants \"sm\", \"hover\"\n */\n variants: Map<string, number>;\n}\n\nexport interface ExtractClassTokenOptions {\n /**\n * Maximum number of tokens to process (guardrail for very large inputs).\n */\n maxTokens?: number;\n}\n\nexport function extractClassTokensFromHtml(\n html: string,\n options: ExtractClassTokenOptions = {}\n): ClassTokenCounts {\n const { maxTokens = 20000 } = options;\n\n const utilities = new Map<string, number>();\n const variants = new Map<string, number>();\n\n if (!html) return { utilities, variants };\n\n // Match both HTML and JSX-ish attributes.\n // - class=\"...\"\n // - className=\"...\"\n const attrPattern = /\\b(?:class|className)\\s*=\\s*[\"']([^\"']+)[\"']/g;\n\n let tokenBudget = maxTokens;\n let match: RegExpExecArray | null;\n while ((match = attrPattern.exec(html)) && tokenBudget > 0) {\n const raw = match[1];\n if (!raw) continue;\n\n const tokens = raw.split(/\\s+/g).filter(Boolean);\n for (const token of tokens) {\n if (tokenBudget-- <= 0) break;\n\n const { base, variantList } = splitVariants(token);\n const normalizedBase = normalizeUtility(base);\n if (!normalizedBase) continue;\n\n increment(utilities, normalizedBase);\n for (const v of variantList) increment(variants, v);\n }\n }\n\n return { utilities, variants };\n}\n\nexport function topEntries(\n map: Map<string, number>,\n limit: number\n): Array<{ token: string; count: number }> {\n return [...map.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, limit)\n .map(([token, count]) => ({ token, count }));\n}\n\nfunction increment(map: Map<string, number>, key: string): void {\n map.set(key, (map.get(key) || 0) + 1);\n}\n\nfunction normalizeUtility(token: string): string | null {\n const t = token.trim();\n if (!t) return null;\n\n // Strip important modifier\n const noImportant = t.startsWith(\"!\") ? t.slice(1) : t;\n\n // Ignore obviously non-class tokens\n if (!noImportant || noImportant === \"{\" || noImportant === \"}\") return null;\n\n return noImportant;\n}\n\n/**\n * Splits a class token into variants and base utility.\n * Bracket-aware to avoid splitting arbitrary values on \":\".\n *\n * Examples:\n * - \"sm:hover:bg-gray-50\" => variants [\"sm\",\"hover\"], base \"bg-gray-50\"\n * - \"bg-[color:var(--x)]\" => variants [], base \"bg-[color:var(--x)]\"\n * - \"sm:bg-[color:var(--x)]\" => variants [\"sm\"], base \"bg-[color:var(--x)]\"\n */\nfunction splitVariants(token: string): { base: string; variantList: string[] } {\n const parts: string[] = [];\n let buf = \"\";\n let bracketDepth = 0;\n\n for (let i = 0; i < token.length; i++) {\n const ch = token[i];\n if (ch === \"[\") bracketDepth++;\n if (ch === \"]\" && bracketDepth > 0) bracketDepth--;\n\n if (ch === \":\" && bracketDepth === 0) {\n parts.push(buf);\n buf = \"\";\n continue;\n }\n\n buf += ch;\n }\n parts.push(buf);\n\n if (parts.length <= 1) return { base: token, variantList: [] };\n\n const base = parts[parts.length - 1] || \"\";\n const variantList = parts\n .slice(0, -1)\n .map((v) => v.trim())\n .filter(Boolean);\n\n return { base, variantList };\n}\n","/**\n * Style extraction from DOM elements\n */\n\nimport type {\n ExtractedStyles,\n SerializedStyles,\n TailwindThemeTokens,\n} from \"../types.js\";\nimport {\n extractClassTokensFromHtml,\n topEntries,\n} from \"../tailwind/class-tokens.js\";\n\n/**\n * Extracts all computed styles from elements in the document\n * Works in both browser and JSDOM environments\n */\nexport function extractStyles(\n root: Element | Document,\n getComputedStyle: (el: Element) => CSSStyleDeclaration\n): ExtractedStyles {\n const styles: ExtractedStyles = {\n colors: new Map(),\n fontSizes: new Map(),\n fontFamilies: new Map(),\n fontWeights: new Map(),\n spacing: new Map(),\n borderRadius: new Map(),\n };\n\n const elements = root.querySelectorAll(\"*\");\n\n elements.forEach((element) => {\n // nodeType === 1 means Element node (works in both browser and JSDOM)\n if (element.nodeType !== 1) return;\n\n const computed = getComputedStyle(element);\n\n // Extract colors\n extractColor(computed.color, styles.colors);\n extractColor(computed.backgroundColor, styles.colors);\n extractColor(computed.borderColor, styles.colors);\n\n // Extract typography\n incrementMap(styles.fontSizes, computed.fontSize);\n incrementMap(styles.fontFamilies, normalizeFontFamily(computed.fontFamily));\n incrementMap(styles.fontWeights, computed.fontWeight);\n\n // Extract spacing\n extractSpacing(computed.margin, styles.spacing);\n extractSpacing(computed.padding, styles.spacing);\n incrementMap(styles.spacing, computed.gap);\n\n // Extract border radius\n incrementMap(styles.borderRadius, computed.borderRadius);\n });\n\n return styles;\n}\n\n/**\n * Extracts styles from browser DOM (uses window.getComputedStyle)\n */\nexport function extractStylesFromDOM(\n root?: Element | Document\n): ExtractedStyles {\n const targetRoot = root || document.body;\n return extractStyles(targetRoot, (el) => window.getComputedStyle(el));\n}\n\nfunction extractColor(color: string, map: Map<string, number>): void {\n if (!color || color === \"transparent\" || color === \"rgba(0, 0, 0, 0)\") return;\n\n // Normalize to hex\n const hex = rgbToHex(color);\n if (hex) {\n incrementMap(map, hex);\n }\n}\n\nfunction extractSpacing(value: string, map: Map<string, number>): void {\n if (!value || value === \"0px\") return;\n\n // Split compound values (e.g., \"10px 20px 10px 20px\")\n const values = value.split(\" \").filter((v) => v && v !== \"0px\");\n values.forEach((v) => incrementMap(map, v));\n}\n\nfunction incrementMap(map: Map<string, number>, value: string): void {\n if (!value || value === \"normal\" || value === \"auto\") return;\n map.set(value, (map.get(value) || 0) + 1);\n}\n\nfunction normalizeFontFamily(fontFamily: string): string {\n // Get the primary font (first in the stack)\n const primary = fontFamily.split(\",\")[0].trim();\n return primary.replace(/['\"]/g, \"\");\n}\n\nfunction rgbToHex(rgb: string): string | null {\n // Handle hex values\n if (rgb.startsWith(\"#\")) return rgb.toUpperCase();\n\n // Handle rgb/rgba values\n const match = rgb.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n if (!match) return null;\n\n const [, r, g, b] = match;\n const toHex = (n: string) => parseInt(n).toString(16).padStart(2, \"0\");\n\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\n}\n\n/**\n * Converts ExtractedStyles maps to plain objects for serialization\n */\nexport function serializeStyles(styles: ExtractedStyles): SerializedStyles {\n return {\n colors: Object.fromEntries(styles.colors),\n fontSizes: Object.fromEntries(styles.fontSizes),\n fontFamilies: Object.fromEntries(styles.fontFamilies),\n fontWeights: Object.fromEntries(styles.fontWeights),\n spacing: Object.fromEntries(styles.spacing),\n borderRadius: Object.fromEntries(styles.borderRadius),\n };\n}\n\n/**\n * Converts SerializedStyles back to ExtractedStyles\n */\nexport function deserializeStyles(\n serialized: SerializedStyles\n): ExtractedStyles {\n return {\n colors: new Map(Object.entries(serialized.colors)),\n fontSizes: new Map(Object.entries(serialized.fontSizes)),\n fontFamilies: new Map(Object.entries(serialized.fontFamilies)),\n fontWeights: new Map(Object.entries(serialized.fontWeights)),\n spacing: new Map(Object.entries(serialized.spacing)),\n borderRadius: new Map(Object.entries(serialized.borderRadius)),\n };\n}\n\n/**\n * Creates a summary of extracted styles for LLM analysis\n */\nexport function createStyleSummary(\n styles: ExtractedStyles,\n options: CreateStyleSummaryOptions = {}\n): string {\n return createStyleSummaryWithOptions(styles, options);\n}\n\nexport interface CreateStyleSummaryOptions {\n /**\n * Optional HTML/TSX-ish string used to extract utility classes (Tailwind etc).\n */\n html?: string;\n /**\n * Optional Tailwind theme tokens (typically from tailwind.config.*).\n */\n tailwindTheme?: TailwindThemeTokens | null;\n}\n\n/**\n * Creates a summary of extracted styles for LLM analysis.\n * Accepts optional Tailwind context to make Tailwind-heavy projects analyzable\n * even when computed styles are sparse (e.g., JSDOM without loaded CSS).\n */\nexport function createStyleSummaryWithOptions(\n styles: ExtractedStyles,\n options: CreateStyleSummaryOptions = {}\n): string {\n const lines: string[] = [];\n\n lines.push(\"## Detected Styles Summary\\n\");\n\n // Colors\n lines.push(\"### Colors\");\n const sortedColors = [...styles.colors.entries()].sort((a, b) => b[1] - a[1]);\n sortedColors.slice(0, 20).forEach(([color, count]) => {\n lines.push(`- ${color}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Font sizes\n lines.push(\"### Font Sizes\");\n const sortedFontSizes = [...styles.fontSizes.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedFontSizes.forEach(([size, count]) => {\n lines.push(`- ${size}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Font families\n lines.push(\"### Font Families\");\n const sortedFontFamilies = [...styles.fontFamilies.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedFontFamilies.forEach(([family, count]) => {\n lines.push(`- ${family}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Font weights\n lines.push(\"### Font Weights\");\n const sortedFontWeights = [...styles.fontWeights.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedFontWeights.forEach(([weight, count]) => {\n lines.push(`- ${weight}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Spacing\n lines.push(\"### Spacing Values\");\n const sortedSpacing = [...styles.spacing.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedSpacing.slice(0, 15).forEach(([value, count]) => {\n lines.push(`- ${value}: ${count} occurrences`);\n });\n lines.push(\"\");\n\n // Border radius\n lines.push(\"### Border Radius\");\n const sortedBorderRadius = [...styles.borderRadius.entries()].sort(\n (a, b) => b[1] - a[1]\n );\n sortedBorderRadius.forEach(([value, count]) => {\n lines.push(`- ${value}: ${count} occurrences`);\n });\n\n // Tailwind / utility classes (optional)\n if (options.html) {\n const tokens = extractClassTokensFromHtml(options.html);\n const topUtilities = topEntries(tokens.utilities, 40);\n const topVariants = topEntries(tokens.variants, 15);\n\n lines.push(\"\");\n lines.push(\"### Utility Classes (from markup)\");\n if (topUtilities.length === 0) {\n lines.push(\"- (none detected)\");\n } else {\n topUtilities.forEach(({ token, count }) => {\n lines.push(`- ${token}: ${count} occurrences`);\n });\n }\n\n if (topVariants.length > 0) {\n lines.push(\"\");\n lines.push(\"### Common Variants\");\n topVariants.forEach(({ token, count }) => {\n lines.push(`- ${token}: ${count} occurrences`);\n });\n }\n }\n\n // Tailwind theme tokens (optional)\n if (options.tailwindTheme) {\n const tt = options.tailwindTheme;\n lines.push(\"\");\n lines.push(\"### Tailwind Theme Tokens (from config)\");\n lines.push(`- configPath: ${tt.configPath}`);\n lines.push(`- colors: ${tt.colors.length}`);\n lines.push(`- spacingKeys: ${tt.spacingKeys.length}`);\n lines.push(`- borderRadiusKeys: ${tt.borderRadiusKeys.length}`);\n lines.push(`- fontFamilyKeys: ${tt.fontFamilyKeys.length}`);\n lines.push(`- fontSizeKeys: ${tt.fontSizeKeys.length}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Truncates HTML to a maximum length\n */\nexport function truncateHTML(html: string, maxLength: number = 50000): string {\n if (html.length <= maxLength) return html;\n return html.slice(0, maxLength) + \"<!-- truncated -->\";\n}\n","/**\n * Style guide schema and utilities\n */\n\nimport type { StyleGuide, ColorRule, TypographyRule, SpacingRule, ComponentRule } from \"../types.js\";\n\n/**\n * Creates an empty style guide structure\n */\nexport function createEmptyStyleGuide(): StyleGuide {\n return {\n colors: [],\n typography: [],\n spacing: [],\n components: [],\n };\n}\n\n/**\n * Validates a style guide object\n */\nexport function validateStyleGuide(guide: unknown): guide is StyleGuide {\n if (!guide || typeof guide !== \"object\") return false;\n\n const g = guide as Record<string, unknown>;\n\n return (\n Array.isArray(g.colors) &&\n Array.isArray(g.typography) &&\n Array.isArray(g.spacing) &&\n Array.isArray(g.components)\n );\n}\n\n/**\n * Merges detected styles into an existing style guide\n */\nexport function mergeStyleGuides(\n existing: StyleGuide,\n detected: Partial<StyleGuide>\n): StyleGuide {\n return {\n colors: mergeColorRules(existing.colors, detected.colors || []),\n typography: mergeTypographyRules(existing.typography, detected.typography || []),\n spacing: mergeSpacingRules(existing.spacing, detected.spacing || []),\n components: mergeComponentRules(existing.components, detected.components || []),\n };\n}\n\nfunction mergeColorRules(existing: ColorRule[], detected: ColorRule[]): ColorRule[] {\n const merged = [...existing];\n detected.forEach((rule) => {\n const existingIndex = merged.findIndex(\n (e) => e.name === rule.name || e.value === rule.value\n );\n if (existingIndex === -1) {\n merged.push(rule);\n }\n });\n return merged;\n}\n\nfunction mergeTypographyRules(existing: TypographyRule[], detected: TypographyRule[]): TypographyRule[] {\n const merged = [...existing];\n detected.forEach((rule) => {\n const existingIndex = merged.findIndex((e) => e.element === rule.element);\n if (existingIndex === -1) {\n merged.push(rule);\n }\n });\n return merged;\n}\n\nfunction mergeSpacingRules(existing: SpacingRule[], detected: SpacingRule[]): SpacingRule[] {\n const merged = [...existing];\n detected.forEach((rule) => {\n const existingIndex = merged.findIndex(\n (e) => e.name === rule.name || e.value === rule.value\n );\n if (existingIndex === -1) {\n merged.push(rule);\n }\n });\n return merged;\n}\n\nfunction mergeComponentRules(existing: ComponentRule[], detected: ComponentRule[]): ComponentRule[] {\n const merged = [...existing];\n detected.forEach((rule) => {\n const existingIndex = merged.findIndex((e) => e.name === rule.name);\n if (existingIndex === -1) {\n merged.push(rule);\n }\n });\n return merged;\n}\n\n/**\n * Creates a color rule\n */\nexport function createColorRule(\n name: string,\n value: string,\n usage: string = \"\"\n): ColorRule {\n return { name, value: value.toUpperCase(), usage };\n}\n\n/**\n * Creates a typography rule\n */\nexport function createTypographyRule(\n element: string,\n options: Partial<Omit<TypographyRule, \"element\">> = {}\n): TypographyRule {\n return { element, ...options };\n}\n\n/**\n * Creates a spacing rule\n */\nexport function createSpacingRule(name: string, value: string): SpacingRule {\n return { name, value };\n}\n\n/**\n * Creates a component rule\n */\nexport function createComponentRule(name: string, styles: string[]): ComponentRule {\n return { name, styles };\n}\n\n","/**\n * Parse Markdown style guides into structured data\n */\n\nimport type {\n StyleGuide,\n ColorRule,\n TypographyRule,\n SpacingRule,\n ComponentRule,\n ExtractedStyleValues,\n} from \"../types.js\";\nimport { createEmptyStyleGuide } from \"./schema.js\";\n\n/**\n * Parses a Markdown style guide into a structured object\n */\nexport function parseStyleGuide(markdown: string): StyleGuide {\n const guide = createEmptyStyleGuide();\n const sections = splitIntoSections(markdown);\n\n sections.forEach(({ title, content }) => {\n const lowerTitle = title.toLowerCase();\n\n if (lowerTitle.includes(\"color\")) {\n guide.colors = parseColorSection(content);\n } else if (\n lowerTitle.includes(\"typography\") ||\n lowerTitle.includes(\"font\")\n ) {\n guide.typography = parseTypographySection(content);\n } else if (lowerTitle.includes(\"spacing\")) {\n guide.spacing = parseSpacingSection(content);\n } else if (lowerTitle.includes(\"component\")) {\n guide.components = parseComponentSection(content);\n }\n });\n\n return guide;\n}\n\ninterface Section {\n title: string;\n content: string;\n}\n\nfunction splitIntoSections(markdown: string): Section[] {\n const sections: Section[] = [];\n const lines = markdown.split(\"\\n\");\n\n let currentTitle = \"\";\n let currentContent: string[] = [];\n\n lines.forEach((line) => {\n const headerMatch = line.match(/^##\\s+(.+)$/);\n\n if (headerMatch) {\n if (currentTitle) {\n sections.push({\n title: currentTitle,\n content: currentContent.join(\"\\n\"),\n });\n }\n currentTitle = headerMatch[1];\n currentContent = [];\n } else {\n currentContent.push(line);\n }\n });\n\n if (currentTitle) {\n sections.push({\n title: currentTitle,\n content: currentContent.join(\"\\n\"),\n });\n }\n\n return sections;\n}\n\nfunction parseColorSection(content: string): ColorRule[] {\n const colors: ColorRule[] = [];\n const lines = content.split(\"\\n\");\n\n lines.forEach((line) => {\n // Match patterns like: - **Primary**: #3B82F6 (used in buttons)\n const match = line.match(\n /[-*]\\s*\\*?\\*?([^*:]+)\\*?\\*?:\\s*(#[A-Fa-f0-9]{6})\\s*(?:\\(([^)]+)\\))?/\n );\n\n if (match) {\n colors.push({\n name: match[1].trim(),\n value: match[2].toUpperCase(),\n usage: match[3] || \"\",\n });\n }\n });\n\n return colors;\n}\n\nfunction parseTypographySection(content: string): TypographyRule[] {\n const typography: TypographyRule[] = [];\n const lines = content.split(\"\\n\");\n\n lines.forEach((line) => {\n // Match patterns like: - **Headings**: font-family: \"Inter\", font-size: 24px\n const elementMatch = line.match(/[-*]\\s*\\*?\\*?([^*:]+)\\*?\\*?:\\s*(.+)/);\n\n if (elementMatch) {\n const rule: TypographyRule = {\n element: elementMatch[1].trim(),\n };\n\n const props = elementMatch[2];\n\n const fontFamilyMatch = props.match(/font-family:\\s*\"?([^\",]+)\"?/);\n if (fontFamilyMatch) rule.fontFamily = fontFamilyMatch[1].trim();\n\n const fontSizeMatch = props.match(/font-size:\\s*(\\d+px)/);\n if (fontSizeMatch) rule.fontSize = fontSizeMatch[1];\n\n const fontWeightMatch = props.match(/font-weight:\\s*(\\d+)/);\n if (fontWeightMatch) rule.fontWeight = fontWeightMatch[1];\n\n const lineHeightMatch = props.match(/line-height:\\s*([\\d.]+)/);\n if (lineHeightMatch) rule.lineHeight = lineHeightMatch[1];\n\n typography.push(rule);\n }\n });\n\n return typography;\n}\n\nfunction parseSpacingSection(content: string): SpacingRule[] {\n const spacing: SpacingRule[] = [];\n const lines = content.split(\"\\n\");\n\n lines.forEach((line) => {\n // Match patterns like: - **Base unit**: 4px\n const match = line.match(/[-*]\\s*\\*?\\*?([^*:]+)\\*?\\*?:\\s*(.+)/);\n\n if (match) {\n spacing.push({\n name: match[1].trim(),\n value: match[2].trim(),\n });\n }\n });\n\n return spacing;\n}\n\nfunction parseComponentSection(content: string): ComponentRule[] {\n const components: ComponentRule[] = [];\n const lines = content.split(\"\\n\");\n\n lines.forEach((line) => {\n // Match patterns like: - **Buttons**: rounded-lg, px-4 py-2\n const match = line.match(/[-*]\\s*\\*?\\*?([^*:]+)\\*?\\*?:\\s*(.+)/);\n\n if (match) {\n components.push({\n name: match[1].trim(),\n styles: match[2].split(\",\").map((s) => s.trim()),\n });\n }\n });\n\n return components;\n}\n\n/**\n * Parses sections from a Markdown style guide (simpler format)\n */\nexport function parseStyleGuideSections(\n content: string\n): Record<string, string> {\n const sections: Record<string, string> = {};\n const lines = content.split(\"\\n\");\n\n let currentSection = \"intro\";\n let currentContent: string[] = [];\n\n for (const line of lines) {\n const headerMatch = line.match(/^##\\s+(.+)$/);\n\n if (headerMatch) {\n if (currentContent.length > 0) {\n sections[currentSection.toLowerCase()] = currentContent\n .join(\"\\n\")\n .trim();\n }\n\n currentSection = headerMatch[1];\n currentContent = [];\n } else {\n currentContent.push(line);\n }\n }\n\n if (currentContent.length > 0) {\n sections[currentSection.toLowerCase()] = currentContent.join(\"\\n\").trim();\n }\n\n return sections;\n}\n\n/**\n * Extracts specific values from the style guide\n */\nexport function extractStyleValues(content: string): ExtractedStyleValues {\n const result: ExtractedStyleValues = {\n colors: [],\n fontSizes: [],\n fontFamilies: [],\n spacing: [],\n borderRadius: [],\n };\n\n // Extract hex colors\n const colorMatches = content.matchAll(/#[A-Fa-f0-9]{6}\\b/g);\n for (const match of colorMatches) {\n if (!result.colors.includes(match[0].toUpperCase())) {\n result.colors.push(match[0].toUpperCase());\n }\n }\n\n // Extract font sizes (e.g., 16px, 1.5rem)\n const fontSizeMatches = content.matchAll(/\\b(\\d+(?:\\.\\d+)?(?:px|rem|em))\\b/g);\n for (const match of fontSizeMatches) {\n if (!result.fontSizes.includes(match[1])) {\n result.fontSizes.push(match[1]);\n }\n }\n\n // Extract font families (quoted strings in font context)\n const fontFamilyMatches = content.matchAll(\n /font-family:\\s*[\"']?([^\"',\\n]+)/gi\n );\n for (const match of fontFamilyMatches) {\n const family = match[1].trim();\n if (!result.fontFamilies.includes(family)) {\n result.fontFamilies.push(family);\n }\n }\n\n return result;\n}\n\nexport interface TailwindAllowlist {\n allowAnyColor: boolean;\n allowStandardSpacing: boolean;\n allowedTailwindColors: Set<string>;\n allowedUtilities: Set<string>;\n allowedSpacingKeys: Set<string>;\n allowedBorderRadiusKeys: Set<string>;\n allowedFontSizeKeys: Set<string>;\n allowedFontFamilyKeys: Set<string>;\n}\n\n/**\n * Extract Tailwind / utility-class allowlist configuration from a style guide.\n *\n * Expected formats:\n * - A JSON code block inside a \"## Tailwind\" section (preferred; produced by UILint)\n * - Fallback: inline backticked utilities within the Tailwind section\n */\nexport function extractTailwindAllowlist(content: string): TailwindAllowlist {\n const empty: TailwindAllowlist = {\n allowAnyColor: false,\n allowStandardSpacing: false,\n allowedTailwindColors: new Set(),\n allowedUtilities: new Set(),\n allowedSpacingKeys: new Set(),\n allowedBorderRadiusKeys: new Set(),\n allowedFontSizeKeys: new Set(),\n allowedFontFamilyKeys: new Set(),\n };\n\n // Only look for allowlist details in the Tailwind section.\n const sections = parseStyleGuideSections(content);\n const tailwindSection =\n sections[\"tailwind\"] ??\n // defensive: some styleguides use different casing/spacing\n sections[\"tailwind utilities\"] ??\n \"\";\n\n if (!tailwindSection) return empty;\n\n const parsed = tryParseFirstJsonCodeBlock(tailwindSection);\n if (parsed && typeof parsed === \"object\") {\n const parsedObj = parsed as Record<string, unknown>;\n const allowAnyColor = Boolean(parsedObj.allowAnyColor);\n const allowStandardSpacing = Boolean(parsedObj.allowStandardSpacing);\n\n const allowedUtilitiesArr = Array.isArray(parsedObj.allowedUtilities)\n ? (parsedObj.allowedUtilities as unknown[]).filter(\n (u): u is string => typeof u === \"string\"\n )\n : [];\n\n const themeTokens = (parsedObj.themeTokens ?? {}) as Record<string, unknown>;\n const themeColors = Array.isArray(themeTokens.colors)\n ? (themeTokens.colors as unknown[]).filter(\n (c): c is string => typeof c === \"string\"\n )\n : [];\n const spacingKeys = Array.isArray(themeTokens.spacingKeys)\n ? (themeTokens.spacingKeys as unknown[]).filter(\n (k): k is string => typeof k === \"string\"\n )\n : [];\n const borderRadiusKeys = Array.isArray(themeTokens.borderRadiusKeys)\n ? (themeTokens.borderRadiusKeys as unknown[]).filter(\n (k): k is string => typeof k === \"string\"\n )\n : [];\n const fontFamilyKeys = Array.isArray(themeTokens.fontFamilyKeys)\n ? (themeTokens.fontFamilyKeys as unknown[]).filter(\n (k): k is string => typeof k === \"string\"\n )\n : [];\n const fontSizeKeys = Array.isArray(themeTokens.fontSizeKeys)\n ? (themeTokens.fontSizeKeys as unknown[]).filter(\n (k): k is string => typeof k === \"string\"\n )\n : [];\n\n const allowedTailwindColors = new Set<string>();\n for (const c of themeColors) {\n const raw = c.trim();\n if (!raw) continue;\n if (raw.toLowerCase().startsWith(\"tailwind:\")) {\n allowedTailwindColors.add(raw.toLowerCase());\n continue;\n }\n const m = raw.match(/^([a-zA-Z]+)-(\\d{2,3})$/);\n if (m) {\n allowedTailwindColors.add(`tailwind:${m[1].toLowerCase()}-${m[2]}`);\n }\n }\n\n return {\n allowAnyColor,\n allowStandardSpacing,\n allowedTailwindColors,\n allowedUtilities: new Set(\n allowedUtilitiesArr.map((s) => s.trim()).filter(Boolean)\n ),\n allowedSpacingKeys: new Set(\n spacingKeys.map((s) => s.trim()).filter(Boolean)\n ),\n allowedBorderRadiusKeys: new Set(\n borderRadiusKeys.map((s) => s.trim()).filter(Boolean)\n ),\n allowedFontSizeKeys: new Set(\n fontSizeKeys.map((s) => s.trim()).filter(Boolean)\n ),\n allowedFontFamilyKeys: new Set(\n fontFamilyKeys.map((s) => s.trim()).filter(Boolean)\n ),\n };\n }\n\n // Fallback: harvest backticked utilities from markdown.\n const backticked: string[] = [];\n for (const m of tailwindSection.matchAll(/`([^`]+)`/g)) {\n backticked.push(m[1]);\n }\n\n return {\n ...empty,\n allowedUtilities: new Set(\n backticked\n .flatMap((s) => s.split(/[,\\s]+/g))\n .map((s) => s.trim())\n .filter(Boolean)\n ),\n };\n}\n\nfunction tryParseFirstJsonCodeBlock(section: string): unknown | null {\n // Prefer ```json fenced blocks, but fall back to any fenced block.\n const jsonBlocks = [...section.matchAll(/```json\\s*([\\s\\S]*?)```/gi)];\n const anyBlocks = [...section.matchAll(/```\\s*([\\s\\S]*?)```/g)];\n\n const candidates = (jsonBlocks.length ? jsonBlocks : anyBlocks).map(\n (m) => m[1]\n );\n for (const raw of candidates) {\n const trimmed = raw.trim();\n if (!trimmed) continue;\n try {\n return JSON.parse(trimmed);\n } catch {\n continue;\n }\n }\n return null;\n}\n","/**\n * Generate Markdown style guides from extracted styles\n */\n\nimport type {\n ExtractedStyles,\n StyleGuide,\n TailwindThemeTokens,\n} from \"../types.js\";\n\n/**\n * Generates a Markdown style guide from extracted styles\n */\nexport interface GenerateStyleGuideOptions {\n /**\n * Optional HTML/TSX-ish string used to extract utility classes (Tailwind etc).\n */\n html?: string;\n /**\n * Optional Tailwind theme tokens (typically from tailwind.config.*).\n */\n tailwindTheme?: TailwindThemeTokens | null;\n}\n\nexport function generateStyleGuideFromStyles(\n styles: ExtractedStyles,\n options: GenerateStyleGuideOptions = {}\n): string {\n // NOTE: Style guide auto-generation has been removed.\n // UILint now requires an explicit, user-owned style guide file (typically\n // `.uilint/styleguide.md`) to avoid silently producing/overwriting rules.\n void styles;\n void options;\n throw new Error(\n 'Style guide auto-generation has been removed. Create \".uilint/styleguide.md\" at your workspace root (recommended: run \"/genstyleguide\" in Cursor).'\n );\n}\n\n/**\n * Converts a StyleGuide object back to Markdown\n */\nexport function styleGuideToMarkdown(guide: StyleGuide): string {\n const lines: string[] = [];\n\n lines.push(\"# UI Style Guide\");\n lines.push(\"\");\n\n // Colors\n lines.push(\"## Colors\");\n guide.colors.forEach((color) => {\n const usage = color.usage ? ` (${color.usage})` : \"\";\n lines.push(`- **${color.name}**: ${color.value}${usage}`);\n });\n lines.push(\"\");\n\n // Typography\n lines.push(\"## Typography\");\n guide.typography.forEach((typo) => {\n const props: string[] = [];\n if (typo.fontFamily) props.push(`font-family: \"${typo.fontFamily}\"`);\n if (typo.fontSize) props.push(`font-size: ${typo.fontSize}`);\n if (typo.fontWeight) props.push(`font-weight: ${typo.fontWeight}`);\n if (typo.lineHeight) props.push(`line-height: ${typo.lineHeight}`);\n lines.push(`- **${typo.element}**: ${props.join(\", \")}`);\n });\n lines.push(\"\");\n\n // Spacing\n lines.push(\"## Spacing\");\n guide.spacing.forEach((space) => {\n lines.push(`- **${space.name}**: ${space.value}`);\n });\n lines.push(\"\");\n\n // Components\n lines.push(\"## Components\");\n guide.components.forEach((comp) => {\n lines.push(`- **${comp.name}**: ${comp.styles.join(\", \")}`);\n });\n\n return lines.join(\"\\n\");\n}\n","/**\n * LLM prompt builders for UI consistency analysis\n */\n\nimport type {\n ElementSnapshot,\n GroupedSnapshot,\n} from \"./types.js\";\n\n/**\n * Formats an element for inclusion in the prompt (minimal data for LLM)\n */\nfunction formatElement(el: ElementSnapshot): string {\n const parts = [\n `id: ${el.id}`,\n `text: \"${el.text}\"`,\n el.component ? `component: ${el.component}` : null,\n `context: ${el.context || \"root\"}`,\n ].filter(Boolean);\n\n // Only include non-empty style values\n const styleEntries = Object.entries(el.styles).filter(\n ([, v]) => v && v !== \"0px\" && v !== \"none\" && v !== \"normal\"\n );\n if (styleEntries.length > 0) {\n const styleStr = styleEntries.map(([k, v]) => `${k}: ${v}`).join(\", \");\n parts.push(`styles: { ${styleStr} }`);\n }\n\n if (el.rect.width > 0 || el.rect.height > 0) {\n parts.push(`size: ${Math.round(el.rect.width)}x${Math.round(el.rect.height)}`);\n }\n\n return ` { ${parts.join(\", \")} }`;\n}\n\n/**\n * Formats a group of elements for the prompt\n */\nfunction formatGroup(\n groupName: string,\n elements: ElementSnapshot[]\n): string | null {\n if (elements.length < 2) return null;\n\n const lines = [`## ${groupName} (${elements.length} elements)`];\n elements.forEach((el) => {\n lines.push(formatElement(el));\n });\n return lines.join(\"\\n\");\n}\n\n/**\n * Builds a single prompt for analyzing ALL element groups\n * This reduces LLM calls to 1 for better performance\n */\nexport function buildConsistencyPrompt(snapshot: GroupedSnapshot): string {\n const groupSections: string[] = [];\n\n // Format each group that has 2+ elements\n const groups: Array<{ name: string; key: keyof GroupedSnapshot }> = [\n { name: \"Buttons\", key: \"buttons\" },\n { name: \"Headings\", key: \"headings\" },\n { name: \"Cards\", key: \"cards\" },\n { name: \"Links\", key: \"links\" },\n { name: \"Inputs\", key: \"inputs\" },\n { name: \"Containers\", key: \"containers\" },\n ];\n\n for (const { name, key } of groups) {\n const section = formatGroup(name, snapshot[key]);\n if (section) {\n groupSections.push(section);\n }\n }\n\n if (groupSections.length === 0) {\n return \"No element groups with 2+ elements found for consistency analysis.\";\n }\n\n return `You are a UI consistency analyzer. Your task is to find visual inconsistencies between similar UI elements that SHOULD match but DON'T.\n\n# Instructions\n\nAnalyze the following groups of UI elements. Within each group, elements should generally have consistent styling unless they're intentionally different (e.g., primary vs secondary buttons).\n\n## What to FLAG (violations):\n- Padding/spacing differences between similar elements (e.g., one button has 12px padding, another has 16px)\n- Font size or weight inconsistencies within same element types\n- Unintentional color variations (e.g., slightly different blues: #3B82F6 vs #3575E2)\n- Border radius mismatches (e.g., one card has 8px radius, another has 12px)\n- Shadow inconsistencies between similar components\n- Heading hierarchy issues (h1 should be larger than h2, h2 larger than h3)\n\n## What to NOT FLAG:\n- Intentional variations (primary vs secondary buttons, different heading levels)\n- Elements in different contexts that reasonably differ (header vs footer)\n- Sub-2px differences (likely rounding or subpixel rendering)\n- Different element types that shouldn't match\n\n# Element Groups\n\n${groupSections.join(\"\\n\\n\")}\n\n# Response Format\n\nRespond with JSON ONLY. Return a single JSON object with a \"violations\" array.\n\nEach violation should have:\n- elementIds: array of element IDs involved, e.g. [\"el-3\", \"el-7\"]\n- category: one of \"spacing\", \"color\", \"typography\", \"sizing\", \"borders\", \"shadows\"\n- severity: one of \"error\" (major inconsistency), \"warning\" (minor but noticeable), \"info\" (subtle)\n- message: short human-readable description\n- details: { property: the CSS property, values: array of differing values found, suggestion?: optional fix }\n\nExample response:\n{\n \"violations\": [\n {\n \"elementIds\": [\"el-3\", \"el-7\"],\n \"category\": \"spacing\",\n \"severity\": \"warning\",\n \"message\": \"Inconsistent padding on buttons\",\n \"details\": {\n \"property\": \"padding\",\n \"values\": [\"12px 24px\", \"16px 32px\"],\n \"suggestion\": \"Use consistent padding of 12px 24px\"\n }\n }\n ]\n}\n\nBe minimal. Only report significant inconsistencies. If no violations found, return {\"violations\": []}.`;\n}\n\n/**\n * Counts total elements across all groups\n */\nexport function countElements(snapshot: GroupedSnapshot): number {\n return (\n snapshot.buttons.length +\n snapshot.headings.length +\n snapshot.cards.length +\n snapshot.links.length +\n snapshot.inputs.length +\n snapshot.containers.length\n );\n}\n\n/**\n * Checks if a snapshot has any groups worth analyzing (2+ elements)\n */\nexport function hasAnalyzableGroups(snapshot: GroupedSnapshot): boolean {\n return (\n snapshot.buttons.length >= 2 ||\n snapshot.headings.length >= 2 ||\n snapshot.cards.length >= 2 ||\n snapshot.links.length >= 2 ||\n snapshot.inputs.length >= 2 ||\n snapshot.containers.length >= 2\n );\n}\n","/**\n * Consistency analysis logic - shared between CLI and API routes\n */\n\nimport type {\n GroupedSnapshot,\n Violation,\n ConsistencyResult,\n ViolationCategory,\n ViolationSeverity,\n} from \"./types.js\";\nimport {\n buildConsistencyPrompt,\n countElements,\n hasAnalyzableGroups,\n} from \"./prompts.js\";\nimport { OllamaClient } from \"../ollama/client.js\";\n\nconst VALID_CATEGORIES: ViolationCategory[] = [\n \"spacing\",\n \"color\",\n \"typography\",\n \"sizing\",\n \"borders\",\n \"shadows\",\n];\n\nconst VALID_SEVERITIES: ViolationSeverity[] = [\"error\", \"warning\", \"info\"];\n\n/**\n * Parses and validates a GroupedSnapshot from JSON string\n */\nexport function parseGroupedSnapshot(json: string): GroupedSnapshot | null {\n try {\n const parsed = JSON.parse(json);\n\n // Validate structure\n if (!parsed || typeof parsed !== \"object\") return null;\n\n const result: GroupedSnapshot = {\n buttons: Array.isArray(parsed.buttons) ? parsed.buttons : [],\n headings: Array.isArray(parsed.headings) ? parsed.headings : [],\n cards: Array.isArray(parsed.cards) ? parsed.cards : [],\n links: Array.isArray(parsed.links) ? parsed.links : [],\n inputs: Array.isArray(parsed.inputs) ? parsed.inputs : [],\n containers: Array.isArray(parsed.containers) ? parsed.containers : [],\n };\n\n return result;\n } catch {\n return null;\n }\n}\n\n/**\n * Parses violations from LLM response with defensive handling\n */\nexport function parseViolationsResponse(response: string): Violation[] {\n try {\n // Try direct JSON parse first\n const parsed = JSON.parse(response);\n if (Array.isArray(parsed.violations)) {\n return validateViolations(parsed.violations);\n }\n return [];\n } catch {\n // Try to extract JSON from response using regex\n const jsonMatch = response.match(/\\{[\\s\\S]*\"violations\"[\\s\\S]*\\}/);\n if (jsonMatch) {\n try {\n const parsed = JSON.parse(jsonMatch[0]);\n if (Array.isArray(parsed.violations)) {\n return validateViolations(parsed.violations);\n }\n } catch {\n // Fallback failed\n }\n }\n return [];\n }\n}\n\n/**\n * Validates and filters violations to ensure correct structure\n */\nexport function validateViolations(violations: unknown[]): Violation[] {\n return violations\n .filter((v): v is Violation => {\n if (!v || typeof v !== \"object\") return false;\n const obj = v as Record<string, unknown>;\n\n // Required fields\n if (!Array.isArray(obj.elementIds)) return false;\n if (typeof obj.category !== \"string\") return false;\n if (typeof obj.severity !== \"string\") return false;\n if (typeof obj.message !== \"string\") return false;\n if (!obj.details || typeof obj.details !== \"object\") return false;\n\n // Validate category\n if (!VALID_CATEGORIES.includes(obj.category as ViolationCategory))\n return false;\n\n // Validate severity\n if (!VALID_SEVERITIES.includes(obj.severity as ViolationSeverity))\n return false;\n\n return true;\n })\n .map((v) => ({\n elementIds: v.elementIds,\n category: v.category,\n severity: v.severity,\n message: v.message,\n details: {\n property:\n typeof v.details.property === \"string\" ? v.details.property : \"\",\n values: Array.isArray(v.details.values) ? v.details.values : [],\n suggestion:\n typeof v.details.suggestion === \"string\"\n ? v.details.suggestion\n : undefined,\n },\n }));\n}\n\nexport interface AnalyzeConsistencyOptions {\n /** Ollama model to use */\n model?: string;\n /** Ollama base URL */\n baseUrl?: string;\n}\n\n/**\n * Analyzes a grouped snapshot for UI consistency violations\n * This is the main entry point for consistency analysis\n */\nexport async function analyzeConsistency(\n snapshot: GroupedSnapshot,\n options: AnalyzeConsistencyOptions = {}\n): Promise<ConsistencyResult> {\n const startTime = Date.now();\n const elementCount = countElements(snapshot);\n\n // Check if there are analyzable groups\n if (!hasAnalyzableGroups(snapshot)) {\n return {\n violations: [],\n elementCount,\n analysisTime: Date.now() - startTime,\n };\n }\n\n // Build prompt and call LLM\n const prompt = buildConsistencyPrompt(snapshot);\n const client = new OllamaClient({\n model: options.model,\n baseUrl: options.baseUrl,\n });\n\n const response = await client.complete(prompt, { json: true });\n\n // Parse violations\n const violations = parseViolationsResponse(response);\n\n return {\n violations,\n elementCount,\n analysisTime: Date.now() - startTime,\n };\n}\n\n/**\n * Formats violations for plain text output\n */\nexport function formatConsistencyViolations(violations: Violation[]): string {\n if (violations.length === 0) {\n return \"✓ No consistency issues found.\";\n }\n\n const lines: string[] = [\n `Found ${violations.length} consistency issue(s):\\n`,\n ];\n\n const severityIcons: Record<ViolationSeverity, string> = {\n error: \"✖\",\n warning: \"⚠\",\n info: \"ℹ\",\n };\n\n violations.forEach((v, i) => {\n const icon = severityIcons[v.severity] || \"•\";\n\n lines.push(`${i + 1}. ${icon} [${v.category}] ${v.message}`);\n lines.push(` Elements: ${v.elementIds.join(\", \")}`);\n\n if (v.details.property) {\n lines.push(` Property: ${v.details.property}`);\n }\n if (v.details.values.length > 0) {\n lines.push(` Values: ${v.details.values.join(\" vs \")}`);\n }\n if (v.details.suggestion) {\n lines.push(` Suggestion: ${v.details.suggestion}`);\n }\n lines.push(\"\");\n });\n\n return lines.join(\"\\n\");\n}\n","/**\n * Shared logger for UILint packages\n * Outputs styled messages to stderr to avoid interfering with stdout\n */\n\nimport pc from \"picocolors\";\n\nconst PREFIX = pc.cyan(\"[uilint]\");\n\n// ============================================================================\n// Test Environment Detection\n// ============================================================================\n\n/**\n * Check if we're running in a test environment.\n * Vitest sets VITEST=true when running tests.\n */\nfunction isTestEnvironment(): boolean {\n return (\n typeof process !== \"undefined\" &&\n (process.env.VITEST === \"true\" || process.env.NODE_ENV === \"test\")\n );\n}\n\n// Cache the result since it won't change during runtime\nconst IS_TEST = isTestEnvironment();\n\n// ============================================================================\n// Test-Aware Logging (silent during tests)\n// ============================================================================\n\n/**\n * Log a message to the console (silent during tests).\n * Use this for development/debugging logs that shouldn't appear in test output.\n */\nexport function devLog(...args: unknown[]): void {\n if (!IS_TEST) {\n console.log(...args);\n }\n}\n\n/**\n * Log a warning to the console (silent during tests).\n * Use this for development warnings that shouldn't appear in test output.\n */\nexport function devWarn(...args: unknown[]): void {\n if (!IS_TEST) {\n console.warn(...args);\n }\n}\n\n/**\n * Log an error to the console (silent during tests).\n * Use this for development errors that shouldn't appear in test output.\n */\nexport function devError(...args: unknown[]): void {\n if (!IS_TEST) {\n console.error(...args);\n }\n}\n\n/**\n * Log an info message to stderr\n */\nexport function logInfo(message: string): void {\n console.error(`${PREFIX} ${pc.blue(\"ℹ\")} ${message}`);\n}\n\n/**\n * Log a success message to stderr\n */\nexport function logSuccess(message: string): void {\n console.error(`${PREFIX} ${pc.green(\"✓\")} ${message}`);\n}\n\n/**\n * Log a warning message to stderr\n */\nexport function logWarning(message: string): void {\n console.error(`${PREFIX} ${pc.yellow(\"⚠\")} ${message}`);\n}\n\n/**\n * Log an error message to stderr\n */\nexport function logError(message: string): void {\n console.error(`${PREFIX} ${pc.red(\"✗\")} ${message}`);\n}\n\n/**\n * Log a debug message to stderr (dimmed)\n */\nexport function logDebug(message: string): void {\n console.error(`${PREFIX} ${pc.dim(message)}`);\n}\n\n/**\n * Create a progress logger that updates the same line\n * Returns methods to update and finish the progress\n */\nexport function createProgress(initialMessage: string) {\n let lastLength = 0;\n\n const write = (message: string) => {\n // Clear the previous line if needed\n if (lastLength > 0) {\n process.stderr.write(\"\\r\" + \" \".repeat(lastLength) + \"\\r\");\n }\n const line = `${PREFIX} ${pc.magenta(\"⟳\")} ${message}`;\n process.stderr.write(line);\n lastLength = line.length;\n };\n\n write(initialMessage);\n\n return {\n update: (message: string) => {\n write(message);\n },\n succeed: (message: string) => {\n if (lastLength > 0) {\n process.stderr.write(\"\\r\" + \" \".repeat(lastLength) + \"\\r\");\n }\n console.error(`${PREFIX} ${pc.green(\"✓\")} ${message}`);\n lastLength = 0;\n },\n fail: (message: string) => {\n if (lastLength > 0) {\n process.stderr.write(\"\\r\" + \" \".repeat(lastLength) + \"\\r\");\n }\n console.error(`${PREFIX} ${pc.red(\"✗\")} ${message}`);\n lastLength = 0;\n },\n };\n}\n\n// Re-export picocolors for consistent styling\nexport { pc };\n","/**\n * Plugin Registry\n *\n * Global registry for plugins to register themselves with.\n * Plugins call pluginRegistry.register() on import (side effect)\n * or explicitly via direct call.\n *\n * The host (uilint-react, CLI) reads from this registry to\n * discover and initialize plugins.\n */\n\nimport type { PluginWithHandlers } from \"./context.js\";\n\n/**\n * Error thrown when plugin registration fails.\n */\nexport class PluginRegistrationError extends Error {\n constructor(\n message: string,\n public readonly pluginId?: string\n ) {\n super(message);\n this.name = \"PluginRegistrationError\";\n }\n}\n\n/**\n * Event types for registry changes.\n */\nexport type PluginRegistryEvent =\n | { type: \"registered\"; plugin: PluginWithHandlers }\n | { type: \"unregistered\"; pluginId: string };\n\n/**\n * Listener for registry events.\n */\nexport type PluginRegistryListener = (event: PluginRegistryEvent) => void;\n\n/**\n * Plugin Registry\n *\n * Singleton registry for plugin registration and discovery.\n */\nexport class PluginRegistry {\n private plugins = new Map<string, PluginWithHandlers>();\n private registrationOrder: string[] = [];\n private listeners = new Set<PluginRegistryListener>();\n\n /**\n * Register a plugin.\n *\n * @param plugin - Plugin definition with handlers\n * @throws PluginRegistrationError if plugin with same ID already registered\n */\n register<TState>(plugin: PluginWithHandlers<TState>): void {\n // Validate required fields\n if (!plugin.id) {\n throw new PluginRegistrationError(\"Plugin must have an id\");\n }\n if (!plugin.name) {\n throw new PluginRegistrationError(\n `Plugin \"${plugin.id}\" must have a name`,\n plugin.id\n );\n }\n if (!plugin.version) {\n throw new PluginRegistrationError(\n `Plugin \"${plugin.id}\" must have a version`,\n plugin.id\n );\n }\n if (!plugin.state) {\n throw new PluginRegistrationError(\n `Plugin \"${plugin.id}\" must have a state definition`,\n plugin.id\n );\n }\n\n // Check for duplicate registration\n if (this.plugins.has(plugin.id)) {\n throw new PluginRegistrationError(\n `Plugin \"${plugin.id}\" is already registered`,\n plugin.id\n );\n }\n\n // Register the plugin\n this.plugins.set(plugin.id, plugin as PluginWithHandlers);\n this.registrationOrder.push(plugin.id);\n\n // Notify listeners\n this.notifyListeners({ type: \"registered\", plugin: plugin as PluginWithHandlers });\n }\n\n /**\n * Unregister a plugin.\n *\n * @param id - Plugin ID to unregister\n * @returns true if plugin was unregistered, false if not found\n */\n unregister(id: string): boolean {\n if (!this.plugins.has(id)) {\n return false;\n }\n\n this.plugins.delete(id);\n this.registrationOrder = this.registrationOrder.filter((pid) => pid !== id);\n\n // Notify listeners\n this.notifyListeners({ type: \"unregistered\", pluginId: id });\n\n return true;\n }\n\n /**\n * Get a plugin by ID.\n *\n * @param id - Plugin ID\n * @returns Plugin definition or undefined if not found\n */\n get<TState = unknown>(id: string): PluginWithHandlers<TState> | undefined {\n return this.plugins.get(id) as PluginWithHandlers<TState> | undefined;\n }\n\n /**\n * Check if a plugin is registered.\n *\n * @param id - Plugin ID\n * @returns true if registered\n */\n has(id: string): boolean {\n return this.plugins.has(id);\n }\n\n /**\n * Get all registered plugins in registration order.\n *\n * @returns Array of plugin definitions\n */\n getAll(): PluginWithHandlers[] {\n return this.registrationOrder.map((id) => this.plugins.get(id)!);\n }\n\n /**\n * Get all plugin IDs in registration order.\n *\n * @returns Array of plugin IDs\n */\n getIds(): string[] {\n return [...this.registrationOrder];\n }\n\n /**\n * Get count of registered plugins.\n */\n get size(): number {\n return this.plugins.size;\n }\n\n /**\n * Subscribe to registry changes.\n *\n * @param listener - Callback for registry events\n * @returns Unsubscribe function\n */\n subscribe(listener: PluginRegistryListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Clear all registered plugins.\n * Useful for testing.\n */\n clear(): void {\n const ids = [...this.registrationOrder];\n for (const id of ids) {\n this.unregister(id);\n }\n }\n\n /**\n * Get plugins sorted by dependencies.\n *\n * Plugins are sorted so that dependencies come before dependents.\n * Throws if there are circular dependencies.\n *\n * @returns Sorted array of plugins\n * @throws Error if circular dependency detected\n */\n getSortedByDependencies(): PluginWithHandlers[] {\n const sorted: PluginWithHandlers[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n\n const visit = (id: string) => {\n if (visited.has(id)) return;\n if (visiting.has(id)) {\n throw new Error(`Circular dependency detected involving plugin \"${id}\"`);\n }\n\n const plugin = this.plugins.get(id);\n if (!plugin) return;\n\n visiting.add(id);\n\n // Visit dependencies first\n for (const depId of plugin.dependencies || []) {\n if (!this.plugins.has(depId)) {\n throw new Error(\n `Plugin \"${id}\" depends on \"${depId}\" which is not registered`\n );\n }\n visit(depId);\n }\n\n visiting.delete(id);\n visited.add(id);\n sorted.push(plugin);\n };\n\n // Visit all plugins\n for (const id of this.registrationOrder) {\n visit(id);\n }\n\n return sorted;\n }\n\n /**\n * Notify all listeners of an event.\n */\n private notifyListeners(event: PluginRegistryEvent): void {\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (error) {\n console.error(\"Plugin registry listener error:\", error);\n }\n }\n }\n}\n\n/**\n * Global plugin registry singleton.\n *\n * Plugins import this and call register() to add themselves:\n *\n * ```typescript\n * import { pluginRegistry } from \"uilint-core\";\n * import { myPlugin } from \"./plugin\";\n *\n * pluginRegistry.register(myPlugin);\n * ```\n */\nexport const pluginRegistry = new PluginRegistry();\n","/**\n * Plugin System Types\n *\n * These types define the contract between plugin packages and the host\n * (uilint-react, uilint CLI). Plugin packages export PluginDefinitions,\n * hosts interpret and render them.\n *\n * NO REACT CODE - This is pure TypeScript.\n */\n\n// =============================================================================\n// DATA BINDING\n// =============================================================================\n\n/**\n * Reference to a value in the panel's data context.\n * Uses dot notation: \"issue.sourceLocation.filePath\"\n */\nexport interface DataBinding {\n binding: string;\n}\n\n/**\n * Reference to a computed expression.\n * Evaluated at runtime: \"screenshots.length === 0\"\n */\nexport interface ExpressionBinding {\n expression: string;\n}\n\n/**\n * Type guard for DataBinding\n */\nexport function isDataBinding(value: unknown): value is DataBinding {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"binding\" in value &&\n typeof (value as DataBinding).binding === \"string\"\n );\n}\n\n/**\n * Type guard for ExpressionBinding\n */\nexport function isExpressionBinding(value: unknown): value is ExpressionBinding {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"expression\" in value &&\n typeof (value as ExpressionBinding).expression === \"string\"\n );\n}\n\n/**\n * Dynamic value that can be a static value or a binding.\n */\nexport type DynamicValue<T> = T | DataBinding;\n\n/**\n * Conditional value based on a binding.\n */\nexport interface ConditionalValue<T> {\n condition: DataBinding;\n true: T;\n false: T;\n}\n\n/**\n * Type guard for ConditionalValue\n */\nexport function isConditionalValue<T>(value: unknown): value is ConditionalValue<T> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"condition\" in value &&\n \"true\" in value &&\n \"false\" in value\n );\n}\n\n// =============================================================================\n// ICONS\n// =============================================================================\n\n/**\n * Icon identifiers - the host maps these to actual icon components.\n * Any lucide-react icon name is accepted; the listed names provide autocompletion.\n */\nexport type IconName =\n // UI Navigation\n | \"target\"\n | \"link\"\n | \"external-link\"\n | \"chevron-right\"\n | \"chevron-down\"\n | \"chevron-up\"\n | \"x\"\n | \"check\"\n | \"alert-triangle\"\n | \"alert-circle\"\n | \"info\"\n | \"help-circle\"\n // Analysis\n | \"eye\"\n | \"camera\"\n | \"crop\"\n | \"scan\"\n | \"search\"\n | \"filter\"\n | \"brain\"\n | \"sparkles\"\n // Code\n | \"code\"\n | \"file\"\n | \"file-code\"\n | \"folder\"\n | \"git-branch\"\n | \"copy\"\n | \"clipboard\"\n // Content\n | \"book\"\n | \"book-open\"\n | \"bookmark\"\n // Actions\n | \"play\"\n | \"pause\"\n | \"refresh\"\n | \"trash\"\n | \"edit\"\n | \"settings\"\n | \"plus\"\n | \"minus\"\n // Status\n | \"loader\"\n | \"check-circle\"\n | \"x-circle\"\n | \"clock\"\n // Allow any string for forward compatibility with lucide-react icons\n | (string & {});\n\n// =============================================================================\n// ACTIONS\n// =============================================================================\n\n/**\n * Reference to an action handler defined by the plugin.\n */\nexport interface ActionReference {\n /** Action type - resolved by plugin's action handlers */\n type: string;\n /** Static payload to pass to the handler */\n payload?: Record<string, unknown>;\n /** Dynamic payload from data bindings (binding path -> payload key) */\n payloadBindings?: Record<string, string>;\n}\n\n// =============================================================================\n// FETCH CONFIGURATION\n// =============================================================================\n\n/**\n * Configuration for fetching dynamic data.\n */\nexport interface FetchConfig {\n /** Type of fetch operation */\n type: \"source-code\" | \"file-content\";\n /** Parameters for the fetch */\n params: {\n /** File path (can be a binding) */\n filePath?: DataBinding;\n /** Line number to center on */\n line?: DataBinding;\n /** Lines of context above the target */\n contextAbove?: number;\n /** Lines of context below the target */\n contextBelow?: number;\n };\n}\n\n// =============================================================================\n// PANEL SECTIONS\n// =============================================================================\n\n/**\n * Header section with optional icon.\n */\nexport interface HeaderSection {\n type: \"header\";\n /** Icon to display */\n icon?: IconName;\n /** Header text (static or bound) */\n text: DynamicValue<string>;\n /** Subtitle text */\n subtitle?: DynamicValue<string>;\n /** Whether header sticks to top when scrolling */\n sticky?: boolean;\n}\n\n/**\n * Code viewer with syntax highlighting.\n */\nexport interface CodeViewerSection {\n type: \"code-viewer\";\n /** Section label */\n label?: string;\n /** Icon for the section header */\n icon?: IconName;\n /** Code content (binding or fetch config) */\n code: DataBinding | { fetch: FetchConfig };\n /** Location information for navigation */\n location?: DataBinding;\n /** Starting line number */\n startLine?: DynamicValue<number>;\n /** Lines to highlight (array of line numbers) */\n highlightLines?: DataBinding;\n /** Enable diff-style highlighting */\n diffHighlighting?: boolean;\n /** Max height before scrolling (px) */\n maxHeight?: number;\n /** Action to execute on code navigation */\n onNavigate?: ActionReference;\n}\n\n/**\n * Configuration for one side of a code comparison.\n */\nexport interface CodePanelConfig {\n /** Panel label */\n label: string;\n /** Icon for the panel */\n icon?: IconName;\n /** Code content */\n code: DataBinding | { fetch: FetchConfig };\n /** Location information */\n location?: DataBinding;\n}\n\n/**\n * Side-by-side or stacked code comparison.\n */\nexport interface CodeComparisonSection {\n type: \"code-comparison\";\n /** Layout mode */\n mode: \"stacked\" | \"side-by-side\";\n /** Source code panel (left/top) */\n source: CodePanelConfig;\n /** Target code panel (right/bottom) */\n target: CodePanelConfig;\n /** Whether to compute and show diff highlighting */\n computeDiff?: boolean;\n /** Max height for each panel (px) */\n maxHeight?: number;\n}\n\n/**\n * Badge/pill display for status, severity, etc.\n */\nexport interface BadgeSection {\n type: \"badge\";\n /** Badge variant determines styling. Plugins can define custom variants. */\n variant: string;\n /** Value to display */\n value: DataBinding;\n /** Optional label before value */\n label?: string;\n /** Center the badge horizontally */\n centered?: boolean;\n}\n\n/**\n * Plain text content.\n */\nexport interface TextSection {\n type: \"text\";\n /** Text content */\n content: DynamicValue<string>;\n /** Text variant for styling */\n variant?: \"body\" | \"caption\" | \"muted\" | \"error\" | \"success\";\n}\n\n/**\n * Action button configuration.\n */\nexport interface ActionButton {\n /** Unique button ID */\n id: string;\n /** Button label (static, bound, or conditional) */\n label: DynamicValue<string> | ConditionalValue<string>;\n /** Button icon */\n icon?: IconName;\n /** Button variant for styling */\n variant?: \"primary\" | \"secondary\" | \"ghost\" | \"danger\";\n /** Action to execute on click */\n action: ActionReference;\n /** Disabled state (binding to boolean) */\n disabled?: DataBinding;\n /** Visibility (binding to boolean) */\n visible?: DataBinding;\n}\n\n/**\n * Action buttons section.\n */\nexport interface ActionsSection {\n type: \"actions\";\n /** Layout direction */\n direction?: \"row\" | \"column\";\n /** Buttons to render */\n actions: ActionButton[];\n}\n\n/**\n * Visual divider.\n */\nexport interface DividerSection {\n type: \"divider\";\n /** Spacing around divider */\n spacing?: \"small\" | \"medium\" | \"large\";\n}\n\n/**\n * Conditional rendering section.\n */\nexport interface ConditionalSection {\n type: \"conditional\";\n /** Condition to evaluate */\n condition: DataBinding | ExpressionBinding;\n /** Sections to render when condition is true */\n then: PanelSection[];\n /** Sections to render when condition is false */\n else?: PanelSection[];\n}\n\n/**\n * List section for rendering arrays of items.\n */\nexport interface ListSection {\n type: \"list\";\n /** Binding to array of items */\n items: DataBinding;\n /** Layout for each item (uses \"item\" prefix for bindings) */\n itemLayout: PanelSection[];\n /** Message when list is empty */\n emptyMessage?: string;\n}\n\n/**\n * Image display section.\n */\nexport interface ImageSection {\n type: \"image\";\n /** Image source (data URL or URL) */\n src: DataBinding;\n /** Alt text for accessibility */\n alt?: string;\n /** Max height (px) */\n maxHeight?: number;\n /** Region to highlight on the image */\n highlightRegion?: DataBinding;\n}\n\n/**\n * Card display for list items.\n */\nexport interface CardSection {\n type: \"card\";\n /** Thumbnail image source */\n thumbnail?: DataBinding;\n /** Card title */\n title: DynamicValue<string>;\n /** Card subtitle */\n subtitle?: DynamicValue<string>;\n /** Badge to display on card */\n badge?: {\n variant: \"severity\" | \"status\" | \"count\";\n value: DataBinding;\n label?: string;\n };\n /** Action on card click */\n onClick?: ActionReference;\n}\n\n/**\n * Progress indicator section.\n */\nexport interface ProgressSection {\n type: \"progress\";\n /** Current value (0-100 or binding) */\n value: DynamicValue<number>;\n /** Label to show */\n label?: DynamicValue<string>;\n /** Whether progress is indeterminate */\n indeterminate?: boolean;\n}\n\n/**\n * Union of all section types.\n */\nexport type PanelSection =\n | HeaderSection\n | CodeViewerSection\n | CodeComparisonSection\n | BadgeSection\n | TextSection\n | ActionsSection\n | DividerSection\n | ConditionalSection\n | ListSection\n | ImageSection\n | CardSection\n | ProgressSection;\n\n// =============================================================================\n// PANEL DEFINITION\n// =============================================================================\n\n/**\n * Loading state configuration.\n */\nexport interface LoadingConfig {\n /** Condition that indicates loading */\n when: DataBinding;\n /** Message to show while loading */\n message?: string;\n /** Sub-message for additional context */\n submessage?: string;\n}\n\n/**\n * Empty state configuration.\n */\nexport interface EmptyConfig {\n /** Condition that indicates empty state */\n when: DataBinding | ExpressionBinding;\n /** Message to show */\n message: string;\n /** Sub-message for additional context */\n submessage?: string;\n /** Icon to display */\n icon?: IconName;\n}\n\n/**\n * Complete panel definition.\n */\nexport interface PanelDefinition {\n /** Unique panel ID */\n id: string;\n /** Panel title (static or bound) */\n title: DynamicValue<string>;\n /** Priority for ordering (higher = earlier) */\n priority?: number;\n /** Panel layout - array of sections */\n layout: PanelSection[];\n /** Loading state configuration */\n loading?: LoadingConfig;\n /** Empty state configuration */\n empty?: EmptyConfig;\n}\n\n// =============================================================================\n// COMMANDS\n// =============================================================================\n\n/**\n * Command palette command definition.\n */\nexport interface CommandDefinition {\n /** Unique command ID (convention: \"pluginId:action-name\") */\n id: string;\n /** Command title shown in palette */\n title: string;\n /** Keywords for search */\n keywords: string[];\n /** Category for grouping */\n category: string;\n /** Subtitle/description */\n subtitle?: string;\n /** Icon to display */\n icon?: IconName;\n /** Keyboard shortcut (e.g., \"Cmd+Shift+C\") */\n shortcut?: string;\n /** Action to execute */\n action: ActionReference;\n /** Availability condition */\n isAvailable?: DataBinding | ExpressionBinding;\n /** Hide from \"All\" category (only show in plugin's category) */\n hideFromAllCategory?: boolean;\n}\n\n// =============================================================================\n// TOOLBAR\n// =============================================================================\n\n/**\n * Single toolbar action.\n */\nexport interface ToolbarActionDefinition {\n /** Unique action ID */\n id: string;\n /** Icon to display */\n icon: IconName;\n /** Tooltip text */\n tooltip: string;\n /** Action to execute */\n action: ActionReference;\n /** Visibility condition */\n isVisible?: DataBinding | ExpressionBinding;\n /** Enabled condition */\n isEnabled?: DataBinding | ExpressionBinding;\n}\n\n/**\n * Toolbar action group (dropdown).\n */\nexport interface ToolbarGroupDefinition {\n /** Unique group ID */\n id: string;\n /** Icon for the group button */\n icon: IconName;\n /** Tooltip for the group */\n tooltip: string;\n /** Priority for ordering (higher = earlier) */\n priority?: number;\n /** Group visibility condition */\n isVisible?: DataBinding | ExpressionBinding;\n /** Actions in this group */\n actions: ToolbarActionDefinition[];\n}\n\n// =============================================================================\n// RULES\n// =============================================================================\n\n/**\n * Rule option field schema for UI generation.\n */\nexport interface RuleOptionField {\n /** Option key */\n key: string;\n /** Display label */\n label: string;\n /** Field type */\n type: \"text\" | \"number\" | \"boolean\" | \"select\";\n /** Default value */\n defaultValue?: unknown;\n /** Options for select type */\n options?: Array<{ value: string; label: string }>;\n /** Field description */\n description?: string;\n /** Placeholder text */\n placeholder?: string;\n}\n\n/**\n * Rule option schema for generating settings UI.\n */\nexport interface RuleOptionSchema {\n fields: RuleOptionField[];\n}\n\n/**\n * Requirement for a rule to function.\n */\nexport interface RuleRequirement {\n /** Type of requirement. Plugins define their own requirement types. */\n type: string;\n /** Human-readable description */\n description: string;\n /** Hint for how to set up */\n setupHint: string;\n}\n\n/**\n * ESLint rule definition with UI metadata.\n */\nexport interface RuleDefinition {\n /** Rule ID */\n id: string;\n /** Human-readable name */\n name: string;\n /** Description */\n description: string;\n /** Category for grouping */\n category: string;\n /** Icon */\n icon?: IconName;\n /** Default severity */\n defaultSeverity: \"error\" | \"warn\" | \"off\";\n /** Whether enabled by default */\n defaultEnabled: boolean;\n /** Heatmap color for this rule's issues */\n heatmapColor?: string;\n /** Custom inspector panel ID to open for this rule's issues */\n customInspectorPanel?: string;\n /**\n * Key identifying a custom content renderer for the IssuesList view.\n * When set, the IssuesList renders this rule's issues with a specialized\n * component instead of the default views.\n */\n contentRenderer?: string;\n /** Requirements for the rule to work */\n requirements?: RuleRequirement[];\n /** Default options */\n defaultOptions?: Record<string, unknown>[];\n /** Option schema for settings UI */\n optionSchema?: RuleOptionSchema;\n /** Documentation (markdown) */\n docs?: string;\n}\n\n// =============================================================================\n// ISSUE AGGREGATION\n// =============================================================================\n\n/**\n * Issue contributed by a plugin.\n */\nexport interface PluginIssue {\n /** Unique issue ID */\n id: string;\n /** Issue message */\n message: string;\n /** Severity level */\n severity: \"error\" | \"warning\" | \"info\";\n /** Associated rule ID */\n ruleId: string;\n /** Issue category */\n category?: string;\n /** Additional data for inspector */\n data?: Record<string, unknown>;\n}\n\n/**\n * Issue contribution from a plugin.\n */\nexport interface IssueContribution {\n /** Plugin that contributed these issues */\n pluginId: string;\n /** Issues keyed by dataLoc */\n issues: Map<string, PluginIssue[]>;\n}\n\n// =============================================================================\n// STATE MANAGEMENT\n// =============================================================================\n\n/**\n * Computed value definition.\n */\nexport type ComputedValue<TState, TResult> = (state: TState) => TResult;\n\n/**\n * Persistence configuration for plugin state.\n */\nexport interface PersistConfig<TState> {\n /** Storage key */\n key: string;\n /** Keys to include (if specified, only these are persisted) */\n include?: (keyof TState)[];\n /** Keys to exclude from persistence */\n exclude?: (keyof TState)[];\n}\n\n/**\n * State definition for a plugin.\n */\nexport interface StateDefinition<TState> {\n /** Initial state */\n initialState: TState;\n /** Computed values derived from state */\n computed?: Record<string, ComputedValue<TState, unknown>>;\n /** Persistence configuration */\n persist?: PersistConfig<TState>;\n}\n\n// =============================================================================\n// PLUGIN DEFINITION\n// =============================================================================\n\n/**\n * Complete plugin definition.\n *\n * This is what analysis packages export. The host (uilint-react, CLI)\n * imports these and wires everything up.\n *\n * NO REACT - Pure TypeScript configuration.\n */\nexport interface PluginDefinition<TState = unknown> {\n // === Metadata ===\n /** Unique plugin ID */\n id: string;\n /** Human-readable name */\n name: string;\n /** Semantic version */\n version: string;\n /** Description */\n description?: string;\n /** Icon */\n icon?: IconName;\n /** Plugin IDs this depends on */\n dependencies?: string[];\n\n // === State ===\n /** State definition */\n state: StateDefinition<TState>;\n\n // === UI Contributions (Declarative) ===\n /** Command palette commands */\n commands?: CommandDefinition[];\n /** Toolbar action groups */\n toolbarGroups?: ToolbarGroupDefinition[];\n /** Inspector panels */\n panels?: PanelDefinition[];\n\n // === Rules ===\n /** Rule definitions */\n rules?: RuleDefinition[];\n /** Rule categories this plugin handles */\n handlesRuleCategories?: string[];\n\n // === Browser Actions ===\n /** Browser-side actions this plugin needs. Plugins register handlers for these. */\n browserActions?: string[];\n}\n","/**\n * Operation Lifecycle Framework\n *\n * Shared types and factory functions for plugins that perform long-running\n * server-mediated operations (indexing, analysis, etc.).\n *\n * Plugins compose these into their own state/actions/messages rather than\n * inheriting from a base class.\n *\n * NO REACT CODE - Pure TypeScript.\n */\n\nimport type { ActionHandlers, MessageHandlers, PluginContext } from \"./context.js\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/**\n * Status lifecycle for a long-running plugin operation.\n *\n * - \"idle\" -- nothing has run yet (initial state)\n * - \"active\" -- operation is in progress\n * - \"complete\" -- finished successfully\n * - \"error\" -- finished with an error\n */\nexport type OperationStatus = \"idle\" | \"active\" | \"complete\" | \"error\";\n\n/**\n * Progress tracking for a long-running operation.\n */\nexport interface OperationProgress {\n current: number;\n total: number;\n /** Optional human-readable context (e.g., file path, phase name) */\n message?: string;\n}\n\n/**\n * State shape for a long-running operation.\n *\n * Plugins embed this inside their own state interface:\n * ```typescript\n * interface MyPluginState {\n * indexing: OperationState<IndexStats>;\n * // ...other plugin-specific fields\n * }\n * ```\n *\n * @template TStats Type of the completion payload (e.g., IndexStats)\n */\nexport interface OperationState<TStats = unknown> {\n /** Current operation status */\n status: OperationStatus;\n /** Progress info (null when idle or complete) */\n progress: OperationProgress | null;\n /** Completion stats from the last successful run */\n stats: TStats | null;\n /** Error message from the last failed run */\n lastError: string | null;\n}\n\n// =============================================================================\n// INITIAL STATE FACTORY\n// =============================================================================\n\n/**\n * Creates default initial state for an operation.\n *\n * @example\n * ```typescript\n * const initialState: MyState = {\n * indexing: createOperationInitialState<IndexStats>(),\n * };\n * ```\n */\nexport function createOperationInitialState<\n TStats = unknown,\n>(): OperationState<TStats> {\n return {\n status: \"idle\",\n progress: null,\n stats: null,\n lastError: null,\n };\n}\n\n// =============================================================================\n// COMPUTED VALUES FACTORY\n// =============================================================================\n\n/**\n * Creates standard computed values for an operation.\n *\n * Returns four computed selectors that can be spread (and optionally renamed)\n * into a plugin's `StateDefinition.computed`:\n *\n * - `isReady` -- status is \"complete\"\n * - `isActive` -- status is \"active\"\n * - `hasError` -- status is \"error\" or lastError is non-null\n * - `progressPercent` -- 0-100 number derived from progress\n *\n * @param getOp Extracts the OperationState from the full plugin state\n *\n * @example\n * ```typescript\n * const opComputed = createOperationComputed<MyState>((s) => s.indexing);\n * const stateDefinition = {\n * computed: {\n * isIndexReady: opComputed.isReady,\n * isIndexing: opComputed.isActive,\n * hasError: opComputed.hasError,\n * progressPercent: opComputed.progressPercent,\n * },\n * };\n * ```\n */\nexport function createOperationComputed<TState>(\n getOp: (state: TState) => OperationState\n) {\n return {\n isReady: (state: TState) => getOp(state).status === \"complete\",\n isActive: (state: TState) => getOp(state).status === \"active\",\n hasError: (state: TState) => {\n const op = getOp(state);\n return op.status === \"error\" || op.lastError !== null;\n },\n progressPercent: (state: TState) => {\n const op = getOp(state);\n if (!op.progress) return 0;\n if (op.progress.total === 0) return 0;\n return Math.round(\n (op.progress.current / op.progress.total) * 100\n );\n },\n };\n}\n\n// =============================================================================\n// ACTION HANDLERS FACTORY\n// =============================================================================\n\n/**\n * Configuration for creating operation action handlers.\n */\nexport interface OperationActionConfig<TState, TStats> {\n /** Extract the OperationState from the full plugin state */\n getOp: (state: TState) => OperationState<TStats>;\n /** Produce a partial state update that sets the operation state */\n setOp: (op: OperationState<TStats>) => Partial<TState>;\n}\n\n/**\n * Creates the four standard lifecycle action handlers for an operation.\n *\n * Returns handlers keyed as:\n * - `\"handle-{prefix}-start\"`\n * - `\"handle-{prefix}-progress\"`\n * - `\"handle-{prefix}-complete\"`\n * - `\"handle-{prefix}-error\"`\n *\n * These can be spread into a plugin's `ActionHandlers` alongside\n * any plugin-specific actions.\n *\n * @param prefix Action name prefix (e.g., \"indexing\", \"analysis\")\n * @param config How to read/write the operation state from plugin state\n *\n * @example\n * ```typescript\n * export const myActions: ActionHandlers<MyState> = {\n * ...createOperationActions<MyState, IndexStats>(\"indexing\", {\n * getOp: (s) => s.indexing,\n * setOp: (op) => ({ indexing: op }),\n * }),\n * // plugin-specific actions\n * \"start-indexing\": (ctx) => { ... },\n * };\n * ```\n */\nexport function createOperationActions<TState, TStats>(\n prefix: string,\n config: OperationActionConfig<TState, TStats>\n): ActionHandlers<TState> {\n const { getOp, setOp } = config;\n\n return {\n [`handle-${prefix}-start`]: (ctx: PluginContext<TState>) => {\n const current = getOp(ctx.getState());\n ctx.setState(\n setOp({\n ...current,\n status: \"active\",\n progress: { current: 0, total: 0 },\n lastError: null,\n })\n );\n },\n\n [`handle-${prefix}-progress`]: (\n ctx: PluginContext<TState>,\n payload: unknown\n ) => {\n const current = getOp(ctx.getState());\n ctx.setState(\n setOp({\n ...current,\n status: \"active\",\n progress: payload as OperationProgress,\n })\n );\n },\n\n [`handle-${prefix}-complete`]: (\n ctx: PluginContext<TState>,\n payload: unknown\n ) => {\n const current = getOp(ctx.getState());\n ctx.setState(\n setOp({\n ...current,\n status: \"complete\",\n progress: null,\n stats: payload as TStats,\n lastError: null,\n })\n );\n },\n\n [`handle-${prefix}-error`]: (\n ctx: PluginContext<TState>,\n payload: unknown\n ) => {\n const current = getOp(ctx.getState());\n const { error } = payload as { error: string };\n ctx.setState(\n setOp({\n ...current,\n status: \"error\",\n progress: null,\n lastError: error,\n })\n );\n },\n };\n}\n\n// =============================================================================\n// MESSAGE HANDLERS FACTORY\n// =============================================================================\n\n/**\n * Configuration for mapping WebSocket messages to operation actions.\n */\nexport interface OperationMessageConfig {\n /** Prefix for action dispatch (e.g., \"indexing\", \"analysis\") */\n actionPrefix: string;\n /** WS message type for start (optional -- some operations don't have one) */\n startMessage?: string;\n /** WS message type for progress */\n progressMessage: string;\n /** WS message type for completion */\n completeMessage: string;\n /** WS message type for error */\n errorMessage: string;\n /** Extract progress payload from the raw WS message */\n extractProgress?: (message: unknown) => OperationProgress;\n /** Extract completion stats from the raw WS message */\n extractStats?: (message: unknown) => unknown;\n /** Extract error string from the raw WS message */\n extractError?: (message: unknown) => string;\n}\n\n/**\n * Creates WebSocket message handlers for a standard operation lifecycle.\n *\n * Each handler dispatches to the corresponding `handle-{prefix}-*` action.\n *\n * @example\n * ```typescript\n * export const myMessages: MessageHandlers<MyState> = {\n * ...createOperationMessageHandlers<MyState>({\n * actionPrefix: \"indexing\",\n * startMessage: \"duplicates:indexing:start\",\n * progressMessage: \"duplicates:indexing:progress\",\n * completeMessage: \"duplicates:indexing:complete\",\n * errorMessage: \"duplicates:indexing:error\",\n * extractProgress: (msg) => ({\n * current: (msg as any).current ?? 0,\n * total: (msg as any).total ?? 0,\n * message: (msg as any).message,\n * }),\n * }),\n * };\n * ```\n */\nexport function createOperationMessageHandlers<TState>(\n config: OperationMessageConfig\n): MessageHandlers<TState> {\n const handlers: MessageHandlers<TState> = {};\n const { actionPrefix } = config;\n\n if (config.startMessage) {\n handlers[config.startMessage] = (ctx: PluginContext<TState>) => {\n ctx.dispatch(`handle-${actionPrefix}-start`);\n };\n }\n\n handlers[config.progressMessage] = (\n ctx: PluginContext<TState>,\n message: unknown\n ) => {\n const progress = config.extractProgress\n ? config.extractProgress(message)\n : (message as OperationProgress);\n ctx.dispatch(`handle-${actionPrefix}-progress`, progress);\n };\n\n handlers[config.completeMessage] = (\n ctx: PluginContext<TState>,\n message: unknown\n ) => {\n const stats = config.extractStats\n ? config.extractStats(message)\n : message;\n ctx.dispatch(`handle-${actionPrefix}-complete`, stats);\n };\n\n handlers[config.errorMessage] = (\n ctx: PluginContext<TState>,\n message: unknown\n ) => {\n const error = config.extractError\n ? config.extractError(message)\n : (message as { error: string }).error;\n ctx.dispatch(`handle-${actionPrefix}-error`, { error });\n };\n\n return handlers;\n}\n"],"mappings":"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","names":[]}
@@ -1088,6 +1088,12 @@ interface RuleDefinition {
1088
1088
  heatmapColor?: string;
1089
1089
  /** Custom inspector panel ID to open for this rule's issues */
1090
1090
  customInspectorPanel?: string;
1091
+ /**
1092
+ * Key identifying a custom content renderer for the IssuesList view.
1093
+ * When set, the IssuesList renders this rule's issues with a specialized
1094
+ * component instead of the default views.
1095
+ */
1096
+ contentRenderer?: string;
1091
1097
  /** Requirements for the rule to work */
1092
1098
  requirements?: RuleRequirement[];
1093
1099
  /** Default options */
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { aL as ActionButton, aq as ActionHandler, ar as ActionHandlers, aD as ActionReference, aM as ActionsSection, A as AnalysisResult, a6 as AnalyzeConsistencyOptions, aJ as BadgeSection, ap as BrowserActionResult, aR as CardSection, aI as CodeComparisonSection, aH as CodePanelConfig, aG as CodeViewerSection, C as ColorRule, aX as CommandDefinition, j as ComponentRule, b4 as ComputedValue, aO as ConditionalSection, aB as ConditionalValue, a5 as ConsistencyResult, D as DOMSnapshot, ay as DataBinding, aw as DisposeHook, aN as DividerSection, aA as DynamicValue, $ as ElementRole, a0 as ElementSnapshot, aV as EmptyConfig, az as ExpressionBinding, m as ExtractedStyleValues, E as ExtractedStyles, aE as FetchConfig, a1 as GroupedSnapshot, aF as HeaderSection, aC as IconName, aQ as ImageSection, av as InitializeHook, I as InstrumentationSpan, au as IssueAggregator, b3 as IssueContribution, L as LLMInstrumentationCallbacks, aP as ListSection, aU as LoadingConfig, as as MessageHandler, at as MessageHandlers, o as OllamaClient, O as OllamaClientOptions, bi as OperationActionConfig, bj as OperationMessageConfig, bg as OperationProgress, bh as OperationState, bf as OperationStatus, aW as PanelDefinition, aT as PanelSection, b5 as PersistConfig, an as PluginContext, b7 as PluginDefinition, b2 as PluginIssue, aj as PluginRegistrationError, ai as PluginRegistry, al as PluginRegistryEvent, am as PluginRegistryListener, ax as PluginWithHandlers, aS as ProgressSection, b1 as RuleDefinition, a_ as RuleOptionField, a$ as RuleOptionSchema, b0 as RuleRequirement, k as SerializedStyles, i as SpacingRule, b6 as StateDefinition, n as StreamProgressCallback, S as StyleGuide, _ as StyleSnapshot, T as TailwindThemeTokens, aK as TextSection, aY as ToolbarActionDefinition, aZ as ToolbarGroupDefinition, h as TypographyRule, q as UILINT_DEFAULT_OLLAMA_MODEL, U as UILintIssue, f as UILintScanIssue, g as UILintSourceScanResult, a4 as Violation, a2 as ViolationCategory, a3 as ViolationSeverity, ao as WebSocketService, ad as analyzeConsistency, r as buildAnalysisPrompt, a7 as buildConsistencyPrompt, s as buildSourceAnalysisPrompt, t as buildSourceScanPrompt, u as buildStyleGuidePrompt, a8 as countElements, W as createColorRule, Z as createComponentRule, Q as createEmptyStyleGuide, bd as createOperationActions, bc as createOperationComputed, bb as createOperationInitialState, be as createOperationMessageHandlers, Y as createSpacingRule, F as createStyleSummary, X as createTypographyRule, B as deserializeStyles, ah as devError, af as devLog, ag as devWarn, K as extractStyleValues, x as extractStyles, y as extractStylesFromDOM, M as extractTailwindAllowlist, ae as formatConsistencyViolations, v as formatViolationsText, N as generateStyleGuideFromStyles, p as getOllamaClient, a9 as hasAnalyzableGroups, ba as isConditionalValue, b8 as isDataBinding, b9 as isExpressionBinding, V as mergeStyleGuides, aa as parseGroupedSnapshot, H as parseStyleGuide, J as parseStyleGuideSections, ab as parseViolationsResponse, ak as pluginRegistry, w as sanitizeIssues, z as serializeStyles, P as styleGuideToMarkdown, G as truncateHTML, R as validateStyleGuide, ac as validateViolations } from './index-rJqAK3nT.js';
1
+ export { aL as ActionButton, aq as ActionHandler, ar as ActionHandlers, aD as ActionReference, aM as ActionsSection, A as AnalysisResult, a6 as AnalyzeConsistencyOptions, aJ as BadgeSection, ap as BrowserActionResult, aR as CardSection, aI as CodeComparisonSection, aH as CodePanelConfig, aG as CodeViewerSection, C as ColorRule, aX as CommandDefinition, j as ComponentRule, b4 as ComputedValue, aO as ConditionalSection, aB as ConditionalValue, a5 as ConsistencyResult, D as DOMSnapshot, ay as DataBinding, aw as DisposeHook, aN as DividerSection, aA as DynamicValue, $ as ElementRole, a0 as ElementSnapshot, aV as EmptyConfig, az as ExpressionBinding, m as ExtractedStyleValues, E as ExtractedStyles, aE as FetchConfig, a1 as GroupedSnapshot, aF as HeaderSection, aC as IconName, aQ as ImageSection, av as InitializeHook, I as InstrumentationSpan, au as IssueAggregator, b3 as IssueContribution, L as LLMInstrumentationCallbacks, aP as ListSection, aU as LoadingConfig, as as MessageHandler, at as MessageHandlers, o as OllamaClient, O as OllamaClientOptions, bi as OperationActionConfig, bj as OperationMessageConfig, bg as OperationProgress, bh as OperationState, bf as OperationStatus, aW as PanelDefinition, aT as PanelSection, b5 as PersistConfig, an as PluginContext, b7 as PluginDefinition, b2 as PluginIssue, aj as PluginRegistrationError, ai as PluginRegistry, al as PluginRegistryEvent, am as PluginRegistryListener, ax as PluginWithHandlers, aS as ProgressSection, b1 as RuleDefinition, a_ as RuleOptionField, a$ as RuleOptionSchema, b0 as RuleRequirement, k as SerializedStyles, i as SpacingRule, b6 as StateDefinition, n as StreamProgressCallback, S as StyleGuide, _ as StyleSnapshot, T as TailwindThemeTokens, aK as TextSection, aY as ToolbarActionDefinition, aZ as ToolbarGroupDefinition, h as TypographyRule, q as UILINT_DEFAULT_OLLAMA_MODEL, U as UILintIssue, f as UILintScanIssue, g as UILintSourceScanResult, a4 as Violation, a2 as ViolationCategory, a3 as ViolationSeverity, ao as WebSocketService, ad as analyzeConsistency, r as buildAnalysisPrompt, a7 as buildConsistencyPrompt, s as buildSourceAnalysisPrompt, t as buildSourceScanPrompt, u as buildStyleGuidePrompt, a8 as countElements, W as createColorRule, Z as createComponentRule, Q as createEmptyStyleGuide, bd as createOperationActions, bc as createOperationComputed, bb as createOperationInitialState, be as createOperationMessageHandlers, Y as createSpacingRule, F as createStyleSummary, X as createTypographyRule, B as deserializeStyles, ah as devError, af as devLog, ag as devWarn, K as extractStyleValues, x as extractStyles, y as extractStylesFromDOM, M as extractTailwindAllowlist, ae as formatConsistencyViolations, v as formatViolationsText, N as generateStyleGuideFromStyles, p as getOllamaClient, a9 as hasAnalyzableGroups, ba as isConditionalValue, b8 as isDataBinding, b9 as isExpressionBinding, V as mergeStyleGuides, aa as parseGroupedSnapshot, H as parseStyleGuide, J as parseStyleGuideSections, ab as parseViolationsResponse, ak as pluginRegistry, w as sanitizeIssues, z as serializeStyles, P as styleGuideToMarkdown, G as truncateHTML, R as validateStyleGuide, ac as validateViolations } from './index-CWI5wLdq.js';
package/dist/index.js CHANGED
@@ -48,7 +48,7 @@ import {
48
48
  truncateHTML,
49
49
  validateStyleGuide,
50
50
  validateViolations
51
- } from "./chunk-J4LUG7RV.js";
51
+ } from "./chunk-OGU2FLMN.js";
52
52
  export {
53
53
  OllamaClient,
54
54
  PluginRegistrationError,
package/dist/node.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { D as DOMSnapshot, T as TailwindThemeTokens } from './index-rJqAK3nT.js';
2
- export { aL as ActionButton, aq as ActionHandler, ar as ActionHandlers, aD as ActionReference, aM as ActionsSection, A as AnalysisResult, a6 as AnalyzeConsistencyOptions, aJ as BadgeSection, ap as BrowserActionResult, aR as CardSection, aI as CodeComparisonSection, aH as CodePanelConfig, aG as CodeViewerSection, C as ColorRule, aX as CommandDefinition, j as ComponentRule, b4 as ComputedValue, aO as ConditionalSection, aB as ConditionalValue, a5 as ConsistencyResult, ay as DataBinding, aw as DisposeHook, aN as DividerSection, aA as DynamicValue, $ as ElementRole, a0 as ElementSnapshot, aV as EmptyConfig, az as ExpressionBinding, m as ExtractedStyleValues, E as ExtractedStyles, aE as FetchConfig, a1 as GroupedSnapshot, aF as HeaderSection, aC as IconName, aQ as ImageSection, av as InitializeHook, I as InstrumentationSpan, au as IssueAggregator, b3 as IssueContribution, L as LLMInstrumentationCallbacks, aP as ListSection, aU as LoadingConfig, as as MessageHandler, at as MessageHandlers, o as OllamaClient, O as OllamaClientOptions, bi as OperationActionConfig, bj as OperationMessageConfig, bg as OperationProgress, bh as OperationState, bf as OperationStatus, aW as PanelDefinition, aT as PanelSection, b5 as PersistConfig, an as PluginContext, b7 as PluginDefinition, b2 as PluginIssue, aj as PluginRegistrationError, ai as PluginRegistry, al as PluginRegistryEvent, am as PluginRegistryListener, ax as PluginWithHandlers, aS as ProgressSection, b1 as RuleDefinition, a_ as RuleOptionField, a$ as RuleOptionSchema, b0 as RuleRequirement, k as SerializedStyles, i as SpacingRule, b6 as StateDefinition, n as StreamProgressCallback, S as StyleGuide, _ as StyleSnapshot, aK as TextSection, aY as ToolbarActionDefinition, aZ as ToolbarGroupDefinition, h as TypographyRule, q as UILINT_DEFAULT_OLLAMA_MODEL, U as UILintIssue, f as UILintScanIssue, g as UILintSourceScanResult, a4 as Violation, a2 as ViolationCategory, a3 as ViolationSeverity, ao as WebSocketService, ad as analyzeConsistency, r as buildAnalysisPrompt, a7 as buildConsistencyPrompt, s as buildSourceAnalysisPrompt, t as buildSourceScanPrompt, u as buildStyleGuidePrompt, a8 as countElements, W as createColorRule, Z as createComponentRule, Q as createEmptyStyleGuide, bd as createOperationActions, bc as createOperationComputed, bb as createOperationInitialState, be as createOperationMessageHandlers, e as createProgress, Y as createSpacingRule, F as createStyleSummary, X as createTypographyRule, B as deserializeStyles, ah as devError, af as devLog, ag as devWarn, K as extractStyleValues, x as extractStyles, y as extractStylesFromDOM, M as extractTailwindAllowlist, ae as formatConsistencyViolations, v as formatViolationsText, N as generateStyleGuideFromStyles, p as getOllamaClient, a9 as hasAnalyzableGroups, ba as isConditionalValue, b8 as isDataBinding, b9 as isExpressionBinding, d as logDebug, c as logError, l as logInfo, a as logSuccess, b as logWarning, V as mergeStyleGuides, aa as parseGroupedSnapshot, H as parseStyleGuide, J as parseStyleGuideSections, ab as parseViolationsResponse, ak as pluginRegistry, w as sanitizeIssues, z as serializeStyles, P as styleGuideToMarkdown, G as truncateHTML, R as validateStyleGuide, ac as validateViolations } from './index-rJqAK3nT.js';
1
+ import { D as DOMSnapshot, T as TailwindThemeTokens } from './index-CWI5wLdq.js';
2
+ export { aL as ActionButton, aq as ActionHandler, ar as ActionHandlers, aD as ActionReference, aM as ActionsSection, A as AnalysisResult, a6 as AnalyzeConsistencyOptions, aJ as BadgeSection, ap as BrowserActionResult, aR as CardSection, aI as CodeComparisonSection, aH as CodePanelConfig, aG as CodeViewerSection, C as ColorRule, aX as CommandDefinition, j as ComponentRule, b4 as ComputedValue, aO as ConditionalSection, aB as ConditionalValue, a5 as ConsistencyResult, ay as DataBinding, aw as DisposeHook, aN as DividerSection, aA as DynamicValue, $ as ElementRole, a0 as ElementSnapshot, aV as EmptyConfig, az as ExpressionBinding, m as ExtractedStyleValues, E as ExtractedStyles, aE as FetchConfig, a1 as GroupedSnapshot, aF as HeaderSection, aC as IconName, aQ as ImageSection, av as InitializeHook, I as InstrumentationSpan, au as IssueAggregator, b3 as IssueContribution, L as LLMInstrumentationCallbacks, aP as ListSection, aU as LoadingConfig, as as MessageHandler, at as MessageHandlers, o as OllamaClient, O as OllamaClientOptions, bi as OperationActionConfig, bj as OperationMessageConfig, bg as OperationProgress, bh as OperationState, bf as OperationStatus, aW as PanelDefinition, aT as PanelSection, b5 as PersistConfig, an as PluginContext, b7 as PluginDefinition, b2 as PluginIssue, aj as PluginRegistrationError, ai as PluginRegistry, al as PluginRegistryEvent, am as PluginRegistryListener, ax as PluginWithHandlers, aS as ProgressSection, b1 as RuleDefinition, a_ as RuleOptionField, a$ as RuleOptionSchema, b0 as RuleRequirement, k as SerializedStyles, i as SpacingRule, b6 as StateDefinition, n as StreamProgressCallback, S as StyleGuide, _ as StyleSnapshot, aK as TextSection, aY as ToolbarActionDefinition, aZ as ToolbarGroupDefinition, h as TypographyRule, q as UILINT_DEFAULT_OLLAMA_MODEL, U as UILintIssue, f as UILintScanIssue, g as UILintSourceScanResult, a4 as Violation, a2 as ViolationCategory, a3 as ViolationSeverity, ao as WebSocketService, ad as analyzeConsistency, r as buildAnalysisPrompt, a7 as buildConsistencyPrompt, s as buildSourceAnalysisPrompt, t as buildSourceScanPrompt, u as buildStyleGuidePrompt, a8 as countElements, W as createColorRule, Z as createComponentRule, Q as createEmptyStyleGuide, bd as createOperationActions, bc as createOperationComputed, bb as createOperationInitialState, be as createOperationMessageHandlers, e as createProgress, Y as createSpacingRule, F as createStyleSummary, X as createTypographyRule, B as deserializeStyles, ah as devError, af as devLog, ag as devWarn, K as extractStyleValues, x as extractStyles, y as extractStylesFromDOM, M as extractTailwindAllowlist, ae as formatConsistencyViolations, v as formatViolationsText, N as generateStyleGuideFromStyles, p as getOllamaClient, a9 as hasAnalyzableGroups, ba as isConditionalValue, b8 as isDataBinding, b9 as isExpressionBinding, d as logDebug, c as logError, l as logInfo, a as logSuccess, b as logWarning, V as mergeStyleGuides, aa as parseGroupedSnapshot, H as parseStyleGuide, J as parseStyleGuideSections, ab as parseViolationsResponse, ak as pluginRegistry, w as sanitizeIssues, z as serializeStyles, P as styleGuideToMarkdown, G as truncateHTML, R as validateStyleGuide, ac as validateViolations } from './index-CWI5wLdq.js';
3
3
  export { default as pc } from 'picocolors';
4
4
 
5
5
  /**
package/dist/node.js CHANGED
@@ -55,7 +55,7 @@ import {
55
55
  truncateHTML,
56
56
  validateStyleGuide,
57
57
  validateViolations
58
- } from "./chunk-J4LUG7RV.js";
58
+ } from "./chunk-OGU2FLMN.js";
59
59
 
60
60
  // src/ollama/bootstrap.ts
61
61
  import { spawn } from "child_process";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uilint-core",
3
- "version": "0.2.147",
3
+ "version": "0.2.149",
4
4
  "description": "Core library for UILint - AI-powered UI consistency checking",
5
5
  "author": "Peter Suggate",
6
6
  "repository": {