let-them-talk 5.4.2 → 5.5.2

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/README.md +3 -2
  2. package/USAGE.md +1 -1
  3. package/cli.js +1193 -1264
  4. package/conversation-templates/autonomous-feature.json +4 -4
  5. package/conversation-templates/code-review.json +3 -3
  6. package/conversation-templates/debug-squad.json +3 -3
  7. package/conversation-templates/feature-build.json +3 -3
  8. package/conversation-templates/research-write.json +3 -3
  9. package/dashboard.html +329 -177
  10. package/dashboard.js +3459 -3476
  11. package/package.json +114 -113
  12. package/scripts/check-dashboard-control-plane.js +7 -63
  13. package/server.js +33 -333
  14. package/templates/debate.json +2 -2
  15. package/templates/managed.json +4 -4
  16. package/templates/pair.json +2 -2
  17. package/templates/review.json +2 -2
  18. package/templates/team.json +3 -3
  19. package/vendor/highlight-github-dark.min.css +10 -0
  20. package/vendor/highlight.min.js +1232 -0
  21. package/vendor/katex-fonts/KaTeX_AMS-Regular.woff2 +0 -0
  22. package/vendor/katex-fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  23. package/vendor/katex-fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  24. package/vendor/katex-fonts/KaTeX_Main-Bold.woff2 +0 -0
  25. package/vendor/katex-fonts/KaTeX_Main-Italic.woff2 +0 -0
  26. package/vendor/katex-fonts/KaTeX_Main-Regular.woff2 +0 -0
  27. package/vendor/katex-fonts/KaTeX_Math-Italic.woff2 +0 -0
  28. package/vendor/katex-fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  29. package/vendor/katex-fonts/KaTeX_Script-Regular.woff2 +0 -0
  30. package/vendor/katex-fonts/KaTeX_Size1-Regular.woff2 +0 -0
  31. package/vendor/katex-fonts/KaTeX_Size2-Regular.woff2 +0 -0
  32. package/vendor/katex-fonts/KaTeX_Size3-Regular.woff2 +0 -0
  33. package/vendor/katex-fonts/KaTeX_Size4-Regular.woff2 +0 -0
  34. package/vendor/katex-fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  35. package/vendor/katex.min.css +1 -0
  36. package/vendor/katex.min.js +1 -0
  37. package/vendor/marked.min.js +6 -0
  38. package/vendor/mermaid.min.js +2314 -0
package/dashboard.html CHANGED
@@ -4252,7 +4252,145 @@
4252
4252
  @media (max-width: 520px) {
4253
4253
  .char-designer { width: 100vw; }
4254
4254
  }
4255
+
4256
+ /* ==================== RICH MESSAGE RENDERING (v5.5) ==================== */
4257
+ .msg-content, .ws-entry-content {
4258
+ line-height: 1.55;
4259
+ font-size: 14px;
4260
+ word-wrap: break-word;
4261
+ }
4262
+ .msg-content p { margin: 0 0 8px; }
4263
+ .msg-content p:last-child { margin-bottom: 0; }
4264
+ .msg-content h1, .msg-content h2, .msg-content h3, .msg-content h4 {
4265
+ margin: 14px 0 6px; font-weight: 600; line-height: 1.3;
4266
+ }
4267
+ .msg-content h1 { font-size: 19px; border-bottom: 1px solid var(--border); padding-bottom: 4px; }
4268
+ .msg-content h2 { font-size: 17px; border-bottom: 1px solid var(--border); padding-bottom: 3px; }
4269
+ .msg-content h3 { font-size: 15px; }
4270
+ .msg-content h4 { font-size: 14px; color: var(--text-muted); }
4271
+ .msg-content ul, .msg-content ol { margin: 6px 0 8px; padding-left: 22px; }
4272
+ .msg-content li { margin: 2px 0; }
4273
+ .msg-content li > input[type="checkbox"] {
4274
+ margin-right: 6px; transform: translateY(1px); accent-color: var(--accent);
4275
+ }
4276
+ .msg-content hr { border: 0; border-top: 1px solid var(--border); margin: 12px 0; }
4277
+ .msg-content a { color: var(--accent); text-decoration: none; }
4278
+ .msg-content a:hover { text-decoration: underline; }
4279
+ .msg-content blockquote {
4280
+ margin: 8px 0; padding: 6px 12px; border-left: 3px solid var(--border);
4281
+ background: var(--surface-2); color: var(--text-muted);
4282
+ }
4283
+ .msg-content img { max-width: 100%; border-radius: 6px; cursor: zoom-in; margin: 4px 0; }
4284
+
4285
+ /* GitHub-quality tables */
4286
+ .msg-content table {
4287
+ width: 100%; border-collapse: collapse; margin: 10px 0;
4288
+ font-size: 13px; overflow: auto; display: block;
4289
+ }
4290
+ .msg-content thead { background: var(--surface-2); position: sticky; top: 0; z-index: 1; }
4291
+ .msg-content th, .msg-content td {
4292
+ border: 1px solid var(--border); padding: 6px 10px; text-align: left; vertical-align: top;
4293
+ }
4294
+ .msg-content th { font-weight: 600; color: var(--text); white-space: nowrap; }
4295
+ .msg-content tbody tr:nth-child(odd) { background: rgba(255,255,255,0.015); }
4296
+ .msg-content tbody tr:hover { background: rgba(88, 166, 255, 0.08); }
4297
+ .msg-content table code { font-size: 12px; }
4298
+
4299
+ /* Code blocks (with language pill + copy button) */
4300
+ .msg-content .code-block {
4301
+ position: relative; margin: 8px 0; border-radius: 6px;
4302
+ background: #0d1117; border: 1px solid #30363d; overflow: hidden;
4303
+ }
4304
+ .msg-content .code-block pre {
4305
+ margin: 0; padding: 12px 14px; overflow-x: auto;
4306
+ font-family: 'JetBrains Mono', 'Consolas', 'Courier New', monospace;
4307
+ font-size: 12.5px; line-height: 1.5;
4308
+ }
4309
+ .msg-content .code-block code.hljs { background: transparent; padding: 0; font-size: inherit; }
4310
+ .msg-content .code-lang-pill {
4311
+ position: absolute; top: 6px; left: 10px; font-size: 10px; font-weight: 600;
4312
+ color: #8b949e; text-transform: uppercase; letter-spacing: 0.5px;
4313
+ pointer-events: none; user-select: none;
4314
+ }
4315
+ .msg-content .code-copy-btn {
4316
+ position: absolute; top: 4px; right: 6px; font-size: 10px;
4317
+ background: #21262d; color: #c9d1d9; border: 1px solid #30363d;
4318
+ border-radius: 4px; padding: 3px 8px; cursor: pointer; opacity: 0; transition: opacity 0.15s, background 0.15s;
4319
+ }
4320
+ .msg-content .code-block:hover .code-copy-btn { opacity: 1; }
4321
+ .msg-content .code-copy-btn:hover { background: #30363d; }
4322
+ .msg-content .code-copy-btn.copied { color: #3fb950; border-color: #3fb950; }
4323
+ .msg-content :not(.code-block) > code {
4324
+ background: var(--surface-2); border: 1px solid var(--border); border-radius: 4px;
4325
+ padding: 1px 5px; font-size: 12px;
4326
+ font-family: 'JetBrains Mono', 'Consolas', 'Courier New', monospace;
4327
+ }
4328
+
4329
+ /* Obsidian-style callouts */
4330
+ .msg-content .callout {
4331
+ display: block; margin: 10px 0; padding: 10px 14px;
4332
+ border-radius: 6px; border-left: 4px solid;
4333
+ background: rgba(255,255,255,0.02);
4334
+ }
4335
+ .msg-content .callout .callout-title {
4336
+ display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 13px;
4337
+ }
4338
+ .msg-content .callout .callout-icon { font-size: 14px; }
4339
+ .msg-content .callout .callout-body { margin-top: 6px; font-size: 13.5px; line-height: 1.55; }
4340
+ .msg-content .callout .callout-body > :first-child { margin-top: 0; }
4341
+ .msg-content .callout .callout-body > :last-child { margin-bottom: 0; }
4342
+ .msg-content details.callout-collapsible > summary { cursor: pointer; user-select: none; list-style: none; }
4343
+ .msg-content details.callout-collapsible > summary::-webkit-details-marker { display: none; }
4344
+ .msg-content details.callout-collapsible > summary::before {
4345
+ content: '▶'; display: inline-block; width: 14px; color: var(--text-muted); transition: transform 0.15s;
4346
+ }
4347
+ .msg-content details.callout-collapsible[open] > summary::before { transform: rotate(90deg); }
4348
+
4349
+ .msg-content .callout-note { border-left-color: #58a6ff; }
4350
+ .msg-content .callout-note .callout-title { color: #58a6ff; }
4351
+ .msg-content .callout-info { border-left-color: #58a6ff; }
4352
+ .msg-content .callout-info .callout-title { color: #58a6ff; }
4353
+ .msg-content .callout-tip { border-left-color: #3fb950; }
4354
+ .msg-content .callout-tip .callout-title { color: #3fb950; }
4355
+ .msg-content .callout-success { border-left-color: #3fb950; }
4356
+ .msg-content .callout-success .callout-title { color: #3fb950; }
4357
+ .msg-content .callout-warning { border-left-color: #e3b341; }
4358
+ .msg-content .callout-warning .callout-title { color: #e3b341; }
4359
+ .msg-content .callout-danger { border-left-color: #f85149; }
4360
+ .msg-content .callout-danger .callout-title { color: #f85149; }
4361
+ .msg-content .callout-question { border-left-color: #bc8cff; }
4362
+ .msg-content .callout-question .callout-title { color: #bc8cff; }
4363
+ .msg-content .callout-summary { border-left-color: #79c0ff; }
4364
+ .msg-content .callout-summary .callout-title { color: #79c0ff; }
4365
+ .msg-content .callout-example { border-left-color: #d2a8ff; }
4366
+ .msg-content .callout-example .callout-title { color: #d2a8ff; }
4367
+ .msg-content .callout-quote { border-left-color: #8b949e; }
4368
+ .msg-content .callout-quote .callout-title { color: #c9d1d9; }
4369
+
4370
+ /* Mermaid container */
4371
+ .msg-content .mermaid-block {
4372
+ margin: 10px 0; padding: 12px; background: #0d1117;
4373
+ border: 1px solid #30363d; border-radius: 6px; overflow-x: auto;
4374
+ display: flex; justify-content: center;
4375
+ }
4376
+ .msg-content .mermaid-block .mermaid { max-width: 100%; }
4377
+
4378
+ /* KaTeX block spacing */
4379
+ .msg-content .katex-display { margin: 10px 0; overflow-x: auto; overflow-y: hidden; }
4380
+
4381
+ /* Image lightbox (click an image in a message) */
4382
+ .image-lightbox {
4383
+ position: fixed; inset: 0; background: rgba(0,0,0,0.85); z-index: 10000;
4384
+ display: flex; align-items: center; justify-content: center;
4385
+ cursor: zoom-out; animation: lightbox-in 0.15s ease-out;
4386
+ }
4387
+ .image-lightbox img { max-width: 94vw; max-height: 94vh; border-radius: 8px; box-shadow: 0 20px 60px rgba(0,0,0,0.8); }
4388
+ @keyframes lightbox-in { from { opacity: 0; } to { opacity: 1; } }
4255
4389
  </style>
4390
+ <!-- Bundled rendering libs (local, offline-safe) -->
4391
+ <link rel="stylesheet" href="/vendor/highlight-github-dark.min.css">
4392
+ <script src="/vendor/marked.min.js"></script>
4393
+ <script src="/vendor/highlight.min.js"></script>
4256
4394
  </head>
4257
4395
  <body>
4258
4396
 
@@ -4533,12 +4671,6 @@
4533
4671
  <button onclick="clearAttachment()" style="position:absolute;top:-6px;right:-6px;background:#ef4444;color:#fff;border:none;border-radius:50%;width:18px;height:18px;cursor:pointer;font-size:11px;line-height:18px;padding:0;">X</button>
4534
4672
  <div id="inject-file-name" style="font-size:9px;color:var(--text-dim);margin-top:2px;"></div>
4535
4673
  </div>
4536
- <div id="assistant-private-row" style="display:none;align-items:center;gap:6px;margin-top:6px;font-size:10px;color:var(--text-muted);">
4537
- <label style="display:flex;align-items:center;gap:6px;cursor:pointer;">
4538
- <input type="checkbox" id="assistant-private-optin" style="accent-color:var(--accent)">
4539
- Send privately to Assistant only
4540
- </label>
4541
- </div>
4542
4674
  </div>
4543
4675
  <button class="send-btn" onclick="doInject()" id="inject-btn" disabled>Send</button>
4544
4676
  </div>
@@ -5455,182 +5587,215 @@ function escapeHtml(s) {
5455
5587
  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/\\/g, '&#92;');
5456
5588
  }
5457
5589
 
5458
- // ==================== MARKDOWN RENDERER ====================
5459
-
5460
- function renderMarkdown(text) {
5461
- // Strip null bytes to prevent placeholder collision attacks
5462
- text = text.replace(/\x00/g, '');
5463
- // Extract fenced code blocks first to protect them
5464
- var codeBlocks = [];
5465
- text = text.replace(/```(\w*)\n([\s\S]*?)```/g, function(match, lang, code) {
5466
- var idx = codeBlocks.length;
5467
- codeBlocks.push({ lang: lang, code: code.replace(/\n$/, '') });
5468
- return '\x00CODEBLOCK' + idx + '\x00';
5469
- });
5470
- // Also handle ``` without newline after lang
5471
- text = text.replace(/```(\w*)([\s\S]*?)```/g, function(match, lang, code) {
5472
- var idx = codeBlocks.length;
5473
- codeBlocks.push({ lang: lang, code: code.replace(/^\n/, '').replace(/\n$/, '') });
5474
- return '\x00CODEBLOCK' + idx + '\x00';
5475
- });
5476
-
5477
- // Split into lines for block-level processing
5478
- var lines = text.split('\n');
5479
- var html = '';
5480
- var inList = false;
5481
- var listType = '';
5482
- var inTable = false;
5483
- var tableRows = [];
5484
- var inBlockquote = false;
5485
-
5486
- for (var i = 0; i < lines.length; i++) {
5487
- var line = lines[i];
5488
-
5489
- // Code block placeholder
5490
- var cbMatch = line.match(/^\x00CODEBLOCK(\d+)\x00$/);
5491
- if (cbMatch) {
5492
- if (inList) { html += '</' + listType + '>'; inList = false; }
5493
- if (inTable) { html += renderTable(tableRows); inTable = false; tableRows = []; }
5494
- if (inBlockquote) { html += '</blockquote>'; inBlockquote = false; }
5495
- var cb = codeBlocks[parseInt(cbMatch[1])];
5496
- if (!cb) { html += escapeHtml(line); continue; }
5497
- var langTag = cb.lang ? '<span class="lang-tag">' + escapeHtml(cb.lang) + '</span>' : '';
5498
- html += '<pre>' + langTag + '<code>' + escapeHtml(cb.code) + '</code></pre>';
5499
- continue;
5500
- }
5501
-
5502
- // Horizontal rule
5503
- if (/^(---|\*\*\*|___)$/.test(line.trim())) {
5504
- if (inList) { html += '</' + listType + '>'; inList = false; }
5505
- if (inTable) { html += renderTable(tableRows); inTable = false; tableRows = []; }
5506
- html += '<hr>';
5507
- continue;
5590
+ // ==================== MARKDOWN RENDERER (v5.5+) ====================
5591
+ // Uses marked (GFM) + highlight.js + Obsidian-style callout preprocessor.
5592
+ // Mermaid + KaTeX are lazy-loaded on demand from /vendor/.
5593
+
5594
+ var _markedConfigured = false;
5595
+ var _mermaidLoaded = false;
5596
+
5597
+ function configureMarked() {
5598
+ if (_markedConfigured || typeof window.marked === 'undefined') return;
5599
+ _markedConfigured = true;
5600
+ var renderer = new window.marked.Renderer();
5601
+ renderer.code = function(code, lang) {
5602
+ var codeText = (code && typeof code === 'object' && 'text' in code) ? code.text : code;
5603
+ var codeLang = (code && typeof code === 'object' && 'lang' in code) ? code.lang : lang;
5604
+ if ((codeLang || '').toLowerCase() === 'mermaid') {
5605
+ queueMermaidRender();
5606
+ return '<div class="mermaid-block"><pre class="mermaid">' + escapeHtml(codeText) + '</pre></div>';
5607
+ }
5608
+ var effectiveLang = codeLang && typeof window.hljs !== 'undefined' && window.hljs.getLanguage(codeLang) ? codeLang : '';
5609
+ var highlighted;
5610
+ if (typeof window.hljs !== 'undefined') {
5611
+ try {
5612
+ highlighted = effectiveLang
5613
+ ? window.hljs.highlight(codeText, { language: effectiveLang, ignoreIllegals: true }).value
5614
+ : window.hljs.highlightAuto(codeText).value;
5615
+ } catch (e) { highlighted = escapeHtml(codeText); }
5616
+ } else {
5617
+ highlighted = escapeHtml(codeText);
5508
5618
  }
5619
+ var langLabel = effectiveLang || (codeLang || '');
5620
+ var pill = langLabel ? '<span class="code-lang-pill">' + escapeHtml(langLabel) + '</span>' : '';
5621
+ var copyBtn = '<button class="code-copy-btn" onclick="copyCodeBlock(this)" title="Copy code">Copy</button>';
5622
+ return '<div class="code-block">' + pill + copyBtn + '<pre><code class="hljs' + (effectiveLang ? ' language-' + effectiveLang : '') + '">' + highlighted + '</code></pre></div>';
5623
+ };
5624
+ window.marked.use({ gfm: true, breaks: true, pedantic: false, renderer: renderer });
5625
+ }
5626
+
5627
+ var CALLOUT_MAP = {
5628
+ note: { icon: '\uD83D\uDCDD', cls: 'callout-note', label: 'Note' },
5629
+ info: { icon: '\u2139\uFE0F', cls: 'callout-info', label: 'Info' },
5630
+ tip: { icon: '\uD83D\uDCA1', cls: 'callout-tip', label: 'Tip' },
5631
+ success: { icon: '\u2705', cls: 'callout-success', label: 'Success' },
5632
+ warning: { icon: '\u26A0\uFE0F', cls: 'callout-warning', label: 'Warning' },
5633
+ danger: { icon: '\uD83D\uDEA8', cls: 'callout-danger', label: 'Danger' },
5634
+ question: { icon: '\u2753', cls: 'callout-question', label: 'Question' },
5635
+ summary: { icon: '\uD83D\uDCCB', cls: 'callout-summary', label: 'Summary' },
5636
+ example: { icon: '\uD83E\uDDEA', cls: 'callout-example', label: 'Example' },
5637
+ quote: { icon: '\uD83D\uDCAC', cls: 'callout-quote', label: 'Quote' },
5638
+ };
5639
+ var _calloutStore = {};
5509
5640
 
5510
- // Table detection
5511
- if (line.indexOf('|') !== -1 && line.trim().charAt(0) === '|') {
5512
- if (inList) { html += '</' + listType + '>'; inList = false; }
5513
- if (inBlockquote) { html += '</blockquote>'; inBlockquote = false; }
5514
- // Check if this is a separator row
5515
- if (/^\|[\s\-:|]+\|$/.test(line.trim())) {
5516
- // separator, skip but mark table as started
5517
- if (!inTable) inTable = true;
5518
- continue;
5641
+ function preprocessCallouts(text) {
5642
+ var lines = text.split('\n');
5643
+ var out = [];
5644
+ var i = 0;
5645
+ while (i < lines.length) {
5646
+ var m = lines[i].match(/^\s*>\s*\[!([a-z]+)\](-?)\s*(.*)$/i);
5647
+ if (m) {
5648
+ var typeKey = m[1].toLowerCase();
5649
+ var meta = CALLOUT_MAP[typeKey] || CALLOUT_MAP.note;
5650
+ var collapsible = m[2] === '-';
5651
+ var title = (m[3] || '').trim() || meta.label;
5652
+ var body = [];
5653
+ i++;
5654
+ while (i < lines.length && /^\s*>\s?/.test(lines[i])) {
5655
+ body.push(lines[i].replace(/^\s*>\s?/, ''));
5656
+ i++;
5519
5657
  }
5520
- inTable = true;
5521
- tableRows.push(line);
5522
- continue;
5523
- } else if (inTable) {
5524
- html += renderTable(tableRows);
5525
- inTable = false;
5526
- tableRows = [];
5527
- }
5528
-
5529
- // Headers
5530
- var hMatch = line.match(/^(#{1,4})\s+(.+)/);
5531
- if (hMatch) {
5532
- if (inList) { html += '</' + listType + '>'; inList = false; }
5533
- if (inBlockquote) { html += '</blockquote>'; inBlockquote = false; }
5534
- var level = hMatch[1].length;
5535
- html += '<h' + level + '>' + inlineMarkdown(hMatch[2]) + '</h' + level + '>';
5658
+ var bodyRaw = body.join('\n');
5659
+ var bodyHtml = bodyRaw ? window.marked.parse(bodyRaw) : '';
5660
+ var id = '__CALLOUT_' + Math.random().toString(36).slice(2, 10) + '__';
5661
+ var openTag = collapsible ? 'details' : 'div';
5662
+ var titleTag = collapsible ? 'summary' : 'div';
5663
+ var html = '<' + openTag + ' class="callout ' + meta.cls + (collapsible ? ' callout-collapsible' : '') + '"' + (collapsible ? ' open' : '') + '>' +
5664
+ '<' + titleTag + ' class="callout-title"><span class="callout-icon">' + meta.icon + '</span>' + escapeHtml(title) + '</' + titleTag + '>' +
5665
+ (bodyHtml ? '<div class="callout-body">' + bodyHtml + '</div>' : '') +
5666
+ '</' + openTag + '>';
5667
+ _calloutStore[id] = html;
5668
+ out.push(id);
5536
5669
  continue;
5537
5670
  }
5671
+ out.push(lines[i]);
5672
+ i++;
5673
+ }
5674
+ return out.join('\n');
5675
+ }
5676
+ function postprocessCallouts(html) {
5677
+ return html
5678
+ .replace(/<p>(__CALLOUT_[a-z0-9]+__)<\/p>/g, function(_, id) { return _calloutStore[id] || id; })
5679
+ .replace(/__CALLOUT_[a-z0-9]+__/g, function(id) { return _calloutStore[id] || id; });
5680
+ }
5538
5681
 
5539
- // Blockquote
5540
- if (line.match(/^>\s?(.*)$/)) {
5541
- if (inList) { html += '</' + listType + '>'; inList = false; }
5542
- var bqContent = line.replace(/^>\s?/, '');
5543
- if (!inBlockquote) { html += '<blockquote>'; inBlockquote = true; }
5544
- html += inlineMarkdown(bqContent) + '<br>';
5545
- continue;
5546
- } else if (inBlockquote) {
5547
- html += '</blockquote>';
5548
- inBlockquote = false;
5549
- }
5550
-
5551
- // Unordered list
5552
- if (line.match(/^[\s]*[-*+]\s+(.+)/)) {
5553
- if (!inList || listType !== 'ul') {
5554
- if (inList) html += '</' + listType + '>';
5555
- html += '<ul>';
5556
- inList = true;
5557
- listType = 'ul';
5558
- }
5559
- html += '<li>' + inlineMarkdown(line.replace(/^[\s]*[-*+]\s+/, '')) + '</li>';
5560
- continue;
5561
- }
5682
+ var _mathStore = {};
5683
+ function preprocessMath(text) {
5684
+ text = text.replace(/\$\$([\s\S]+?)\$\$/g, function(_, expr) {
5685
+ var id = '__MATHBLOCK_' + Math.random().toString(36).slice(2, 10) + '__';
5686
+ _mathStore[id] = { mode: 'block', expr: expr };
5687
+ queueKatexRender();
5688
+ return id;
5689
+ });
5690
+ text = text.replace(/(^|[^a-zA-Z0-9_$])\$([^\$\n]+?)\$(?=[^a-zA-Z0-9_$]|$)/g, function(match, pre, expr) {
5691
+ var id = '__MATHINLINE_' + Math.random().toString(36).slice(2, 10) + '__';
5692
+ _mathStore[id] = { mode: 'inline', expr: expr };
5693
+ queueKatexRender();
5694
+ return pre + id;
5695
+ });
5696
+ return text;
5697
+ }
5698
+ function renderMathPlaceholders(container) {
5699
+ if (typeof window.katex === 'undefined' || !container) return;
5700
+ var html = container.innerHTML;
5701
+ var next = html
5702
+ .replace(/__MATHBLOCK_[a-z0-9]+__/g, function(id) {
5703
+ var entry = _mathStore[id]; if (!entry) return id;
5704
+ try { return window.katex.renderToString(entry.expr, { displayMode: true, throwOnError: false }); } catch (e) { return id; }
5705
+ })
5706
+ .replace(/__MATHINLINE_[a-z0-9]+__/g, function(id) {
5707
+ var entry = _mathStore[id]; if (!entry) return id;
5708
+ try { return window.katex.renderToString(entry.expr, { displayMode: false, throwOnError: false }); } catch (e) { return id; }
5709
+ });
5710
+ if (next !== html) container.innerHTML = next;
5711
+ }
5562
5712
 
5563
- // Ordered list
5564
- if (line.match(/^[\s]*\d+\.\s+(.+)/)) {
5565
- if (!inList || listType !== 'ol') {
5566
- if (inList) html += '</' + listType + '>';
5567
- html += '<ol>';
5568
- inList = true;
5569
- listType = 'ol';
5713
+ function loadScriptOnce(src) {
5714
+ return new Promise(function(resolve, reject) {
5715
+ var existing = document.querySelector('script[data-vsrc="' + src + '"]');
5716
+ if (existing) { if (existing.dataset.loaded === '1') return resolve(); existing.addEventListener('load', function(){ resolve(); }); return; }
5717
+ var s = document.createElement('script'); s.src = src; s.async = true; s.dataset.vsrc = src;
5718
+ s.onload = function(){ s.dataset.loaded = '1'; resolve(); };
5719
+ s.onerror = function(){ reject(new Error('failed to load ' + src)); };
5720
+ document.head.appendChild(s);
5721
+ });
5722
+ }
5723
+ function loadStyleOnce(href) {
5724
+ if (document.querySelector('link[data-vhref="' + href + '"]')) return;
5725
+ var l = document.createElement('link'); l.rel = 'stylesheet'; l.href = href; l.dataset.vhref = href;
5726
+ document.head.appendChild(l);
5727
+ }
5728
+ var _mermaidRenderScheduled = false;
5729
+ function queueMermaidRender() {
5730
+ if (_mermaidRenderScheduled) return;
5731
+ _mermaidRenderScheduled = true;
5732
+ setTimeout(function() {
5733
+ _mermaidRenderScheduled = false;
5734
+ loadScriptOnce('/vendor/mermaid.min.js').then(function() {
5735
+ if (!_mermaidLoaded && typeof window.mermaid !== 'undefined') {
5736
+ window.mermaid.initialize({ startOnLoad: false, theme: 'dark', securityLevel: 'strict' });
5737
+ _mermaidLoaded = true;
5570
5738
  }
5571
- html += '<li>' + inlineMarkdown(line.replace(/^[\s]*\d+\.\s+/, '')) + '</li>';
5572
- continue;
5573
- }
5574
-
5575
- // Close list if line doesn't match
5576
- if (inList && line.trim() === '') {
5577
- html += '</' + listType + '>';
5578
- inList = false;
5579
- }
5580
-
5581
- // Empty line
5582
- if (line.trim() === '') {
5583
- continue;
5584
- }
5585
-
5586
- // Regular paragraph
5587
- if (inList) { html += '</' + listType + '>'; inList = false; }
5588
- html += '<p>' + inlineMarkdown(line) + '</p>';
5739
+ if (typeof window.mermaid !== 'undefined') {
5740
+ try { window.mermaid.run({ querySelector: '.mermaid:not([data-processed="true"])' }); } catch (e) {}
5741
+ }
5742
+ }).catch(function(){});
5743
+ }, 50);
5744
+ }
5745
+ var _katexRenderScheduled = false;
5746
+ function queueKatexRender() {
5747
+ if (_katexRenderScheduled) return;
5748
+ _katexRenderScheduled = true;
5749
+ setTimeout(function() {
5750
+ _katexRenderScheduled = false;
5751
+ loadStyleOnce('/vendor/katex.min.css');
5752
+ loadScriptOnce('/vendor/katex.min.js').then(function() {
5753
+ document.querySelectorAll('.msg-content, .ws-entry-content').forEach(function(el) { renderMathPlaceholders(el); });
5754
+ }).catch(function(){});
5755
+ }, 50);
5756
+ }
5757
+
5758
+ function copyCodeBlock(btn) {
5759
+ var codeEl = btn.parentElement.querySelector('pre code');
5760
+ if (!codeEl) return;
5761
+ var text = codeEl.innerText;
5762
+ if (navigator.clipboard && navigator.clipboard.writeText) {
5763
+ navigator.clipboard.writeText(text).then(function() {
5764
+ var old = btn.textContent; btn.textContent = 'Copied!'; btn.classList.add('copied');
5765
+ setTimeout(function(){ btn.textContent = old; btn.classList.remove('copied'); }, 1200);
5766
+ });
5589
5767
  }
5590
-
5591
- // Close any open elements
5592
- if (inList) html += '</' + listType + '>';
5593
- if (inTable) html += renderTable(tableRows);
5594
- if (inBlockquote) html += '</blockquote>';
5595
-
5596
- return html;
5597
5768
  }
5598
5769
 
5599
- function inlineMarkdown(text) {
5600
- var html = escapeHtml(text);
5601
- // Inline code (must be before bold/italic to avoid conflicts)
5602
- html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
5603
- // Bold + italic
5604
- html = html.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
5605
- // Bold
5606
- html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
5607
- // Italic
5608
- html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
5609
- // Strikethrough
5610
- html = html.replace(/~~(.+?)~~/g, '<del>$1</del>');
5770
+ function renderMarkdown(text) {
5771
+ if (typeof text !== 'string' || !text) return '';
5772
+ text = text.replace(/\x00/g, '');
5773
+ if (typeof window.marked === 'undefined') return escapeHtml(text).replace(/\n/g, '<br>');
5774
+ configureMarked();
5775
+ text = preprocessMath(text);
5776
+ text = preprocessCallouts(text);
5777
+ var html;
5778
+ try { html = window.marked.parse(text); } catch (e) { html = escapeHtml(text).replace(/\n/g, '<br>'); }
5779
+ html = postprocessCallouts(html);
5780
+ if (html.indexOf('class="mermaid"') !== -1) queueMermaidRender();
5611
5781
  return html;
5612
5782
  }
5613
5783
 
5614
- function renderTable(rows) {
5615
- if (rows.length === 0) return '';
5616
- var html = '<table>';
5617
- for (var i = 0; i < rows.length; i++) {
5618
- var cells = rows[i].split('|').filter(function(c, idx, arr) {
5619
- return idx > 0 && idx < arr.length - 1;
5620
- });
5621
- var tag = (i === 0) ? 'th' : 'td';
5622
- html += '<tr>';
5623
- for (var j = 0; j < cells.length; j++) {
5624
- html += '<' + tag + '>' + inlineMarkdown(cells[j].trim()) + '</' + tag + '>';
5625
- }
5626
- html += '</tr>';
5784
+ // Click-to-expand image lightbox (delegated on document)
5785
+ document.addEventListener('click', function(e) {
5786
+ var t = e.target;
5787
+ if (t && t.tagName === 'IMG' && t.closest && t.closest('.msg-content')) {
5788
+ var overlay = document.createElement('div');
5789
+ overlay.className = 'image-lightbox';
5790
+ overlay.addEventListener('click', function(){ overlay.remove(); });
5791
+ var img = document.createElement('img'); img.src = t.src; overlay.appendChild(img);
5792
+ document.body.appendChild(overlay);
5627
5793
  }
5628
- html += '</table>';
5629
- return html;
5630
- }
5794
+ }, true);
5631
5795
 
5632
5796
  // ==================== AGENT MONITORING ====================
5633
5797
 
5798
+
5634
5799
  var PROVIDER_COLORS = { claude: '#d97706', anthropic: '#d97706', openai: '#10b981', gemini: '#3b82f6', google: '#3b82f6' };
5635
5800
 
5636
5801
  function getProviderBadge(provider) {
@@ -6149,7 +6314,6 @@ window.scopedApiUrl = scopedApiUrl;
6149
6314
  function updateSendBtn() {
6150
6315
  var target = document.getElementById('inject-target').value;
6151
6316
  var content = document.getElementById('inject-content').value.trim();
6152
- updateAssistantPrivateVisibility();
6153
6317
  document.getElementById('inject-btn').disabled = !target || (!content && !_attachedFile);
6154
6318
  }
6155
6319
 
@@ -6173,15 +6337,6 @@ function renderMainBranchOnlyView(elementId, surface) {
6173
6337
  if (el) el.innerHTML = mainBranchOnlyViewHtml(surface);
6174
6338
  }
6175
6339
 
6176
- function updateAssistantPrivateVisibility() {
6177
- var row = document.getElementById('assistant-private-row');
6178
- var checkbox = document.getElementById('assistant-private-optin');
6179
- var isAssistantTarget = document.getElementById('inject-target').value === 'Assistant';
6180
- if (!row || !checkbox) return;
6181
- row.style.display = isAssistantTarget ? 'flex' : 'none';
6182
- if (!isAssistantTarget) checkbox.checked = false;
6183
- }
6184
-
6185
6340
  // ==================== FILE ATTACHMENT ====================
6186
6341
  var _attachedFile = null; // { name, mimeType, base64 }
6187
6342
 
@@ -6218,9 +6373,6 @@ function doInject() {
6218
6373
  if (!content && _attachedFile) content = 'Analyze this image';
6219
6374
 
6220
6375
  var payload = { to: target, content: content };
6221
- if (target === 'Assistant') {
6222
- payload.assistant_private = !!document.getElementById('assistant-private-optin').checked;
6223
- }
6224
6376
 
6225
6377
  // Include attachment if present
6226
6378
  if (_attachedFile) {