Skip to content

Commit d0c0a3c

Browse files
committed
feat(client): feed message cells render markdown + drop selection styling
- ActivityRow: Reply/Prompt message cells now render detail through ReactMarkdown with a fragment <p> renderer, so bold/italic/code/links work while multi-paragraph messages flow inline without injecting visible vertical breaks. Switched the outer wrapper from span to div to safely host block markdown children. - ActivityFeed.css: - is-message detail: white-space pre-wrap → normal so embedded '\n' collapses into a space (user didn't want line breaks in messages). Added defensive styling for code/pre/ul/ol/a/strong/em children. - Removed the .feed-entry.is-selected background/box-shadow/name-glow rules. Feed cells no longer render any blue selection marker — selection is conveyed by the Phaser hero halo and the PartyBar row.
1 parent 1f5e000 commit d0c0a3c

2 files changed

Lines changed: 53 additions & 52 deletions

File tree

client/src/components/ActivityFeed.css

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
transition: height 0.18s ease;
2929
}
3030

31-
.activity-feed.fold-full { height: 420px; }
32-
.activity-feed.fold-compact { height: 120px; }
31+
.activity-feed.fold-full { height: 390px; }
32+
.activity-feed.fold-compact { height: 150px; }
3333
.activity-feed.fold-closed { height: 32px; }
3434

3535
/* === Header === */
@@ -80,31 +80,6 @@
8080

8181
.feed-spacer { flex: 1; }
8282

83-
.feed-tabs {
84-
display: flex;
85-
gap: 2px;
86-
background: rgba(0, 0, 0, 0.25);
87-
border-radius: 4px;
88-
padding: 2px;
89-
}
90-
.feed-tab {
91-
padding: 3px 10px;
92-
font-size: 12px;
93-
color: var(--feed-pergamena);
94-
opacity: 0.6;
95-
border: none;
96-
background: transparent;
97-
border-radius: 3px;
98-
cursor: pointer;
99-
font-family: inherit;
100-
}
101-
.feed-tab.active {
102-
background: rgba(196, 163, 90, 0.3);
103-
color: #fff;
104-
opacity: 1;
105-
font-weight: bold;
106-
}
107-
10883
.feed-fold-buttons { display: flex; gap: 4px; }
10984
.feed-fold-btn {
11085
width: 26px; height: 24px;
@@ -313,7 +288,7 @@
313288
.feed-detail.is-path:hover { text-decoration: underline; color: #fff; }
314289
.feed-detail.is-path-unresolved { color: var(--feed-path); opacity: 0.7; }
315290
.feed-detail.is-message {
316-
white-space: pre-wrap;
291+
white-space: normal;
317292
overflow: visible;
318293
text-overflow: clip;
319294
line-height: 1.45;
@@ -328,6 +303,40 @@
328303
.feed-detail.is-message::-webkit-scrollbar-thumb { background: rgba(196, 163, 90, 0.4); border-radius: 2px; }
329304
.feed-detail.is-message.is-reply { color: var(--feed-pergamena); }
330305
.feed-detail.is-message.is-prompt { color: #d8e3f2; font-style: italic; }
306+
/* Markdown rendering inside message cells: keep everything inline so the
307+
detail flows as a single line group. `p` is already stripped via a
308+
fragment renderer in ActivityRow, but guard the rest so lists/code
309+
blocks don't blow up the row height. */
310+
.feed-detail.is-message code {
311+
background: rgba(255, 255, 255, 0.08);
312+
padding: 0 4px;
313+
border-radius: 3px;
314+
font-family: 'Fira Code', ui-monospace, monospace;
315+
font-size: 0.92em;
316+
}
317+
.feed-detail.is-message pre {
318+
margin: 4px 0;
319+
padding: 6px 8px;
320+
background: rgba(0, 0, 0, 0.3);
321+
border-radius: 3px;
322+
overflow-x: auto;
323+
white-space: pre;
324+
}
325+
.feed-detail.is-message pre code {
326+
background: transparent;
327+
padding: 0;
328+
}
329+
.feed-detail.is-message ul,
330+
.feed-detail.is-message ol {
331+
margin: 2px 0;
332+
padding-left: 1.2em;
333+
}
334+
.feed-detail.is-message a {
335+
color: var(--feed-path);
336+
text-decoration: underline;
337+
}
338+
.feed-detail.is-message strong { color: #fff; }
339+
.feed-detail.is-message em { color: #d8d0b8; }
331340

332341
/* === Group (By Agent) === */
333342
.feed-group {
@@ -462,28 +471,10 @@
462471
.feed-entry.is-highlighted.hl-agent { border-left-color: #7ac49a; background: rgba(122, 196, 154, 0.08); }
463472
.feed-entry.is-highlighted.hl-other { border-left-color: #888; background: rgba(136, 136, 136, 0.06); }
464473

465-
.feed-entry.is-selected {
466-
background: rgba(91, 163, 245, 0.1);
467-
animation: feed-select-pulse 1.5s ease-in-out infinite;
468-
}
469-
.feed-entry.is-selected.is-highlighted {
470-
background: rgba(91, 163, 245, 0.14);
471-
}
472-
.feed-entry.is-selected .feed-agent-name {
473-
color: #FFFFFF !important;
474-
text-shadow: 0 0 6px rgba(91, 163, 245, 0.9);
475-
}
476-
477-
@keyframes feed-select-pulse {
478-
0%, 100% { box-shadow: inset 4px 0 0 #5BA3F5, 0 0 6px rgba(91, 163, 245, 0.3); }
479-
50% { box-shadow: inset 4px 0 0 #5BA3F5, 0 0 14px rgba(91, 163, 245, 0.7); }
480-
}
481-
@media (prefers-reduced-motion: reduce) {
482-
.feed-entry.is-selected {
483-
animation: none;
484-
box-shadow: inset 4px 0 0 #5BA3F5, 0 0 10px rgba(91, 163, 245, 0.5);
485-
}
486-
}
474+
/* Selection feedback on the feed cell is intentionally absent — the hero
475+
halo in Phaser and the PartyBar row already convey which agent is
476+
selected, and the click target here only needs to react visually while
477+
pressed (handled natively). */
487478

488479
/* Category pill styling for agent/other/reads/messages (errors/edits/bash reuse existing) */
489480
.feed-pill.messages { background: rgba(224, 138, 63, 0.15); color: #f0c99a; border-color: rgba(224, 138, 63, 0.4); }

client/src/components/ActivityRow.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
import { memo, useCallback, useEffect, useState } from 'react';
1+
import { memo, useCallback, useEffect, useState, type ReactNode } from 'react';
2+
import ReactMarkdown from 'react-markdown';
23
import { HERO_LABEL_COLOR, type ActivityLogEntry, type AgentState } from '../types/agent';
34
import { HeroAvatar } from './HeroAvatar';
45
import { isError, isPath, resolvePath, categorizeEntry } from './activityFeedUtils';
56
import type { ActionFilter } from './activityFeedUtils';
67

8+
// Message cells render detail as markdown (bold, italic, code, links). The
9+
// custom `p` renderer emits a fragment so multiple paragraphs flow inline —
10+
// no extra vertical space, no visible paragraph breaks.
11+
const MD_COMPONENTS = {
12+
p: ({ children }: { children?: ReactNode }) => <>{children}</>,
13+
};
14+
715
interface ActivityRowProps {
816
entry: ActivityLogEntry;
917
agent: AgentState | undefined;
@@ -85,7 +93,9 @@ function ActivityRowImpl({
8593
<span className="feed-time">{time}</span>
8694
</div>
8795
{isMessage ? (
88-
<span className={`feed-detail is-message ${pillVariant}`}>{entry.detail}</span>
96+
<div className={`feed-detail is-message ${pillVariant}`}>
97+
<ReactMarkdown components={MD_COMPONENTS}>{entry.detail}</ReactMarkdown>
98+
</div>
8999
) : detailIsPath && absolute !== null ? (
90100
<a
91101
href={`vscode://file${encodeURI(absolute)}`}

0 commit comments

Comments
 (0)