feat(web): render Lyra's replies as Markdown (readable, not a wall of asterisks)
Her replies are full of **bold**, numbered lists and headings but rendered as raw monospace text, so the chat was a cluttered wall of literal markup. Add a small self-contained Markdown renderer (no deps): headings, ordered/unordered lists, bold/italic, inline + fenced code, links + autolinked URLs, with HTML escaping. Assistant messages now render to HTML; user/system stay literal text. Proportional font + spacing/list/code styling for assistant bubbles. (Renderer avoids literal backticks via String.fromCharCode(96) — a triple-tick regex literal had been corrupting the file with NUL bytes.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+87
-56
@@ -907,59 +907,90 @@ select:hover {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Live Log lines ---- */
|
||||
.log-line {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
font-family: 'Courier New', monospace;
|
||||
border-left: 3px solid var(--text-fade);
|
||||
animation: thinkingSlideIn 0.25s ease-out;
|
||||
word-break: break-word;
|
||||
}
|
||||
.log-time { color: var(--text-fade); flex-shrink: 0; }
|
||||
.log-level {
|
||||
flex-shrink: 0;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.log-msg { color: var(--text); }
|
||||
.log-fields { color: var(--text-fade); width: 100%; padding-left: 4px; }
|
||||
|
||||
.log-info { border-left-color: #00bfff; }
|
||||
.log-info .log-level { color: #7dd3fc; }
|
||||
.log-debug { border-left-color: #8a2be2; }
|
||||
.log-debug .log-level { color: #c79cff; }
|
||||
.log-error { border-left-color: #ff3333; background: rgba(255,51,51,0.08); }
|
||||
.log-error .log-level, .log-error .log-msg { color: #fca5a5; }
|
||||
.log-system { border-left-color: #00ff66; }
|
||||
.log-system .log-level { color: #00ff66; }
|
||||
|
||||
.log-detail { width: 100%; margin-top: 4px; }
|
||||
.log-detail summary {
|
||||
cursor: pointer;
|
||||
color: var(--accent);
|
||||
font-size: 0.72rem;
|
||||
user-select: none;
|
||||
}
|
||||
.log-detail pre {
|
||||
margin: 6px 0 0;
|
||||
padding: 8px;
|
||||
max-height: 340px;
|
||||
overflow: auto;
|
||||
background: rgba(0,0,0,0.25);
|
||||
border-left: 2px solid var(--accent);
|
||||
border-radius: 4px;
|
||||
font-size: 0.72rem;
|
||||
line-height: 1.4;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* ---- Live Log lines ---- */
|
||||
.log-line {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
font-family: 'Courier New', monospace;
|
||||
border-left: 3px solid var(--text-fade);
|
||||
animation: thinkingSlideIn 0.25s ease-out;
|
||||
word-break: break-word;
|
||||
}
|
||||
.log-time { color: var(--text-fade); flex-shrink: 0; }
|
||||
.log-level {
|
||||
flex-shrink: 0;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.log-msg { color: var(--text); }
|
||||
.log-fields { color: var(--text-fade); width: 100%; padding-left: 4px; }
|
||||
|
||||
.log-info { border-left-color: #00bfff; }
|
||||
.log-info .log-level { color: #7dd3fc; }
|
||||
.log-debug { border-left-color: #8a2be2; }
|
||||
.log-debug .log-level { color: #c79cff; }
|
||||
.log-error { border-left-color: #ff3333; background: rgba(255,51,51,0.08); }
|
||||
.log-error .log-level, .log-error .log-msg { color: #fca5a5; }
|
||||
.log-system { border-left-color: #00ff66; }
|
||||
.log-system .log-level { color: #00ff66; }
|
||||
|
||||
.log-detail { width: 100%; margin-top: 4px; }
|
||||
.log-detail summary {
|
||||
cursor: pointer;
|
||||
color: var(--accent);
|
||||
font-size: 0.72rem;
|
||||
user-select: none;
|
||||
}
|
||||
.log-detail pre {
|
||||
margin: 6px 0 0;
|
||||
padding: 8px;
|
||||
max-height: 340px;
|
||||
overflow: auto;
|
||||
background: rgba(0,0,0,0.25);
|
||||
border-left: 2px solid var(--accent);
|
||||
border-radius: 4px;
|
||||
font-size: 0.72rem;
|
||||
line-height: 1.4;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* Rendered markdown in Lyra's replies — readable proportional type + structure. */
|
||||
.msg.assistant {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
line-height: 1.55;
|
||||
max-width: 88%;
|
||||
}
|
||||
.msg.assistant p { margin: 0 0 10px; }
|
||||
.msg.assistant p:last-child { margin-bottom: 0; }
|
||||
.msg.assistant h1, .msg.assistant h2, .msg.assistant h3, .msg.assistant h4 {
|
||||
margin: 14px 0 6px; line-height: 1.3; color: var(--accent);
|
||||
}
|
||||
.msg.assistant h1 { font-size: 1.18rem; }
|
||||
.msg.assistant h2 { font-size: 1.1rem; }
|
||||
.msg.assistant h3 { font-size: 1.02rem; }
|
||||
.msg.assistant h4 { font-size: 0.96rem; }
|
||||
.msg.assistant ul, .msg.assistant ol { margin: 6px 0 10px; padding-left: 22px; }
|
||||
.msg.assistant li { margin: 3px 0; }
|
||||
.msg.assistant li > ul, .msg.assistant li > ol { margin: 3px 0; }
|
||||
.msg.assistant strong { font-weight: 600; color: var(--text); }
|
||||
.msg.assistant em { font-style: italic; }
|
||||
.msg.assistant a { color: var(--accent); text-decoration: underline; }
|
||||
.msg.assistant code {
|
||||
font-family: "IBM Plex Mono", monospace; font-size: 0.88em;
|
||||
background: rgba(255,255,255,0.08); padding: 1px 5px; border-radius: 4px;
|
||||
}
|
||||
.msg.assistant pre {
|
||||
background: rgba(0,0,0,0.32); border: 1px solid rgba(255,102,0,0.3);
|
||||
border-radius: 6px; padding: 10px 12px; margin: 8px 0; overflow-x: auto;
|
||||
}
|
||||
.msg.assistant pre code { background: none; padding: 0; font-size: 0.85em; }
|
||||
|
||||
Reference in New Issue
Block a user