uilint-duplicates 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.
@@ -288,6 +288,7 @@ var noDuplicatesRuleDefinition = {
288
288
  heatmapColor: "#f59e0b",
289
289
  // Amber
290
290
  customInspectorPanel: "duplicates",
291
+ contentRenderer: "duplicate-comparison",
291
292
  requirements: [
292
293
  {
293
294
  type: "semantic-index",
@@ -462,4 +463,4 @@ export {
462
463
  duplicatesPlugin,
463
464
  plugin_default
464
465
  };
465
- //# sourceMappingURL=chunk-3DDDYXCK.js.map
466
+ //# sourceMappingURL=chunk-7B66KZKA.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/plugin/index.ts","../src/plugin/state.ts","../src/plugin/actions.ts","../src/plugin/commands.ts","../src/plugin/panels.ts","../src/plugin/rules.ts","../src/plugin/messages.ts"],"sourcesContent":["/**\n * Duplicates Plugin Definition\n *\n * Complete plugin export - NO REACT.\n * This is the main plugin definition that gets registered with pluginRegistry.\n */\n\nimport type { PluginWithHandlers, IssueContribution } from \"uilint-core\";\nimport { pluginRegistry } from \"uilint-core\";\n\nimport { duplicatesStateDefinition, type DuplicatesState } from \"./state.js\";\nimport { duplicatesActionHandlers } from \"./actions.js\";\nimport { duplicatesCommands } from \"./commands.js\";\nimport { duplicatesPanelDefinitions } from \"./panels.js\";\nimport { duplicatesRuleDefinitions } from \"./rules.js\";\nimport { duplicatesMessageHandlers } from \"./messages.js\";\n\n/**\n * Duplicates plugin definition\n *\n * Contains everything needed for duplicate detection EXCEPT React components.\n * The host (uilint-react) imports this and renders the appropriate UI.\n */\nexport const duplicatesPlugin: PluginWithHandlers<DuplicatesState> = {\n // === Metadata ===\n id: \"duplicates\",\n name: \"Duplicate Detection\",\n version: \"1.0.0\",\n description: \"Embedding-based semantic code duplicate detection\",\n icon: \"copy\",\n\n // === State ===\n state: duplicatesStateDefinition,\n actions: duplicatesActionHandlers,\n\n // === UI Contributions (Declarative) ===\n commands: duplicatesCommands,\n panels: duplicatesPanelDefinitions,\n\n // === Rules ===\n rules: duplicatesRuleDefinitions,\n handlesRuleCategories: [\"duplicates\"],\n\n // === WebSocket ===\n messageHandlers: duplicatesMessageHandlers,\n\n // === Issue Aggregation ===\n // Duplicate issues are reported by the no-duplicates ESLint rule.\n // They don't come from plugin state, so we return empty.\n getIssues: (_state: DuplicatesState): IssueContribution => {\n return { pluginId: \"duplicates\", issues: new Map() };\n },\n\n // === Browser Actions ===\n browserActions: [],\n};\n\n// Auto-register with plugin registry on import\npluginRegistry.register(duplicatesPlugin);\n\n// Export types for host apps\nexport type { DuplicatesState } from \"./state.js\";\n\nexport default duplicatesPlugin;\n","/**\n * Duplicates Plugin State\n *\n * State shape and initial state for the duplicates detection plugin.\n * No React - pure TypeScript.\n */\n\nimport type { StateDefinition, OperationState } from \"uilint-core\";\nimport { createOperationInitialState, createOperationComputed } from \"uilint-core\";\n\n/**\n * Index statistics\n */\nexport interface IndexStats {\n totalChunks: number;\n added: number;\n modified: number;\n deleted: number;\n duration: number;\n}\n\n/**\n * Duplicates plugin state shape\n */\nexport interface DuplicatesState {\n // === Index Operation ===\n /** Indexing operation lifecycle state */\n indexing: OperationState<IndexStats>;\n\n // === UI State ===\n /** Currently selected duplicate for inspector */\n selectedDuplicate: {\n sourceDataLoc: string;\n targetDataLoc: string;\n similarity: number;\n } | null;\n}\n\n/**\n * Initial state for the duplicates plugin\n */\nexport const duplicatesInitialState: DuplicatesState = {\n indexing: createOperationInitialState<IndexStats>(),\n selectedDuplicate: null,\n};\n\nconst opComputed = createOperationComputed<DuplicatesState>((s) => s.indexing);\n\n/**\n * State definition for the plugin system\n */\nexport const duplicatesStateDefinition: StateDefinition<DuplicatesState> = {\n initialState: duplicatesInitialState,\n\n computed: {\n /** Whether index is ready for queries */\n isIndexReady: opComputed.isReady,\n\n /** Whether indexing is in progress */\n isIndexing: opComputed.isActive,\n\n /** Whether there was an error */\n hasError: opComputed.hasError,\n\n /** Progress percentage (0-100) */\n progressPercent: opComputed.progressPercent,\n },\n\n persist: {\n key: \"uilint-duplicates\",\n include: [],\n },\n};\n","/**\n * Duplicates Plugin Action Handlers\n *\n * Plain functions that handle plugin actions.\n * No React - uses PluginContext for state management.\n */\n\nimport type { ActionHandlers, PluginContext } from \"uilint-core\";\nimport { createOperationActions } from \"uilint-core\";\nimport type { DuplicatesState, IndexStats } from \"./state.js\";\n\n// Helper type for cleaner action handler definitions\ntype Handler<TPayload = void> = (\n ctx: PluginContext<DuplicatesState>,\n payload: TPayload\n) => void | Promise<void>;\n\n// Cast helper for proper typing\nconst h = <TPayload = void>(fn: Handler<TPayload>): Handler<unknown> =>\n fn as Handler<unknown>;\n\n/**\n * Generated lifecycle action handlers for the indexing operation.\n *\n * Provides: handle-indexing-start, handle-indexing-progress,\n * handle-indexing-complete, handle-indexing-error\n */\nconst indexingActions = createOperationActions<DuplicatesState, IndexStats>(\n \"indexing\",\n {\n getOp: (s) => s.indexing,\n setOp: (op) => ({ indexing: op }),\n }\n);\n\n/**\n * Action handlers for the duplicates plugin\n */\nexport const duplicatesActionHandlers: ActionHandlers<DuplicatesState> = {\n // Spread in generated lifecycle handlers\n ...indexingActions,\n\n /**\n * Start indexing\n */\n \"start-indexing\": h((ctx) => {\n ctx.dispatch(\"handle-indexing-start\");\n ctx.websocket.send({ type: \"duplicates:index\" });\n }),\n\n /**\n * Select a duplicate for viewing in inspector\n */\n \"select-duplicate\": h<{ sourceDataLoc: string; targetDataLoc: string; similarity: number }>(\n (ctx, payload) => {\n ctx.setState({ selectedDuplicate: payload });\n ctx.openInspector(\"duplicates\", payload);\n }\n ),\n\n /**\n * Clear selected duplicate\n */\n \"clear-selected-duplicate\": h((ctx) => {\n ctx.setState({ selectedDuplicate: null });\n }),\n\n /**\n * Toggle heatmap filter for duplicates\n */\n \"toggle-heatmap-filter\": h<{ sourceDataLoc: string; targetDataLoc: string }>(\n (ctx, payload) => {\n ctx.setHeatmapFilter(\n [payload.sourceDataLoc, payload.targetDataLoc],\n \"Duplicate Code\"\n );\n }\n ),\n\n /**\n * Clear heatmap filter\n */\n \"clear-heatmap-filter\": h((ctx) => {\n ctx.clearHeatmapFilter();\n }),\n\n /**\n * Open file in editor\n */\n \"open-editor\": h<{ dataLoc: string }>((ctx, payload) => {\n ctx.openInEditor(payload.dataLoc);\n }),\n};\n","/**\n * Duplicates Plugin Commands\n *\n * Command palette commands for the duplicates plugin.\n * Declarative - no React.\n */\n\nimport type { CommandDefinition } from \"uilint-core\";\n\n/**\n * Duplicates plugin commands\n */\nexport const duplicatesCommands: CommandDefinition[] = [\n {\n id: \"duplicates:rebuild-index\",\n title: \"Rebuild Duplicates Index\",\n keywords: [\"duplicates\", \"index\", \"rebuild\", \"scan\", \"embeddings\"],\n category: \"Duplicates\",\n subtitle: \"Rebuild the semantic code index for duplicate detection\",\n icon: \"refresh\",\n action: { type: \"start-indexing\" },\n },\n {\n id: \"duplicates:clear-filter\",\n title: \"Clear Duplicate Filter\",\n keywords: [\"duplicates\", \"clear\", \"filter\", \"heatmap\"],\n category: \"Duplicates\",\n subtitle: \"Clear the heatmap filter for duplicates\",\n icon: \"x\",\n action: { type: \"clear-heatmap-filter\" },\n },\n];\n","/**\n * Duplicates Plugin Panels\n *\n * Inspector panel definitions for the duplicates plugin.\n * Declarative - no React.\n */\n\nimport type { PanelDefinition } from \"uilint-core\";\n\n/**\n * Duplicates inspector panel\n */\nexport const duplicatesPanelDefinition: PanelDefinition = {\n id: \"duplicates\",\n title: \"Duplicate Code\",\n priority: 10,\n\n loading: {\n when: { binding: \"isLoading\" },\n message: \"Loading code comparison...\",\n },\n\n empty: {\n when: { expression: \"!sourceCode && !targetCode\" },\n message: \"No duplicate information available.\",\n icon: \"search\",\n },\n\n layout: [\n // Similarity badge\n {\n type: \"badge\",\n variant: \"similarity\",\n value: { binding: \"similarity\" },\n centered: true,\n },\n\n // Source code section\n {\n type: \"code-viewer\",\n label: \"This Code\",\n icon: \"target\",\n code: {\n fetch: {\n type: \"source-code\",\n params: {\n filePath: { binding: \"sourceLocation.filePath\" },\n line: { binding: \"sourceLocation.startLine\" },\n contextAbove: 10,\n contextBelow: 10,\n },\n },\n },\n location: { binding: \"sourceLocation\" },\n diffHighlighting: true,\n maxHeight: 250,\n onNavigate: {\n type: \"open-editor\",\n payloadBindings: { dataLoc: \"sourceDataLoc\" },\n },\n },\n\n // Target code section\n {\n type: \"code-viewer\",\n label: \"Similar Code\",\n icon: \"link\",\n code: {\n fetch: {\n type: \"source-code\",\n params: {\n filePath: { binding: \"targetLocation.filePath\" },\n line: { binding: \"targetLocation.startLine\" },\n contextAbove: 10,\n contextBelow: 10,\n },\n },\n },\n location: { binding: \"targetLocation\" },\n diffHighlighting: true,\n maxHeight: 250,\n onNavigate: {\n type: \"open-editor\",\n payloadBindings: { dataLoc: \"targetDataLoc\" },\n },\n },\n\n { type: \"divider\", spacing: \"medium\" },\n\n // Actions\n {\n type: \"actions\",\n direction: \"row\",\n actions: [\n {\n id: \"toggle-heatmap\",\n label: {\n condition: { binding: \"heatmapFilterActive\" },\n true: \"Clear Heatmap Filter\",\n false: \"Focus in Heatmap\",\n },\n icon: \"filter\",\n variant: \"primary\",\n action: {\n type: \"toggle-heatmap-filter\",\n payloadBindings: {\n sourceDataLoc: \"sourceDataLoc\",\n targetDataLoc: \"targetDataLoc\",\n },\n },\n },\n ],\n },\n ],\n};\n\n/**\n * Index status panel\n */\nexport const indexStatusPanelDefinition: PanelDefinition = {\n id: \"duplicates-index-status\",\n title: \"Index Status\",\n priority: 5,\n\n layout: [\n // Status badge\n {\n type: \"badge\",\n variant: \"status\",\n value: { binding: \"indexing.status\" },\n centered: true,\n },\n\n // Progress (when indexing)\n {\n type: \"conditional\",\n condition: { expression: \"indexing.status === 'active'\" },\n then: [\n {\n type: \"progress\",\n value: { binding: \"progressPercent\" },\n label: { binding: \"indexing.progress.message\" },\n },\n ],\n },\n\n // Stats (when complete)\n {\n type: \"conditional\",\n condition: { expression: \"indexing.status === 'complete'\" },\n then: [\n {\n type: \"text\",\n content: { binding: \"indexing.stats.totalChunks\" },\n variant: \"body\",\n },\n ],\n },\n\n // Error (when error)\n {\n type: \"conditional\",\n condition: { expression: \"indexing.status === 'error'\" },\n then: [\n {\n type: \"text\",\n content: { binding: \"indexing.lastError\" },\n variant: \"error\",\n },\n ],\n },\n\n { type: \"divider\" },\n\n // Rebuild action\n {\n type: \"actions\",\n direction: \"column\",\n actions: [\n {\n id: \"rebuild-index\",\n label: \"Rebuild Index\",\n icon: \"refresh\",\n variant: \"secondary\",\n action: { type: \"start-indexing\" },\n disabled: { binding: \"isIndexing\" },\n },\n ],\n },\n ],\n};\n\n/**\n * All duplicates panel definitions\n */\nexport const duplicatesPanelDefinitions: PanelDefinition[] = [\n duplicatesPanelDefinition,\n indexStatusPanelDefinition,\n];\n","/**\n * Duplicates Plugin Rules\n *\n * Rule definitions for the duplicates plugin.\n * Declarative - no React.\n */\n\nimport type { RuleDefinition } from \"uilint-core\";\n\n/**\n * no-duplicates rule definition\n */\nexport const noDuplicatesRuleDefinition: RuleDefinition = {\n id: \"no-duplicates\",\n name: \"No Duplicates\",\n description: \"Warns when code is semantically similar to existing code\",\n category: \"duplicates\",\n icon: \"copy\",\n defaultSeverity: \"warn\",\n defaultEnabled: false,\n heatmapColor: \"#f59e0b\", // Amber\n customInspectorPanel: \"duplicates\",\n requirements: [\n {\n type: \"semantic-index\",\n description: \"Requires semantic index for duplicate detection\",\n setupHint: \"Run: uilint duplicates index\",\n },\n ],\n defaultOptions: [\n {\n threshold: 0.75,\n indexPath: \".uilint/.duplicates-index\",\n minLines: 3,\n confidenceLevel: \"low\",\n useStructuralBoost: true,\n includeSameFile: false,\n kind: \"all\",\n },\n ],\n optionSchema: {\n fields: [\n {\n key: \"threshold\",\n label: \"Similarity Threshold\",\n type: \"number\",\n defaultValue: 0.75,\n description: \"Minimum similarity score (0-1) to report as duplicate.\",\n },\n {\n key: \"confidenceLevel\",\n label: \"Minimum Confidence\",\n type: \"select\",\n defaultValue: \"low\",\n options: [\n { value: \"high\", label: \"High (>90%) - Likely copy-paste\" },\n { value: \"medium\", label: \"Medium (>75%) - Semantically similar\" },\n { value: \"low\", label: \"Low (>60%) - Possibly related\" },\n ],\n description: \"Minimum confidence level for reported duplicates.\",\n },\n {\n key: \"kind\",\n label: \"Chunk Type\",\n type: \"select\",\n defaultValue: \"all\",\n options: [\n { value: \"all\", label: \"All\" },\n { value: \"component\", label: \"Components\" },\n { value: \"hook\", label: \"Hooks\" },\n { value: \"function\", label: \"Functions\" },\n ],\n description: \"Filter duplicates by code type.\",\n },\n {\n key: \"useStructuralBoost\",\n label: \"Use Structural Analysis\",\n type: \"boolean\",\n defaultValue: true,\n description: \"Include structural similarity (props, JSX, hooks) in scoring.\",\n },\n {\n key: \"includeSameFile\",\n label: \"Include Same-File Duplicates\",\n type: \"boolean\",\n defaultValue: false,\n description: \"Report duplicates within the same file.\",\n },\n {\n key: \"minLines\",\n label: \"Minimum Lines\",\n type: \"number\",\n defaultValue: 3,\n description: \"Minimum number of lines for a code chunk.\",\n },\n ],\n },\n docs: `\n## No Duplicates Rule\n\nThis rule detects code that is semantically similar to existing code in your codebase.\n\n### How it Works\n\n1. The codebase is indexed to extract \"chunks\" (components, hooks, functions)\n2. Each chunk is embedded using an AI model to capture semantic meaning\n3. During linting, chunks are compared against the index\n4. Similar chunks above the threshold are reported\n\n### Confidence Levels\n\n- **High (>90%)**: Very likely copy-paste or near-identical code\n- **Medium (>75%)**: Semantically similar, possibly refactorable\n- **Low (>60%)**: Possibly related, worth reviewing\n\n### Setup\n\n1. Run \\`uilint duplicates index\\` to build the semantic index\n2. Enable this rule in your ESLint config\n3. The rule will report duplicates during linting\n\n### Performance\n\nThe rule uses a pre-built index, so it's fast during linting.\nRe-index periodically to keep the index up to date.\n `.trim(),\n};\n\n/**\n * All duplicates rule definitions\n */\nexport const duplicatesRuleDefinitions: RuleDefinition[] = [\n noDuplicatesRuleDefinition,\n];\n","/**\n * Duplicates Plugin Message Handlers\n *\n * WebSocket message handlers for the duplicates plugin.\n * Plain functions - no React.\n */\n\nimport type { MessageHandlers } from \"uilint-core\";\nimport { createOperationMessageHandlers } from \"uilint-core\";\nimport type { DuplicatesState } from \"./state.js\";\n\n/**\n * WebSocket message types for duplicates indexing\n */\nexport interface DuplicatesIndexingStartMessage {\n type: \"duplicates:indexing:start\";\n}\n\nexport interface DuplicatesIndexingProgressMessage {\n type: \"duplicates:indexing:progress\";\n message: string;\n current?: number;\n total?: number;\n}\n\nexport interface DuplicatesIndexingCompleteMessage {\n type: \"duplicates:indexing:complete\";\n added: number;\n modified: number;\n deleted: number;\n totalChunks: number;\n duration: number;\n}\n\nexport interface DuplicatesIndexingErrorMessage {\n type: \"duplicates:indexing:error\";\n error: string;\n}\n\nexport type DuplicatesMessage =\n | DuplicatesIndexingStartMessage\n | DuplicatesIndexingProgressMessage\n | DuplicatesIndexingCompleteMessage\n | DuplicatesIndexingErrorMessage;\n\n/**\n * Message handlers for the duplicates plugin.\n *\n * Uses the operation lifecycle framework for the standard\n * start -> progress -> complete/error message flow.\n */\nexport const duplicatesMessageHandlers: MessageHandlers<DuplicatesState> = {\n ...createOperationMessageHandlers<DuplicatesState>({\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 const m = msg as DuplicatesIndexingProgressMessage;\n return {\n current: m.current ?? 0,\n total: m.total ?? 0,\n message: m.message,\n };\n },\n extractStats: (msg) => {\n const m = msg as DuplicatesIndexingCompleteMessage;\n return {\n added: m.added,\n modified: m.modified,\n deleted: m.deleted,\n totalChunks: m.totalChunks,\n duration: m.duration,\n };\n },\n }),\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/plugin/index.ts","../src/plugin/state.ts","../src/plugin/actions.ts","../src/plugin/commands.ts","../src/plugin/panels.ts","../src/plugin/rules.ts","../src/plugin/messages.ts"],"sourcesContent":["/**\n * Duplicates Plugin Definition\n *\n * Complete plugin export - NO REACT.\n * This is the main plugin definition that gets registered with pluginRegistry.\n */\n\nimport type { PluginWithHandlers, IssueContribution } from \"uilint-core\";\nimport { pluginRegistry } from \"uilint-core\";\n\nimport { duplicatesStateDefinition, type DuplicatesState } from \"./state.js\";\nimport { duplicatesActionHandlers } from \"./actions.js\";\nimport { duplicatesCommands } from \"./commands.js\";\nimport { duplicatesPanelDefinitions } from \"./panels.js\";\nimport { duplicatesRuleDefinitions } from \"./rules.js\";\nimport { duplicatesMessageHandlers } from \"./messages.js\";\n\n/**\n * Duplicates plugin definition\n *\n * Contains everything needed for duplicate detection EXCEPT React components.\n * The host (uilint-react) imports this and renders the appropriate UI.\n */\nexport const duplicatesPlugin: PluginWithHandlers<DuplicatesState> = {\n // === Metadata ===\n id: \"duplicates\",\n name: \"Duplicate Detection\",\n version: \"1.0.0\",\n description: \"Embedding-based semantic code duplicate detection\",\n icon: \"copy\",\n\n // === State ===\n state: duplicatesStateDefinition,\n actions: duplicatesActionHandlers,\n\n // === UI Contributions (Declarative) ===\n commands: duplicatesCommands,\n panels: duplicatesPanelDefinitions,\n\n // === Rules ===\n rules: duplicatesRuleDefinitions,\n handlesRuleCategories: [\"duplicates\"],\n\n // === WebSocket ===\n messageHandlers: duplicatesMessageHandlers,\n\n // === Issue Aggregation ===\n // Duplicate issues are reported by the no-duplicates ESLint rule.\n // They don't come from plugin state, so we return empty.\n getIssues: (_state: DuplicatesState): IssueContribution => {\n return { pluginId: \"duplicates\", issues: new Map() };\n },\n\n // === Browser Actions ===\n browserActions: [],\n};\n\n// Auto-register with plugin registry on import\npluginRegistry.register(duplicatesPlugin);\n\n// Export types for host apps\nexport type { DuplicatesState } from \"./state.js\";\n\nexport default duplicatesPlugin;\n","/**\n * Duplicates Plugin State\n *\n * State shape and initial state for the duplicates detection plugin.\n * No React - pure TypeScript.\n */\n\nimport type { StateDefinition, OperationState } from \"uilint-core\";\nimport { createOperationInitialState, createOperationComputed } from \"uilint-core\";\n\n/**\n * Index statistics\n */\nexport interface IndexStats {\n totalChunks: number;\n added: number;\n modified: number;\n deleted: number;\n duration: number;\n}\n\n/**\n * Duplicates plugin state shape\n */\nexport interface DuplicatesState {\n // === Index Operation ===\n /** Indexing operation lifecycle state */\n indexing: OperationState<IndexStats>;\n\n // === UI State ===\n /** Currently selected duplicate for inspector */\n selectedDuplicate: {\n sourceDataLoc: string;\n targetDataLoc: string;\n similarity: number;\n } | null;\n}\n\n/**\n * Initial state for the duplicates plugin\n */\nexport const duplicatesInitialState: DuplicatesState = {\n indexing: createOperationInitialState<IndexStats>(),\n selectedDuplicate: null,\n};\n\nconst opComputed = createOperationComputed<DuplicatesState>((s) => s.indexing);\n\n/**\n * State definition for the plugin system\n */\nexport const duplicatesStateDefinition: StateDefinition<DuplicatesState> = {\n initialState: duplicatesInitialState,\n\n computed: {\n /** Whether index is ready for queries */\n isIndexReady: opComputed.isReady,\n\n /** Whether indexing is in progress */\n isIndexing: opComputed.isActive,\n\n /** Whether there was an error */\n hasError: opComputed.hasError,\n\n /** Progress percentage (0-100) */\n progressPercent: opComputed.progressPercent,\n },\n\n persist: {\n key: \"uilint-duplicates\",\n include: [],\n },\n};\n","/**\n * Duplicates Plugin Action Handlers\n *\n * Plain functions that handle plugin actions.\n * No React - uses PluginContext for state management.\n */\n\nimport type { ActionHandlers, PluginContext } from \"uilint-core\";\nimport { createOperationActions } from \"uilint-core\";\nimport type { DuplicatesState, IndexStats } from \"./state.js\";\n\n// Helper type for cleaner action handler definitions\ntype Handler<TPayload = void> = (\n ctx: PluginContext<DuplicatesState>,\n payload: TPayload\n) => void | Promise<void>;\n\n// Cast helper for proper typing\nconst h = <TPayload = void>(fn: Handler<TPayload>): Handler<unknown> =>\n fn as Handler<unknown>;\n\n/**\n * Generated lifecycle action handlers for the indexing operation.\n *\n * Provides: handle-indexing-start, handle-indexing-progress,\n * handle-indexing-complete, handle-indexing-error\n */\nconst indexingActions = createOperationActions<DuplicatesState, IndexStats>(\n \"indexing\",\n {\n getOp: (s) => s.indexing,\n setOp: (op) => ({ indexing: op }),\n }\n);\n\n/**\n * Action handlers for the duplicates plugin\n */\nexport const duplicatesActionHandlers: ActionHandlers<DuplicatesState> = {\n // Spread in generated lifecycle handlers\n ...indexingActions,\n\n /**\n * Start indexing\n */\n \"start-indexing\": h((ctx) => {\n ctx.dispatch(\"handle-indexing-start\");\n ctx.websocket.send({ type: \"duplicates:index\" });\n }),\n\n /**\n * Select a duplicate for viewing in inspector\n */\n \"select-duplicate\": h<{ sourceDataLoc: string; targetDataLoc: string; similarity: number }>(\n (ctx, payload) => {\n ctx.setState({ selectedDuplicate: payload });\n ctx.openInspector(\"duplicates\", payload);\n }\n ),\n\n /**\n * Clear selected duplicate\n */\n \"clear-selected-duplicate\": h((ctx) => {\n ctx.setState({ selectedDuplicate: null });\n }),\n\n /**\n * Toggle heatmap filter for duplicates\n */\n \"toggle-heatmap-filter\": h<{ sourceDataLoc: string; targetDataLoc: string }>(\n (ctx, payload) => {\n ctx.setHeatmapFilter(\n [payload.sourceDataLoc, payload.targetDataLoc],\n \"Duplicate Code\"\n );\n }\n ),\n\n /**\n * Clear heatmap filter\n */\n \"clear-heatmap-filter\": h((ctx) => {\n ctx.clearHeatmapFilter();\n }),\n\n /**\n * Open file in editor\n */\n \"open-editor\": h<{ dataLoc: string }>((ctx, payload) => {\n ctx.openInEditor(payload.dataLoc);\n }),\n};\n","/**\n * Duplicates Plugin Commands\n *\n * Command palette commands for the duplicates plugin.\n * Declarative - no React.\n */\n\nimport type { CommandDefinition } from \"uilint-core\";\n\n/**\n * Duplicates plugin commands\n */\nexport const duplicatesCommands: CommandDefinition[] = [\n {\n id: \"duplicates:rebuild-index\",\n title: \"Rebuild Duplicates Index\",\n keywords: [\"duplicates\", \"index\", \"rebuild\", \"scan\", \"embeddings\"],\n category: \"Duplicates\",\n subtitle: \"Rebuild the semantic code index for duplicate detection\",\n icon: \"refresh\",\n action: { type: \"start-indexing\" },\n },\n {\n id: \"duplicates:clear-filter\",\n title: \"Clear Duplicate Filter\",\n keywords: [\"duplicates\", \"clear\", \"filter\", \"heatmap\"],\n category: \"Duplicates\",\n subtitle: \"Clear the heatmap filter for duplicates\",\n icon: \"x\",\n action: { type: \"clear-heatmap-filter\" },\n },\n];\n","/**\n * Duplicates Plugin Panels\n *\n * Inspector panel definitions for the duplicates plugin.\n * Declarative - no React.\n */\n\nimport type { PanelDefinition } from \"uilint-core\";\n\n/**\n * Duplicates inspector panel\n */\nexport const duplicatesPanelDefinition: PanelDefinition = {\n id: \"duplicates\",\n title: \"Duplicate Code\",\n priority: 10,\n\n loading: {\n when: { binding: \"isLoading\" },\n message: \"Loading code comparison...\",\n },\n\n empty: {\n when: { expression: \"!sourceCode && !targetCode\" },\n message: \"No duplicate information available.\",\n icon: \"search\",\n },\n\n layout: [\n // Similarity badge\n {\n type: \"badge\",\n variant: \"similarity\",\n value: { binding: \"similarity\" },\n centered: true,\n },\n\n // Source code section\n {\n type: \"code-viewer\",\n label: \"This Code\",\n icon: \"target\",\n code: {\n fetch: {\n type: \"source-code\",\n params: {\n filePath: { binding: \"sourceLocation.filePath\" },\n line: { binding: \"sourceLocation.startLine\" },\n contextAbove: 10,\n contextBelow: 10,\n },\n },\n },\n location: { binding: \"sourceLocation\" },\n diffHighlighting: true,\n maxHeight: 250,\n onNavigate: {\n type: \"open-editor\",\n payloadBindings: { dataLoc: \"sourceDataLoc\" },\n },\n },\n\n // Target code section\n {\n type: \"code-viewer\",\n label: \"Similar Code\",\n icon: \"link\",\n code: {\n fetch: {\n type: \"source-code\",\n params: {\n filePath: { binding: \"targetLocation.filePath\" },\n line: { binding: \"targetLocation.startLine\" },\n contextAbove: 10,\n contextBelow: 10,\n },\n },\n },\n location: { binding: \"targetLocation\" },\n diffHighlighting: true,\n maxHeight: 250,\n onNavigate: {\n type: \"open-editor\",\n payloadBindings: { dataLoc: \"targetDataLoc\" },\n },\n },\n\n { type: \"divider\", spacing: \"medium\" },\n\n // Actions\n {\n type: \"actions\",\n direction: \"row\",\n actions: [\n {\n id: \"toggle-heatmap\",\n label: {\n condition: { binding: \"heatmapFilterActive\" },\n true: \"Clear Heatmap Filter\",\n false: \"Focus in Heatmap\",\n },\n icon: \"filter\",\n variant: \"primary\",\n action: {\n type: \"toggle-heatmap-filter\",\n payloadBindings: {\n sourceDataLoc: \"sourceDataLoc\",\n targetDataLoc: \"targetDataLoc\",\n },\n },\n },\n ],\n },\n ],\n};\n\n/**\n * Index status panel\n */\nexport const indexStatusPanelDefinition: PanelDefinition = {\n id: \"duplicates-index-status\",\n title: \"Index Status\",\n priority: 5,\n\n layout: [\n // Status badge\n {\n type: \"badge\",\n variant: \"status\",\n value: { binding: \"indexing.status\" },\n centered: true,\n },\n\n // Progress (when indexing)\n {\n type: \"conditional\",\n condition: { expression: \"indexing.status === 'active'\" },\n then: [\n {\n type: \"progress\",\n value: { binding: \"progressPercent\" },\n label: { binding: \"indexing.progress.message\" },\n },\n ],\n },\n\n // Stats (when complete)\n {\n type: \"conditional\",\n condition: { expression: \"indexing.status === 'complete'\" },\n then: [\n {\n type: \"text\",\n content: { binding: \"indexing.stats.totalChunks\" },\n variant: \"body\",\n },\n ],\n },\n\n // Error (when error)\n {\n type: \"conditional\",\n condition: { expression: \"indexing.status === 'error'\" },\n then: [\n {\n type: \"text\",\n content: { binding: \"indexing.lastError\" },\n variant: \"error\",\n },\n ],\n },\n\n { type: \"divider\" },\n\n // Rebuild action\n {\n type: \"actions\",\n direction: \"column\",\n actions: [\n {\n id: \"rebuild-index\",\n label: \"Rebuild Index\",\n icon: \"refresh\",\n variant: \"secondary\",\n action: { type: \"start-indexing\" },\n disabled: { binding: \"isIndexing\" },\n },\n ],\n },\n ],\n};\n\n/**\n * All duplicates panel definitions\n */\nexport const duplicatesPanelDefinitions: PanelDefinition[] = [\n duplicatesPanelDefinition,\n indexStatusPanelDefinition,\n];\n","/**\n * Duplicates Plugin Rules\n *\n * Rule definitions for the duplicates plugin.\n * Declarative - no React.\n */\n\nimport type { RuleDefinition } from \"uilint-core\";\n\n/**\n * no-duplicates rule definition\n */\nexport const noDuplicatesRuleDefinition: RuleDefinition = {\n id: \"no-duplicates\",\n name: \"No Duplicates\",\n description: \"Warns when code is semantically similar to existing code\",\n category: \"duplicates\",\n icon: \"copy\",\n defaultSeverity: \"warn\",\n defaultEnabled: false,\n heatmapColor: \"#f59e0b\", // Amber\n customInspectorPanel: \"duplicates\",\n contentRenderer: \"duplicate-comparison\",\n requirements: [\n {\n type: \"semantic-index\",\n description: \"Requires semantic index for duplicate detection\",\n setupHint: \"Run: uilint duplicates index\",\n },\n ],\n defaultOptions: [\n {\n threshold: 0.75,\n indexPath: \".uilint/.duplicates-index\",\n minLines: 3,\n confidenceLevel: \"low\",\n useStructuralBoost: true,\n includeSameFile: false,\n kind: \"all\",\n },\n ],\n optionSchema: {\n fields: [\n {\n key: \"threshold\",\n label: \"Similarity Threshold\",\n type: \"number\",\n defaultValue: 0.75,\n description: \"Minimum similarity score (0-1) to report as duplicate.\",\n },\n {\n key: \"confidenceLevel\",\n label: \"Minimum Confidence\",\n type: \"select\",\n defaultValue: \"low\",\n options: [\n { value: \"high\", label: \"High (>90%) - Likely copy-paste\" },\n { value: \"medium\", label: \"Medium (>75%) - Semantically similar\" },\n { value: \"low\", label: \"Low (>60%) - Possibly related\" },\n ],\n description: \"Minimum confidence level for reported duplicates.\",\n },\n {\n key: \"kind\",\n label: \"Chunk Type\",\n type: \"select\",\n defaultValue: \"all\",\n options: [\n { value: \"all\", label: \"All\" },\n { value: \"component\", label: \"Components\" },\n { value: \"hook\", label: \"Hooks\" },\n { value: \"function\", label: \"Functions\" },\n ],\n description: \"Filter duplicates by code type.\",\n },\n {\n key: \"useStructuralBoost\",\n label: \"Use Structural Analysis\",\n type: \"boolean\",\n defaultValue: true,\n description: \"Include structural similarity (props, JSX, hooks) in scoring.\",\n },\n {\n key: \"includeSameFile\",\n label: \"Include Same-File Duplicates\",\n type: \"boolean\",\n defaultValue: false,\n description: \"Report duplicates within the same file.\",\n },\n {\n key: \"minLines\",\n label: \"Minimum Lines\",\n type: \"number\",\n defaultValue: 3,\n description: \"Minimum number of lines for a code chunk.\",\n },\n ],\n },\n docs: `\n## No Duplicates Rule\n\nThis rule detects code that is semantically similar to existing code in your codebase.\n\n### How it Works\n\n1. The codebase is indexed to extract \"chunks\" (components, hooks, functions)\n2. Each chunk is embedded using an AI model to capture semantic meaning\n3. During linting, chunks are compared against the index\n4. Similar chunks above the threshold are reported\n\n### Confidence Levels\n\n- **High (>90%)**: Very likely copy-paste or near-identical code\n- **Medium (>75%)**: Semantically similar, possibly refactorable\n- **Low (>60%)**: Possibly related, worth reviewing\n\n### Setup\n\n1. Run \\`uilint duplicates index\\` to build the semantic index\n2. Enable this rule in your ESLint config\n3. The rule will report duplicates during linting\n\n### Performance\n\nThe rule uses a pre-built index, so it's fast during linting.\nRe-index periodically to keep the index up to date.\n `.trim(),\n};\n\n/**\n * All duplicates rule definitions\n */\nexport const duplicatesRuleDefinitions: RuleDefinition[] = [\n noDuplicatesRuleDefinition,\n];\n","/**\n * Duplicates Plugin Message Handlers\n *\n * WebSocket message handlers for the duplicates plugin.\n * Plain functions - no React.\n */\n\nimport type { MessageHandlers } from \"uilint-core\";\nimport { createOperationMessageHandlers } from \"uilint-core\";\nimport type { DuplicatesState } from \"./state.js\";\n\n/**\n * WebSocket message types for duplicates indexing\n */\nexport interface DuplicatesIndexingStartMessage {\n type: \"duplicates:indexing:start\";\n}\n\nexport interface DuplicatesIndexingProgressMessage {\n type: \"duplicates:indexing:progress\";\n message: string;\n current?: number;\n total?: number;\n}\n\nexport interface DuplicatesIndexingCompleteMessage {\n type: \"duplicates:indexing:complete\";\n added: number;\n modified: number;\n deleted: number;\n totalChunks: number;\n duration: number;\n}\n\nexport interface DuplicatesIndexingErrorMessage {\n type: \"duplicates:indexing:error\";\n error: string;\n}\n\nexport type DuplicatesMessage =\n | DuplicatesIndexingStartMessage\n | DuplicatesIndexingProgressMessage\n | DuplicatesIndexingCompleteMessage\n | DuplicatesIndexingErrorMessage;\n\n/**\n * Message handlers for the duplicates plugin.\n *\n * Uses the operation lifecycle framework for the standard\n * start -> progress -> complete/error message flow.\n */\nexport const duplicatesMessageHandlers: MessageHandlers<DuplicatesState> = {\n ...createOperationMessageHandlers<DuplicatesState>({\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 const m = msg as DuplicatesIndexingProgressMessage;\n return {\n current: m.current ?? 0,\n total: m.total ?? 0,\n message: m.message,\n };\n },\n extractStats: (msg) => {\n const m = msg as DuplicatesIndexingCompleteMessage;\n return {\n added: m.added,\n modified: m.modified,\n deleted: m.deleted,\n totalChunks: m.totalChunks,\n duration: m.duration,\n };\n },\n }),\n};\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -53,7 +53,7 @@ import {
53
53
  } from "./chunk-7YAFHXIU.js";
54
54
  import {
55
55
  duplicatesPlugin
56
- } from "./chunk-3DDDYXCK.js";
56
+ } from "./chunk-7B66KZKA.js";
57
57
  export {
58
58
  DEFAULT_COMBINED_SCORER_OPTIONS,
59
59
  DEFAULT_CONFIDENCE_CONFIG,
package/dist/node.js CHANGED
@@ -53,7 +53,7 @@ import {
53
53
  } from "./chunk-7YAFHXIU.js";
54
54
  import {
55
55
  duplicatesPlugin
56
- } from "./chunk-3DDDYXCK.js";
56
+ } from "./chunk-7B66KZKA.js";
57
57
  export {
58
58
  DEFAULT_COMBINED_SCORER_OPTIONS,
59
59
  DEFAULT_CONFIDENCE_CONFIG,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  duplicatesPlugin,
3
3
  plugin_default
4
- } from "../chunk-3DDDYXCK.js";
4
+ } from "../chunk-7B66KZKA.js";
5
5
  export {
6
6
  plugin_default as default,
7
7
  duplicatesPlugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uilint-duplicates",
3
- "version": "0.2.147",
3
+ "version": "0.2.149",
4
4
  "description": "Semantic code duplicate detection for React/TypeScript codebases",
5
5
  "author": "Peter Suggate",
6
6
  "repository": {
@@ -75,8 +75,8 @@
75
75
  "vitest": "^4.0.16"
76
76
  },
77
77
  "peerDependencies": {
78
- "uilint-core": "0.2.147",
79
- "uilint-eslint": "0.2.147"
78
+ "uilint-core": "0.2.149",
79
+ "uilint-eslint": "0.2.149"
80
80
  },
81
81
  "keywords": [
82
82
  "duplicate-detection",