@fresh-editor/fresh-editor 0.1.4

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 (38) hide show
  1. package/.gitignore +2 -0
  2. package/LICENSE +117 -0
  3. package/README.md +54 -0
  4. package/binary-install.js +212 -0
  5. package/binary.js +126 -0
  6. package/install.js +4 -0
  7. package/npm-shrinkwrap.json +900 -0
  8. package/package.json +100 -0
  9. package/plugins/README.md +121 -0
  10. package/plugins/clangd_support.md +20 -0
  11. package/plugins/clangd_support.ts +323 -0
  12. package/plugins/color_highlighter.ts +302 -0
  13. package/plugins/diagnostics_panel.ts +308 -0
  14. package/plugins/examples/README.md +245 -0
  15. package/plugins/examples/async_demo.ts +165 -0
  16. package/plugins/examples/bookmarks.ts +329 -0
  17. package/plugins/examples/buffer_query_demo.ts +110 -0
  18. package/plugins/examples/git_grep.ts +262 -0
  19. package/plugins/examples/hello_world.ts +93 -0
  20. package/plugins/examples/virtual_buffer_demo.ts +116 -0
  21. package/plugins/find_references.ts +357 -0
  22. package/plugins/git_find_file.ts +298 -0
  23. package/plugins/git_grep.ts +188 -0
  24. package/plugins/git_log.ts +1283 -0
  25. package/plugins/lib/fresh.d.ts +849 -0
  26. package/plugins/lib/index.ts +24 -0
  27. package/plugins/lib/navigation-controller.ts +214 -0
  28. package/plugins/lib/panel-manager.ts +218 -0
  29. package/plugins/lib/types.ts +72 -0
  30. package/plugins/lib/virtual-buffer-factory.ts +158 -0
  31. package/plugins/manual_help.ts +243 -0
  32. package/plugins/markdown_compose.ts +1207 -0
  33. package/plugins/merge_conflict.ts +1811 -0
  34. package/plugins/path_complete.ts +163 -0
  35. package/plugins/search_replace.ts +481 -0
  36. package/plugins/todo_highlighter.ts +204 -0
  37. package/plugins/welcome.ts +74 -0
  38. package/run-fresh.js +4 -0
@@ -0,0 +1,302 @@
1
+ // TypeScript Color Highlighter Plugin
2
+ // Highlights color codes in source code with a colored swatch
3
+ // Supports: #RGB, #RRGGBB, rgb(), rgba(), hsl(), hsla(), Color::Rgb()
4
+
5
+ interface ColorHighlighterConfig {
6
+ enabled: boolean;
7
+ }
8
+
9
+ // Plugin configuration
10
+ const config: ColorHighlighterConfig = {
11
+ enabled: false, // Start disabled, use Enable or Toggle to activate
12
+ };
13
+
14
+ // Track which buffers need their virtual texts refreshed (content changed)
15
+ const dirtyBuffers = new Set<number>();
16
+
17
+ // Color block character for display
18
+ const COLOR_BLOCK = "█";
19
+
20
+ // Parse a hex color string to RGB
21
+ function parseHexColor(hex: string): [number, number, number] | null {
22
+ // Remove # prefix
23
+ hex = hex.replace(/^#/, "");
24
+
25
+ let r: number, g: number, b: number;
26
+
27
+ if (hex.length === 3) {
28
+ // #RGB format
29
+ r = parseInt(hex[0] + hex[0], 16);
30
+ g = parseInt(hex[1] + hex[1], 16);
31
+ b = parseInt(hex[2] + hex[2], 16);
32
+ } else if (hex.length === 6 || hex.length === 8) {
33
+ // #RRGGBB or #RRGGBBAA format
34
+ r = parseInt(hex.substring(0, 2), 16);
35
+ g = parseInt(hex.substring(2, 4), 16);
36
+ b = parseInt(hex.substring(4, 6), 16);
37
+ } else {
38
+ return null;
39
+ }
40
+
41
+ if (isNaN(r) || isNaN(g) || isNaN(b)) {
42
+ return null;
43
+ }
44
+
45
+ return [r, g, b];
46
+ }
47
+
48
+ // Parse rgb() or rgba() color to RGB
49
+ function parseRgbColor(match: string): [number, number, number] | null {
50
+ const rgbMatch = match.match(/rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
51
+ if (!rgbMatch) {
52
+ return null;
53
+ }
54
+
55
+ const r = parseInt(rgbMatch[1], 10);
56
+ const g = parseInt(rgbMatch[2], 10);
57
+ const b = parseInt(rgbMatch[3], 10);
58
+
59
+ if (r > 255 || g > 255 || b > 255) {
60
+ return null;
61
+ }
62
+
63
+ return [r, g, b];
64
+ }
65
+
66
+ // Convert HSL to RGB
67
+ function hslToRgb(h: number, s: number, l: number): [number, number, number] {
68
+ // Normalize h to 0-360, s and l to 0-1
69
+ h = h % 360;
70
+ if (h < 0) h += 360;
71
+ s = Math.max(0, Math.min(1, s / 100));
72
+ l = Math.max(0, Math.min(1, l / 100));
73
+
74
+ const c = (1 - Math.abs(2 * l - 1)) * s;
75
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
76
+ const m = l - c / 2;
77
+
78
+ let r = 0, g = 0, b = 0;
79
+
80
+ if (h >= 0 && h < 60) {
81
+ r = c; g = x; b = 0;
82
+ } else if (h >= 60 && h < 120) {
83
+ r = x; g = c; b = 0;
84
+ } else if (h >= 120 && h < 180) {
85
+ r = 0; g = c; b = x;
86
+ } else if (h >= 180 && h < 240) {
87
+ r = 0; g = x; b = c;
88
+ } else if (h >= 240 && h < 300) {
89
+ r = x; g = 0; b = c;
90
+ } else {
91
+ r = c; g = 0; b = x;
92
+ }
93
+
94
+ return [
95
+ Math.round((r + m) * 255),
96
+ Math.round((g + m) * 255),
97
+ Math.round((b + m) * 255),
98
+ ];
99
+ }
100
+
101
+ // Parse hsl() or hsla() color to RGB
102
+ function parseHslColor(match: string): [number, number, number] | null {
103
+ const hslMatch = match.match(/hsla?\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%/);
104
+ if (!hslMatch) {
105
+ return null;
106
+ }
107
+
108
+ const h = parseFloat(hslMatch[1]);
109
+ const s = parseFloat(hslMatch[2]);
110
+ const l = parseFloat(hslMatch[3]);
111
+
112
+ return hslToRgb(h, s, l);
113
+ }
114
+
115
+ // Parse Rust Color::Rgb(r, g, b) to RGB
116
+ function parseRustRgbColor(match: string): [number, number, number] | null {
117
+ const rustMatch = match.match(/Color::Rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/);
118
+ if (!rustMatch) {
119
+ return null;
120
+ }
121
+
122
+ const r = parseInt(rustMatch[1], 10);
123
+ const g = parseInt(rustMatch[2], 10);
124
+ const b = parseInt(rustMatch[3], 10);
125
+
126
+ if (r > 255 || g > 255 || b > 255) {
127
+ return null;
128
+ }
129
+
130
+ return [r, g, b];
131
+ }
132
+
133
+ // Color patterns to match
134
+ const colorPatterns = [
135
+ {
136
+ // Hex colors: #RGB, #RRGGBB, #RRGGBBAA
137
+ regex: /#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b/g,
138
+ parse: parseHexColor,
139
+ },
140
+ {
141
+ // CSS rgb() and rgba()
142
+ regex: /rgba?\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(?:,\s*[\d.]+\s*)?\)/g,
143
+ parse: parseRgbColor,
144
+ },
145
+ {
146
+ // CSS hsl() and hsla()
147
+ regex: /hsla?\s*\(\s*-?\d+(?:\.\d+)?\s*,\s*\d+(?:\.\d+)?%\s*,\s*\d+(?:\.\d+)?%\s*(?:,\s*[\d.]+\s*)?\)/g,
148
+ parse: parseHslColor,
149
+ },
150
+ {
151
+ // Rust Color::Rgb(r, g, b)
152
+ regex: /Color::Rgb\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)/g,
153
+ parse: parseRustRgbColor,
154
+ },
155
+ ];
156
+
157
+ // Process a single line for color highlighting
158
+ function highlightLine(
159
+ bufferId: number,
160
+ lineNumber: number,
161
+ byteStart: number,
162
+ content: string
163
+ ): void {
164
+ // Search for color patterns
165
+ for (const pattern of colorPatterns) {
166
+ // Reset regex lastIndex
167
+ pattern.regex.lastIndex = 0;
168
+
169
+ let match;
170
+ while ((match = pattern.regex.exec(content)) !== null) {
171
+ const matchText = match[0];
172
+ const pos = match.index;
173
+ const color = pattern.parse(matchText);
174
+
175
+ if (color) {
176
+ const absolutePos = byteStart + pos;
177
+ const virtualTextId = `color-${bufferId}-${lineNumber}-${pos}`;
178
+
179
+ // Add virtual text with color swatch before the color code
180
+ editor.addVirtualText(
181
+ bufferId,
182
+ virtualTextId,
183
+ absolutePos,
184
+ COLOR_BLOCK + " ",
185
+ color[0],
186
+ color[1],
187
+ color[2],
188
+ true // before the character
189
+ );
190
+ }
191
+ }
192
+ }
193
+ }
194
+
195
+ // Clear color highlights for a buffer
196
+ function clearHighlights(bufferId: number): void {
197
+ editor.removeVirtualTextsByPrefix(bufferId, "color-");
198
+ }
199
+
200
+ // Handle render-start events (only clear virtual texts if buffer content changed)
201
+ globalThis.onColorRenderStart = function(data: { buffer_id: number }): void {
202
+ if (!config.enabled) return;
203
+
204
+ // Only clear and recreate virtual texts if the buffer content changed
205
+ if (dirtyBuffers.has(data.buffer_id)) {
206
+ clearHighlights(data.buffer_id);
207
+ dirtyBuffers.delete(data.buffer_id);
208
+ }
209
+ };
210
+
211
+ // Handle lines_changed events (batched for efficiency)
212
+ globalThis.onColorLinesChanged = function(data: {
213
+ buffer_id: number;
214
+ lines: Array<{
215
+ line_number: number;
216
+ byte_start: number;
217
+ byte_end: number;
218
+ content: string;
219
+ }>;
220
+ }): void {
221
+ if (!config.enabled) return;
222
+
223
+ // Process all changed lines
224
+ for (const line of data.lines) {
225
+ highlightLine(data.buffer_id, line.line_number, line.byte_start, line.content);
226
+ }
227
+ };
228
+
229
+ // Handle buffer content changes - mark buffer as needing virtual text refresh
230
+ globalThis.onColorAfterInsert = function(data: { buffer_id: number }): void {
231
+ dirtyBuffers.add(data.buffer_id);
232
+ };
233
+
234
+ globalThis.onColorAfterDelete = function(data: { buffer_id: number }): void {
235
+ dirtyBuffers.add(data.buffer_id);
236
+ };
237
+
238
+ // Handle buffer close events
239
+ globalThis.onColorBufferClosed = function(data: { buffer_id: number }): void {
240
+ dirtyBuffers.delete(data.buffer_id);
241
+ };
242
+
243
+ // Register hooks
244
+ editor.on("render_start", "onColorRenderStart");
245
+ editor.on("lines_changed", "onColorLinesChanged");
246
+ editor.on("after-insert", "onColorAfterInsert");
247
+ editor.on("after-delete", "onColorAfterDelete");
248
+ editor.on("buffer_closed", "onColorBufferClosed");
249
+
250
+ // Plugin commands
251
+ globalThis.colorHighlighterEnable = function(): void {
252
+ config.enabled = true;
253
+ // Refresh lines so next render processes all visible lines
254
+ const bufferId = editor.getActiveBufferId();
255
+ editor.refreshLines(bufferId);
256
+ editor.setStatus("Color Highlighter: Enabled");
257
+ };
258
+
259
+ globalThis.colorHighlighterDisable = function(): void {
260
+ config.enabled = false;
261
+ const bufferId = editor.getActiveBufferId();
262
+ clearHighlights(bufferId);
263
+ editor.setStatus("Color Highlighter: Disabled");
264
+ };
265
+
266
+ globalThis.colorHighlighterToggle = function(): void {
267
+ config.enabled = !config.enabled;
268
+ const bufferId = editor.getActiveBufferId();
269
+ if (config.enabled) {
270
+ // Refresh lines so next render processes all visible lines
271
+ editor.refreshLines(bufferId);
272
+ } else {
273
+ clearHighlights(bufferId);
274
+ }
275
+ editor.setStatus(`Color Highlighter: ${config.enabled ? "Enabled" : "Disabled"}`);
276
+ };
277
+
278
+ // Register commands
279
+ editor.registerCommand(
280
+ "Color Highlighter: Enable",
281
+ "Enable color code highlighting with swatches",
282
+ "colorHighlighterEnable",
283
+ "normal"
284
+ );
285
+
286
+ editor.registerCommand(
287
+ "Color Highlighter: Disable",
288
+ "Disable color code highlighting",
289
+ "colorHighlighterDisable",
290
+ "normal"
291
+ );
292
+
293
+ editor.registerCommand(
294
+ "Color Highlighter: Toggle",
295
+ "Toggle color code highlighting",
296
+ "colorHighlighterToggle",
297
+ "normal"
298
+ );
299
+
300
+ // Initialization
301
+ editor.setStatus("Color Highlighter plugin loaded");
302
+ editor.debug("Color Highlighter initialized - supports hex, rgb, hsl, and Rust Color::Rgb");
@@ -0,0 +1,308 @@
1
+ /// <reference path="../types/fresh.d.ts" />
2
+
3
+ /**
4
+ * Diagnostics Panel Plugin (TypeScript)
5
+ *
6
+ * Full diagnostics panel implementation with virtual buffer split view.
7
+ * Provides LSP-like diagnostics display with severity icons and navigation.
8
+ */
9
+
10
+ // Panel state
11
+ let panelOpen = false;
12
+ let diagnosticsBufferId: number | null = null;
13
+ let sourceSplitId: number | null = null; // The split where source code is displayed
14
+ let currentDiagnostics: DiagnosticItem[] = [];
15
+ let selectedIndex = 0;
16
+
17
+ // Diagnostic item structure
18
+ interface DiagnosticItem {
19
+ severity: "error" | "warning" | "info" | "hint";
20
+ message: string;
21
+ file: string;
22
+ line: number;
23
+ column: number;
24
+ }
25
+
26
+ // Severity icons
27
+ const severityIcons: Record<string, string> = {
28
+ error: "[E]",
29
+ warning: "[W]",
30
+ info: "[I]",
31
+ hint: "[H]",
32
+ };
33
+
34
+ // Define the diagnostics mode with keybindings
35
+ editor.defineMode(
36
+ "diagnostics-list",
37
+ null, // no parent mode
38
+ [
39
+ ["Return", "diagnostics_goto"],
40
+ ["n", "diagnostics_next"],
41
+ ["p", "diagnostics_prev"],
42
+ ["j", "diagnostics_next"],
43
+ ["k", "diagnostics_prev"],
44
+ ["q", "diagnostics_close"],
45
+ ["Escape", "diagnostics_close"],
46
+ ],
47
+ true // read-only
48
+ );
49
+
50
+ // Format a diagnostic for display
51
+ function formatDiagnostic(item: DiagnosticItem, index: number): string {
52
+ const icon = severityIcons[item.severity] || "[?]";
53
+ const marker = index === selectedIndex ? ">" : " ";
54
+ return `${marker} ${icon} ${item.file}:${item.line}:${item.column} - ${item.message}\n`;
55
+ }
56
+
57
+ // Build entries for the virtual buffer
58
+ function buildPanelEntries(): TextPropertyEntry[] {
59
+ const entries: TextPropertyEntry[] = [];
60
+
61
+ // Header
62
+ entries.push({
63
+ text: "═══ LSP Diagnostics ═══\n",
64
+ properties: { type: "header" },
65
+ });
66
+
67
+ if (currentDiagnostics.length === 0) {
68
+ entries.push({
69
+ text: " No diagnostics available\n",
70
+ properties: { type: "empty" },
71
+ });
72
+ } else {
73
+ // Add each diagnostic
74
+ for (let i = 0; i < currentDiagnostics.length; i++) {
75
+ const diag = currentDiagnostics[i];
76
+ entries.push({
77
+ text: formatDiagnostic(diag, i),
78
+ properties: {
79
+ type: "diagnostic",
80
+ index: i,
81
+ severity: diag.severity,
82
+ location: {
83
+ file: diag.file,
84
+ line: diag.line,
85
+ column: diag.column,
86
+ },
87
+ },
88
+ });
89
+ }
90
+ }
91
+
92
+ // Footer with summary
93
+ const errorCount = currentDiagnostics.filter((d) => d.severity === "error").length;
94
+ const warningCount = currentDiagnostics.filter((d) => d.severity === "warning").length;
95
+ entries.push({
96
+ text: `───────────────────────\n`,
97
+ properties: { type: "separator" },
98
+ });
99
+ entries.push({
100
+ text: `Total: ${errorCount} error(s), ${warningCount} warning(s)\n`,
101
+ properties: { type: "summary" },
102
+ });
103
+
104
+ return entries;
105
+ }
106
+
107
+ // Update the panel content
108
+ function updatePanelContent(): void {
109
+ if (diagnosticsBufferId !== null) {
110
+ const entries = buildPanelEntries();
111
+ editor.setVirtualBufferContent(diagnosticsBufferId, entries);
112
+ }
113
+ }
114
+
115
+ // Generate sample diagnostics for the current file
116
+ function generateSampleDiagnostics(): DiagnosticItem[] {
117
+ const bufferId = editor.getActiveBufferId();
118
+ const filePath = editor.getBufferPath(bufferId);
119
+
120
+ // Return sample diagnostics
121
+ return [
122
+ {
123
+ severity: "error",
124
+ message: "unused import",
125
+ file: filePath || "unknown.rs",
126
+ line: 1,
127
+ column: 1,
128
+ },
129
+ {
130
+ severity: "warning",
131
+ message: "variable never used",
132
+ file: filePath || "unknown.rs",
133
+ line: 2,
134
+ column: 5,
135
+ },
136
+ {
137
+ severity: "info",
138
+ message: "consider using pattern matching",
139
+ file: filePath || "unknown.rs",
140
+ line: 3,
141
+ column: 10,
142
+ },
143
+ ];
144
+ }
145
+
146
+ // Show diagnostics panel
147
+ globalThis.show_diagnostics_panel = async function (): Promise<void> {
148
+ if (panelOpen) {
149
+ editor.setStatus("Diagnostics panel already open");
150
+ updatePanelContent();
151
+ return;
152
+ }
153
+
154
+ // Save the current split ID before creating the diagnostics split
155
+ // This is where we'll open files when jumping to diagnostics
156
+ sourceSplitId = editor.getActiveSplitId();
157
+
158
+ // Generate sample diagnostics
159
+ currentDiagnostics = generateSampleDiagnostics();
160
+ selectedIndex = 0;
161
+
162
+ // Build panel entries
163
+ const entries = buildPanelEntries();
164
+
165
+ // Create virtual buffer in horizontal split
166
+ try {
167
+ diagnosticsBufferId = await editor.createVirtualBufferInSplit({
168
+ name: "*Diagnostics*",
169
+ mode: "diagnostics-list",
170
+ read_only: true,
171
+ entries: entries,
172
+ ratio: 0.7, // Original pane takes 70%, diagnostics takes 30%
173
+ panel_id: "diagnostics-panel",
174
+ show_line_numbers: false,
175
+ show_cursors: true,
176
+ });
177
+
178
+ panelOpen = true;
179
+ editor.setStatus(`Diagnostics: ${currentDiagnostics.length} item(s) - Press RET to jump, n/p to navigate, q to close`);
180
+ editor.debug(`Diagnostics panel opened with buffer ID ${diagnosticsBufferId}`);
181
+ } catch (error) {
182
+ const errorMessage = error instanceof Error ? error.message : String(error);
183
+ editor.setStatus("Failed to open diagnostics panel");
184
+ editor.debug(`ERROR: createVirtualBufferInSplit failed: ${errorMessage}`);
185
+ }
186
+ };
187
+
188
+ // Hide diagnostics panel
189
+ globalThis.hide_diagnostics_panel = function (): void {
190
+ if (!panelOpen) {
191
+ editor.setStatus("Diagnostics panel not open");
192
+ return;
193
+ }
194
+
195
+ panelOpen = false;
196
+ diagnosticsBufferId = null;
197
+ sourceSplitId = null;
198
+ selectedIndex = 0;
199
+ currentDiagnostics = [];
200
+ editor.setStatus("Diagnostics panel closed");
201
+ };
202
+
203
+ // Toggle diagnostics panel
204
+ globalThis.toggle_diagnostics_panel = function (): void {
205
+ if (panelOpen) {
206
+ globalThis.hide_diagnostics_panel();
207
+ } else {
208
+ globalThis.show_diagnostics_panel();
209
+ }
210
+ };
211
+
212
+ // Show diagnostic count
213
+ globalThis.show_diagnostics_count = function (): void {
214
+ const errorCount = currentDiagnostics.filter((d) => d.severity === "error").length;
215
+ const warningCount = currentDiagnostics.filter((d) => d.severity === "warning").length;
216
+ editor.setStatus(`Diagnostics: ${errorCount} errors, ${warningCount} warnings`);
217
+ };
218
+
219
+ // Navigation: go to selected diagnostic
220
+ globalThis.diagnostics_goto = function (): void {
221
+ if (currentDiagnostics.length === 0) {
222
+ editor.setStatus("No diagnostics to jump to");
223
+ return;
224
+ }
225
+
226
+ if (sourceSplitId === null) {
227
+ editor.setStatus("Source split not available");
228
+ return;
229
+ }
230
+
231
+ const bufferId = editor.getActiveBufferId();
232
+ const props = editor.getTextPropertiesAtCursor(bufferId);
233
+
234
+ if (props.length > 0) {
235
+ const location = props[0].location as { file: string; line: number; column: number } | undefined;
236
+ if (location) {
237
+ // Open file in the source split, not the diagnostics split
238
+ editor.openFileInSplit(sourceSplitId, location.file, location.line, location.column || 0);
239
+ editor.setStatus(`Jumped to ${location.file}:${location.line}`);
240
+ } else {
241
+ editor.setStatus("No location info for this diagnostic");
242
+ }
243
+ } else {
244
+ // Fallback: use selectedIndex
245
+ const diag = currentDiagnostics[selectedIndex];
246
+ if (diag) {
247
+ // Open file in the source split, not the diagnostics split
248
+ editor.openFileInSplit(sourceSplitId, diag.file, diag.line, diag.column);
249
+ editor.setStatus(`Jumped to ${diag.file}:${diag.line}`);
250
+ }
251
+ }
252
+ };
253
+
254
+ // Navigation: next diagnostic
255
+ globalThis.diagnostics_next = function (): void {
256
+ if (currentDiagnostics.length === 0) return;
257
+
258
+ selectedIndex = (selectedIndex + 1) % currentDiagnostics.length;
259
+ updatePanelContent();
260
+ editor.setStatus(`Diagnostic ${selectedIndex + 1}/${currentDiagnostics.length}`);
261
+ };
262
+
263
+ // Navigation: previous diagnostic
264
+ globalThis.diagnostics_prev = function (): void {
265
+ if (currentDiagnostics.length === 0) return;
266
+
267
+ selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : currentDiagnostics.length - 1;
268
+ updatePanelContent();
269
+ editor.setStatus(`Diagnostic ${selectedIndex + 1}/${currentDiagnostics.length}`);
270
+ };
271
+
272
+ // Close the diagnostics panel
273
+ globalThis.diagnostics_close = function (): void {
274
+ globalThis.hide_diagnostics_panel();
275
+ };
276
+
277
+ // Register commands
278
+ editor.registerCommand(
279
+ "Show Diagnostics Panel",
280
+ "Open the diagnostics panel",
281
+ "show_diagnostics_panel",
282
+ "normal"
283
+ );
284
+
285
+ editor.registerCommand(
286
+ "Hide Diagnostics Panel",
287
+ "Close the diagnostics panel",
288
+ "hide_diagnostics_panel",
289
+ "normal"
290
+ );
291
+
292
+ editor.registerCommand(
293
+ "Toggle Diagnostics Panel",
294
+ "Toggle diagnostics panel visibility",
295
+ "toggle_diagnostics_panel",
296
+ "normal"
297
+ );
298
+
299
+ editor.registerCommand(
300
+ "Diagnostics Count",
301
+ "Show count of current diagnostics",
302
+ "show_diagnostics_count",
303
+ "normal"
304
+ );
305
+
306
+ // Plugin initialization
307
+ editor.setStatus("Diagnostics Panel plugin loaded (TypeScript)");
308
+ editor.debug("Diagnostics Panel plugin initialized - 4 commands registered");