statusbar-quick-actions 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/history.ts ADDED
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Command history management for StatusBar Quick Actions
3
+ */
4
+
5
+ import { CommandHistoryEntry, ExecutionResult } from "./types";
6
+
7
+ export class HistoryManager {
8
+ private history: CommandHistoryEntry[] = [];
9
+ private maxEntries = 100;
10
+
11
+ /**
12
+ * Add a command execution to history
13
+ */
14
+ public addEntry(
15
+ buttonId: string,
16
+ command: string,
17
+ result: ExecutionResult,
18
+ ): void {
19
+ const entry: CommandHistoryEntry = {
20
+ id: this.generateId(),
21
+ buttonId,
22
+ command,
23
+ result,
24
+ };
25
+
26
+ this.history.unshift(entry);
27
+
28
+ // Limit history size
29
+ if (this.history.length > this.maxEntries) {
30
+ this.history = this.history.slice(0, this.maxEntries);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Get all history entries
36
+ */
37
+ public getHistory(): CommandHistoryEntry[] {
38
+ return [...this.history];
39
+ }
40
+
41
+ /**
42
+ * Get history entries for a specific button
43
+ */
44
+ public getHistoryForButton(buttonId: string): CommandHistoryEntry[] {
45
+ return this.history.filter((entry) => entry.buttonId === buttonId);
46
+ }
47
+
48
+ /**
49
+ * Clear all history
50
+ */
51
+ public clearHistory(): void {
52
+ this.history = [];
53
+ }
54
+
55
+ /**
56
+ * Get statistics
57
+ */
58
+ public getStatistics(): {
59
+ totalCommands: number;
60
+ successfulCommands: number;
61
+ failedCommands: number;
62
+ averageExecutionTime: number;
63
+ } {
64
+ const totalCommands = this.history.length;
65
+ const successfulCommands = this.history.filter(
66
+ (entry) => entry.result.code === 0,
67
+ ).length;
68
+ const failedCommands = totalCommands - successfulCommands;
69
+
70
+ const executionTimes = this.history
71
+ .filter((entry) => entry.result.duration > 0)
72
+ .map((entry) => entry.result.duration);
73
+
74
+ const averageExecutionTime =
75
+ executionTimes.length > 0
76
+ ? executionTimes.reduce((sum, time) => sum + time, 0) /
77
+ executionTimes.length
78
+ : 0;
79
+
80
+ return {
81
+ totalCommands,
82
+ successfulCommands,
83
+ failedCommands,
84
+ averageExecutionTime: Math.round(averageExecutionTime),
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Search history entries
90
+ */
91
+ public searchHistory(query: string): CommandHistoryEntry[] {
92
+ const lowerQuery = query.toLowerCase();
93
+ return this.history.filter(
94
+ (entry) =>
95
+ entry.command.toLowerCase().includes(lowerQuery) ||
96
+ entry.buttonId.toLowerCase().includes(lowerQuery) ||
97
+ entry.result.stdout.toLowerCase().includes(lowerQuery),
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Export history to JSON
103
+ */
104
+ public exportHistory(): string {
105
+ return JSON.stringify(this.history, null, 2);
106
+ }
107
+
108
+ /**
109
+ * Import history from JSON
110
+ */
111
+ public importHistory(jsonData: string): void {
112
+ try {
113
+ const importedEntries = JSON.parse(jsonData) as CommandHistoryEntry[];
114
+ this.history = [...importedEntries, ...this.history];
115
+
116
+ // Limit after import
117
+ if (this.history.length > this.maxEntries) {
118
+ this.history = this.history.slice(0, this.maxEntries);
119
+ }
120
+ } catch (error) {
121
+ console.error("Failed to import history:", error);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Set maximum number of entries to keep
127
+ */
128
+ public setMaxEntries(maxEntries: number): void {
129
+ this.maxEntries = maxEntries;
130
+
131
+ // Trim history if necessary
132
+ if (this.history.length > maxEntries) {
133
+ this.history = this.history.slice(0, maxEntries);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Generate unique ID for history entry
139
+ */
140
+ private generateId(): string {
141
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
142
+ }
143
+
144
+ /**
145
+ * Get most frequently used commands
146
+ */
147
+ public getMostUsedCommands(
148
+ limit = 10,
149
+ ): { command: string; count: number; buttonId: string }[] {
150
+ const commandCounts = new Map<
151
+ string,
152
+ { count: number; buttonId: string }
153
+ >();
154
+
155
+ this.history.forEach((entry) => {
156
+ const key = entry.command;
157
+ const existing = commandCounts.get(key);
158
+
159
+ if (existing) {
160
+ existing.count++;
161
+ } else {
162
+ commandCounts.set(key, { count: 1, buttonId: entry.buttonId });
163
+ }
164
+ });
165
+
166
+ return Array.from(commandCounts.entries())
167
+ .map(([command, data]) => ({
168
+ command,
169
+ count: data.count,
170
+ buttonId: data.buttonId,
171
+ }))
172
+ .sort((a, b) => b.count - a.count)
173
+ .slice(0, limit);
174
+ }
175
+ }
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Material Design Icons to VSCode Codicons mapping manager
3
+ */
4
+
5
+ import { IconConfig } from "./types";
6
+
7
+ /**
8
+ * Manages Material Design Icons mapping to VSCode Codicons
9
+ */
10
+ export class MaterialIconManager {
11
+ private iconMapping: Map<string, string>;
12
+
13
+ constructor() {
14
+ this.iconMapping = this.buildIconMapping();
15
+ }
16
+
17
+ /**
18
+ * Resolve icon configuration to a VSCode icon ID
19
+ */
20
+ public resolveIcon(config: IconConfig): string {
21
+ if (!config.library || config.library === "vscode") {
22
+ // VSCode Codicons - use as is
23
+ return config.id;
24
+ }
25
+
26
+ // Material Design Icons - resolve to Codicon equivalent
27
+ const resolvedIcon = this.getMaterialIcon(config.id, config.variant);
28
+ if (resolvedIcon) {
29
+ return resolvedIcon;
30
+ }
31
+
32
+ // Fallback chain: try base icon name without variant → generic icon → original ID
33
+ const baseIcon = this.iconMapping.get(config.id);
34
+ return baseIcon || config.id;
35
+ }
36
+
37
+ /**
38
+ * Get Material icon with variant support
39
+ */
40
+ public getMaterialIcon(name: string, variant?: string): string | null {
41
+ // Try to find icon with variant first
42
+ if (variant) {
43
+ const variantKey = `${name}:${variant}`;
44
+ if (this.iconMapping.has(variantKey)) {
45
+ return this.iconMapping.get(variantKey)!;
46
+ }
47
+ }
48
+
49
+ // Fall back to base icon name
50
+ return this.iconMapping.get(name) || null;
51
+ }
52
+
53
+ /**
54
+ * Check if a Material icon is valid and mapped
55
+ */
56
+ public isValidMaterialIcon(name: string): boolean {
57
+ return this.iconMapping.has(name);
58
+ }
59
+
60
+ /**
61
+ * Get all available Material icon names
62
+ */
63
+ public getAvailableIcons(): string[] {
64
+ return Array.from(this.iconMapping.keys());
65
+ }
66
+
67
+ /**
68
+ * Get suggested icon based on keyword
69
+ */
70
+ public suggestIcon(keyword: string): string[] {
71
+ const lowerKeyword = keyword.toLowerCase();
72
+ return this.getAvailableIcons().filter((icon) =>
73
+ icon.toLowerCase().includes(lowerKeyword),
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Build the icon mapping dictionary
79
+ * Maps Material Design Icon names to VSCode Codicon equivalents
80
+ */
81
+ private buildIconMapping(): Map<string, string> {
82
+ return new Map([
83
+ // Actions & Controls
84
+ ["play_arrow", "play"],
85
+ ["play_circle", "play-circle"],
86
+ ["pause", "debug-pause"],
87
+ ["pause_circle", "debug-pause"],
88
+ ["stop", "debug-stop"],
89
+ ["stop_circle", "stop-circle"],
90
+ ["refresh", "refresh"],
91
+ ["sync", "sync"],
92
+ ["cached", "refresh"],
93
+ ["replay", "debug-restart"],
94
+ ["undo", "discard"],
95
+ ["redo", "redo"],
96
+ ["save", "save"],
97
+ ["save_alt", "save-all"],
98
+ ["delete", "trash"],
99
+ ["delete_forever", "trash"],
100
+ ["delete_outline", "trash"],
101
+ ["add", "add"],
102
+ ["add_circle", "add"],
103
+ ["add_circle_outline", "add"],
104
+ ["remove", "remove"],
105
+ ["remove_circle", "remove"],
106
+ ["remove_circle_outline", "remove"],
107
+ ["close", "close"],
108
+ ["cancel", "close"],
109
+ ["check", "check"],
110
+ ["check_circle", "pass"],
111
+ ["check_circle_outline", "pass-filled"],
112
+ ["done", "check"],
113
+ ["done_all", "check-all"],
114
+ ["clear", "clear-all"],
115
+
116
+ // Files & Folders
117
+ ["folder", "folder"],
118
+ ["folder_open", "folder-opened"],
119
+ ["folder_outlined", "folder"],
120
+ ["create_new_folder", "new-folder"],
121
+ ["file", "file"],
122
+ ["insert_drive_file", "file-text"],
123
+ ["description", "file-code"],
124
+ ["article", "file-text"],
125
+ ["note", "note"],
126
+ ["draft", "edit"],
127
+ ["upload_file", "cloud-upload"],
128
+ ["download", "cloud-download"],
129
+ ["attachment", "link"],
130
+
131
+ // Git & Version Control
132
+ ["git", "git-commit"],
133
+ ["source", "source-control"],
134
+ ["commit", "git-commit"],
135
+ ["merge", "git-merge"],
136
+ ["pull_request", "git-pull-request"],
137
+ ["branch", "git-branch"],
138
+ ["fork", "repo-forked"],
139
+ ["compare_arrows", "git-compare"],
140
+
141
+ // Development & Code
142
+ ["code", "code"],
143
+ ["terminal", "terminal"],
144
+ ["console", "debug-console"],
145
+ ["bug_report", "bug"],
146
+ ["build", "tools"],
147
+ ["settings", "gear"],
148
+ ["settings_applications", "settings-gear"],
149
+ ["tune", "settings"],
150
+ ["construction", "tools"],
151
+ ["extension", "extensions"],
152
+ ["api", "symbol-interface"],
153
+ ["functions", "symbol-method"],
154
+ ["data_object", "json"],
155
+ ["schema", "symbol-structure"],
156
+ ["database", "database"],
157
+ ["storage", "database"],
158
+ ["table_chart", "table"],
159
+
160
+ // Navigation & UI
161
+ ["home", "home"],
162
+ ["arrow_back", "arrow-left"],
163
+ ["arrow_forward", "arrow-right"],
164
+ ["arrow_upward", "arrow-up"],
165
+ ["arrow_downward", "arrow-down"],
166
+ ["expand_more", "chevron-down"],
167
+ ["expand_less", "chevron-up"],
168
+ ["chevron_right", "chevron-right"],
169
+ ["chevron_left", "chevron-left"],
170
+ ["menu", "menu"],
171
+ ["more_vert", "kebab-vertical"],
172
+ ["more_horiz", "ellipsis"],
173
+ ["apps", "apps"],
174
+ ["dashboard", "dashboard"],
175
+ ["view_module", "layout"],
176
+ ["view_list", "list-unordered"],
177
+ ["view_compact", "list-tree"],
178
+ ["grid_view", "gripper"],
179
+
180
+ // Search & Filter
181
+ ["search", "search"],
182
+ ["filter_list", "filter"],
183
+ ["filter_alt", "filter-filled"],
184
+ ["sort", "symbol-namespace"],
185
+ ["find_in_page", "search"],
186
+ ["find_replace", "find-replace"],
187
+ ["zoom_in", "zoom-in"],
188
+ ["zoom_out", "zoom-out"],
189
+
190
+ // Status & Indicators
191
+ ["error", "error"],
192
+ ["error_outline", "error"],
193
+ ["warning", "warning"],
194
+ ["warning_amber", "warning"],
195
+ ["info", "info"],
196
+ ["info_outline", "info"],
197
+ ["help", "question"],
198
+ ["help_outline", "question"],
199
+ ["priority_high", "warning"],
200
+ ["report_problem", "warning"],
201
+ ["verified", "verified"],
202
+ ["verified_user", "verified-filled"],
203
+ ["check_circle", "pass"],
204
+ ["check_circle_outline", "pass-filled"],
205
+ ["cancel", "error"],
206
+ ["unpublished", "circle-slash"],
207
+ ["block", "circle-slash"],
208
+ ["do_not_disturb", "circle-slash"],
209
+
210
+ // Communication
211
+ ["mail", "mail"],
212
+ ["email", "mail"],
213
+ ["send", "mail"],
214
+ ["inbox", "inbox"],
215
+ ["chat", "comment"],
216
+ ["comment", "comment-discussion"],
217
+ ["message", "comment"],
218
+ ["notification", "bell"],
219
+ ["notifications", "bell"],
220
+ ["notifications_active", "bell-dot"],
221
+
222
+ // Media & Player
223
+ ["play", "play"],
224
+ ["pause", "debug-pause"],
225
+ ["stop", "debug-stop"],
226
+ ["skip_next", "debug-step-over"],
227
+ ["skip_previous", "debug-step-back"],
228
+ ["fast_forward", "run-all"],
229
+ ["fast_rewind", "debug-reverse-continue"],
230
+ ["volume_up", "unmute"],
231
+ ["volume_off", "mute"],
232
+ ["mic", "mic"],
233
+ ["mic_off", "mic-filled"],
234
+ ["videocam", "device-camera-video"],
235
+ ["image", "file-media"],
236
+ ["photo", "file-media"],
237
+
238
+ // Editing & Format
239
+ ["edit", "edit"],
240
+ ["create", "edit"],
241
+ ["mode_edit", "edit"],
242
+ ["content_copy", "copy"],
243
+ ["content_cut", "scissors"],
244
+ ["content_paste", "clippy"],
245
+ ["format_bold", "bold"],
246
+ ["format_italic", "italic"],
247
+ ["format_underlined", "underline"],
248
+ ["format_list_bulleted", "list-unordered"],
249
+ ["format_list_numbered", "list-ordered"],
250
+ ["format_quote", "quote"],
251
+ ["text_format", "text-size"],
252
+
253
+ // Time & Calendar
254
+ ["access_time", "watch"],
255
+ ["schedule", "watch"],
256
+ ["today", "calendar"],
257
+ ["event", "calendar"],
258
+ ["date_range", "calendar"],
259
+ ["history", "history"],
260
+ ["update", "sync"],
261
+ ["timer", "watch"],
262
+
263
+ // Security & Access
264
+ ["lock", "lock"],
265
+ ["lock_open", "unlock"],
266
+ ["security", "shield"],
267
+ ["verified_user", "shield"],
268
+ ["admin_panel_settings", "settings-gear"],
269
+ ["key", "key"],
270
+ ["vpn_key", "key"],
271
+ ["password", "key"],
272
+ ["visibility", "eye"],
273
+ ["visibility_off", "eye-closed"],
274
+
275
+ // People & Account
276
+ ["person", "person"],
277
+ ["account_circle", "account"],
278
+ ["group", "organization"],
279
+ ["people", "organization"],
280
+ ["contacts", "organization"],
281
+ ["badge", "pass"],
282
+ ["supervisor_account", "organization"],
283
+
284
+ // Location & Navigation
285
+ ["location_on", "location"],
286
+ ["place", "location"],
287
+ ["map", "location"],
288
+ ["navigation", "location"],
289
+ ["explore", "compass"],
290
+ ["public", "globe"],
291
+ ["language", "globe"],
292
+
293
+ // Device & Hardware
294
+ ["computer", "device-desktop"],
295
+ ["laptop", "device-desktop"],
296
+ ["phone", "device-mobile"],
297
+ ["tablet", "device-mobile"],
298
+ ["watch", "watch"],
299
+ ["tv", "device-desktop"],
300
+ ["keyboard", "terminal"],
301
+ ["mouse", "terminal"],
302
+
303
+ // File Types & Extensions
304
+ ["javascript", "symbol-method"],
305
+ ["typescript", "symbol-method"],
306
+ ["python", "file-code"],
307
+ ["java", "file-code"],
308
+ ["html", "file-code"],
309
+ ["css", "symbol-color"],
310
+ ["json", "json"],
311
+ ["xml", "file-code"],
312
+ ["markdown", "markdown"],
313
+ ["yaml", "file-code"],
314
+
315
+ // Cloud & Network
316
+ ["cloud", "cloud"],
317
+ ["cloud_upload", "cloud-upload"],
318
+ ["cloud_download", "cloud-download"],
319
+ ["cloud_done", "cloud"],
320
+ ["cloud_off", "cloud"],
321
+ ["wifi", "radio-tower"],
322
+ ["wifi_off", "radio-tower"],
323
+ ["signal", "radio-tower"],
324
+ ["network_check", "radio-tower"],
325
+
326
+ // Misc Icons
327
+ ["star", "star-full"],
328
+ ["star_outline", "star-empty"],
329
+ ["star_half", "star-half"],
330
+ ["favorite", "heart"],
331
+ ["favorite_outline", "heart"],
332
+ ["bookmark", "bookmark"],
333
+ ["bookmark_outline", "bookmark"],
334
+ ["label", "tag"],
335
+ ["local_offer", "tag"],
336
+ ["sell", "tag"],
337
+ ["shopping_cart", "symbol-event"],
338
+ ["lightbulb", "lightbulb"],
339
+ ["lightbulb_outline", "lightbulb"],
340
+ ["flash_on", "zap"],
341
+ ["power", "circle-filled"],
342
+ ["power_settings_new", "circle-filled"],
343
+ ]);
344
+ }
345
+
346
+ /**
347
+ * Get icon size CSS class (for future use with custom styling)
348
+ */
349
+ public getIconSizeClass(size?: "small" | "medium" | "large"): string {
350
+ switch (size) {
351
+ case "small":
352
+ return "icon-small";
353
+ case "large":
354
+ return "icon-large";
355
+ case "medium":
356
+ default:
357
+ return "icon-medium";
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Get recommended icon for common actions
363
+ */
364
+ public getRecommendedIcon(action: string): string {
365
+ const recommendations: Record<string, string> = {
366
+ build: "tools",
367
+ test: "beaker",
368
+ deploy: "rocket",
369
+ debug: "bug",
370
+ run: "play",
371
+ start: "play",
372
+ stop: "debug-stop",
373
+ restart: "debug-restart",
374
+ install: "cloud-download",
375
+ update: "sync",
376
+ clean: "trash",
377
+ format: "text-size",
378
+ lint: "checklist",
379
+ commit: "git-commit",
380
+ push: "cloud-upload",
381
+ pull: "cloud-download",
382
+ branch: "git-branch",
383
+ merge: "git-merge",
384
+ };
385
+
386
+ return recommendations[action.toLowerCase()] || "gear";
387
+ }
388
+ }