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/.github/FUNDING.yml +3 -0
- package/.vscodeignore +11 -0
- package/CLAUDE.md +230 -0
- package/LICENSE +21 -0
- package/README.md +529 -0
- package/assets/icon.png +0 -0
- package/bun.lock +908 -0
- package/docs/PERFORMANCE_OPTIMIZATIONS.md +240 -0
- package/docs/PRESET_AND_DYNAMIC_LABELS.md +536 -0
- package/docs/SAMPLE-CONFIGURATIONS.md +973 -0
- package/eslint.config.mjs +41 -0
- package/package.json +605 -0
- package/src/config-cli.ts +1287 -0
- package/src/configuration.ts +530 -0
- package/src/dynamic-label.ts +360 -0
- package/src/executor.ts +406 -0
- package/src/extension.ts +1754 -0
- package/src/history.ts +175 -0
- package/src/material-icons.ts +388 -0
- package/src/notifications.ts +189 -0
- package/src/output-panel.ts +403 -0
- package/src/preset-manager.ts +406 -0
- package/src/theme.ts +318 -0
- package/src/types.ts +368 -0
- package/src/utils/debounce.ts +91 -0
- package/src/visibility.ts +283 -0
- package/tsconfig.dev.json +10 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification management for StatusBar Quick Actions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as vscode from "vscode";
|
|
6
|
+
import { NotificationConfig, ExecutionResult } from "./types";
|
|
7
|
+
|
|
8
|
+
export class NotificationManager {
|
|
9
|
+
private config: NotificationConfig;
|
|
10
|
+
|
|
11
|
+
constructor(config: NotificationConfig) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Show success notification
|
|
17
|
+
*/
|
|
18
|
+
public showSuccess(buttonText: string, result: ExecutionResult): void {
|
|
19
|
+
if (!this.config.showSuccess) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const message = this.getSuccessMessage(buttonText, result);
|
|
24
|
+
|
|
25
|
+
vscode.window
|
|
26
|
+
.showInformationMessage(message, "View Details")
|
|
27
|
+
.then((selection) => {
|
|
28
|
+
if (selection === "View Details") {
|
|
29
|
+
this.showDetails(result);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Show error notification
|
|
36
|
+
*/
|
|
37
|
+
public showError(buttonText: string, error: string | Error): void {
|
|
38
|
+
if (!this.config.showError) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const message = `${buttonText}: ${error instanceof Error ? error.message : error}`;
|
|
43
|
+
|
|
44
|
+
vscode.window
|
|
45
|
+
.showErrorMessage(message, "View Details")
|
|
46
|
+
.then((selection) => {
|
|
47
|
+
if (selection === "View Details") {
|
|
48
|
+
const errorDetails =
|
|
49
|
+
error instanceof Error ? error.stack || error.message : error;
|
|
50
|
+
vscode.window.showErrorMessage(errorDetails, { modal: true });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Show progress notification
|
|
57
|
+
*/
|
|
58
|
+
public showProgress(buttonText: string): void {
|
|
59
|
+
if (!this.config.showProgress) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
vscode.window.withProgress(
|
|
64
|
+
{
|
|
65
|
+
location: vscode.ProgressLocation.Notification,
|
|
66
|
+
title: `Executing: ${buttonText}`,
|
|
67
|
+
cancellable: false,
|
|
68
|
+
},
|
|
69
|
+
async (_progress) => {
|
|
70
|
+
// Progress will be updated by the caller
|
|
71
|
+
return new Promise<void>((resolve) => {
|
|
72
|
+
// This would be resolved when the command completes
|
|
73
|
+
resolve();
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Show warning notification
|
|
81
|
+
*/
|
|
82
|
+
public showWarning(message: string, details?: string): void {
|
|
83
|
+
const fullMessage = details ? `${message}\n${details}` : message;
|
|
84
|
+
vscode.window.showWarningMessage(fullMessage);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Show info notification
|
|
89
|
+
*/
|
|
90
|
+
public showInfo(message: string, details?: string): void {
|
|
91
|
+
const fullMessage = details ? `${message}\n${details}` : message;
|
|
92
|
+
vscode.window.showInformationMessage(fullMessage);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Show notification with custom actions
|
|
97
|
+
*/
|
|
98
|
+
public showCustomNotification(
|
|
99
|
+
type: "info" | "warning" | "error",
|
|
100
|
+
message: string,
|
|
101
|
+
actions: string[],
|
|
102
|
+
): Thenable<string | undefined> {
|
|
103
|
+
switch (type) {
|
|
104
|
+
case "error":
|
|
105
|
+
return vscode.window.showErrorMessage(message, ...actions);
|
|
106
|
+
case "warning":
|
|
107
|
+
return vscode.window.showWarningMessage(message, ...actions);
|
|
108
|
+
default:
|
|
109
|
+
return vscode.window.showInformationMessage(message, ...actions);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Update configuration
|
|
115
|
+
*/
|
|
116
|
+
public updateConfig(config: NotificationConfig): void {
|
|
117
|
+
this.config = config;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get success message
|
|
122
|
+
*/
|
|
123
|
+
private getSuccessMessage(
|
|
124
|
+
buttonText: string,
|
|
125
|
+
result: ExecutionResult,
|
|
126
|
+
): string {
|
|
127
|
+
const duration = result.duration ? ` in ${result.duration}ms` : "";
|
|
128
|
+
const output =
|
|
129
|
+
this.config.includeOutput && result.stdout
|
|
130
|
+
? `: ${result.stdout.substring(0, 50)}${result.stdout.length > 50 ? "..." : ""}`
|
|
131
|
+
: "";
|
|
132
|
+
|
|
133
|
+
return `✅ ${buttonText} completed successfully${duration}${output}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Show execution details
|
|
138
|
+
*/
|
|
139
|
+
private showDetails(result: ExecutionResult): void {
|
|
140
|
+
const output = `Command Output:\n${result.stdout}\n\nErrors:\n${result.stderr}`;
|
|
141
|
+
vscode.window.showInformationMessage(output, { modal: true });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Show progress update
|
|
146
|
+
*/
|
|
147
|
+
public updateProgress(_increment: number, _message?: string): void {
|
|
148
|
+
// This would be called to update an existing progress notification
|
|
149
|
+
// Implementation depends on how progress is tracked
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Dismiss notifications
|
|
154
|
+
*/
|
|
155
|
+
public dismissAll(): void {
|
|
156
|
+
// VSCode doesn't provide a way to dismiss specific notifications
|
|
157
|
+
// This would be a no-op for now
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if notifications are enabled
|
|
162
|
+
*/
|
|
163
|
+
public areNotificationsEnabled(): boolean {
|
|
164
|
+
return (
|
|
165
|
+
this.config.showSuccess ||
|
|
166
|
+
this.config.showError ||
|
|
167
|
+
this.config.showProgress
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get notification statistics
|
|
173
|
+
*/
|
|
174
|
+
public getStats(): {
|
|
175
|
+
successEnabled: boolean;
|
|
176
|
+
errorEnabled: boolean;
|
|
177
|
+
progressEnabled: boolean;
|
|
178
|
+
position: string;
|
|
179
|
+
duration: number;
|
|
180
|
+
} {
|
|
181
|
+
return {
|
|
182
|
+
successEnabled: this.config.showSuccess,
|
|
183
|
+
errorEnabled: this.config.showError,
|
|
184
|
+
progressEnabled: this.config.showProgress,
|
|
185
|
+
position: this.config.position,
|
|
186
|
+
duration: this.config.duration,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output panel management for StatusBar Quick Actions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as vscode from "vscode";
|
|
6
|
+
import { OutputPanelConfig } from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Manages output panels for command execution with streaming support
|
|
10
|
+
*/
|
|
11
|
+
export class OutputPanelManager {
|
|
12
|
+
private panels: Map<string, vscode.OutputChannel>;
|
|
13
|
+
private sharedPanel: vscode.OutputChannel | null;
|
|
14
|
+
private config: OutputPanelConfig;
|
|
15
|
+
private outputHistory: Map<string, string[]>;
|
|
16
|
+
|
|
17
|
+
constructor(config: OutputPanelConfig) {
|
|
18
|
+
this.config = config;
|
|
19
|
+
this.panels = new Map();
|
|
20
|
+
this.sharedPanel = null;
|
|
21
|
+
this.outputHistory = new Map();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get or create an output panel for a button
|
|
26
|
+
*/
|
|
27
|
+
public getOrCreatePanel(
|
|
28
|
+
buttonId: string,
|
|
29
|
+
buttonName: string,
|
|
30
|
+
): vscode.OutputChannel {
|
|
31
|
+
if (this.config.mode === "shared") {
|
|
32
|
+
return this.getSharedPanel();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Per-button mode
|
|
36
|
+
if (!this.panels.has(buttonId)) {
|
|
37
|
+
const panel = vscode.window.createOutputChannel(
|
|
38
|
+
`StatusBar: ${buttonName}`,
|
|
39
|
+
);
|
|
40
|
+
this.panels.set(buttonId, panel);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return this.panels.get(buttonId)!;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get or create the shared output panel
|
|
48
|
+
*/
|
|
49
|
+
private getSharedPanel(): vscode.OutputChannel {
|
|
50
|
+
if (!this.sharedPanel) {
|
|
51
|
+
this.sharedPanel = vscode.window.createOutputChannel(
|
|
52
|
+
"StatusBar Quick Actions",
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return this.sharedPanel;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Append output to a panel
|
|
60
|
+
*/
|
|
61
|
+
public appendOutput(
|
|
62
|
+
buttonId: string,
|
|
63
|
+
data: string,
|
|
64
|
+
type: "stdout" | "stderr",
|
|
65
|
+
): void {
|
|
66
|
+
const panel =
|
|
67
|
+
this.config.mode === "shared"
|
|
68
|
+
? this.getSharedPanel()
|
|
69
|
+
: this.panels.get(buttonId);
|
|
70
|
+
|
|
71
|
+
if (!panel) {
|
|
72
|
+
console.warn(`Output panel not found for button ${buttonId}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const formattedOutput = this.formatOutput(data, type);
|
|
77
|
+
panel.append(formattedOutput);
|
|
78
|
+
|
|
79
|
+
// Add to history if preservation is enabled
|
|
80
|
+
if (this.config.preserveHistory) {
|
|
81
|
+
this.addToHistory(buttonId, formattedOutput);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Trim panel if max lines exceeded
|
|
85
|
+
this.trimPanelIfNeeded(buttonId);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Append a line to the output panel
|
|
90
|
+
*/
|
|
91
|
+
public appendLine(
|
|
92
|
+
buttonId: string,
|
|
93
|
+
data: string,
|
|
94
|
+
type: "stdout" | "stderr",
|
|
95
|
+
): void {
|
|
96
|
+
const panel =
|
|
97
|
+
this.config.mode === "shared"
|
|
98
|
+
? this.getSharedPanel()
|
|
99
|
+
: this.panels.get(buttonId);
|
|
100
|
+
|
|
101
|
+
if (!panel) {
|
|
102
|
+
console.warn(`Output panel not found for button ${buttonId}`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const formattedOutput = this.formatOutput(data, type);
|
|
107
|
+
panel.appendLine(formattedOutput);
|
|
108
|
+
|
|
109
|
+
// Add to history
|
|
110
|
+
if (this.config.preserveHistory) {
|
|
111
|
+
this.addToHistory(buttonId, formattedOutput + "\n");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Clear a specific panel
|
|
117
|
+
*/
|
|
118
|
+
public clearPanel(buttonId: string): void {
|
|
119
|
+
const panel =
|
|
120
|
+
this.config.mode === "shared"
|
|
121
|
+
? this.getSharedPanel()
|
|
122
|
+
: this.panels.get(buttonId);
|
|
123
|
+
|
|
124
|
+
if (panel) {
|
|
125
|
+
panel.clear();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Clear history
|
|
129
|
+
this.outputHistory.delete(buttonId);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Show a panel
|
|
134
|
+
*/
|
|
135
|
+
public showPanel(buttonId: string, preserveFocus = false): void {
|
|
136
|
+
const panel =
|
|
137
|
+
this.config.mode === "shared"
|
|
138
|
+
? this.getSharedPanel()
|
|
139
|
+
: this.panels.get(buttonId);
|
|
140
|
+
|
|
141
|
+
if (panel) {
|
|
142
|
+
panel.show(preserveFocus);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Hide a panel
|
|
148
|
+
*/
|
|
149
|
+
public hidePanel(buttonId: string): void {
|
|
150
|
+
const panel =
|
|
151
|
+
this.config.mode === "shared"
|
|
152
|
+
? this.getSharedPanel()
|
|
153
|
+
: this.panels.get(buttonId);
|
|
154
|
+
|
|
155
|
+
if (panel) {
|
|
156
|
+
panel.hide();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Format output based on configuration
|
|
162
|
+
*/
|
|
163
|
+
private formatOutput(data: string, type: "stdout" | "stderr"): string {
|
|
164
|
+
let output = data;
|
|
165
|
+
|
|
166
|
+
// Handle formatting mode
|
|
167
|
+
switch (this.config.format) {
|
|
168
|
+
case "raw":
|
|
169
|
+
// Strip ANSI codes
|
|
170
|
+
output = this.stripAnsiCodes(data);
|
|
171
|
+
break;
|
|
172
|
+
|
|
173
|
+
case "formatted":
|
|
174
|
+
// Strip ANSI codes and add timestamp
|
|
175
|
+
output = this.stripAnsiCodes(data);
|
|
176
|
+
if (this.config.showTimestamps) {
|
|
177
|
+
const timestamp = this.getTimestamp();
|
|
178
|
+
// Only add timestamp to non-empty lines
|
|
179
|
+
if (output.trim()) {
|
|
180
|
+
output = `[${timestamp}] ${output}`;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
|
|
185
|
+
case "ansi":
|
|
186
|
+
// Preserve ANSI codes
|
|
187
|
+
if (this.config.showTimestamps && output.trim()) {
|
|
188
|
+
const timestamp = this.getTimestamp();
|
|
189
|
+
output = `[${timestamp}] ${output}`;
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Add error prefix for stderr
|
|
195
|
+
if (type === "stderr" && output.trim()) {
|
|
196
|
+
output = `[ERROR] ${output}`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return output;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Strip ANSI color codes from text
|
|
204
|
+
*/
|
|
205
|
+
private stripAnsiCodes(text: string): string {
|
|
206
|
+
// eslint-disable-next-line no-control-regex
|
|
207
|
+
return text.replace(/\x1b\[[0-9;]*m/g, "");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get formatted timestamp
|
|
212
|
+
*/
|
|
213
|
+
private getTimestamp(): string {
|
|
214
|
+
const now = new Date();
|
|
215
|
+
const hours = String(now.getHours()).padStart(2, "0");
|
|
216
|
+
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
217
|
+
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
218
|
+
return `${hours}:${minutes}:${seconds}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Add output to history
|
|
223
|
+
*/
|
|
224
|
+
private addToHistory(buttonId: string, output: string): void {
|
|
225
|
+
if (!this.outputHistory.has(buttonId)) {
|
|
226
|
+
this.outputHistory.set(buttonId, []);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const history = this.outputHistory.get(buttonId)!;
|
|
230
|
+
history.push(output);
|
|
231
|
+
|
|
232
|
+
// Trim history if needed
|
|
233
|
+
if (history.length > this.config.maxLines) {
|
|
234
|
+
history.splice(0, history.length - this.config.maxLines);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Trim panel content if max lines exceeded
|
|
240
|
+
*/
|
|
241
|
+
private trimPanelIfNeeded(buttonId: string): void {
|
|
242
|
+
// Note: VSCode OutputChannel doesn't provide direct line count access
|
|
243
|
+
// This is a placeholder for future optimization if needed
|
|
244
|
+
// The history tracking serves as a workaround for now
|
|
245
|
+
|
|
246
|
+
// Check if history exceeds max lines and trim if needed
|
|
247
|
+
const history = this.outputHistory.get(buttonId);
|
|
248
|
+
if (history && history.length > this.config.maxLines) {
|
|
249
|
+
history.splice(0, history.length - this.config.maxLines);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Get output history for a button
|
|
255
|
+
*/
|
|
256
|
+
public getHistory(buttonId: string): string[] {
|
|
257
|
+
return this.outputHistory.get(buttonId) || [];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Clear history for a button
|
|
262
|
+
*/
|
|
263
|
+
public clearHistory(buttonId: string): void {
|
|
264
|
+
this.outputHistory.delete(buttonId);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Clear all history
|
|
269
|
+
*/
|
|
270
|
+
public clearAllHistory(): void {
|
|
271
|
+
this.outputHistory.clear();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Update configuration
|
|
276
|
+
*/
|
|
277
|
+
public updateConfig(config: OutputPanelConfig): void {
|
|
278
|
+
this.config = config;
|
|
279
|
+
|
|
280
|
+
// If switching modes, clean up existing panels
|
|
281
|
+
if (config.mode === "shared" && this.panels.size > 0) {
|
|
282
|
+
// Dispose per-button panels
|
|
283
|
+
this.panels.forEach((panel) => panel.dispose());
|
|
284
|
+
this.panels.clear();
|
|
285
|
+
} else if (config.mode === "per-button" && this.sharedPanel) {
|
|
286
|
+
// Dispose shared panel
|
|
287
|
+
this.sharedPanel.dispose();
|
|
288
|
+
this.sharedPanel = null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Get current configuration
|
|
294
|
+
*/
|
|
295
|
+
public getConfig(): OutputPanelConfig {
|
|
296
|
+
return { ...this.config };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Check if output panel is enabled
|
|
301
|
+
*/
|
|
302
|
+
public isEnabled(): boolean {
|
|
303
|
+
return this.config.enabled;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Get panel for a button (if exists)
|
|
308
|
+
*/
|
|
309
|
+
public getPanel(buttonId: string): vscode.OutputChannel | null {
|
|
310
|
+
if (this.config.mode === "shared") {
|
|
311
|
+
return this.sharedPanel;
|
|
312
|
+
}
|
|
313
|
+
return this.panels.get(buttonId) || null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Check if a panel exists for a button
|
|
318
|
+
*/
|
|
319
|
+
public hasPanel(buttonId: string): boolean {
|
|
320
|
+
if (this.config.mode === "shared") {
|
|
321
|
+
return this.sharedPanel !== null;
|
|
322
|
+
}
|
|
323
|
+
return this.panels.has(buttonId);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Get all panel IDs
|
|
328
|
+
*/
|
|
329
|
+
public getPanelIds(): string[] {
|
|
330
|
+
return Array.from(this.panels.keys());
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Dispose of a specific panel
|
|
335
|
+
*/
|
|
336
|
+
public disposePanel(buttonId: string): void {
|
|
337
|
+
const panel = this.panels.get(buttonId);
|
|
338
|
+
if (panel) {
|
|
339
|
+
panel.dispose();
|
|
340
|
+
this.panels.delete(buttonId);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Clear history
|
|
344
|
+
this.outputHistory.delete(buttonId);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Dispose of all panels
|
|
349
|
+
*/
|
|
350
|
+
public dispose(): void {
|
|
351
|
+
// Dispose all per-button panels
|
|
352
|
+
this.panels.forEach((panel) => panel.dispose());
|
|
353
|
+
this.panels.clear();
|
|
354
|
+
|
|
355
|
+
// Dispose shared panel
|
|
356
|
+
if (this.sharedPanel) {
|
|
357
|
+
this.sharedPanel.dispose();
|
|
358
|
+
this.sharedPanel = null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Clear all history
|
|
362
|
+
this.outputHistory.clear();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Get statistics about output panels
|
|
367
|
+
*/
|
|
368
|
+
public getStatistics(): {
|
|
369
|
+
panelCount: number;
|
|
370
|
+
mode: string;
|
|
371
|
+
totalHistoryLines: number;
|
|
372
|
+
} {
|
|
373
|
+
let totalHistoryLines = 0;
|
|
374
|
+
this.outputHistory.forEach((history) => {
|
|
375
|
+
totalHistoryLines += history.length;
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
panelCount: this.config.mode === "shared" ? 1 : this.panels.size,
|
|
380
|
+
mode: this.config.mode,
|
|
381
|
+
totalHistoryLines,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Export history to string
|
|
387
|
+
*/
|
|
388
|
+
public exportHistory(buttonId: string): string {
|
|
389
|
+
const history = this.getHistory(buttonId);
|
|
390
|
+
return history.join("");
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Export all history to string
|
|
395
|
+
*/
|
|
396
|
+
public exportAllHistory(): Record<string, string> {
|
|
397
|
+
const result: Record<string, string> = {};
|
|
398
|
+
this.outputHistory.forEach((history, buttonId) => {
|
|
399
|
+
result[buttonId] = history.join("");
|
|
400
|
+
});
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
}
|