mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-17 05:00:16 +00:00
Fix export-html styling and behavior
- Fix UTF-8 decoding with TextDecoder for base64 session data - Use toolOutput color for expand hints (not borderAccent) - Remove '- click to expand' text to match TUI - Ctrl+O toggles expanded state (not visibility) on tool outputs - Edit diffs always visible (not affected by Ctrl+O) - Remove Ctrl+F override to allow browser search - Replace tabs with 3 spaces in all tool outputs - Fix syntax highlighting default color to use --text - Add more hljs token selectors (property, punctuation, operator) - Compaction uses customMessageBg/Label/Text colors - Model change uses dim color without background - Scroll to end on initial load - Pointer cursor on expandable elements
This commit is contained in:
parent
55fd8d9fed
commit
fcb700701e
1 changed files with 112 additions and 68 deletions
|
|
@ -214,6 +214,14 @@
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Help bar */
|
||||||
|
.help-bar {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--dim);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
.header {
|
.header {
|
||||||
background: var(--container-bg);
|
background: var(--container-bg);
|
||||||
|
|
@ -278,6 +286,10 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.assistant-message > .message-timestamp {
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.assistant-text {
|
.assistant-text {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
}
|
}
|
||||||
|
|
@ -289,6 +301,13 @@
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.thinking-collapsed {
|
||||||
|
display: none;
|
||||||
|
padding: 12px 16px;
|
||||||
|
color: var(--thinkingText);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tool execution */
|
/* Tool execution */
|
||||||
.tool-execution {
|
.tool-execution {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
|
|
@ -305,7 +324,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-path {
|
.tool-path {
|
||||||
color: var(--borderAccent);
|
color: var(--accent);
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,6 +372,7 @@
|
||||||
.tool-output code {
|
.tool-output code {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: none;
|
background: none;
|
||||||
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-output.expandable {
|
.tool-output.expandable {
|
||||||
|
|
@ -387,9 +407,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand-hint {
|
.expand-hint {
|
||||||
color: var(--borderAccent);
|
color: var(--toolOutput);
|
||||||
font-style: italic;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Diff */
|
/* Diff */
|
||||||
|
|
@ -397,6 +415,7 @@
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
white-space: pre;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diff-added { color: var(--toolDiffAdded); }
|
.diff-added { color: var(--toolDiffAdded); }
|
||||||
|
|
@ -406,8 +425,6 @@
|
||||||
/* Model change */
|
/* Model change */
|
||||||
.model-change {
|
.model-change {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
background: var(--info-bg);
|
|
||||||
border-radius: 4px;
|
|
||||||
color: var(--dim);
|
color: var(--dim);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
@ -417,59 +434,41 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compaction */
|
/* Compaction / Branch Summary - matches customMessage colors from TUI */
|
||||||
.compaction {
|
.compaction {
|
||||||
background: var(--info-bg);
|
background: var(--customMessageBg);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compaction-header {
|
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.compaction-header:hover {
|
.compaction-label {
|
||||||
background: var(--selectedBg);
|
color: var(--customMessageLabel);
|
||||||
}
|
|
||||||
|
|
||||||
.compaction-toggle {
|
|
||||||
color: var(--borderAccent);
|
|
||||||
font-size: 10px;
|
|
||||||
transition: transform 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compaction.expanded .compaction-toggle {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.compaction-title {
|
|
||||||
color: var(--text);
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.compaction-collapsed {
|
||||||
|
color: var(--customMessageText);
|
||||||
|
}
|
||||||
|
|
||||||
.compaction-content {
|
.compaction-content {
|
||||||
display: none;
|
display: none;
|
||||||
padding: 0 16px 16px;
|
color: var(--customMessageText);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compaction.expanded .compaction-collapsed {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compaction.expanded .compaction-content {
|
.compaction.expanded .compaction-content {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compaction-summary {
|
|
||||||
background: var(--selectedBg);
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 12px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* System prompt */
|
/* System prompt */
|
||||||
.system-prompt {
|
.system-prompt {
|
||||||
background: var(--info-bg);
|
background: var(--customMessageBg);
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
@ -477,12 +476,12 @@
|
||||||
|
|
||||||
.system-prompt-header {
|
.system-prompt-header {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--warning);
|
color: var(--customMessageLabel);
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.system-prompt-content {
|
.system-prompt-content {
|
||||||
color: var(--dim);
|
color: var(--customMessageText);
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|
@ -656,15 +655,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Syntax highlighting */
|
/* Syntax highlighting */
|
||||||
.hljs { background: transparent; }
|
.hljs { background: transparent; color: var(--text); }
|
||||||
.hljs-comment, .hljs-quote { color: var(--syntaxComment); }
|
.hljs-comment, .hljs-quote { color: var(--syntaxComment); }
|
||||||
.hljs-keyword, .hljs-selector-tag { color: var(--syntaxKeyword); }
|
.hljs-keyword, .hljs-selector-tag { color: var(--syntaxKeyword); }
|
||||||
.hljs-number, .hljs-literal { color: var(--syntaxNumber); }
|
.hljs-number, .hljs-literal { color: var(--syntaxNumber); }
|
||||||
.hljs-string, .hljs-doctag { color: var(--syntaxString); }
|
.hljs-string, .hljs-doctag { color: var(--syntaxString); }
|
||||||
.hljs-title, .hljs-section, .hljs-name { color: var(--syntaxFunction); }
|
.hljs-title, .hljs-section, .hljs-name { color: var(--syntaxFunction); }
|
||||||
.hljs-type, .hljs-class, .hljs-built_in { color: var(--syntaxType); }
|
.hljs-type, .hljs-class, .hljs-built_in { color: var(--syntaxType); }
|
||||||
.hljs-attr, .hljs-variable, .hljs-params { color: var(--syntaxVariable); }
|
.hljs-attr, .hljs-variable, .hljs-params, .hljs-property { color: var(--syntaxVariable); }
|
||||||
.hljs-meta { color: var(--syntaxKeyword); }
|
.hljs-meta { color: var(--syntaxKeyword); }
|
||||||
|
.hljs-punctuation, .hljs-operator { color: var(--syntaxOperator); }
|
||||||
|
.hljs-subst { color: var(--text); }
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
.footer {
|
.footer {
|
||||||
|
|
@ -783,6 +784,7 @@
|
||||||
</aside>
|
</aside>
|
||||||
<main id="content">
|
<main id="content">
|
||||||
<div id="header-container"></div>
|
<div id="header-container"></div>
|
||||||
|
<div class="help-bar">Ctrl+T toggle thinking · Ctrl+O toggle tools · Esc reset</div>
|
||||||
<div id="messages"></div>
|
<div id="messages"></div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
Generated by {{APP_NAME}} on {{GENERATED_DATE}}
|
Generated by {{APP_NAME}} on {{GENERATED_DATE}}
|
||||||
|
|
@ -801,7 +803,14 @@
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const data = JSON.parse(atob(document.getElementById('session-data').textContent));
|
// Decode base64 with proper UTF-8 handling
|
||||||
|
const base64 = document.getElementById('session-data').textContent;
|
||||||
|
const binary = atob(base64);
|
||||||
|
const bytes = new Uint8Array(binary.length);
|
||||||
|
for (let i = 0; i < binary.length; i++) {
|
||||||
|
bytes[i] = binary.charCodeAt(i);
|
||||||
|
}
|
||||||
|
const data = JSON.parse(new TextDecoder('utf-8').decode(bytes));
|
||||||
const { header, entries, leafId, systemPrompt, tools } = data;
|
const { header, entries, leafId, systemPrompt, tools } = data;
|
||||||
|
|
||||||
// Build entry index
|
// Build entry index
|
||||||
|
|
@ -1328,6 +1337,8 @@
|
||||||
|
|
||||||
// Format expandable output (matches old export-html.ts exactly)
|
// Format expandable output (matches old export-html.ts exactly)
|
||||||
function formatExpandableOutput(text, maxLines, lang) {
|
function formatExpandableOutput(text, maxLines, lang) {
|
||||||
|
// Replace tabs with spaces first
|
||||||
|
text = replaceTabs(text);
|
||||||
const lines = text.split('\n');
|
const lines = text.split('\n');
|
||||||
const displayLines = lines.slice(0, maxLines);
|
const displayLines = lines.slice(0, maxLines);
|
||||||
const remaining = lines.length - maxLines;
|
const remaining = lines.length - maxLines;
|
||||||
|
|
@ -1353,7 +1364,7 @@
|
||||||
|
|
||||||
let out = '<div class="tool-output expandable" onclick="this.classList.toggle(\'expanded\')">';
|
let out = '<div class="tool-output expandable" onclick="this.classList.toggle(\'expanded\')">';
|
||||||
out += `<div class="output-preview"><pre><code class="hljs">${previewHighlighted}</code></pre>`;
|
out += `<div class="output-preview"><pre><code class="hljs">${previewHighlighted}</code></pre>`;
|
||||||
out += `<div class="expand-hint">... (${remaining} more lines) - click to expand</div>`;
|
out += `<div class="expand-hint">... (${remaining} more lines)</div>`;
|
||||||
out += '</div>';
|
out += '</div>';
|
||||||
out += `<div class="output-full"><pre><code class="hljs">${highlighted}</code></pre></div></div>`;
|
out += `<div class="output-full"><pre><code class="hljs">${highlighted}</code></pre></div></div>`;
|
||||||
return out;
|
return out;
|
||||||
|
|
@ -1369,7 +1380,7 @@
|
||||||
for (const line of displayLines) {
|
for (const line of displayLines) {
|
||||||
out += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
out += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
||||||
}
|
}
|
||||||
out += `<div class="expand-hint">... (${remaining} more lines) - click to expand</div>`;
|
out += `<div class="expand-hint">... (${remaining} more lines)</div>`;
|
||||||
out += '</div>';
|
out += '</div>';
|
||||||
out += '<div class="output-full">';
|
out += '<div class="output-full">';
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
|
|
@ -1493,13 +1504,8 @@
|
||||||
const diffLines = result.details.diff.split('\n');
|
const diffLines = result.details.diff.split('\n');
|
||||||
html += '<div class="tool-diff">';
|
html += '<div class="tool-diff">';
|
||||||
for (const line of diffLines) {
|
for (const line of diffLines) {
|
||||||
if (line.startsWith('+')) {
|
const cls = line.match(/^\+/) ? 'diff-added' : line.match(/^-/) ? 'diff-removed' : 'diff-context';
|
||||||
html += `<div class="diff-added">${escapeHtml(line)}</div>`;
|
html += `<div class="${cls}">${escapeHtml(replaceTabs(line))}</div>`;
|
||||||
} else if (line.startsWith('-')) {
|
|
||||||
html += `<div class="diff-removed">${escapeHtml(line)}</div>`;
|
|
||||||
} else {
|
|
||||||
html += `<div class="diff-context">${escapeHtml(line)}</div>`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|
@ -1570,7 +1576,10 @@
|
||||||
if (block.type === 'text' && block.text.trim()) {
|
if (block.type === 'text' && block.text.trim()) {
|
||||||
html += `<div class="assistant-text markdown-content">${renderMarkdown(block.text)}</div>`;
|
html += `<div class="assistant-text markdown-content">${renderMarkdown(block.text)}</div>`;
|
||||||
} else if (block.type === 'thinking' && block.thinking.trim()) {
|
} else if (block.type === 'thinking' && block.thinking.trim()) {
|
||||||
|
html += `<div class="thinking-block">`;
|
||||||
html += `<div class="thinking-text">${escapeHtml(block.thinking)}</div>`;
|
html += `<div class="thinking-text">${escapeHtml(block.thinking)}</div>`;
|
||||||
|
html += `<div class="thinking-collapsed">Thinking ...</div>`;
|
||||||
|
html += `</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1617,14 +1626,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.type === 'compaction') {
|
if (entry.type === 'compaction') {
|
||||||
return `<div class="compaction" id="${entryId}">
|
return `<div class="compaction" id="${entryId}" onclick="this.classList.toggle('expanded')">
|
||||||
<div class="compaction-header" onclick="this.parentElement.classList.toggle('expanded')">
|
<div class="compaction-label">[compaction]</div>
|
||||||
<span class="compaction-toggle">▶</span>
|
<div class="compaction-collapsed">Compacted from ${entry.tokensBefore.toLocaleString()} tokens (ctrl+o to expand)</div>
|
||||||
<span class="compaction-title">Context compacted from ${entry.tokensBefore.toLocaleString()} tokens</span>
|
<div class="compaction-content"><strong>Compacted from ${entry.tokensBefore.toLocaleString()} tokens</strong>\n\n${escapeHtml(entry.summary)}</div>
|
||||||
</div>
|
|
||||||
<div class="compaction-content">
|
|
||||||
<div class="compaction-summary">${escapeHtml(entry.summary)}</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1738,7 +1743,8 @@
|
||||||
if (scrollToTarget) {
|
if (scrollToTarget) {
|
||||||
const targetEl = document.getElementById(`entry-${targetId}`);
|
const targetEl = document.getElementById(`entry-${targetId}`);
|
||||||
if (targetEl) {
|
if (targetEl) {
|
||||||
targetEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
// Use 'end' to ensure we scroll far enough to see the target
|
||||||
|
targetEl.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||||
// Brief highlight
|
// Brief highlight
|
||||||
targetEl.style.outline = '2px solid var(--accent)';
|
targetEl.style.outline = '2px solid var(--accent)';
|
||||||
setTimeout(() => { targetEl.style.outline = ''; }, 1500);
|
setTimeout(() => { targetEl.style.outline = ''; }, 1500);
|
||||||
|
|
@ -1791,18 +1797,56 @@
|
||||||
// Close sidebar when clicking close button
|
// Close sidebar when clicking close button
|
||||||
document.getElementById('sidebar-close').addEventListener('click', closeSidebar);
|
document.getElementById('sidebar-close').addEventListener('click', closeSidebar);
|
||||||
|
|
||||||
// Keyboard shortcut: Escape to reset to leaf
|
// Track toggle states
|
||||||
|
let thinkingExpanded = true;
|
||||||
|
let toolOutputsExpanded = true;
|
||||||
|
|
||||||
|
const toggleThinking = () => {
|
||||||
|
thinkingExpanded = !thinkingExpanded;
|
||||||
|
document.querySelectorAll('.thinking-text').forEach(el => {
|
||||||
|
el.style.display = thinkingExpanded ? '' : 'none';
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.thinking-collapsed').forEach(el => {
|
||||||
|
el.style.display = thinkingExpanded ? 'none' : '';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleToolOutputs = () => {
|
||||||
|
toolOutputsExpanded = !toolOutputsExpanded;
|
||||||
|
// Toggle expanded state on expandable tool outputs
|
||||||
|
document.querySelectorAll('.tool-output.expandable').forEach(el => {
|
||||||
|
if (toolOutputsExpanded) {
|
||||||
|
el.classList.add('expanded');
|
||||||
|
} else {
|
||||||
|
el.classList.remove('expanded');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Toggle compaction/branch-summary expanded state
|
||||||
|
document.querySelectorAll('.compaction').forEach(el => {
|
||||||
|
if (toolOutputsExpanded) {
|
||||||
|
el.classList.add('expanded');
|
||||||
|
} else {
|
||||||
|
el.classList.remove('expanded');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keyboard shortcuts
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
searchInput.value = '';
|
searchInput.value = '';
|
||||||
searchQuery = '';
|
searchQuery = '';
|
||||||
navigateTo(leafId);
|
navigateTo(leafId);
|
||||||
}
|
}
|
||||||
// Focus search on Ctrl/Cmd+F
|
// Toggle thinking blocks on Ctrl+T
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
|
if (e.ctrlKey && e.key === 't') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
searchInput.focus();
|
toggleThinking();
|
||||||
searchInput.select();
|
}
|
||||||
|
// Toggle tool outputs on Ctrl+O
|
||||||
|
if (e.ctrlKey && e.key === 'o') {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleToolOutputs();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue