Enhance HTML export with collapsible messages, diff highlighting, and progress badges
Major overhaul of the static HTML export to match the interactive viewer: Collapsible messages: - thinking, tool_call, and tool_result categories render collapsed by default with data-collapsed attribute - Chevron toggle button rotates on expand/collapse - Collapsed preview shows: line count (thinking), tool name (tool_call), or first 120 chars (tool_result) - Print media query forces all sections expanded and hides toggle UI Diff highlighting: - tool_result content is checked for diff patterns (hunk headers, +/- lines) via isDiffContent() heuristic - Diff content renders with color-coded spans: green additions, red deletions, purple hunk headers, gray meta lines Progress badges: - tool_call messages with associated progress events render a clickable pill row showing event counts by subtype (hook/bash/mcp/agent) - Clicking toggles a drawer with timestamped event log - Subtype colors match the client-side ProgressBadge component Interactive JavaScript: - Export now includes a <script> block for toggle interactivity - Single delegated click handler on document for both collapsible messages and progress drawers CSS additions: - Left color bar via ::before pseudo-element on .message - Collapsible toggle, collapsed preview, diff highlighting, and progress badge/drawer styles - Print overrides to show all content and hide interactive controls Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
import hljs from "highlight.js";
|
import hljs from "highlight.js";
|
||||||
import { markedHighlight } from "marked-highlight";
|
import { markedHighlight } from "marked-highlight";
|
||||||
import type { ExportRequest, ParsedMessage } from "../../shared/types.js";
|
import type { ExportRequest, ParsedMessage, ProgressSubtype } from "../../shared/types.js";
|
||||||
import { CATEGORY_LABELS } from "../../shared/types.js";
|
import { CATEGORY_LABELS } from "../../shared/types.js";
|
||||||
import { redactMessage } from "../../shared/sensitive-redactor.js";
|
import { redactMessage } from "../../shared/sensitive-redactor.js";
|
||||||
import { escapeHtml } from "../../shared/escape-html.js";
|
import { escapeHtml } from "../../shared/escape-html.js";
|
||||||
@@ -22,7 +22,11 @@ marked.use(
|
|||||||
|
|
||||||
// Categories whose content is structured data (JSON, logs, snapshots) — not markdown.
|
// Categories whose content is structured data (JSON, logs, snapshots) — not markdown.
|
||||||
// Rendered as preformatted text to avoid the cost of markdown parsing on large blobs.
|
// Rendered as preformatted text to avoid the cost of markdown parsing on large blobs.
|
||||||
const PREFORMATTED_CATEGORIES = new Set(["hook_progress", "tool_result", "file_snapshot"]);
|
// Note: tool_result is handled explicitly in renderMessage() for diff detection.
|
||||||
|
const PREFORMATTED_CATEGORIES = new Set(["hook_progress", "file_snapshot"]);
|
||||||
|
|
||||||
|
// Categories that render collapsed by default
|
||||||
|
const COLLAPSIBLE_CATEGORIES = new Set(["thinking", "tool_call", "tool_result"]);
|
||||||
|
|
||||||
// Category dot/border colors matching the client-side design
|
// Category dot/border colors matching the client-side design
|
||||||
const CATEGORY_STYLES: Record<string, { dot: string; border: string; text: string }> = {
|
const CATEGORY_STYLES: Record<string, { dot: string; border: string; text: string }> = {
|
||||||
@@ -37,10 +41,19 @@ const CATEGORY_STYLES: Record<string, { dot: string; border: string; text: strin
|
|||||||
summary: { dot: "#2dd4bf", border: "#1a4d45", text: "#2dd4bf" },
|
summary: { dot: "#2dd4bf", border: "#1a4d45", text: "#2dd4bf" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Progress subtype colors for the export badge pills
|
||||||
|
const PROGRESS_SUBTYPE_COLORS: Record<ProgressSubtype, { text: string; bg: string }> = {
|
||||||
|
hook: { text: "#484f58", bg: "rgba(72,79,88,0.1)" },
|
||||||
|
bash: { text: "#d29922", bg: "rgba(210,153,34,0.1)" },
|
||||||
|
mcp: { text: "#8b8cf8", bg: "rgba(139,140,248,0.1)" },
|
||||||
|
agent: { text: "#bc8cff", bg: "rgba(188,140,255,0.1)" },
|
||||||
|
};
|
||||||
|
|
||||||
export async function generateExportHtml(
|
export async function generateExportHtml(
|
||||||
req: ExportRequest
|
req: ExportRequest
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const { session, visibleMessageUuids, redactedMessageUuids, autoRedactEnabled } = req;
|
const { session, visibleMessageUuids, redactedMessageUuids, autoRedactEnabled } = req;
|
||||||
|
const toolProgress = session.toolProgress || {};
|
||||||
|
|
||||||
const visibleSet = new Set(visibleMessageUuids);
|
const visibleSet = new Set(visibleMessageUuids);
|
||||||
const redactedSet = new Set(redactedMessageUuids);
|
const redactedSet = new Set(redactedMessageUuids);
|
||||||
@@ -65,7 +78,8 @@ export async function generateExportHtml(
|
|||||||
lastWasRedacted = false;
|
lastWasRedacted = false;
|
||||||
}
|
}
|
||||||
const msgToRender = autoRedactEnabled ? redactMessage(msg) : msg;
|
const msgToRender = autoRedactEnabled ? redactMessage(msg) : msg;
|
||||||
messageHtmlParts.push(renderMessage(msgToRender));
|
const progressEvents = msg.toolUseId ? toolProgress[msg.toolUseId] : undefined;
|
||||||
|
messageHtmlParts.push(renderMessage(msgToRender, progressEvents));
|
||||||
}
|
}
|
||||||
|
|
||||||
const hljsCss = getHighlightCss();
|
const hljsCss = getHighlightCss();
|
||||||
@@ -100,7 +114,7 @@ ${hljsCss}
|
|||||||
</span>
|
</span>
|
||||||
<span class="meta-item">
|
<span class="meta-item">
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z"/></svg>
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z"/></svg>
|
||||||
${messageCount} message${messageCount !== 1 ? "s" : ""}
|
${messageCount} message${messageCount !== 1 ? "s" : ""}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -108,6 +122,9 @@ ${hljsCss}
|
|||||||
${messageHtmlParts.join("\n ")}
|
${messageHtmlParts.join("\n ")}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
${getExportJs()}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
}
|
}
|
||||||
@@ -123,9 +140,11 @@ function renderRedactedDivider(): string {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMessage(msg: ParsedMessage): string {
|
function renderMessage(msg: ParsedMessage, progressEvents?: ParsedMessage[]): string {
|
||||||
const style = CATEGORY_STYLES[msg.category] || CATEGORY_STYLES.system_message;
|
const style = CATEGORY_STYLES[msg.category] || CATEGORY_STYLES.system_message;
|
||||||
const label = CATEGORY_LABELS[msg.category];
|
const label = CATEGORY_LABELS[msg.category];
|
||||||
|
const isCollapsible = COLLAPSIBLE_CATEGORIES.has(msg.category);
|
||||||
|
|
||||||
let bodyHtml: string;
|
let bodyHtml: string;
|
||||||
|
|
||||||
if (msg.category === "tool_call") {
|
if (msg.category === "tool_call") {
|
||||||
@@ -133,10 +152,11 @@ function renderMessage(msg: ParsedMessage): string {
|
|||||||
? `<pre class="hljs"><code>${escapeHtml(msg.toolInput)}</code></pre>`
|
? `<pre class="hljs"><code>${escapeHtml(msg.toolInput)}</code></pre>`
|
||||||
: "";
|
: "";
|
||||||
bodyHtml = `<div class="tool-name" style="color: ${style.text}">${escapeHtml(msg.toolName || "Unknown Tool")}</div>${inputHtml}`;
|
bodyHtml = `<div class="tool-name" style="color: ${style.text}">${escapeHtml(msg.toolName || "Unknown Tool")}</div>${inputHtml}`;
|
||||||
|
} else if (msg.category === "tool_result") {
|
||||||
|
bodyHtml = isDiffContent(msg.content)
|
||||||
|
? renderDiffHtml(msg.content)
|
||||||
|
: `<pre class="hljs"><code>${escapeHtml(msg.content)}</code></pre>`;
|
||||||
} else if (PREFORMATTED_CATEGORIES.has(msg.category)) {
|
} else if (PREFORMATTED_CATEGORIES.has(msg.category)) {
|
||||||
// These categories contain structured data (JSON, logs, snapshots), not prose.
|
|
||||||
// Rendering them through marked is both incorrect and extremely slow on large
|
|
||||||
// content (370KB JSON blobs take ~300ms each in marked.parse).
|
|
||||||
bodyHtml = `<pre class="hljs"><code>${escapeHtml(msg.content)}</code></pre>`;
|
bodyHtml = `<pre class="hljs"><code>${escapeHtml(msg.content)}</code></pre>`;
|
||||||
} else {
|
} else {
|
||||||
bodyHtml = renderMarkdown(msg.content);
|
bodyHtml = renderMarkdown(msg.content);
|
||||||
@@ -147,16 +167,119 @@ function renderMessage(msg: ParsedMessage): string {
|
|||||||
? `<span class="header-sep">·</span><span class="message-time">${escapeHtml(timestamp)}</span>`
|
? `<span class="header-sep">·</span><span class="message-time">${escapeHtml(timestamp)}</span>`
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
return `<div class="message" style="border-color: ${style.border}">
|
// Build collapsed preview for collapsible categories
|
||||||
|
let previewHtml = "";
|
||||||
|
if (isCollapsible) {
|
||||||
|
let previewText: string;
|
||||||
|
if (msg.category === "thinking") {
|
||||||
|
const lineCount = msg.content.split("\n").filter(l => l.trim()).length;
|
||||||
|
previewText = `${lineCount} line${lineCount !== 1 ? "s" : ""}`;
|
||||||
|
} else if (msg.category === "tool_call") {
|
||||||
|
previewText = msg.toolName || "Unknown Tool";
|
||||||
|
} else {
|
||||||
|
// tool_result — first 120 chars of first line
|
||||||
|
previewText = (msg.content.split("\n")[0] || "Result").substring(0, 120);
|
||||||
|
}
|
||||||
|
previewHtml = `<span class="header-sep">·</span><span class="collapsed-preview">${escapeHtml(previewText)}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chevron toggle button for collapsible messages
|
||||||
|
const chevronHtml = isCollapsible
|
||||||
|
? `<button class="collapsible-toggle" aria-label="Toggle"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M8.25 4.5l7.5 7.5-7.5 7.5"/></svg></button>`
|
||||||
|
: "";
|
||||||
|
|
||||||
|
const dataAttrs = isCollapsible ? ' data-collapsed="true"' : "";
|
||||||
|
|
||||||
|
// Progress badge for tool_call messages
|
||||||
|
const progressHtml = (msg.category === "tool_call" && progressEvents && progressEvents.length > 0)
|
||||||
|
? renderProgressBadge(progressEvents)
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return `<div class="message"${dataAttrs} style="border-color: ${style.border}">
|
||||||
<div class="message-header">
|
<div class="message-header">
|
||||||
<span class="message-dot" style="background: ${style.dot}"></span>
|
${chevronHtml}<span class="message-dot" style="background: ${style.dot}"></span>
|
||||||
<span class="message-label">${escapeHtml(label)}</span>
|
<span class="message-label">${escapeHtml(label)}</span>
|
||||||
${timestampHtml}
|
${timestampHtml}
|
||||||
|
${previewHtml}
|
||||||
</div>
|
</div>
|
||||||
<div class="message-body prose-message">${bodyHtml}</div>
|
<div class="message-body prose-message">${bodyHtml}</div>
|
||||||
|
${progressHtml}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isDiffContent(content: string): boolean {
|
||||||
|
const lines = content.split("\n").slice(0, 30);
|
||||||
|
let hunkHeaders = 0;
|
||||||
|
let diffLines = 0;
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith("@@") || line.startsWith("diff --")) {
|
||||||
|
hunkHeaders++;
|
||||||
|
} else if (line.startsWith("+++") || line.startsWith("---")) {
|
||||||
|
hunkHeaders++;
|
||||||
|
} else if (line.startsWith("+") || line.startsWith("-")) {
|
||||||
|
diffLines++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hunkHeaders >= 1 && diffLines >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDiffHtml(content: string): string {
|
||||||
|
const lines = content.split("\n");
|
||||||
|
const htmlLines = lines.map((line) => {
|
||||||
|
const escaped = escapeHtml(line);
|
||||||
|
if (line.startsWith("@@")) {
|
||||||
|
return `<span class="diff-hunk">${escaped}</span>`;
|
||||||
|
}
|
||||||
|
if (line.startsWith("+++") || line.startsWith("---")) {
|
||||||
|
return `<span class="diff-meta">${escaped}</span>`;
|
||||||
|
}
|
||||||
|
if (line.startsWith("diff --")) {
|
||||||
|
return `<span class="diff-header">${escaped}</span>`;
|
||||||
|
}
|
||||||
|
if (line.startsWith("+")) {
|
||||||
|
return `<span class="diff-add">${escaped}</span>`;
|
||||||
|
}
|
||||||
|
if (line.startsWith("-")) {
|
||||||
|
return `<span class="diff-del">${escaped}</span>`;
|
||||||
|
}
|
||||||
|
return escaped;
|
||||||
|
});
|
||||||
|
return `<pre class="hljs diff-view"><code>${htmlLines.join("\n")}</code></pre>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderProgressBadge(events: ParsedMessage[]): string {
|
||||||
|
// Count by subtype
|
||||||
|
const counts: Partial<Record<ProgressSubtype, number>> = {};
|
||||||
|
for (const e of events) {
|
||||||
|
const sub = e.progressSubtype || "hook";
|
||||||
|
counts[sub] = (counts[sub] || 0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pill row
|
||||||
|
const pills = (Object.entries(counts) as [ProgressSubtype, number][])
|
||||||
|
.map(([sub, count]) => {
|
||||||
|
const colors = PROGRESS_SUBTYPE_COLORS[sub] || PROGRESS_SUBTYPE_COLORS.hook;
|
||||||
|
return `<span class="progress-pill" style="color:${colors.text};background:${colors.bg}">${escapeHtml(sub)}: ${count}</span>`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
// Drawer rows
|
||||||
|
const rows = events
|
||||||
|
.map((e) => {
|
||||||
|
const time = e.timestamp ? formatTimestamp(e.timestamp) : "--:--:--";
|
||||||
|
const sub = e.progressSubtype || "hook";
|
||||||
|
return `<div class="progress-row"><span class="progress-time">${escapeHtml(time)}</span><span class="progress-subtype">${escapeHtml(sub)}</span><span class="progress-content">${escapeHtml(e.content)}</span></div>`;
|
||||||
|
})
|
||||||
|
.join("\n ");
|
||||||
|
|
||||||
|
return `<div class="progress-badge">
|
||||||
|
<button class="progress-toggle">${pills}</button>
|
||||||
|
<div class="progress-drawer" style="display:none">
|
||||||
|
${rows}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
// marked.parse() is called synchronously here. In marked v14+ it can return
|
// marked.parse() is called synchronously here. In marked v14+ it can return
|
||||||
// Promise<string> if async extensions are configured. Our markedHighlight setup
|
// Promise<string> if async extensions are configured. Our markedHighlight setup
|
||||||
// is synchronous, so the cast is safe — but do not add async extensions without
|
// is synchronous, so the cast is safe — but do not add async extensions without
|
||||||
@@ -179,6 +302,28 @@ function formatTimestamp(ts: string): string {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getExportJs(): string {
|
||||||
|
return `
|
||||||
|
document.addEventListener("click", function(e) {
|
||||||
|
var toggle = e.target.closest(".collapsible-toggle");
|
||||||
|
if (toggle) {
|
||||||
|
var msg = toggle.closest(".message");
|
||||||
|
if (!msg) return;
|
||||||
|
var collapsed = msg.getAttribute("data-collapsed") === "true";
|
||||||
|
msg.setAttribute("data-collapsed", collapsed ? "false" : "true");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var progressToggle = e.target.closest(".progress-toggle");
|
||||||
|
if (progressToggle) {
|
||||||
|
var drawer = progressToggle.nextElementSibling;
|
||||||
|
if (drawer && drawer.classList.contains("progress-drawer")) {
|
||||||
|
drawer.style.display = drawer.style.display === "none" ? "block" : "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
function getHighlightCss(): string {
|
function getHighlightCss(): string {
|
||||||
// Dark theme highlight.js (GitHub Dark) matching the client
|
// Dark theme highlight.js (GitHub Dark) matching the client
|
||||||
return `
|
return `
|
||||||
@@ -259,6 +404,19 @@ body {
|
|||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
border: 1px solid #30363d;
|
border: 1px solid #30363d;
|
||||||
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3);
|
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.message::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 3px;
|
||||||
|
border-radius: 0.75rem 0 0 0.75rem;
|
||||||
|
background: currentColor;
|
||||||
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
.message-header {
|
.message-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -298,6 +456,97 @@ body {
|
|||||||
line-height: 1.5rem;
|
line-height: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Collapsible toggle */
|
||||||
|
.collapsible-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #484f58;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: color 0.15s, transform 0.15s;
|
||||||
|
}
|
||||||
|
.collapsible-toggle:hover { color: #e6edf3; }
|
||||||
|
.message[data-collapsed="false"] .collapsible-toggle svg { transform: rotate(90deg); }
|
||||||
|
.message[data-collapsed="true"] .message-body { display: none; }
|
||||||
|
.collapsed-preview {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #484f58;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Diff highlighting */
|
||||||
|
.diff-view { font-size: 0.8125rem; line-height: 1.6; }
|
||||||
|
.diff-add { color: #7ee787; background: rgba(46,160,67,0.15); display: block; }
|
||||||
|
.diff-del { color: #ffa198; background: rgba(248,81,73,0.15); display: block; }
|
||||||
|
.diff-hunk { color: #bc8cff; display: block; }
|
||||||
|
.diff-meta { color: #8b949e; display: block; }
|
||||||
|
.diff-header { color: #e6edf3; font-weight: 600; display: block; }
|
||||||
|
|
||||||
|
/* Progress badge */
|
||||||
|
.progress-badge {
|
||||||
|
padding: 0.25rem 1rem 0.75rem;
|
||||||
|
}
|
||||||
|
.progress-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.375rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
}
|
||||||
|
.progress-pill {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
}
|
||||||
|
.progress-drawer {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: #161b22;
|
||||||
|
border: 1px solid #21262d;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
.progress-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding: 0.125rem 0;
|
||||||
|
}
|
||||||
|
.progress-time {
|
||||||
|
width: 5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: #484f58;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
.progress-subtype {
|
||||||
|
width: 3.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.progress-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
color: #8b949e;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tool name */
|
/* Tool name */
|
||||||
.tool-name { font-weight: 500; margin-bottom: 0.5rem; }
|
.tool-name { font-weight: 500; margin-bottom: 0.5rem; }
|
||||||
|
|
||||||
@@ -405,6 +654,10 @@ body {
|
|||||||
body { background: #1c2128; }
|
body { background: #1c2128; }
|
||||||
.session-export { padding: 0; max-width: 100%; }
|
.session-export { padding: 0; max-width: 100%; }
|
||||||
.session-header, .message { box-shadow: none; break-inside: avoid; }
|
.session-header, .message { box-shadow: none; break-inside: avoid; }
|
||||||
|
.message-body { display: block !important; }
|
||||||
|
.collapsed-preview { display: none !important; }
|
||||||
|
.collapsible-toggle { display: none !important; }
|
||||||
|
.progress-drawer { display: block !important; }
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user