agentgui 1.0.127 → 1.0.128

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.127",
3
+ "version": "1.0.128",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -991,81 +991,64 @@ class StreamingRenderer {
991
991
  * Render code with basic syntax highlighting
992
992
  */
993
993
  static renderCodeWithHighlight(code, esc) {
994
- // Escape HTML first
995
- let highlighted = esc(code);
994
+ // Tokenize-then-replace approach: collect spans as tokens so regexes
995
+ // never see previously-injected HTML, preventing cascading corruption.
996
+ const tokens = [];
997
+ const mkToken = (cls, text) => {
998
+ const id = `\x00T${tokens.length}\x00`;
999
+ tokens.push({ id, cls, text });
1000
+ return id;
1001
+ };
1002
+
1003
+ let src = code;
996
1004
 
997
- // Detect if this is JSON and apply JSON-specific highlighting
1005
+ // Detect JSON
998
1006
  const isJSON = (code.trim().startsWith('{') || code.trim().startsWith('[')) &&
999
1007
  code.includes('"') && (code.includes(':') || code.includes(','));
1000
1008
 
1001
1009
  if (isJSON) {
1002
- // JSON-specific highlighting
1003
- const jsonHighlights = [
1004
- // Property names (keys) in quotes
1005
- { pattern: /"([^"]+)"\s*:/g, replacement: '"<span style="color:#3b82f6;font-weight:600">$1</span>":' },
1006
- // String values
1007
- { pattern: /:\s*"([^"]*)"/g, replacement: ': "<span style="color:#10b981">$1</span>"' },
1008
- // Numbers
1009
- { pattern: /:\s*(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, replacement: ': <span style="color:#f59e0b">$1</span>' },
1010
- // Booleans and null
1011
- { pattern: /:\s*(true|false|null)/g, replacement: ': <span style="color:#ef4444">$1</span>' },
1012
- // Array/object brackets
1013
- { pattern: /([\[\]{}])/g, replacement: '<span style="color:#6b7280;font-weight:600">$1</span>' },
1014
- ];
1015
-
1016
- jsonHighlights.forEach(({ pattern, replacement }) => {
1017
- highlighted = highlighted.replace(pattern, replacement);
1018
- });
1010
+ // JSON keys
1011
+ src = src.replace(/"([^"]+)"\s*:/g, (m, k) => `"${mkToken('jk', k)}":`);
1012
+ // JSON string values
1013
+ src = src.replace(/:\s*"([^"]*)"/g, (m, v) => `: "${mkToken('js', v)}"`);
1014
+ // JSON numbers
1015
+ src = src.replace(/:\s*(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, (m, n) => `: ${mkToken('jn', n)}`);
1016
+ // JSON booleans/null
1017
+ src = src.replace(/:\s*(true|false|null)/g, (m, b) => `: ${mkToken('jb', b)}`);
1019
1018
  } else {
1020
- // General code syntax highlighting
1021
- const highlights = [
1022
- // Comments (do these first to avoid conflicts)
1023
- { pattern: /(\/\/[^\n]*)/g, replacement: '<span style="color:#6b7280;font-style:italic">$1</span>' },
1024
- { pattern: /(\/\*[\s\S]*?\*\/)/g, replacement: '<span style="color:#6b7280;font-style:italic">$1</span>' },
1025
- { pattern: /(#[^\n]*)/g, replacement: '<span style="color:#6b7280;font-style:italic">$1</span>' },
1026
-
1027
- // Strings (improved to handle escaped quotes)
1028
- { pattern: /(["'])(?:[^\\]|\\.)*?\1/g, replacement: (match) => `<span style="color:#10b981">${match}</span>` },
1029
-
1030
- // Template literals (backticks)
1031
- { pattern: /`([^`]*)`/g, replacement: '<span style="color:#10b981">`$1`</span>' },
1032
-
1033
- // Keywords
1034
- { pattern: /\b(function|const|let|var|class|import|export|async|await|return|if|else|for|while|try|catch|throw|new|typeof|instanceof|this|super|switch|case|default|break|continue|do)\b/g,
1035
- replacement: '<span style="color:#8b5cf6;font-weight:600">$1</span>' },
1036
- { pattern: /\b(def|class|import|from|return|if|elif|else|for|while|try|except|raise|with|as|lambda|pass|break|continue|yield|global|nonlocal)\b/g,
1037
- replacement: '<span style="color:#8b5cf6;font-weight:600">$1</span>' },
1038
- { pattern: /\b(public|private|protected|static|final|abstract|interface|extends|implements|package|void|int|string|boolean|float|double|char)\b/g,
1039
- replacement: '<span style="color:#8b5cf6;font-weight:600">$1</span>' },
1040
-
1041
- // Type annotations
1042
- { pattern: /:\s*([A-Z][a-zA-Z0-9_]*)/g, replacement: ': <span style="color:#0891b2">$1</span>' },
1043
-
1044
- // Numbers
1045
- { pattern: /\b(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\b/g, replacement: '<span style="color:#f59e0b">$1</span>' },
1046
-
1047
- // Booleans and null
1048
- { pattern: /\b(true|false|null|undefined|None|True|False|nil)\b/g, replacement: '<span style="color:#ef4444">$1</span>' },
1049
-
1050
- // Function/method names (improved)
1051
- { pattern: /\b([a-zA-Z_][a-zA-Z0-9_]*)(?=\s*\()/g, replacement: '<span style="color:#3b82f6">$1</span>' },
1052
-
1053
- // Operators
1054
- { pattern: /(===|!==|==|!=|<=|>=|&&|\|\||\+=|-=|\*=|\/=|%=|=>|->)/g, replacement: '<span style="color:#a855f7">$1</span>' },
1055
- ];
1056
-
1057
- // Apply highlights
1058
- highlights.forEach(({ pattern, replacement }) => {
1059
- if (typeof replacement === 'function') {
1060
- highlighted = highlighted.replace(pattern, replacement);
1061
- } else {
1062
- highlighted = highlighted.replace(pattern, replacement);
1063
- }
1064
- });
1019
+ // Comments
1020
+ src = src.replace(/(\/\/[^\n]*)/g, (m) => mkToken('cm', m));
1021
+ src = src.replace(/(\/\*[\s\S]*?\*\/)/g, (m) => mkToken('cm', m));
1022
+ src = src.replace(/(#[^\n]*)/g, (m) => mkToken('cm', m));
1023
+ // Strings
1024
+ src = src.replace(/(["'])(?:[^\\]|\\.)*?\1/g, (m) => mkToken('st', m));
1025
+ src = src.replace(/`([^`]*)`/g, (m) => mkToken('st', m));
1026
+ // Keywords
1027
+ src = src.replace(/\b(function|const|let|var|class|import|export|async|await|return|if|else|for|while|try|catch|throw|new|typeof|instanceof|this|super|switch|case|default|break|continue|do|def|from|elif|except|raise|with|as|lambda|pass|yield|global|nonlocal|public|private|protected|static|final|abstract|interface|extends|implements|package|void)\b/g, (m) => mkToken('kw', m));
1028
+ // Booleans/null
1029
+ src = src.replace(/\b(true|false|null|undefined|None|True|False|nil)\b/g, (m) => mkToken('bl', m));
1030
+ // Numbers
1031
+ src = src.replace(/\b(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\b/g, (m) => mkToken('nu', m));
1032
+ // Functions
1033
+ src = src.replace(/\b([a-zA-Z_][a-zA-Z0-9_]*)(?=\s*\()/g, (m) => mkToken('fn', m));
1034
+ // Operators
1035
+ src = src.replace(/(===|!==|==|!=|<=|>=|&&|\|\||\+=|-=|\*=|\/=|%=|=>|->)/g, (m) => mkToken('op', m));
1036
+ }
1037
+
1038
+ // Now escape HTML on the tokenized source (tokens are safe null-byte markers)
1039
+ let highlighted = esc(src);
1040
+
1041
+ // Replace tokens with actual styled spans
1042
+ const styles = {
1043
+ jk: 'color:#3b82f6;font-weight:600', js: 'color:#10b981', jn: 'color:#f59e0b', jb: 'color:#ef4444',
1044
+ cm: 'color:#6b7280;font-style:italic', st: 'color:#10b981', kw: 'color:#8b5cf6;font-weight:600',
1045
+ bl: 'color:#ef4444', nu: 'color:#f59e0b', fn: 'color:#3b82f6', op: 'color:#a855f7'
1046
+ };
1047
+ for (const { id, cls, text } of tokens) {
1048
+ highlighted = highlighted.replace(id, `<span style="${styles[cls]}">${esc(text)}</span>`);
1065
1049
  }
1066
1050
 
1067
- // Use a dark theme that works well for code
1068
- return `<pre style="background:#1e293b;padding:1rem;border-radius:0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.875rem;line-height:1.6;color:#e2e8f0;border:1px solid #334155;box-shadow:0 2px 4px rgba(0,0,0,0.1)">${highlighted}</pre>`;
1051
+ return `<pre style="background:#1e293b;padding:1rem;border-radius:0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.875rem;line-height:1.6;color:#e2e8f0;border:1px solid #334155">${highlighted}</pre>`;
1069
1052
  }
1070
1053
 
1071
1054
  /**