driftdetect-vscode 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test.log +138 -0
  3. package/.vscode/launch.json +17 -0
  4. package/.vscode/tasks.json +15 -0
  5. package/LICENSE +121 -0
  6. package/dist/activation/activation-controller.d.ts +61 -0
  7. package/dist/activation/activation-controller.d.ts.map +1 -0
  8. package/dist/activation/activation-controller.js +235 -0
  9. package/dist/activation/activation-controller.js.map +1 -0
  10. package/dist/activation/activation-phases.d.ts +28 -0
  11. package/dist/activation/activation-phases.d.ts.map +1 -0
  12. package/dist/activation/activation-phases.js +80 -0
  13. package/dist/activation/activation-phases.js.map +1 -0
  14. package/dist/activation/index.d.ts +6 -0
  15. package/dist/activation/index.d.ts.map +1 -0
  16. package/dist/activation/index.js +6 -0
  17. package/dist/activation/index.js.map +1 -0
  18. package/dist/client/connection-config.d.ts +50 -0
  19. package/dist/client/connection-config.d.ts.map +1 -0
  20. package/dist/client/connection-config.js +56 -0
  21. package/dist/client/connection-config.js.map +1 -0
  22. package/dist/client/connection-manager.d.ts +70 -0
  23. package/dist/client/connection-manager.d.ts.map +1 -0
  24. package/dist/client/connection-manager.js +214 -0
  25. package/dist/client/connection-manager.js.map +1 -0
  26. package/dist/client/index.d.ts +8 -0
  27. package/dist/client/index.d.ts.map +1 -0
  28. package/dist/client/index.js +8 -0
  29. package/dist/client/index.js.map +1 -0
  30. package/dist/client/language-client-factory.d.ts +29 -0
  31. package/dist/client/language-client-factory.d.ts.map +1 -0
  32. package/dist/client/language-client-factory.js +76 -0
  33. package/dist/client/language-client-factory.js.map +1 -0
  34. package/dist/client/request-middleware.d.ts +38 -0
  35. package/dist/client/request-middleware.d.ts.map +1 -0
  36. package/dist/client/request-middleware.js +85 -0
  37. package/dist/client/request-middleware.js.map +1 -0
  38. package/dist/commands/command-definitions.d.ts +53 -0
  39. package/dist/commands/command-definitions.d.ts.map +1 -0
  40. package/dist/commands/command-definitions.js +212 -0
  41. package/dist/commands/command-definitions.js.map +1 -0
  42. package/dist/commands/command-router.d.ts +80 -0
  43. package/dist/commands/command-router.d.ts.map +1 -0
  44. package/dist/commands/command-router.js +127 -0
  45. package/dist/commands/command-router.js.map +1 -0
  46. package/dist/commands/handlers/connection-handlers.d.ts +14 -0
  47. package/dist/commands/handlers/connection-handlers.d.ts.map +1 -0
  48. package/dist/commands/handlers/connection-handlers.js +57 -0
  49. package/dist/commands/handlers/connection-handlers.js.map +1 -0
  50. package/dist/commands/handlers/constants-handlers.d.ts +11 -0
  51. package/dist/commands/handlers/constants-handlers.d.ts.map +1 -0
  52. package/dist/commands/handlers/constants-handlers.js +84 -0
  53. package/dist/commands/handlers/constants-handlers.js.map +1 -0
  54. package/dist/commands/handlers/index.d.ts +10 -0
  55. package/dist/commands/handlers/index.d.ts.map +1 -0
  56. package/dist/commands/handlers/index.js +10 -0
  57. package/dist/commands/handlers/index.js.map +1 -0
  58. package/dist/commands/handlers/pattern-handlers.d.ts +13 -0
  59. package/dist/commands/handlers/pattern-handlers.d.ts.map +1 -0
  60. package/dist/commands/handlers/pattern-handlers.js +127 -0
  61. package/dist/commands/handlers/pattern-handlers.js.map +1 -0
  62. package/dist/commands/handlers/scan-handlers.d.ts +15 -0
  63. package/dist/commands/handlers/scan-handlers.d.ts.map +1 -0
  64. package/dist/commands/handlers/scan-handlers.js +74 -0
  65. package/dist/commands/handlers/scan-handlers.js.map +1 -0
  66. package/dist/commands/handlers/ui-handlers.d.ts +12 -0
  67. package/dist/commands/handlers/ui-handlers.d.ts.map +1 -0
  68. package/dist/commands/handlers/ui-handlers.js +74 -0
  69. package/dist/commands/handlers/ui-handlers.js.map +1 -0
  70. package/dist/commands/handlers/violation-handlers.d.ts +13 -0
  71. package/dist/commands/handlers/violation-handlers.d.ts.map +1 -0
  72. package/dist/commands/handlers/violation-handlers.js +76 -0
  73. package/dist/commands/handlers/violation-handlers.js.map +1 -0
  74. package/dist/commands/index.d.ts +7 -0
  75. package/dist/commands/index.d.ts.map +1 -0
  76. package/dist/commands/index.js +7 -0
  77. package/dist/commands/index.js.map +1 -0
  78. package/dist/commands/middleware/connection-check-middleware.d.ts +12 -0
  79. package/dist/commands/middleware/connection-check-middleware.d.ts.map +1 -0
  80. package/dist/commands/middleware/connection-check-middleware.js +34 -0
  81. package/dist/commands/middleware/connection-check-middleware.js.map +1 -0
  82. package/dist/commands/middleware/index.d.ts +7 -0
  83. package/dist/commands/middleware/index.d.ts.map +1 -0
  84. package/dist/commands/middleware/index.js +7 -0
  85. package/dist/commands/middleware/index.js.map +1 -0
  86. package/dist/commands/middleware/logging-middleware.d.ts +12 -0
  87. package/dist/commands/middleware/logging-middleware.d.ts.map +1 -0
  88. package/dist/commands/middleware/logging-middleware.js +24 -0
  89. package/dist/commands/middleware/logging-middleware.js.map +1 -0
  90. package/dist/commands/middleware/telemetry-middleware.d.ts +22 -0
  91. package/dist/commands/middleware/telemetry-middleware.d.ts.map +1 -0
  92. package/dist/commands/middleware/telemetry-middleware.js +30 -0
  93. package/dist/commands/middleware/telemetry-middleware.js.map +1 -0
  94. package/dist/config/config-manager.d.ts +53 -0
  95. package/dist/config/config-manager.d.ts.map +1 -0
  96. package/dist/config/config-manager.js +178 -0
  97. package/dist/config/config-manager.js.map +1 -0
  98. package/dist/config/defaults.d.ts +11 -0
  99. package/dist/config/defaults.d.ts.map +1 -0
  100. package/dist/config/defaults.js +43 -0
  101. package/dist/config/defaults.js.map +1 -0
  102. package/dist/config/index.d.ts +7 -0
  103. package/dist/config/index.d.ts.map +1 -0
  104. package/dist/config/index.js +7 -0
  105. package/dist/config/index.js.map +1 -0
  106. package/dist/config/validator.d.ts +22 -0
  107. package/dist/config/validator.d.ts.map +1 -0
  108. package/dist/config/validator.js +93 -0
  109. package/dist/config/validator.js.map +1 -0
  110. package/dist/extension.d.ts +32 -0
  111. package/dist/extension.d.ts.map +1 -0
  112. package/dist/extension.js +50 -0
  113. package/dist/extension.js.map +1 -0
  114. package/dist/infrastructure/disposable-manager.d.ts +43 -0
  115. package/dist/infrastructure/disposable-manager.d.ts.map +1 -0
  116. package/dist/infrastructure/disposable-manager.js +75 -0
  117. package/dist/infrastructure/disposable-manager.js.map +1 -0
  118. package/dist/infrastructure/event-bus.d.ts +85 -0
  119. package/dist/infrastructure/event-bus.d.ts.map +1 -0
  120. package/dist/infrastructure/event-bus.js +74 -0
  121. package/dist/infrastructure/event-bus.js.map +1 -0
  122. package/dist/infrastructure/index.d.ts +10 -0
  123. package/dist/infrastructure/index.d.ts.map +1 -0
  124. package/dist/infrastructure/index.js +10 -0
  125. package/dist/infrastructure/index.js.map +1 -0
  126. package/dist/infrastructure/logger.d.ts +37 -0
  127. package/dist/infrastructure/logger.d.ts.map +1 -0
  128. package/dist/infrastructure/logger.js +86 -0
  129. package/dist/infrastructure/logger.js.map +1 -0
  130. package/dist/infrastructure/service-container.d.ts +68 -0
  131. package/dist/infrastructure/service-container.d.ts.map +1 -0
  132. package/dist/infrastructure/service-container.js +94 -0
  133. package/dist/infrastructure/service-container.js.map +1 -0
  134. package/dist/state/index.d.ts +7 -0
  135. package/dist/state/index.d.ts.map +1 -0
  136. package/dist/state/index.js +7 -0
  137. package/dist/state/index.js.map +1 -0
  138. package/dist/state/initial-state.d.ts +11 -0
  139. package/dist/state/initial-state.d.ts.map +1 -0
  140. package/dist/state/initial-state.js +58 -0
  141. package/dist/state/initial-state.js.map +1 -0
  142. package/dist/state/selectors.d.ts +41 -0
  143. package/dist/state/selectors.d.ts.map +1 -0
  144. package/dist/state/selectors.js +61 -0
  145. package/dist/state/selectors.js.map +1 -0
  146. package/dist/state/state-manager.d.ts +54 -0
  147. package/dist/state/state-manager.d.ts.map +1 -0
  148. package/dist/state/state-manager.js +166 -0
  149. package/dist/state/state-manager.js.map +1 -0
  150. package/dist/types/config-types.d.ts +69 -0
  151. package/dist/types/config-types.d.ts.map +1 -0
  152. package/dist/types/config-types.js +5 -0
  153. package/dist/types/config-types.js.map +1 -0
  154. package/dist/types/extension-types.d.ts +45 -0
  155. package/dist/types/extension-types.d.ts.map +1 -0
  156. package/dist/types/extension-types.js +5 -0
  157. package/dist/types/extension-types.js.map +1 -0
  158. package/dist/types/index.d.ts +12 -0
  159. package/dist/types/index.d.ts.map +1 -0
  160. package/dist/types/index.js +12 -0
  161. package/dist/types/index.js.map +1 -0
  162. package/dist/types/lsp-types.d.ts +70 -0
  163. package/dist/types/lsp-types.d.ts.map +1 -0
  164. package/dist/types/lsp-types.js +5 -0
  165. package/dist/types/lsp-types.js.map +1 -0
  166. package/dist/types/state-types.d.ts +82 -0
  167. package/dist/types/state-types.d.ts.map +1 -0
  168. package/dist/types/state-types.js +5 -0
  169. package/dist/types/state-types.js.map +1 -0
  170. package/dist/types/vscode-types.d.ts +36 -0
  171. package/dist/types/vscode-types.d.ts.map +1 -0
  172. package/dist/types/vscode-types.js +7 -0
  173. package/dist/types/vscode-types.js.map +1 -0
  174. package/dist/ui/decorations/decoration-controller.d.ts +45 -0
  175. package/dist/ui/decorations/decoration-controller.d.ts.map +1 -0
  176. package/dist/ui/decorations/decoration-controller.js +198 -0
  177. package/dist/ui/decorations/decoration-controller.js.map +1 -0
  178. package/dist/ui/decorations/decoration-types.d.ts +28 -0
  179. package/dist/ui/decorations/decoration-types.d.ts.map +1 -0
  180. package/dist/ui/decorations/decoration-types.js +98 -0
  181. package/dist/ui/decorations/decoration-types.js.map +1 -0
  182. package/dist/ui/decorations/index.d.ts +7 -0
  183. package/dist/ui/decorations/index.d.ts.map +1 -0
  184. package/dist/ui/decorations/index.js +6 -0
  185. package/dist/ui/decorations/index.js.map +1 -0
  186. package/dist/ui/index.d.ts +7 -0
  187. package/dist/ui/index.d.ts.map +1 -0
  188. package/dist/ui/index.js +7 -0
  189. package/dist/ui/index.js.map +1 -0
  190. package/dist/ui/notifications/index.d.ts +5 -0
  191. package/dist/ui/notifications/index.d.ts.map +1 -0
  192. package/dist/ui/notifications/index.js +5 -0
  193. package/dist/ui/notifications/index.js.map +1 -0
  194. package/dist/ui/notifications/notification-service.d.ts +66 -0
  195. package/dist/ui/notifications/notification-service.d.ts.map +1 -0
  196. package/dist/ui/notifications/notification-service.js +103 -0
  197. package/dist/ui/notifications/notification-service.js.map +1 -0
  198. package/dist/ui/status-bar/index.d.ts +6 -0
  199. package/dist/ui/status-bar/index.d.ts.map +1 -0
  200. package/dist/ui/status-bar/index.js +6 -0
  201. package/dist/ui/status-bar/index.js.map +1 -0
  202. package/dist/ui/status-bar/status-bar-controller.d.ts +37 -0
  203. package/dist/ui/status-bar/status-bar-controller.d.ts.map +1 -0
  204. package/dist/ui/status-bar/status-bar-controller.js +111 -0
  205. package/dist/ui/status-bar/status-bar-controller.js.map +1 -0
  206. package/dist/ui/status-bar/status-bar-modes.d.ts +26 -0
  207. package/dist/ui/status-bar/status-bar-modes.d.ts.map +1 -0
  208. package/dist/ui/status-bar/status-bar-modes.js +97 -0
  209. package/dist/ui/status-bar/status-bar-modes.js.map +1 -0
  210. package/dist/views/base-tree-provider.d.ts +74 -0
  211. package/dist/views/base-tree-provider.d.ts.map +1 -0
  212. package/dist/views/base-tree-provider.js +95 -0
  213. package/dist/views/base-tree-provider.js.map +1 -0
  214. package/dist/views/constants-tree-provider.d.ts +112 -0
  215. package/dist/views/constants-tree-provider.d.ts.map +1 -0
  216. package/dist/views/constants-tree-provider.js +344 -0
  217. package/dist/views/constants-tree-provider.js.map +1 -0
  218. package/dist/views/files-tree-provider.d.ts +37 -0
  219. package/dist/views/files-tree-provider.d.ts.map +1 -0
  220. package/dist/views/files-tree-provider.js +98 -0
  221. package/dist/views/files-tree-provider.js.map +1 -0
  222. package/dist/views/index.d.ts +10 -0
  223. package/dist/views/index.d.ts.map +1 -0
  224. package/dist/views/index.js +10 -0
  225. package/dist/views/index.js.map +1 -0
  226. package/dist/views/patterns-tree-provider.d.ts +39 -0
  227. package/dist/views/patterns-tree-provider.d.ts.map +1 -0
  228. package/dist/views/patterns-tree-provider.js +139 -0
  229. package/dist/views/patterns-tree-provider.js.map +1 -0
  230. package/dist/views/violations-tree-provider.d.ts +46 -0
  231. package/dist/views/violations-tree-provider.d.ts.map +1 -0
  232. package/dist/views/violations-tree-provider.js +158 -0
  233. package/dist/views/violations-tree-provider.js.map +1 -0
  234. package/dist/webview/index.d.ts +7 -0
  235. package/dist/webview/index.d.ts.map +1 -0
  236. package/dist/webview/index.js +7 -0
  237. package/dist/webview/index.js.map +1 -0
  238. package/dist/webview/webview-manager.d.ts +57 -0
  239. package/dist/webview/webview-manager.d.ts.map +1 -0
  240. package/dist/webview/webview-manager.js +167 -0
  241. package/dist/webview/webview-manager.js.map +1 -0
  242. package/package.json +405 -0
  243. package/resources/drift-icon.png +0 -0
  244. package/resources/drift-icon.svg +5 -0
  245. package/resources/icons/error.svg +4 -0
  246. package/resources/icons/info.svg +4 -0
  247. package/resources/icons/lightbulb.svg +4 -0
  248. package/resources/icons/warning.svg +4 -0
  249. package/src/activation/activation-controller.ts +320 -0
  250. package/src/activation/activation-phases.ts +96 -0
  251. package/src/activation/index.ts +6 -0
  252. package/src/client/connection-config.ts +64 -0
  253. package/src/client/connection-manager.ts +263 -0
  254. package/src/client/index.ts +8 -0
  255. package/src/client/language-client-factory.ts +111 -0
  256. package/src/client/request-middleware.ts +117 -0
  257. package/src/commands/command-definitions.ts +243 -0
  258. package/src/commands/command-router.ts +194 -0
  259. package/src/commands/handlers/connection-handlers.ts +74 -0
  260. package/src/commands/handlers/constants-handlers.ts +99 -0
  261. package/src/commands/handlers/index.ts +10 -0
  262. package/src/commands/handlers/pattern-handlers.ts +167 -0
  263. package/src/commands/handlers/scan-handlers.ts +107 -0
  264. package/src/commands/handlers/ui-handlers.ts +88 -0
  265. package/src/commands/handlers/violation-handlers.ts +97 -0
  266. package/src/commands/index.ts +7 -0
  267. package/src/commands/middleware/connection-check-middleware.ts +46 -0
  268. package/src/commands/middleware/index.ts +7 -0
  269. package/src/commands/middleware/logging-middleware.ts +28 -0
  270. package/src/commands/middleware/telemetry-middleware.ts +46 -0
  271. package/src/config/config-manager.ts +213 -0
  272. package/src/config/defaults.ts +45 -0
  273. package/src/config/index.ts +7 -0
  274. package/src/config/validator.ts +118 -0
  275. package/src/extension.ts +57 -0
  276. package/src/infrastructure/disposable-manager.ts +87 -0
  277. package/src/infrastructure/event-bus.ts +121 -0
  278. package/src/infrastructure/index.ts +10 -0
  279. package/src/infrastructure/logger.ts +108 -0
  280. package/src/infrastructure/service-container.ts +123 -0
  281. package/src/state/index.ts +7 -0
  282. package/src/state/initial-state.ts +60 -0
  283. package/src/state/selectors.ts +126 -0
  284. package/src/state/state-manager.ts +198 -0
  285. package/src/types/config-types.ts +77 -0
  286. package/src/types/extension-types.ts +58 -0
  287. package/src/types/index.ts +12 -0
  288. package/src/types/lsp-types.ts +77 -0
  289. package/src/types/state-types.ts +92 -0
  290. package/src/types/vscode-types.ts +40 -0
  291. package/src/ui/decorations/decoration-controller.ts +252 -0
  292. package/src/ui/decorations/decoration-types.ts +129 -0
  293. package/src/ui/decorations/index.ts +7 -0
  294. package/src/ui/index.ts +7 -0
  295. package/src/ui/notifications/index.ts +5 -0
  296. package/src/ui/notifications/notification-service.ts +167 -0
  297. package/src/ui/status-bar/index.ts +6 -0
  298. package/src/ui/status-bar/status-bar-controller.ts +135 -0
  299. package/src/ui/status-bar/status-bar-modes.ts +119 -0
  300. package/src/views/base-tree-provider.ts +127 -0
  301. package/src/views/constants-tree-provider.ts +525 -0
  302. package/src/views/files-tree-provider.ts +140 -0
  303. package/src/views/index.ts +10 -0
  304. package/src/views/patterns-tree-provider.ts +179 -0
  305. package/src/views/violations-tree-provider.ts +210 -0
  306. package/src/webview/index.ts +7 -0
  307. package/src/webview/webview-manager.ts +238 -0
  308. package/tsconfig.json +22 -0
  309. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,179 @@
1
+ /**
2
+ * PatternsTreeProvider - Pattern tree view
3
+ *
4
+ * Single responsibility: Display patterns in a tree view.
5
+ */
6
+
7
+ import * as vscode from 'vscode';
8
+
9
+ import { BaseTreeProvider, type BaseTreeItem } from './base-tree-provider.js';
10
+
11
+ import type { PatternData, CategoryData } from '../types/index.js';
12
+ import type { LanguageClient } from 'vscode-languageclient/node';
13
+
14
+ /**
15
+ * Tree item types
16
+ */
17
+ type PatternTreeItemType = 'category' | 'pattern' | 'location';
18
+
19
+ /**
20
+ * Pattern tree item
21
+ */
22
+ export interface PatternTreeItem extends BaseTreeItem {
23
+ type: PatternTreeItemType;
24
+ data?: PatternData | CategoryData;
25
+ }
26
+
27
+ /**
28
+ * Patterns tree provider
29
+ */
30
+ export class PatternsTreeProvider extends BaseTreeProvider<PatternTreeItem> {
31
+ constructor(client: LanguageClient | null) {
32
+ super(client);
33
+ }
34
+
35
+ async getChildren(element?: PatternTreeItem): Promise<PatternTreeItem[]> {
36
+ if (!this.client) {
37
+ return [];
38
+ }
39
+
40
+ const cacheKey = this.getCacheKey(element);
41
+ const cached = this.getCached(cacheKey);
42
+ if (cached) {
43
+ return cached;
44
+ }
45
+
46
+ let items: PatternTreeItem[];
47
+
48
+ if (!element) {
49
+ // Root level: show categories
50
+ items = await this.getCategories();
51
+ } else if (element.type === 'category') {
52
+ // Category level: show patterns
53
+ items = await this.getPatterns(element.label as string);
54
+ } else {
55
+ // Pattern level: no children for now
56
+ items = [];
57
+ }
58
+
59
+ this.setCache(cacheKey, items);
60
+ return items;
61
+ }
62
+
63
+ private async getCategories(): Promise<PatternTreeItem[]> {
64
+ try {
65
+ const response = await this.sendRequest<{ categories: CategoryData[] }>(
66
+ 'drift/patterns/categories'
67
+ );
68
+
69
+ return response.categories.map((cat) => this.createCategoryItem(cat));
70
+ } catch {
71
+ return [];
72
+ }
73
+ }
74
+
75
+ private async getPatterns(category: string): Promise<PatternTreeItem[]> {
76
+ try {
77
+ const response = await this.sendRequest<{ patterns: PatternData[] }>(
78
+ 'drift/patterns/list',
79
+ { categories: [category] }
80
+ );
81
+
82
+ return response.patterns.map((pattern) => this.createPatternItem(pattern));
83
+ } catch {
84
+ return [];
85
+ }
86
+ }
87
+
88
+ private createCategoryItem(category: CategoryData): PatternTreeItem {
89
+ const item: PatternTreeItem = {
90
+ type: 'category',
91
+ label: category.name,
92
+ description: `${category.count} patterns`,
93
+ iconPath: this.getCategoryIcon(category.name),
94
+ collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
95
+ contextValue: 'category',
96
+ data: category,
97
+ };
98
+ return item;
99
+ }
100
+
101
+ private createPatternItem(pattern: PatternData): PatternTreeItem {
102
+ const item: PatternTreeItem = {
103
+ type: 'pattern',
104
+ id: pattern.id,
105
+ label: pattern.name,
106
+ description: `${pattern.locationCount} locations • ${Math.round(pattern.confidence * 100)}%`,
107
+ iconPath: this.getStatusIcon(pattern.status),
108
+ collapsibleState: vscode.TreeItemCollapsibleState.None,
109
+ contextValue: 'pattern',
110
+ tooltip: this.createPatternTooltip(pattern),
111
+ command: {
112
+ command: 'drift.showPatternDetail',
113
+ title: 'Show Pattern Details',
114
+ arguments: [pattern.id],
115
+ },
116
+ data: pattern,
117
+ };
118
+ return item;
119
+ }
120
+
121
+ private getCategoryIcon(category: string): vscode.ThemeIcon {
122
+ const iconMap: Record<string, string> = {
123
+ api: 'globe',
124
+ auth: 'lock',
125
+ security: 'shield',
126
+ errors: 'error',
127
+ logging: 'output',
128
+ 'data-access': 'database',
129
+ config: 'gear',
130
+ testing: 'beaker',
131
+ performance: 'dashboard',
132
+ components: 'symbol-class',
133
+ styling: 'paintcan',
134
+ structural: 'folder',
135
+ types: 'symbol-interface',
136
+ accessibility: 'accessibility',
137
+ documentation: 'book',
138
+ };
139
+
140
+ return new vscode.ThemeIcon(iconMap[category] || 'symbol-misc');
141
+ }
142
+
143
+ private getStatusIcon(status: string): vscode.ThemeIcon {
144
+ switch (status) {
145
+ case 'approved':
146
+ return new vscode.ThemeIcon('check', new vscode.ThemeColor('charts.green'));
147
+ case 'ignored':
148
+ return new vscode.ThemeIcon('x', new vscode.ThemeColor('charts.gray'));
149
+ default:
150
+ return new vscode.ThemeIcon('circle-outline');
151
+ }
152
+ }
153
+
154
+ private createPatternTooltip(pattern: PatternData): vscode.MarkdownString {
155
+ const md = new vscode.MarkdownString();
156
+ md.isTrusted = true;
157
+
158
+ md.appendMarkdown(`### ${pattern.name}\n\n`);
159
+ md.appendMarkdown(`**Category:** ${pattern.category}\n\n`);
160
+ md.appendMarkdown(`**Status:** ${pattern.status}\n\n`);
161
+ md.appendMarkdown(`**Confidence:** ${Math.round(pattern.confidence * 100)}%\n\n`);
162
+ md.appendMarkdown(`**Locations:** ${pattern.locationCount}\n\n`);
163
+
164
+ if (pattern.description) {
165
+ md.appendMarkdown(`---\n\n${pattern.description}\n`);
166
+ }
167
+
168
+ return md;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Factory function for creating patterns tree provider
174
+ */
175
+ export function createPatternsTreeProvider(
176
+ client: LanguageClient | null
177
+ ): PatternsTreeProvider {
178
+ return new PatternsTreeProvider(client);
179
+ }
@@ -0,0 +1,210 @@
1
+ /**
2
+ * ViolationsTreeProvider - Violations tree view
3
+ *
4
+ * Single responsibility: Display violations in a tree view.
5
+ */
6
+
7
+ import * as vscode from 'vscode';
8
+
9
+ import { BaseTreeProvider, type BaseTreeItem } from './base-tree-provider.js';
10
+
11
+ import type { ViolationData, Severity } from '../types/index.js';
12
+ import type { LanguageClient } from 'vscode-languageclient/node';
13
+
14
+ /**
15
+ * Tree item types
16
+ */
17
+ type ViolationTreeItemType = 'severity' | 'file' | 'violation';
18
+
19
+ /**
20
+ * Violation tree item
21
+ */
22
+ export interface ViolationTreeItem extends BaseTreeItem {
23
+ type: ViolationTreeItemType;
24
+ data?: ViolationData | { severity: Severity; count: number } | { file: string; count: number };
25
+ }
26
+
27
+ /**
28
+ * Violations tree provider
29
+ */
30
+ export class ViolationsTreeProvider extends BaseTreeProvider<ViolationTreeItem> {
31
+ constructor(client: LanguageClient | null) {
32
+ super(client);
33
+ }
34
+
35
+ async getChildren(element?: ViolationTreeItem): Promise<ViolationTreeItem[]> {
36
+ if (!this.client) {
37
+ return [];
38
+ }
39
+
40
+ const cacheKey = this.getCacheKey(element);
41
+ const cached = this.getCached(cacheKey);
42
+ if (cached) {
43
+ return cached;
44
+ }
45
+
46
+ let items: ViolationTreeItem[];
47
+
48
+ if (!element) {
49
+ // Root level: show by severity
50
+ items = await this.getSeverityGroups();
51
+ } else if (element.type === 'severity') {
52
+ // Severity level: show files
53
+ items = await this.getFilesBySeverity(element.label as Severity);
54
+ } else if (element.type === 'file') {
55
+ // File level: show violations
56
+ const data = element.data as { file: string };
57
+ items = await this.getViolationsInFile(data.file);
58
+ } else {
59
+ items = [];
60
+ }
61
+
62
+ this.setCache(cacheKey, items);
63
+ return items;
64
+ }
65
+
66
+ private async getSeverityGroups(): Promise<ViolationTreeItem[]> {
67
+ try {
68
+ const response = await this.sendRequest<{
69
+ bySeverity: Record<Severity, number>;
70
+ }>('drift/violations/summary');
71
+
72
+ const severities: Severity[] = ['error', 'warning', 'info', 'hint'];
73
+
74
+ return severities
75
+ .filter((s) => response.bySeverity[s] > 0)
76
+ .map((severity) =>
77
+ this.createSeverityItem(severity, response.bySeverity[severity])
78
+ );
79
+ } catch {
80
+ return [];
81
+ }
82
+ }
83
+
84
+ private async getFilesBySeverity(severity: Severity): Promise<ViolationTreeItem[]> {
85
+ try {
86
+ const response = await this.sendRequest<{
87
+ files: Array<{ file: string; count: number }>;
88
+ }>('drift/violations/byFile', { severity });
89
+
90
+ return response.files.map((f) => this.createFileItem(f.file, f.count));
91
+ } catch {
92
+ return [];
93
+ }
94
+ }
95
+
96
+ private async getViolationsInFile(file: string): Promise<ViolationTreeItem[]> {
97
+ try {
98
+ const response = await this.sendRequest<{ violations: ViolationData[] }>(
99
+ 'drift/violations/list',
100
+ { file }
101
+ );
102
+
103
+ return response.violations.map((v) => this.createViolationItem(v));
104
+ } catch {
105
+ return [];
106
+ }
107
+ }
108
+
109
+ private createSeverityItem(severity: Severity, count: number): ViolationTreeItem {
110
+ return {
111
+ type: 'severity',
112
+ label: severity.charAt(0).toUpperCase() + severity.slice(1),
113
+ description: `${count} violation${count === 1 ? '' : 's'}`,
114
+ iconPath: this.getSeverityIcon(severity),
115
+ collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
116
+ contextValue: 'severity',
117
+ data: { severity, count },
118
+ };
119
+ }
120
+
121
+ private createFileItem(file: string, count: number): ViolationTreeItem {
122
+ const fileName = file.split('/').pop() || file;
123
+
124
+ return {
125
+ type: 'file',
126
+ label: fileName,
127
+ description: `${count} violation${count === 1 ? '' : 's'}`,
128
+ iconPath: vscode.ThemeIcon.File,
129
+ collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
130
+ contextValue: 'file',
131
+ resourceUri: vscode.Uri.file(file),
132
+ data: { file, count },
133
+ };
134
+ }
135
+
136
+ private createViolationItem(violation: ViolationData): ViolationTreeItem {
137
+ const line = violation.range.start.line + 1;
138
+
139
+ return {
140
+ type: 'violation',
141
+ id: violation.id,
142
+ label: violation.message,
143
+ description: `Line ${line}`,
144
+ iconPath: this.getSeverityIcon(violation.severity),
145
+ collapsibleState: vscode.TreeItemCollapsibleState.None,
146
+ contextValue: 'violation',
147
+ tooltip: this.createViolationTooltip(violation),
148
+ command: {
149
+ command: 'vscode.open',
150
+ title: 'Go to Violation',
151
+ arguments: [
152
+ vscode.Uri.file(violation.file),
153
+ {
154
+ selection: new vscode.Range(
155
+ violation.range.start.line,
156
+ violation.range.start.character,
157
+ violation.range.end.line,
158
+ violation.range.end.character
159
+ ),
160
+ },
161
+ ],
162
+ },
163
+ data: violation,
164
+ };
165
+ }
166
+
167
+ private getSeverityIcon(severity: Severity): vscode.ThemeIcon {
168
+ switch (severity) {
169
+ case 'error':
170
+ return new vscode.ThemeIcon('error', new vscode.ThemeColor('errorForeground'));
171
+ case 'warning':
172
+ return new vscode.ThemeIcon('warning', new vscode.ThemeColor('editorWarning.foreground'));
173
+ case 'info':
174
+ return new vscode.ThemeIcon('info', new vscode.ThemeColor('editorInfo.foreground'));
175
+ case 'hint':
176
+ return new vscode.ThemeIcon('lightbulb', new vscode.ThemeColor('editorHint.foreground'));
177
+ }
178
+ }
179
+
180
+ private createViolationTooltip(violation: ViolationData): vscode.MarkdownString {
181
+ const md = new vscode.MarkdownString();
182
+ md.isTrusted = true;
183
+
184
+ md.appendMarkdown(`### ${violation.message}\n\n`);
185
+ md.appendMarkdown(`**Pattern:** \`${violation.patternId}\`\n\n`);
186
+ md.appendMarkdown(`**Severity:** ${violation.severity}\n\n`);
187
+ md.appendMarkdown(`**File:** ${violation.file}\n\n`);
188
+ md.appendMarkdown(`**Line:** ${violation.range.start.line + 1}\n\n`);
189
+
190
+ md.appendMarkdown(`---\n\n`);
191
+
192
+ if (violation.hasQuickFix) {
193
+ md.appendMarkdown(`$(lightbulb) Quick fix available\n\n`);
194
+ }
195
+ if (violation.aiExplainAvailable) {
196
+ md.appendMarkdown(`$(sparkle) AI explanation available\n\n`);
197
+ }
198
+
199
+ return md;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Factory function for creating violations tree provider
205
+ */
206
+ export function createViolationsTreeProvider(
207
+ client: LanguageClient | null
208
+ ): ViolationsTreeProvider {
209
+ return new ViolationsTreeProvider(client);
210
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Webview module exports
3
+ *
4
+ * Webview panels for rich UI experiences.
5
+ */
6
+
7
+ export { WebviewManager, createWebviewManager } from './webview-manager.js';
@@ -0,0 +1,238 @@
1
+ /**
2
+ * WebviewManager - Webview panel management
3
+ *
4
+ * Single responsibility: Create and manage webview panels.
5
+ */
6
+
7
+ import * as vscode from 'vscode';
8
+
9
+ import type { Logger } from '../infrastructure/index.js';
10
+
11
+ /**
12
+ * Webview panel configuration
13
+ */
14
+ export interface WebviewPanelConfig {
15
+ viewType: string;
16
+ title: string;
17
+ localResourceRoots?: string[];
18
+ enableScripts?: boolean;
19
+ retainContextWhenHidden?: boolean;
20
+ }
21
+
22
+ /**
23
+ * Message handler type
24
+ */
25
+ export type MessageHandler = (data: unknown) => Promise<unknown>;
26
+
27
+ /**
28
+ * Webview manager for creating and managing panels
29
+ */
30
+ export class WebviewManager implements vscode.Disposable {
31
+ private readonly panels = new Map<string, vscode.WebviewPanel>();
32
+ private readonly messageHandlers = new Map<string, Map<string, MessageHandler>>();
33
+ private readonly disposables: vscode.Disposable[] = [];
34
+
35
+ constructor(
36
+ private readonly context: vscode.ExtensionContext,
37
+ private readonly logger: Logger
38
+ ) {}
39
+
40
+ /**
41
+ * Show or create a webview panel
42
+ */
43
+ async showPanel(
44
+ config: WebviewPanelConfig,
45
+ handlers?: Record<string, MessageHandler>
46
+ ): Promise<vscode.WebviewPanel> {
47
+ // Reuse existing panel if available
48
+ const existing = this.panels.get(config.viewType);
49
+ if (existing) {
50
+ existing.reveal();
51
+ return existing;
52
+ }
53
+
54
+ // Create new panel
55
+ const panel = vscode.window.createWebviewPanel(
56
+ config.viewType,
57
+ config.title,
58
+ vscode.ViewColumn.Beside,
59
+ {
60
+ enableScripts: config.enableScripts ?? true,
61
+ retainContextWhenHidden: config.retainContextWhenHidden ?? false,
62
+ localResourceRoots: this.getLocalResourceRoots(config),
63
+ }
64
+ );
65
+
66
+ // Set up message handling
67
+ if (handlers) {
68
+ this.messageHandlers.set(config.viewType, new Map(Object.entries(handlers)));
69
+ }
70
+
71
+ panel.webview.onDidReceiveMessage(
72
+ (message) => this.handleMessage(config.viewType, panel, message),
73
+ undefined,
74
+ this.disposables
75
+ );
76
+
77
+ // Handle disposal
78
+ panel.onDidDispose(
79
+ () => {
80
+ this.panels.delete(config.viewType);
81
+ this.messageHandlers.delete(config.viewType);
82
+ },
83
+ undefined,
84
+ this.disposables
85
+ );
86
+
87
+ // Set content
88
+ panel.webview.html = await this.getWebviewContent(panel.webview, config);
89
+
90
+ this.panels.set(config.viewType, panel);
91
+ return panel;
92
+ }
93
+
94
+ /**
95
+ * Send a message to a webview
96
+ */
97
+ postMessage(viewType: string, message: unknown): boolean {
98
+ const panel = this.panels.get(viewType);
99
+ if (panel) {
100
+ panel.webview.postMessage(message);
101
+ return true;
102
+ }
103
+ return false;
104
+ }
105
+
106
+ /**
107
+ * Close a webview panel
108
+ */
109
+ closePanel(viewType: string): void {
110
+ const panel = this.panels.get(viewType);
111
+ if (panel) {
112
+ panel.dispose();
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Dispose all panels
118
+ */
119
+ dispose(): void {
120
+ for (const panel of this.panels.values()) {
121
+ panel.dispose();
122
+ }
123
+ this.panels.clear();
124
+ this.messageHandlers.clear();
125
+
126
+ for (const d of this.disposables) {
127
+ d.dispose();
128
+ }
129
+ }
130
+
131
+ private getLocalResourceRoots(config: WebviewPanelConfig): vscode.Uri[] {
132
+ const roots = [
133
+ vscode.Uri.joinPath(this.context.extensionUri, 'dist', 'webview'),
134
+ vscode.Uri.joinPath(this.context.extensionUri, 'resources'),
135
+ ];
136
+
137
+ if (config.localResourceRoots) {
138
+ for (const root of config.localResourceRoots) {
139
+ roots.push(vscode.Uri.joinPath(this.context.extensionUri, root));
140
+ }
141
+ }
142
+
143
+ return roots;
144
+ }
145
+
146
+ private async handleMessage(
147
+ viewType: string,
148
+ panel: vscode.WebviewPanel,
149
+ message: { type: string; requestId?: string; data?: unknown }
150
+ ): Promise<void> {
151
+ const handlers = this.messageHandlers.get(viewType);
152
+ const handler = handlers?.get(message.type);
153
+
154
+ if (!handler) {
155
+ this.logger.warn(`No handler for message type: ${message.type}`);
156
+ return;
157
+ }
158
+
159
+ try {
160
+ const result = await handler(message.data);
161
+
162
+ if (message.requestId) {
163
+ panel.webview.postMessage({
164
+ type: `${message.type}:response`,
165
+ requestId: message.requestId,
166
+ data: result,
167
+ });
168
+ }
169
+ } catch (error) {
170
+ this.logger.error(`Error handling message ${message.type}:`, error);
171
+
172
+ if (message.requestId) {
173
+ panel.webview.postMessage({
174
+ type: `${message.type}:error`,
175
+ requestId: message.requestId,
176
+ error: error instanceof Error ? error.message : String(error),
177
+ });
178
+ }
179
+ }
180
+ }
181
+
182
+ private async getWebviewContent(
183
+ webview: vscode.Webview,
184
+ config: WebviewPanelConfig
185
+ ): Promise<string> {
186
+ const scriptUri = webview.asWebviewUri(
187
+ vscode.Uri.joinPath(this.context.extensionUri, 'dist', 'webview', `${config.viewType}.js`)
188
+ );
189
+ const styleUri = webview.asWebviewUri(
190
+ vscode.Uri.joinPath(this.context.extensionUri, 'dist', 'webview', `${config.viewType}.css`)
191
+ );
192
+
193
+ const nonce = this.generateNonce();
194
+
195
+ return `<!DOCTYPE html>
196
+ <html lang="en">
197
+ <head>
198
+ <meta charset="UTF-8">
199
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
200
+ <meta http-equiv="Content-Security-Policy" content="
201
+ default-src 'none';
202
+ style-src ${webview.cspSource} 'unsafe-inline';
203
+ script-src 'nonce-${nonce}';
204
+ img-src ${webview.cspSource} https: data:;
205
+ font-src ${webview.cspSource};
206
+ ">
207
+ <link href="${styleUri}" rel="stylesheet">
208
+ <title>${config.title}</title>
209
+ </head>
210
+ <body>
211
+ <div id="root"></div>
212
+ <script nonce="${nonce}">
213
+ window.vscode = acquireVsCodeApi();
214
+ </script>
215
+ <script nonce="${nonce}" src="${scriptUri}"></script>
216
+ </body>
217
+ </html>`;
218
+ }
219
+
220
+ private generateNonce(): string {
221
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
222
+ let nonce = '';
223
+ for (let i = 0; i < 32; i++) {
224
+ nonce += chars.charAt(Math.floor(Math.random() * chars.length));
225
+ }
226
+ return nonce;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Factory function for creating webview manager
232
+ */
233
+ export function createWebviewManager(
234
+ context: vscode.ExtensionContext,
235
+ logger: Logger
236
+ ): WebviewManager {
237
+ return new WebviewManager(context, logger);
238
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "module": "ESNext",
7
+ "moduleResolution": "bundler",
8
+ "target": "ES2022",
9
+ "lib": ["ES2022"],
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true,
17
+ "noEmit": false,
18
+ "verbatimModuleSyntax": false
19
+ },
20
+ "include": ["src/**/*"],
21
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
22
+ }