diff --git a/src/web/public/app.js b/src/web/public/app.js index b95fa4a3..1c535ddb 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -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. @@ -1168,7 +1169,7 @@ class CodemanApp { } catch { /* fall through */ } } // Fallback: escape HTML and preserve whitespace - const escaped = text.replace(/&/g, '&').replace(//g, '>'); + const escaped = src.replace(/&/g, '&').replace(//g, '>'); return `
${escaped}`;
}
diff --git a/src/web/public/styles.css b/src/web/public/styles.css
index b96277ef..0272c7ae 100644
--- a/src/web/public/styles.css
+++ b/src/web/public/styles.css
@@ -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 {
@@ -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 {
@@ -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
+ 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;
@@ -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;
@@ -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,
@@ -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 {