agentgui 1.0.106 → 1.0.108

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/.prd CHANGED
@@ -0,0 +1,66 @@
1
+ # Response Block Rendering Analysis & Fix
2
+
3
+ ## Problem Statement
4
+ Response blocks in GUI are not being beautified/rendered correctly. Issues:
5
+ - Blocks may be separated incorrectly
6
+ - Detection logic may be failing
7
+ - Rendering may not match intended HTML structure
8
+ - Need perfect rendering with no edge cases or surprises
9
+
10
+ ## Investigation Phase (Completed)
11
+ - [x] Search for response block rendering code in app.js
12
+ - [x] Search for block type definitions and separators
13
+ - [x] Review message bubble structure and flex/layout CSS
14
+ - [x] Examine response block detection logic in app.js
15
+ - [x] Check HTML/CSS rendering for response blocks
16
+ - [x] Review message rendering pipeline
17
+
18
+ ## Root Cause Analysis (Findings)
19
+ - [x] Traced message flow: Claude outputs (stream-json) → claude-runner.js parses JSON → processMessage collects blocks
20
+ - [x] server.js line 738: extracts output.message.content blocks from assistant responses
21
+ - [x] app.js lines 194-209: renderMessageBlock handles each block based on type field
22
+ - [x] Block types: 'text', 'tool_use', 'tool_result', 'file_operation'
23
+ - [x] Text blocks parsed for markdown code blocks with regex
24
+ - [x] Found potential issue: blocks may not be properly formatted/beautified
25
+ - [x] HTML structure looks correct but need to verify actual output
26
+
27
+ ## Edge Cases & Issues
28
+ - [ ] Multiple consecutive blocks of same type
29
+ - [ ] Empty blocks or whitespace handling
30
+ - [ ] Mixed block types in same message
31
+ - [ ] Long content overflow in blocks
32
+ - [ ] Special characters in block content
33
+ - [ ] Code/pre tag interaction with beautification
34
+
35
+ ## Root Cause Identified
36
+ ROOT CAUSE: Text blocks from Claude contain MARKDOWN but are being rendered as plain escaped text
37
+ - Claude outputs blocks with type:'text' containing markdown (headings, lists, bold, code, links)
38
+ - Current code only extracts code blocks with regex, escapes everything else
39
+ - Markdown structure (headings, lists, etc.) is lost
40
+
41
+ ## Solution: Implement Markdown Parser (Completed)
42
+ - [x] Create markdown to HTML converter (markdownToHtml method)
43
+ - [x] Handle markdown syntax: # headings, - lists, **bold**, `code`, ```code blocks```, links
44
+ - [x] Integrate parser into renderMessageBlock for text type
45
+ - [x] Ensure HTML is properly escaped to prevent XSS
46
+ - [x] Tested all markdown features: h1-h6, ul/ol lists, bold/italic, inline code, code blocks, links
47
+ - [x] Verify blocks render with proper semantic HTML
48
+
49
+ ## CSS Updates (Completed)
50
+ - [x] Added styling for markdown elements (h1-h3, ul/ol, lists, inline code, links)
51
+ - [x] Remove duplicate .code-block definition (removed old version at lines 1454-1470)
52
+ - [x] Keep modern version at line 842 with CSS variables
53
+
54
+ ## Execution & Verification (Ready)
55
+ - [ ] Commit changes to git
56
+ - [ ] Test with real Claude Code execution output via browser
57
+ - [ ] Verify blocks render with perfect formatting
58
+ - [ ] Verify no edge cases or surprises
59
+ - [ ] Verify layout doesn't break on wide screens
60
+
61
+ ## Completion Criteria
62
+ - Response blocks detect correctly
63
+ - Blocks render with proper styling
64
+ - All block types display correctly
65
+ - No edge cases or surprises
66
+ - Verified through real execution
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.106",
3
+ "version": "1.0.108",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/static/app.js CHANGED
@@ -222,16 +222,8 @@ class GMGUIApp {
222
222
  switch (block.type) {
223
223
  case 'text': {
224
224
  const text = block.text || '';
225
- const parts = this.parseMarkdownCodeBlocks(text);
226
- html += '<div class="block-text">';
227
- for (const part of parts) {
228
- if (part.type === 'text') {
229
- html += `<div>${this.escapeHtml(part.content)}</div>`;
230
- } else if (part.type === 'code') {
231
- html += this.renderCodeBlock(part.language, part.content);
232
- }
233
- }
234
- html += '</div>';
225
+ const beautified = this.markdownToHtml(text);
226
+ html += `<div class="block-text">${beautified}</div>`;
235
227
  break;
236
228
  }
237
229
 
@@ -454,6 +446,122 @@ class GMGUIApp {
454
446
  return parts.length > 0 ? parts : [{ type: 'text', content: text }];
455
447
  }
456
448
 
449
+ markdownToHtml(markdown) {
450
+ const lines = markdown.split('\n');
451
+ let html = '';
452
+ let inList = false;
453
+ let listType = null;
454
+ let i = 0;
455
+
456
+ while (i < lines.length) {
457
+ const line = lines[i];
458
+ const trimmed = line.trim();
459
+
460
+ // Code blocks
461
+ if (trimmed.startsWith('```')) {
462
+ if (inList) {
463
+ html += listType === 'ul' ? '</ul>' : '</ol>';
464
+ inList = false;
465
+ }
466
+ const match = trimmed.match(/^```(\w*)/);
467
+ const lang = (match && match[1]) || 'text';
468
+ i++;
469
+ const codeLines = [];
470
+ while (i < lines.length && !lines[i].trim().startsWith('```')) {
471
+ codeLines.push(lines[i]);
472
+ i++;
473
+ }
474
+ html += `<div class="code-block" data-language="${this.escapeHtml(lang)}"><pre><code>${this.escapeHtml(codeLines.join('\n'))}</code></pre></div>`;
475
+ i++;
476
+ continue;
477
+ }
478
+
479
+ // Headings
480
+ if (trimmed.startsWith('#')) {
481
+ if (inList) {
482
+ html += listType === 'ul' ? '</ul>' : '</ol>';
483
+ inList = false;
484
+ }
485
+ const match = trimmed.match(/^(#+)\s+(.*)/);
486
+ if (match) {
487
+ const level = match[1].length;
488
+ const text = this.escapeAndFormatInline(match[2]);
489
+ html += `<h${level}>${text}</h${level}>`;
490
+ i++;
491
+ continue;
492
+ }
493
+ }
494
+
495
+ // Lists
496
+ if (trimmed.match(/^[-*+]\s/)) {
497
+ if (!inList) {
498
+ html += '<ul>';
499
+ inList = true;
500
+ listType = 'ul';
501
+ }
502
+ const match = trimmed.match(/^[-*+]\s+(.*)/);
503
+ if (match) {
504
+ const text = this.escapeAndFormatInline(match[1]);
505
+ html += `<li>${text}</li>`;
506
+ }
507
+ i++;
508
+ continue;
509
+ }
510
+
511
+ if (trimmed.match(/^\d+\.\s/)) {
512
+ if (!inList || listType !== 'ol') {
513
+ if (inList) html += '</ul>';
514
+ html += '<ol>';
515
+ inList = true;
516
+ listType = 'ol';
517
+ }
518
+ const match = trimmed.match(/^\d+\.\s+(.*)/);
519
+ if (match) {
520
+ const text = this.escapeAndFormatInline(match[1]);
521
+ html += `<li>${text}</li>`;
522
+ }
523
+ i++;
524
+ continue;
525
+ }
526
+
527
+ // End list if not a list item
528
+ if (inList && trimmed && !trimmed.match(/^[-*+]\s/) && !trimmed.match(/^\d+\.\s/)) {
529
+ html += listType === 'ul' ? '</ul>' : '</ol>';
530
+ inList = false;
531
+ }
532
+
533
+ // Paragraphs
534
+ if (trimmed) {
535
+ const text = this.escapeAndFormatInline(trimmed);
536
+ html += `<p>${text}</p>`;
537
+ }
538
+
539
+ i++;
540
+ }
541
+
542
+ // Close any open list
543
+ if (inList) {
544
+ html += listType === 'ul' ? '</ul>' : '</ol>';
545
+ }
546
+
547
+ return html;
548
+ }
549
+
550
+ escapeAndFormatInline(text) {
551
+ text = this.escapeHtml(text);
552
+ // Bold and italic (must be before single asterisk)
553
+ text = text.replace(/\*\*\*(.*?)\*\*\*/g, '<strong><em>$1</em></strong>');
554
+ text = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
555
+ text = text.replace(/\*(.*?)\*/g, '<em>$1</em>');
556
+ text = text.replace(/__(.*?)__/g, '<strong>$1</strong>');
557
+ text = text.replace(/_(.*?)_/g, '<em>$1</em>');
558
+ // Inline code
559
+ text = text.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>');
560
+ // Links
561
+ text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
562
+ return text;
563
+ }
564
+
457
565
  renderCodeBlock(language, code) {
458
566
  if (language === 'html') {
459
567
  return `<div class="html-block">
package/static/index.html CHANGED
@@ -26,7 +26,7 @@
26
26
  --color-warning: #f59e0b;
27
27
  --sidebar-width: 300px;
28
28
  --header-height: 52px;
29
- --msg-max-width: 768px;
29
+ --msg-max-width: 100%;
30
30
  }
31
31
 
32
32
  html.dark {
@@ -318,10 +318,12 @@
318
318
  max-width: var(--msg-max-width);
319
319
  margin: 0 auto;
320
320
  width: 100%;
321
- padding: 1.5rem 1rem;
321
+ padding: 1.5rem 2rem;
322
322
  display: flex;
323
323
  flex-direction: column;
324
324
  min-height: 100%;
325
+ padding-left: calc(max(2rem, (100vw - 900px) / 2));
326
+ padding-right: calc(max(2rem, (100vw - 900px) / 2));
325
327
  }
326
328
 
327
329
  #output {
@@ -817,6 +819,49 @@
817
819
  .agent-selector { display: none; }
818
820
  }
819
821
 
822
+ /* ===== SCROLLBAR STYLING ===== */
823
+ ::-webkit-scrollbar {
824
+ width: 10px;
825
+ height: 10px;
826
+ }
827
+
828
+ ::-webkit-scrollbar-track {
829
+ background: transparent;
830
+ }
831
+
832
+ ::-webkit-scrollbar-thumb {
833
+ background: #cbd5e1;
834
+ border-radius: 8px;
835
+ border: 3px solid transparent;
836
+ background-clip: padding-box;
837
+ transition: background-color 0.2s;
838
+ }
839
+
840
+ ::-webkit-scrollbar-thumb:hover {
841
+ background-color: #94a3b8;
842
+ background-clip: padding-box;
843
+ }
844
+
845
+ html.dark ::-webkit-scrollbar-thumb {
846
+ background: #475569;
847
+ background-clip: padding-box;
848
+ }
849
+
850
+ html.dark ::-webkit-scrollbar-thumb:hover {
851
+ background-color: #64748b;
852
+ background-clip: padding-box;
853
+ }
854
+
855
+ /* Firefox scrollbar */
856
+ * {
857
+ scrollbar-width: thin;
858
+ scrollbar-color: #cbd5e1 transparent;
859
+ }
860
+
861
+ html.dark * {
862
+ scrollbar-color: #475569 transparent;
863
+ }
864
+
820
865
  /* ===== RESPONSIVE: TABLET ===== */
821
866
  @media (min-width: 769px) and (max-width: 1024px) {
822
867
  :root { --sidebar-width: 260px; }
package/static/styles.css CHANGED
@@ -1451,24 +1451,6 @@ html, body {
1451
1451
  margin: 1rem 0;
1452
1452
  }
1453
1453
 
1454
- .code-block {
1455
- background: #f5f5f5;
1456
- border-left: 4px solid #007acc;
1457
- padding: 1rem;
1458
- border-radius: 4px;
1459
- overflow-x: auto;
1460
- font-family: 'Courier New', monospace;
1461
- font-size: 0.9rem;
1462
- line-height: 1.5;
1463
- color: #333;
1464
- }
1465
-
1466
- .code-block code {
1467
- color: inherit;
1468
- background: none;
1469
- padding: 0;
1470
- }
1471
-
1472
1454
  /* Inline code */
1473
1455
  p code {
1474
1456
  background: #f0f0f0;
@@ -1734,8 +1716,65 @@ p code {
1734
1716
 
1735
1717
  .block-text {
1736
1718
  color: var(--text-primary);
1737
- white-space: pre-wrap;
1738
- line-height: 1.5;
1719
+ line-height: 1.6;
1720
+ }
1721
+
1722
+ .block-text h1 {
1723
+ font-size: 1.75rem;
1724
+ font-weight: 700;
1725
+ margin: 1rem 0 0.5rem 0;
1726
+ color: var(--text-primary);
1727
+ }
1728
+
1729
+ .block-text h2 {
1730
+ font-size: 1.5rem;
1731
+ font-weight: 700;
1732
+ margin: 0.875rem 0 0.5rem 0;
1733
+ color: var(--text-primary);
1734
+ }
1735
+
1736
+ .block-text h3 {
1737
+ font-size: 1.25rem;
1738
+ font-weight: 600;
1739
+ margin: 0.75rem 0 0.375rem 0;
1740
+ color: var(--text-primary);
1741
+ }
1742
+
1743
+ .block-text p {
1744
+ margin: 0.5rem 0;
1745
+ white-space: normal;
1746
+ }
1747
+
1748
+ .block-text ul,
1749
+ .block-text ol {
1750
+ margin: 0.5rem 0 0.5rem 1.5rem;
1751
+ padding-left: 1rem;
1752
+ }
1753
+
1754
+ .block-text li {
1755
+ margin: 0.25rem 0;
1756
+ }
1757
+
1758
+ .block-text code.inline-code {
1759
+ background: var(--bg-tertiary);
1760
+ color: var(--color-info);
1761
+ padding: 0.15rem 0.4rem;
1762
+ border-radius: 0.25rem;
1763
+ font-family: 'Courier New', monospace;
1764
+ font-size: 0.9em;
1765
+ white-space: nowrap;
1766
+ }
1767
+
1768
+ .block-text a {
1769
+ color: var(--color-primary);
1770
+ text-decoration: none;
1771
+ border-bottom: 1px solid transparent;
1772
+ transition: var(--transition-fast);
1773
+ }
1774
+
1775
+ .block-text a:hover {
1776
+ border-bottom-color: var(--color-primary);
1777
+ text-decoration: underline;
1739
1778
  }
1740
1779
 
1741
1780
  .block-tool-use {