Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/web/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -1110,9 +1110,10 @@ class CodemanApp {

/** Render markdown to sanitized HTML, falling back to plain text if marked.js unavailable */
_renderMarkdown(text) {
const src = text || '';
if (typeof marked !== 'undefined' && marked.parse) {
try {
const prepared = this._preprocessAsciiArt(text);
const prepared = this._preprocessAsciiArt(src);
let html = this._sanitizeHtml(marked.parse(prepared, { breaks: true, gfm: true }));
// Wrap tables in a horizontal-scroll container so they overflow gracefully
// on mobile without collapsing into block-level cells.
Expand Down Expand Up @@ -1168,7 +1169,7 @@ class CodemanApp {
} catch { /* fall through */ }
}
// Fallback: escape HTML and preserve whitespace
const escaped = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
const escaped = src.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return `<pre style="white-space:pre-wrap;word-break:break-word">${escaped}</pre>`;
}

Expand Down
179 changes: 121 additions & 58 deletions src/web/public/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -7971,14 +7971,15 @@ kbd {
bottom: 0;
left: 0;
right: 0;
max-height: 85vh;
background: #1a1a2e;
border-top: 1px solid #333;
border-radius: 12px 12px 0 0;
max-height: 88vh;
background: #14141f;
border-top: 1px solid #2a2a3a;
border-radius: 14px 14px 0 0;
box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.45);
z-index: 5000;
flex-direction: column;
transform: translateY(100%);
transition: transform 0.25s ease-out;
transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

.response-viewer.visible {
Expand All @@ -7990,12 +7991,13 @@ kbd {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-bottom: 1px solid #333;
padding: 14px 20px;
border-bottom: 1px solid #2a2a3a;
flex-shrink: 0;
font-size: 14px;
font-weight: 600;
color: #e0e0e0;
color: #e8e8ec;
letter-spacing: 0.2px;
}

.response-viewer-actions {
Expand Down Expand Up @@ -8078,42 +8080,80 @@ kbd {
background: rgba(109, 219, 127, 0.12);
}

/* Markdown rendered content inside response viewer */
.rv-text {
word-break: break-word;
line-height: 1.6;
/* Markdown rendered content inside response viewer.
Prose uses a proportional font for readability; code keeps monospace. */
.rv-text,
.response-viewer-body > :not(.rv-message) {
word-break: normal;
overflow-wrap: anywhere;
line-height: 1.7;
}

.rv-text p {
margin: 0 0 0.6em;
.rv-text p,
.response-viewer-body > p {
margin: 0 0 0.85em;
}

.rv-text p:last-child {
.rv-text p:last-child,
.response-viewer-body > p:last-child {
margin-bottom: 0;
}

.rv-text h1, .rv-text h2, .rv-text h3, .rv-text h4 {
color: #e0e0e0;
margin: 1em 0 0.4em;
.rv-text h1, .rv-text h2, .rv-text h3, .rv-text h4,
.response-viewer-body > h1, .response-viewer-body > h2,
.response-viewer-body > h3, .response-viewer-body > h4 {
color: #f2f2f6;
margin: 1.4em 0 0.5em;
line-height: 1.3;
font-weight: 700;
letter-spacing: -0.01em;
}

.rv-text h1 { font-size: 1.3em; }
.rv-text h2 { font-size: 1.15em; }
.rv-text h3 { font-size: 1.05em; }
.rv-text h1:first-child, .rv-text h2:first-child,
.response-viewer-body > h1:first-child, .response-viewer-body > h2:first-child {
margin-top: 0;
}

.rv-text code {
background: #2a2a3e;
padding: 1px 5px;
border-radius: 3px;
.rv-text h1, .response-viewer-body > h1 {
font-size: 1.55em;
padding-bottom: 0.3em;
border-bottom: 1px solid #2d2d40;
}
.rv-text h2, .response-viewer-body > h2 {
font-size: 1.3em;
color: #ffd27a;
}
.rv-text h3, .response-viewer-body > h3 {
font-size: 1.13em;
color: #bfc8ff;
}
.rv-text h4, .response-viewer-body > h4 {
font-size: 1em;
color: #c9c9d5;
text-transform: uppercase;
letter-spacing: 0.05em;
}

.rv-text code,
.response-viewer-body > :not(pre) code {
background: #262638;
color: #ffb4a2;
padding: 1px 6px;
border-radius: 4px;
font-family: 'Fira Code', 'JetBrains Mono', 'SF Mono', Menlo, Monaco, monospace;
font-size: 0.9em;
}

.rv-text pre {
background: #1e1e2e;
border: 1px solid #333;
border-radius: 6px;
padding: 10px 12px;
/* Descendant (not child) combinator: code blocks are wrapped in .rv-code-wrap,
so the latest-response view (markdown rendered straight into the body) nests
<pre> one level deeper than a direct child. The historical .rv-text path
already matched via descendant; keep both in lockstep. */
.rv-text pre,
.response-viewer-body pre {
background: #0f0f1a;
border: 1px solid #2a2a3d;
border-radius: 8px;
padding: 14px 16px;
overflow-x: auto;
margin: 1em 0;
-webkit-overflow-scrolling: touch;
Expand All @@ -8125,7 +8165,7 @@ kbd {
Preserve indentation (pre-wrap) but allow breaks inside long tokens
(URLs, paths, identifiers) so they don't overflow. */
.rv-text pre code,
.response-viewer-body > pre code {
.response-viewer-body pre code {
background: none;
color: #e6e6f0;
padding: 0;
Expand Down Expand Up @@ -8294,38 +8334,43 @@ kbd {
.rv-text ul, .rv-text ol,
.response-viewer-body > ul, .response-viewer-body > ol {
margin: 0.6em 0;
padding-left: 1.5em;
}

.rv-text pre code {
background: none;
padding: 0;
font-size: 0.85em;
line-height: 1.5;
.rv-text li,
.response-viewer-body > ul > li, .response-viewer-body > ol > li {
margin-bottom: 0.3em;
}

.rv-text ul, .rv-text ol {
margin: 0.4em 0;
padding-left: 1.4em;
}
.rv-text li > p { margin: 0.2em 0; }

.rv-text li {
margin-bottom: 0.2em;
.rv-text blockquote,
.response-viewer-body > blockquote {
border-left: 3px solid #5c7cfa;
background: rgba(92, 124, 250, 0.06);
margin: 0.8em 0;
padding: 0.5em 14px;
color: #b8b8c8;
border-radius: 0 6px 6px 0;
}

.rv-text blockquote {
border-left: 3px solid #444;
margin: 0.6em 0;
padding: 0.3em 0 0.3em 12px;
color: #999;
.rv-text strong,
.response-viewer-body > p strong,
.response-viewer-body > li strong {
color: #ffffff;
font-weight: 700;
}

.rv-text strong {
color: #f0f0f0;
.rv-text em,
.response-viewer-body em {
color: #e0e0ec;
}

.rv-text a {
color: #5c7cfa;
.rv-text a,
.response-viewer-body a {
color: #7aa2ff;
text-decoration: none;
border-bottom: 1px solid rgba(122, 162, 255, 0.35);
}

.rv-text a:hover,
Expand Down Expand Up @@ -8403,19 +8448,37 @@ kbd {
.rv-text hr,
.response-viewer-body > hr {
border: none;
border-top: 1px solid #333;
margin: 1em 0;
border-top: 1px solid #2d2d40;
margin: 1.5em 0;
}

.response-viewer-body {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 16px;
font-family: 'Fira Code', 'Cascadia Code', 'JetBrains Mono', 'SF Mono', Monaco, monospace;
font-size: 13px;
line-height: 1.5;
color: #d4d4d4;
overscroll-behavior: contain;
padding: 20px 22px 28px;
/* Proportional font for prose — monospace only for code/pre */
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'PingFang SC',
'Hiragino Sans GB', 'Segoe UI', 'Helvetica Neue', Helvetica, Arial,
'Noto Sans CJK SC', sans-serif;
font-size: 15px;
line-height: 1.7;
color: #d8d8e0;
/* Comfortable reading width on wider viewports */
--rv-content-max: 720px;
}

/* Constrain content width for readability; code blocks can still scroll horizontally */
.response-viewer-body > * {
max-width: var(--rv-content-max);
margin-left: auto;
margin-right: auto;
}
.response-viewer-body > pre,
.response-viewer-body > table,
.response-viewer-body > .rv-message {
max-width: var(--rv-content-max);
}

.response-viewer-body:empty::after {
Expand Down