agentgui 1.0.17 → 1.0.18

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.
@@ -0,0 +1,117 @@
1
+ /**
2
+ * HTML Wrapper for Claude Responses
3
+ * Converts plain text/markdown responses into beautiful HTML
4
+ */
5
+
6
+ export class HTMLWrapper {
7
+ /**
8
+ * Wrap plain text response in HTML with RippleUI styling
9
+ */
10
+ static wrapResponse(text) {
11
+ if (!text || typeof text !== 'string') return text;
12
+
13
+ // If already HTML, return as-is
14
+ if (text.trim().startsWith('<')) return text;
15
+
16
+ // Parse markdown-style text and convert to HTML
17
+ const lines = text.split('\n');
18
+ const html = this.parseMarkdownToHTML(lines);
19
+
20
+ // Wrap in container
21
+ return `<div class="space-y-4 p-6 max-w-4xl">${html}</div>`;
22
+ }
23
+
24
+ static parseMarkdownToHTML(lines) {
25
+ let html = '';
26
+ let inCodeBlock = false;
27
+ let codeLanguage = 'text';
28
+ let codeContent = '';
29
+ let listItems = [];
30
+ let inList = false;
31
+
32
+ for (let i = 0; i < lines.length; i++) {
33
+ const line = lines[i];
34
+
35
+ // Code blocks
36
+ if (line.match(/^```(\w+)?$/)) {
37
+ if (!inCodeBlock) {
38
+ // Start code block
39
+ inCodeBlock = true;
40
+ codeLanguage = line.match(/```(\w+)?/)?.[1] || 'text';
41
+ codeContent = '';
42
+ } else {
43
+ // End code block
44
+ inCodeBlock = false;
45
+ html += `<pre class="bg-gray-900 text-white p-4 rounded-lg overflow-x-auto"><code class="language-${codeLanguage}">${this.escapeHtml(codeContent)}</code></pre>`;
46
+ codeContent = '';
47
+ }
48
+ continue;
49
+ }
50
+
51
+ if (inCodeBlock) {
52
+ codeContent += (codeContent ? '\n' : '') + line;
53
+ continue;
54
+ }
55
+
56
+ // Headings
57
+ if (line.match(/^#+\s/)) {
58
+ const level = line.match(/^#+/)[0].length;
59
+ const heading = line.replace(/^#+\s/, '');
60
+ html += `<h${level} class="text-${level === 1 ? '3xl' : level === 2 ? '2xl' : 'xl'} font-bold text-gray-900 mt-4">${this.escapeHtml(heading)}</h${level}>`;
61
+ continue;
62
+ }
63
+
64
+ // Lists
65
+ if (line.match(/^[-*•]\s/) || line.match(/^\d+\.\s/)) {
66
+ const itemText = line.replace(/^[-*•\d+.]\s+/, '');
67
+ if (!inList) {
68
+ inList = true;
69
+ listItems = [];
70
+ }
71
+ listItems.push(itemText);
72
+ continue;
73
+ } else if (inList && line.trim()) {
74
+ // End list
75
+ html += `<ul class="list-none space-y-2 ml-0"><li class="p-3 bg-gray-100 rounded border-l-4 border-blue-500">${listItems.map(item => `• ${this.escapeHtml(item)}`).join('</li><li class="p-3 bg-gray-100 rounded border-l-4 border-blue-500">')}</li></ul>`;
76
+ inList = false;
77
+ listItems = [];
78
+ }
79
+
80
+ // Empty lines
81
+ if (!line.trim()) {
82
+ if (!html.endsWith('</p>')) html += '<br>';
83
+ continue;
84
+ }
85
+
86
+ // Regular paragraphs
87
+ if (line.trim()) {
88
+ // Format inline markdown
89
+ let formatted = this.escapeHtml(line);
90
+ formatted = formatted.replace(/\*\*(.*?)\*\*/g, '<strong class="font-bold">$1</strong>');
91
+ formatted = formatted.replace(/\*(.*?)\*/g, '<em class="italic">$1</em>');
92
+ formatted = formatted.replace(/`([^`]+)`/g, '<code class="bg-gray-200 px-2 py-1 rounded font-mono text-sm">$1</code>');
93
+
94
+ html += `<p class="text-gray-700 leading-relaxed">${formatted}</p>`;
95
+ }
96
+ }
97
+
98
+ // Close any remaining list
99
+ if (inList) {
100
+ html += `<ul class="list-none space-y-2 ml-0"><li class="p-3 bg-gray-100 rounded border-l-4 border-blue-500">${listItems.map(item => `• ${this.escapeHtml(item)}`).join('</li><li class="p-3 bg-gray-100 rounded border-l-4 border-blue-500">')}</li></ul>`;
101
+ }
102
+
103
+ return html;
104
+ }
105
+
106
+ static escapeHtml(text) {
107
+ if (typeof text !== 'string') return '';
108
+ return text
109
+ .replace(/&/g, '&amp;')
110
+ .replace(/</g, '&lt;')
111
+ .replace(/>/g, '&gt;')
112
+ .replace(/"/g, '&quot;')
113
+ .replace(/'/g, '&#039;');
114
+ }
115
+ }
116
+
117
+ export default HTMLWrapper;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -8,6 +8,7 @@ import { execSync } from 'child_process';
8
8
  import { queries } from './database.js';
9
9
  import ACPConnection from './acp-launcher.js';
10
10
  import { ResponseFormatter } from './response-formatter.js';
11
+ import { HTMLWrapper } from './html-wrapper.js';
11
12
 
12
13
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
14
  const PORT = process.env.PORT || 3000;
@@ -337,7 +338,13 @@ async function processMessage(conversationId, messageId, sessionId, content, age
337
338
  const result = await conn.sendPrompt(content);
338
339
  conn.onUpdate = null;
339
340
 
340
- const responseText = fullText || result?.result || (result?.stopReason ? `Completed: ${result.stopReason}` : 'No response.');
341
+ let responseText = fullText || result?.result || (result?.stopReason ? `Completed: ${result.stopReason}` : 'No response.');
342
+
343
+ // Wrap response in HTML if it's not already
344
+ const isHTML = responseText.trim().startsWith('<');
345
+ if (!isHTML) {
346
+ responseText = HTMLWrapper.wrapResponse(responseText);
347
+ }
341
348
 
342
349
  // Segment and format the response for better display
343
350
  const segments = ResponseFormatter.segmentResponse(responseText);
@@ -348,12 +355,14 @@ async function processMessage(conversationId, messageId, sessionId, content, age
348
355
  blocks,
349
356
  segments,
350
357
  metadata,
351
- updateChunks
358
+ updateChunks,
359
+ isHTML: true
352
360
  } : {
353
361
  text: responseText,
354
362
  segments,
355
363
  metadata,
356
- updateChunks
364
+ updateChunks,
365
+ isHTML: true
357
366
  };
358
367
 
359
368
  const assistantMessage = queries.createMessage(conversationId, 'assistant', messageContent);