agentgui 1.0.274 → 1.0.276

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 (70) hide show
  1. package/CLAUDE.md +280 -280
  2. package/IPFS_DOWNLOADER.md +277 -277
  3. package/TASK_2C_COMPLETION.md +334 -334
  4. package/agentgui.ico +0 -0
  5. package/bin/gmgui.cjs +54 -54
  6. package/build-portable.js +13 -42
  7. package/database.js +1422 -1406
  8. package/lib/claude-runner.js +1130 -1130
  9. package/lib/ipfs-downloader.js +459 -459
  10. package/lib/speech.js +159 -152
  11. package/package.json +1 -1
  12. package/readme.md +76 -76
  13. package/server.js +3787 -3794
  14. package/setup-npm-token.sh +68 -68
  15. package/static/app.js +773 -773
  16. package/static/event-rendering-showcase.html +708 -708
  17. package/static/index.html +3178 -3180
  18. package/static/js/agent-auth.js +298 -298
  19. package/static/js/audio-recorder-processor.js +18 -18
  20. package/static/js/client.js +2656 -2656
  21. package/static/js/conversations.js +583 -583
  22. package/static/js/dialogs.js +267 -267
  23. package/static/js/event-consolidator.js +101 -101
  24. package/static/js/event-filter.js +311 -311
  25. package/static/js/event-processor.js +452 -452
  26. package/static/js/features.js +413 -413
  27. package/static/js/kalman-filter.js +67 -67
  28. package/static/js/progress-dialog.js +130 -130
  29. package/static/js/script-runner.js +219 -219
  30. package/static/js/streaming-renderer.js +2123 -2120
  31. package/static/js/syntax-highlighter.js +269 -269
  32. package/static/js/tts-websocket-handler.js +152 -152
  33. package/static/js/ui-components.js +431 -431
  34. package/static/js/voice.js +849 -849
  35. package/static/js/websocket-manager.js +596 -596
  36. package/static/templates/INDEX.html +465 -465
  37. package/static/templates/README.md +190 -190
  38. package/static/templates/agent-capabilities.html +56 -56
  39. package/static/templates/agent-metadata-panel.html +44 -44
  40. package/static/templates/agent-status-badge.html +30 -30
  41. package/static/templates/code-annotation-panel.html +155 -155
  42. package/static/templates/code-suggestion-panel.html +184 -184
  43. package/static/templates/command-header.html +77 -77
  44. package/static/templates/command-output-scrollable.html +118 -118
  45. package/static/templates/elapsed-time.html +54 -54
  46. package/static/templates/error-alert.html +106 -106
  47. package/static/templates/error-history-timeline.html +160 -160
  48. package/static/templates/error-recovery-options.html +109 -109
  49. package/static/templates/error-stack-trace.html +95 -95
  50. package/static/templates/error-summary.html +80 -80
  51. package/static/templates/event-counter.html +48 -48
  52. package/static/templates/execution-actions.html +97 -97
  53. package/static/templates/execution-progress-bar.html +80 -80
  54. package/static/templates/execution-stepper.html +120 -120
  55. package/static/templates/file-breadcrumb.html +118 -118
  56. package/static/templates/file-diff-viewer.html +121 -121
  57. package/static/templates/file-metadata.html +133 -133
  58. package/static/templates/file-read-panel.html +66 -66
  59. package/static/templates/file-write-panel.html +120 -120
  60. package/static/templates/git-branch-remote.html +107 -107
  61. package/static/templates/git-diff-list.html +101 -101
  62. package/static/templates/git-log-visualization.html +153 -153
  63. package/static/templates/git-status-panel.html +115 -115
  64. package/static/templates/quality-metrics-display.html +170 -170
  65. package/static/templates/terminal-output-panel.html +87 -87
  66. package/static/templates/test-results-display.html +144 -144
  67. package/static/theme.js +72 -72
  68. package/test-download-progress.js +223 -223
  69. package/test-websocket-broadcast.js +147 -147
  70. package/tests/ipfs-downloader.test.js +370 -370
@@ -1,452 +1,452 @@
1
- /**
2
- * Event Processor
3
- * Transforms, validates, and enriches streaming events
4
- * Handles ANSI colors, markdown, diffs, and other data transformations
5
- */
6
-
7
- class EventProcessor {
8
- constructor(config = {}) {
9
- this.config = {
10
- enableSyntaxHighlight: config.enableSyntaxHighlight !== false,
11
- enableMarkdown: config.enableMarkdown !== false,
12
- enableANSI: config.enableANSI !== false,
13
- maxContentLength: config.maxContentLength || 100000,
14
- ...config
15
- };
16
-
17
- // ANSI color codes mapping
18
- this.ansiCodes = {
19
- reset: '\x1b[0m',
20
- bold: '\x1b[1m',
21
- dim: '\x1b[2m',
22
- italic: '\x1b[3m',
23
- underline: '\x1b[4m',
24
- blink: '\x1b[5m',
25
- reverse: '\x1b[7m',
26
- hidden: '\x1b[8m',
27
- strikethrough: '\x1b[9m',
28
- // Foreground colors
29
- black: '\x1b[30m',
30
- red: '\x1b[31m',
31
- green: '\x1b[32m',
32
- yellow: '\x1b[33m',
33
- blue: '\x1b[34m',
34
- magenta: '\x1b[35m',
35
- cyan: '\x1b[36m',
36
- white: '\x1b[37m',
37
- // Background colors
38
- bgBlack: '\x1b[40m',
39
- bgRed: '\x1b[41m',
40
- bgGreen: '\x1b[42m',
41
- bgYellow: '\x1b[43m',
42
- bgBlue: '\x1b[44m',
43
- bgMagenta: '\x1b[45m',
44
- bgCyan: '\x1b[46m',
45
- bgWhite: '\x1b[47m'
46
- };
47
-
48
- // CSS color mapping
49
- this.colorMap = {
50
- '30': '#000000', // black
51
- '31': '#ff6b6b', // red
52
- '32': '#51cf66', // green
53
- '33': '#ffd43b', // yellow
54
- '34': '#4dabf7', // blue
55
- '35': '#da77f2', // magenta
56
- '36': '#20c997', // cyan
57
- '37': '#ffffff', // white
58
- '90': '#666666', // bright black
59
- '91': '#ff8787', // bright red
60
- '92': '#69db7c', // bright green
61
- '93': '#ffe066', // bright yellow
62
- '94': '#74c0fc', // bright blue
63
- '95': '#e599f7', // bright magenta
64
- '96': '#38f9d7', // bright cyan
65
- '97': '#f8f9fa' // bright white
66
- };
67
-
68
- // Statistics
69
- this.stats = {
70
- totalEvents: 0,
71
- processedEvents: 0,
72
- validatedEvents: 0,
73
- transformedEvents: 0,
74
- errorCount: 0,
75
- avgProcessTime: 0
76
- };
77
- }
78
-
79
- /**
80
- * Process and enrich event
81
- */
82
- processEvent(event) {
83
- if (!event || typeof event !== 'object') {
84
- return null;
85
- }
86
-
87
- const startTime = performance.now();
88
- this.stats.totalEvents++;
89
-
90
- try {
91
- // Validate event structure
92
- if (!this.validateEvent(event)) {
93
- this.stats.errorCount++;
94
- return null;
95
- }
96
-
97
- this.stats.validatedEvents++;
98
-
99
- // Add processing metadata
100
- const processed = {
101
- ...event,
102
- processedAt: Date.now(),
103
- processTime: 0
104
- };
105
-
106
- // Transform event based on type
107
- if (event.type === 'text_block' || event.type === 'code_block') {
108
- processed.content = this.transformContent(event.content || '', event.type);
109
- this.stats.transformedEvents++;
110
- }
111
-
112
- if (event.type === 'command_execute' && event.output) {
113
- processed.output = this.transformANSI(event.output);
114
- this.stats.transformedEvents++;
115
- }
116
-
117
- if (event.type === 'file_diff' || event.type === 'git_diff') {
118
- processed.diff = this.transformDiff(event.diff || event.content || '');
119
- this.stats.transformedEvents++;
120
- }
121
-
122
- processed.processTime = performance.now() - startTime;
123
- this.stats.processedEvents++;
124
-
125
- // Update average processing time
126
- this.stats.avgProcessTime = (this.stats.avgProcessTime * (this.stats.processedEvents - 1) + processed.processTime) / this.stats.processedEvents;
127
-
128
- return processed;
129
- } catch (error) {
130
- console.error('Event processing error:', error);
131
- this.stats.errorCount++;
132
- return null;
133
- }
134
- }
135
-
136
- /**
137
- * Validate event structure
138
- */
139
- validateEvent(event) {
140
- if (!event.type) {
141
- return false;
142
- }
143
-
144
- // Required fields by type
145
- const typeRequirements = {
146
- streaming_start: ['sessionId', 'conversationId'],
147
- streaming_complete: ['sessionId'],
148
- file_read: ['path'],
149
- file_write: ['path'],
150
- command_execute: ['command'],
151
- git_status: [],
152
- error: ['message']
153
- };
154
-
155
- const requirements = typeRequirements[event.type];
156
- if (requirements) {
157
- for (const field of requirements) {
158
- if (!event[field]) {
159
- return false;
160
- }
161
- }
162
- }
163
-
164
- return true;
165
- }
166
-
167
- /**
168
- * Transform content based on type
169
- */
170
- transformContent(content, type) {
171
- if (typeof content !== 'string') {
172
- return content;
173
- }
174
-
175
- if (content.length > this.config.maxContentLength) {
176
- return content.substring(0, this.config.maxContentLength) + '\n... (truncated)';
177
- }
178
-
179
- return content;
180
- }
181
-
182
- /**
183
- * Transform ANSI escape codes to HTML/CSS
184
- */
185
- transformANSI(text) {
186
- if (!this.config.enableANSI || typeof text !== 'string') {
187
- return text;
188
- }
189
-
190
- let result = '';
191
- let currentStyle = { fg: null, bg: null, bold: false };
192
- const stack = [];
193
-
194
- // Pattern for ANSI escape sequences
195
- const pattern = /\x1b\[([0-9;]*?)m/g;
196
- let lastIndex = 0;
197
- let match;
198
-
199
- while ((match = pattern.exec(text)) !== null) {
200
- // Add text before this escape sequence
201
- if (match.index > lastIndex) {
202
- const plainText = text.substring(lastIndex, match.index);
203
- result += this.escapeHtml(plainText);
204
- }
205
-
206
- // Parse ANSI code
207
- const codes = match[1].split(';').map(c => parseInt(c, 10));
208
- for (const code of codes) {
209
- if (code === 0) {
210
- // Reset
211
- currentStyle = { fg: null, bg: null, bold: false };
212
- } else if (code === 1) {
213
- currentStyle.bold = true;
214
- } else if (code >= 30 && code <= 37) {
215
- currentStyle.fg = this.colorMap[code];
216
- } else if (code >= 40 && code <= 47) {
217
- currentStyle.bg = this.colorMap[String(code - 10)];
218
- } else if (code >= 90 && code <= 97) {
219
- currentStyle.fg = this.colorMap[code];
220
- }
221
- }
222
-
223
- lastIndex = pattern.lastIndex;
224
- }
225
-
226
- // Add remaining text
227
- if (lastIndex < text.length) {
228
- result += this.escapeHtml(text.substring(lastIndex));
229
- }
230
-
231
- return result;
232
- }
233
-
234
- /**
235
- * Transform unified diff format
236
- */
237
- transformDiff(diffText) {
238
- if (typeof diffText !== 'string') {
239
- return diffText;
240
- }
241
-
242
- const lines = diffText.split('\n');
243
- const parsed = {
244
- headers: [],
245
- hunks: []
246
- };
247
-
248
- let currentHunk = null;
249
-
250
- for (const line of lines) {
251
- if (line.startsWith('---') || line.startsWith('+++')) {
252
- parsed.headers.push(line);
253
- } else if (line.startsWith('@@')) {
254
- if (currentHunk) {
255
- parsed.hunks.push(currentHunk);
256
- }
257
- currentHunk = {
258
- header: line,
259
- changes: []
260
- };
261
- } else if (currentHunk) {
262
- if (line.startsWith('-')) {
263
- currentHunk.changes.push({ type: 'deleted', line: line.substring(1) });
264
- } else if (line.startsWith('+')) {
265
- currentHunk.changes.push({ type: 'added', line: line.substring(1) });
266
- } else if (line.startsWith(' ')) {
267
- currentHunk.changes.push({ type: 'context', line: line.substring(1) });
268
- }
269
- }
270
- }
271
-
272
- if (currentHunk) {
273
- parsed.hunks.push(currentHunk);
274
- }
275
-
276
- return parsed;
277
- }
278
-
279
- /**
280
- * Convert markdown to HTML (simple implementation)
281
- */
282
- transformMarkdown(markdown) {
283
- if (!this.config.enableMarkdown || typeof markdown !== 'string') {
284
- return markdown;
285
- }
286
-
287
- let html = this.escapeHtml(markdown);
288
-
289
- // Bold
290
- html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
291
-
292
- // Italic
293
- html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
294
-
295
- // Code
296
- html = html.replace(/`(.+?)`/g, '<code>$1</code>');
297
-
298
- // Links
299
- html = html.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2" target="_blank">$1</a>');
300
-
301
- // Headings
302
- html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
303
- html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
304
- html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
305
-
306
- // Line breaks
307
- html = html.replace(/\n/g, '<br>');
308
-
309
- return html;
310
- }
311
-
312
- /**
313
- * Detect language from content or hint
314
- */
315
- detectLanguage(content, hint = null) {
316
- if (hint) {
317
- return hint.toLowerCase();
318
- }
319
-
320
- // Simple language detection based on shebang or content patterns
321
- if (content.startsWith('#!/')) {
322
- if (content.includes('python')) return 'python';
323
- if (content.includes('node') || content.includes('javascript')) return 'javascript';
324
- if (content.includes('bash') || content.includes('sh')) return 'bash';
325
- if (content.includes('ruby')) return 'ruby';
326
- }
327
-
328
- // Pattern detection
329
- if (content.includes('def ') && content.includes(':')) return 'python';
330
- if (content.includes('function') || content.includes('=>')) return 'javascript';
331
- if (content.includes('fn ') && content.includes('->')) return 'rust';
332
- if (content.includes('public static void') || content.includes('class ')) return 'java';
333
-
334
- return 'plaintext';
335
- }
336
-
337
- /**
338
- * Parse JSON safely
339
- */
340
- parseJSON(jsonStr) {
341
- try {
342
- return JSON.parse(jsonStr);
343
- } catch (e) {
344
- console.error('JSON parse error:', e);
345
- return null;
346
- }
347
- }
348
-
349
- /**
350
- * Format JSON for display
351
- */
352
- formatJSON(obj, indent = 2) {
353
- try {
354
- return JSON.stringify(obj, null, indent);
355
- } catch (e) {
356
- return String(obj);
357
- }
358
- }
359
-
360
- /**
361
- * Extract file extension
362
- */
363
- getFileExtension(filePath) {
364
- const match = filePath.match(/\.([^.]+)$/);
365
- return match ? match[1].toLowerCase() : null;
366
- }
367
-
368
- /**
369
- * Determine syntax highlighter language from file extension
370
- */
371
- getLanguageFromExtension(ext) {
372
- const extMap = {
373
- 'js': 'javascript',
374
- 'jsx': 'jsx',
375
- 'ts': 'typescript',
376
- 'tsx': 'typescript',
377
- 'py': 'python',
378
- 'java': 'java',
379
- 'cpp': 'cpp',
380
- 'c': 'c',
381
- 'cs': 'csharp',
382
- 'php': 'php',
383
- 'rb': 'ruby',
384
- 'go': 'go',
385
- 'rs': 'rust',
386
- 'json': 'json',
387
- 'xml': 'xml',
388
- 'html': 'html',
389
- 'css': 'css',
390
- 'scss': 'scss',
391
- 'yaml': 'yaml',
392
- 'yml': 'yaml',
393
- 'sql': 'sql',
394
- 'sh': 'bash',
395
- 'bash': 'bash',
396
- 'zsh': 'bash'
397
- };
398
-
399
- return extMap[ext?.toLowerCase()] || 'plaintext';
400
- }
401
-
402
- /**
403
- * Truncate text with ellipsis
404
- */
405
- truncateText(text, maxLength = 200) {
406
- if (text.length <= maxLength) {
407
- return text;
408
- }
409
- return text.substring(0, maxLength) + '...';
410
- }
411
-
412
- /**
413
- * HTML escape utility
414
- */
415
- escapeHtml(text) {
416
- return window._escHtml(text);
417
- }
418
-
419
- /**
420
- * Format timestamp
421
- */
422
- formatTimestamp(timestamp) {
423
- const date = new Date(timestamp);
424
- return date.toLocaleTimeString();
425
- }
426
-
427
- /**
428
- * Get statistics
429
- */
430
- getStats() {
431
- return { ...this.stats };
432
- }
433
-
434
- /**
435
- * Reset statistics
436
- */
437
- resetStats() {
438
- this.stats = {
439
- totalEvents: 0,
440
- processedEvents: 0,
441
- validatedEvents: 0,
442
- transformedEvents: 0,
443
- errorCount: 0,
444
- avgProcessTime: 0
445
- };
446
- }
447
- }
448
-
449
- // Export for use in browser
450
- if (typeof module !== 'undefined' && module.exports) {
451
- module.exports = EventProcessor;
452
- }
1
+ /**
2
+ * Event Processor
3
+ * Transforms, validates, and enriches streaming events
4
+ * Handles ANSI colors, markdown, diffs, and other data transformations
5
+ */
6
+
7
+ class EventProcessor {
8
+ constructor(config = {}) {
9
+ this.config = {
10
+ enableSyntaxHighlight: config.enableSyntaxHighlight !== false,
11
+ enableMarkdown: config.enableMarkdown !== false,
12
+ enableANSI: config.enableANSI !== false,
13
+ maxContentLength: config.maxContentLength || 100000,
14
+ ...config
15
+ };
16
+
17
+ // ANSI color codes mapping
18
+ this.ansiCodes = {
19
+ reset: '\x1b[0m',
20
+ bold: '\x1b[1m',
21
+ dim: '\x1b[2m',
22
+ italic: '\x1b[3m',
23
+ underline: '\x1b[4m',
24
+ blink: '\x1b[5m',
25
+ reverse: '\x1b[7m',
26
+ hidden: '\x1b[8m',
27
+ strikethrough: '\x1b[9m',
28
+ // Foreground colors
29
+ black: '\x1b[30m',
30
+ red: '\x1b[31m',
31
+ green: '\x1b[32m',
32
+ yellow: '\x1b[33m',
33
+ blue: '\x1b[34m',
34
+ magenta: '\x1b[35m',
35
+ cyan: '\x1b[36m',
36
+ white: '\x1b[37m',
37
+ // Background colors
38
+ bgBlack: '\x1b[40m',
39
+ bgRed: '\x1b[41m',
40
+ bgGreen: '\x1b[42m',
41
+ bgYellow: '\x1b[43m',
42
+ bgBlue: '\x1b[44m',
43
+ bgMagenta: '\x1b[45m',
44
+ bgCyan: '\x1b[46m',
45
+ bgWhite: '\x1b[47m'
46
+ };
47
+
48
+ // CSS color mapping
49
+ this.colorMap = {
50
+ '30': '#000000', // black
51
+ '31': '#ff6b6b', // red
52
+ '32': '#51cf66', // green
53
+ '33': '#ffd43b', // yellow
54
+ '34': '#4dabf7', // blue
55
+ '35': '#da77f2', // magenta
56
+ '36': '#20c997', // cyan
57
+ '37': '#ffffff', // white
58
+ '90': '#666666', // bright black
59
+ '91': '#ff8787', // bright red
60
+ '92': '#69db7c', // bright green
61
+ '93': '#ffe066', // bright yellow
62
+ '94': '#74c0fc', // bright blue
63
+ '95': '#e599f7', // bright magenta
64
+ '96': '#38f9d7', // bright cyan
65
+ '97': '#f8f9fa' // bright white
66
+ };
67
+
68
+ // Statistics
69
+ this.stats = {
70
+ totalEvents: 0,
71
+ processedEvents: 0,
72
+ validatedEvents: 0,
73
+ transformedEvents: 0,
74
+ errorCount: 0,
75
+ avgProcessTime: 0
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Process and enrich event
81
+ */
82
+ processEvent(event) {
83
+ if (!event || typeof event !== 'object') {
84
+ return null;
85
+ }
86
+
87
+ const startTime = performance.now();
88
+ this.stats.totalEvents++;
89
+
90
+ try {
91
+ // Validate event structure
92
+ if (!this.validateEvent(event)) {
93
+ this.stats.errorCount++;
94
+ return null;
95
+ }
96
+
97
+ this.stats.validatedEvents++;
98
+
99
+ // Add processing metadata
100
+ const processed = {
101
+ ...event,
102
+ processedAt: Date.now(),
103
+ processTime: 0
104
+ };
105
+
106
+ // Transform event based on type
107
+ if (event.type === 'text_block' || event.type === 'code_block') {
108
+ processed.content = this.transformContent(event.content || '', event.type);
109
+ this.stats.transformedEvents++;
110
+ }
111
+
112
+ if (event.type === 'command_execute' && event.output) {
113
+ processed.output = this.transformANSI(event.output);
114
+ this.stats.transformedEvents++;
115
+ }
116
+
117
+ if (event.type === 'file_diff' || event.type === 'git_diff') {
118
+ processed.diff = this.transformDiff(event.diff || event.content || '');
119
+ this.stats.transformedEvents++;
120
+ }
121
+
122
+ processed.processTime = performance.now() - startTime;
123
+ this.stats.processedEvents++;
124
+
125
+ // Update average processing time
126
+ this.stats.avgProcessTime = (this.stats.avgProcessTime * (this.stats.processedEvents - 1) + processed.processTime) / this.stats.processedEvents;
127
+
128
+ return processed;
129
+ } catch (error) {
130
+ console.error('Event processing error:', error);
131
+ this.stats.errorCount++;
132
+ return null;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Validate event structure
138
+ */
139
+ validateEvent(event) {
140
+ if (!event.type) {
141
+ return false;
142
+ }
143
+
144
+ // Required fields by type
145
+ const typeRequirements = {
146
+ streaming_start: ['sessionId', 'conversationId'],
147
+ streaming_complete: ['sessionId'],
148
+ file_read: ['path'],
149
+ file_write: ['path'],
150
+ command_execute: ['command'],
151
+ git_status: [],
152
+ error: ['message']
153
+ };
154
+
155
+ const requirements = typeRequirements[event.type];
156
+ if (requirements) {
157
+ for (const field of requirements) {
158
+ if (!event[field]) {
159
+ return false;
160
+ }
161
+ }
162
+ }
163
+
164
+ return true;
165
+ }
166
+
167
+ /**
168
+ * Transform content based on type
169
+ */
170
+ transformContent(content, type) {
171
+ if (typeof content !== 'string') {
172
+ return content;
173
+ }
174
+
175
+ if (content.length > this.config.maxContentLength) {
176
+ return content.substring(0, this.config.maxContentLength) + '\n... (truncated)';
177
+ }
178
+
179
+ return content;
180
+ }
181
+
182
+ /**
183
+ * Transform ANSI escape codes to HTML/CSS
184
+ */
185
+ transformANSI(text) {
186
+ if (!this.config.enableANSI || typeof text !== 'string') {
187
+ return text;
188
+ }
189
+
190
+ let result = '';
191
+ let currentStyle = { fg: null, bg: null, bold: false };
192
+ const stack = [];
193
+
194
+ // Pattern for ANSI escape sequences
195
+ const pattern = /\x1b\[([0-9;]*?)m/g;
196
+ let lastIndex = 0;
197
+ let match;
198
+
199
+ while ((match = pattern.exec(text)) !== null) {
200
+ // Add text before this escape sequence
201
+ if (match.index > lastIndex) {
202
+ const plainText = text.substring(lastIndex, match.index);
203
+ result += this.escapeHtml(plainText);
204
+ }
205
+
206
+ // Parse ANSI code
207
+ const codes = match[1].split(';').map(c => parseInt(c, 10));
208
+ for (const code of codes) {
209
+ if (code === 0) {
210
+ // Reset
211
+ currentStyle = { fg: null, bg: null, bold: false };
212
+ } else if (code === 1) {
213
+ currentStyle.bold = true;
214
+ } else if (code >= 30 && code <= 37) {
215
+ currentStyle.fg = this.colorMap[code];
216
+ } else if (code >= 40 && code <= 47) {
217
+ currentStyle.bg = this.colorMap[String(code - 10)];
218
+ } else if (code >= 90 && code <= 97) {
219
+ currentStyle.fg = this.colorMap[code];
220
+ }
221
+ }
222
+
223
+ lastIndex = pattern.lastIndex;
224
+ }
225
+
226
+ // Add remaining text
227
+ if (lastIndex < text.length) {
228
+ result += this.escapeHtml(text.substring(lastIndex));
229
+ }
230
+
231
+ return result;
232
+ }
233
+
234
+ /**
235
+ * Transform unified diff format
236
+ */
237
+ transformDiff(diffText) {
238
+ if (typeof diffText !== 'string') {
239
+ return diffText;
240
+ }
241
+
242
+ const lines = diffText.split('\n');
243
+ const parsed = {
244
+ headers: [],
245
+ hunks: []
246
+ };
247
+
248
+ let currentHunk = null;
249
+
250
+ for (const line of lines) {
251
+ if (line.startsWith('---') || line.startsWith('+++')) {
252
+ parsed.headers.push(line);
253
+ } else if (line.startsWith('@@')) {
254
+ if (currentHunk) {
255
+ parsed.hunks.push(currentHunk);
256
+ }
257
+ currentHunk = {
258
+ header: line,
259
+ changes: []
260
+ };
261
+ } else if (currentHunk) {
262
+ if (line.startsWith('-')) {
263
+ currentHunk.changes.push({ type: 'deleted', line: line.substring(1) });
264
+ } else if (line.startsWith('+')) {
265
+ currentHunk.changes.push({ type: 'added', line: line.substring(1) });
266
+ } else if (line.startsWith(' ')) {
267
+ currentHunk.changes.push({ type: 'context', line: line.substring(1) });
268
+ }
269
+ }
270
+ }
271
+
272
+ if (currentHunk) {
273
+ parsed.hunks.push(currentHunk);
274
+ }
275
+
276
+ return parsed;
277
+ }
278
+
279
+ /**
280
+ * Convert markdown to HTML (simple implementation)
281
+ */
282
+ transformMarkdown(markdown) {
283
+ if (!this.config.enableMarkdown || typeof markdown !== 'string') {
284
+ return markdown;
285
+ }
286
+
287
+ let html = this.escapeHtml(markdown);
288
+
289
+ // Bold
290
+ html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
291
+
292
+ // Italic
293
+ html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
294
+
295
+ // Code
296
+ html = html.replace(/`(.+?)`/g, '<code>$1</code>');
297
+
298
+ // Links
299
+ html = html.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2" target="_blank">$1</a>');
300
+
301
+ // Headings
302
+ html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
303
+ html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
304
+ html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
305
+
306
+ // Line breaks
307
+ html = html.replace(/\n/g, '<br>');
308
+
309
+ return html;
310
+ }
311
+
312
+ /**
313
+ * Detect language from content or hint
314
+ */
315
+ detectLanguage(content, hint = null) {
316
+ if (hint) {
317
+ return hint.toLowerCase();
318
+ }
319
+
320
+ // Simple language detection based on shebang or content patterns
321
+ if (content.startsWith('#!/')) {
322
+ if (content.includes('python')) return 'python';
323
+ if (content.includes('node') || content.includes('javascript')) return 'javascript';
324
+ if (content.includes('bash') || content.includes('sh')) return 'bash';
325
+ if (content.includes('ruby')) return 'ruby';
326
+ }
327
+
328
+ // Pattern detection
329
+ if (content.includes('def ') && content.includes(':')) return 'python';
330
+ if (content.includes('function') || content.includes('=>')) return 'javascript';
331
+ if (content.includes('fn ') && content.includes('->')) return 'rust';
332
+ if (content.includes('public static void') || content.includes('class ')) return 'java';
333
+
334
+ return 'plaintext';
335
+ }
336
+
337
+ /**
338
+ * Parse JSON safely
339
+ */
340
+ parseJSON(jsonStr) {
341
+ try {
342
+ return JSON.parse(jsonStr);
343
+ } catch (e) {
344
+ console.error('JSON parse error:', e);
345
+ return null;
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Format JSON for display
351
+ */
352
+ formatJSON(obj, indent = 2) {
353
+ try {
354
+ return JSON.stringify(obj, null, indent);
355
+ } catch (e) {
356
+ return String(obj);
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Extract file extension
362
+ */
363
+ getFileExtension(filePath) {
364
+ const match = filePath.match(/\.([^.]+)$/);
365
+ return match ? match[1].toLowerCase() : null;
366
+ }
367
+
368
+ /**
369
+ * Determine syntax highlighter language from file extension
370
+ */
371
+ getLanguageFromExtension(ext) {
372
+ const extMap = {
373
+ 'js': 'javascript',
374
+ 'jsx': 'jsx',
375
+ 'ts': 'typescript',
376
+ 'tsx': 'typescript',
377
+ 'py': 'python',
378
+ 'java': 'java',
379
+ 'cpp': 'cpp',
380
+ 'c': 'c',
381
+ 'cs': 'csharp',
382
+ 'php': 'php',
383
+ 'rb': 'ruby',
384
+ 'go': 'go',
385
+ 'rs': 'rust',
386
+ 'json': 'json',
387
+ 'xml': 'xml',
388
+ 'html': 'html',
389
+ 'css': 'css',
390
+ 'scss': 'scss',
391
+ 'yaml': 'yaml',
392
+ 'yml': 'yaml',
393
+ 'sql': 'sql',
394
+ 'sh': 'bash',
395
+ 'bash': 'bash',
396
+ 'zsh': 'bash'
397
+ };
398
+
399
+ return extMap[ext?.toLowerCase()] || 'plaintext';
400
+ }
401
+
402
+ /**
403
+ * Truncate text with ellipsis
404
+ */
405
+ truncateText(text, maxLength = 200) {
406
+ if (text.length <= maxLength) {
407
+ return text;
408
+ }
409
+ return text.substring(0, maxLength) + '...';
410
+ }
411
+
412
+ /**
413
+ * HTML escape utility
414
+ */
415
+ escapeHtml(text) {
416
+ return window._escHtml(text);
417
+ }
418
+
419
+ /**
420
+ * Format timestamp
421
+ */
422
+ formatTimestamp(timestamp) {
423
+ const date = new Date(timestamp);
424
+ return date.toLocaleTimeString();
425
+ }
426
+
427
+ /**
428
+ * Get statistics
429
+ */
430
+ getStats() {
431
+ return { ...this.stats };
432
+ }
433
+
434
+ /**
435
+ * Reset statistics
436
+ */
437
+ resetStats() {
438
+ this.stats = {
439
+ totalEvents: 0,
440
+ processedEvents: 0,
441
+ validatedEvents: 0,
442
+ transformedEvents: 0,
443
+ errorCount: 0,
444
+ avgProcessTime: 0
445
+ };
446
+ }
447
+ }
448
+
449
+ // Export for use in browser
450
+ if (typeof module !== 'undefined' && module.exports) {
451
+ module.exports = EventProcessor;
452
+ }