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
11 changes: 9 additions & 2 deletions apps/web/src/components/app/chat/chat-entries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
* record (not a Map) so React Query's structural sharing keeps its identity
* across agent updates that change nothing here.
*/
export function peerDirectory(

Check warning on line 104 in apps/web/src/components/app/chat/chat-entries.tsx

View workflow job for this annotation

GitHub Actions / ci

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components

Check warning on line 104 in apps/web/src/components/app/chat/chat-entries.tsx

View workflow job for this annotation

GitHub Actions / ci

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
agentId: string,
agents: readonly Pick<Agent, "id" | "name" | "type" | "parentAgentId">[]
): PeerDirectory {
Expand Down Expand Up @@ -179,7 +179,7 @@
* (dispatch_launch_agent) is that agent's, named from the agents list when
* it is still there and "Agent" otherwise; every other user post is "You".
*/
export function chatMessageAuthor(

Check warning on line 182 in apps/web/src/components/app/chat/chat-entries.tsx

View workflow job for this annotation

GitHub Actions / ci

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components

Check warning on line 182 in apps/web/src/components/app/chat/chat-entries.tsx

View workflow job for this annotation

GitHub Actions / ci

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
message: ChatMessage,
ctx: FeedContext
): PostAuthor {
Expand Down Expand Up @@ -243,8 +243,15 @@
* reads as one block.
*/
export const POST_TINT: Record<PostAuthor["kind"], string> = {
user: "bg-primary/[0.06] hover:bg-primary/[0.09]",
peer: "bg-violet-500/[0.06] hover:bg-violet-500/[0.09]",
// Only the user's own posts get a fill. At 6% `primary` sat too close to the
// page background in several themes to notice; 10% reads everywhere.
//
// No left accent bar: a rule down the post's left edge competed with the
// connected-agent border the sidebar draws on the pane's left edge.
user: "bg-primary/[0.10] hover:bg-primary/[0.14]",
// Agent-to-agent traffic is a side conversation the user is overhearing, so
// it recedes — indent and muted body carry it, with no fill of its own.
peer: "hover:bg-muted/30",
agent: "hover:bg-muted/40",
};

Expand Down
15 changes: 9 additions & 6 deletions apps/web/src/components/app/chat/chat-feed.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
POST_BODY_MEASURE,
SIDE_POST_INDENT,
peerDirectory,
POST_TINT,
} from "@/components/app/chat/chat-entries";
import {
ChatFeed,
Expand Down Expand Up @@ -512,8 +513,8 @@ describe("ChatFeed", () => {
// Not in the list any more: the generic agent icon.
"Agent agent",
]);
// Still a peer post: violet, with the sender's name.
expect(posts[0]!.className).toContain("bg-violet-500/[0.06]");
// Still a peer post: muted side-conversation treatment, sender's name.
expect(posts[0]!.className).toContain(POST_TINT.peer);
expect(
posts[0]!.querySelector('[data-testid="chat-post-author"]')?.textContent
).toBe("agt_kid");
Expand Down Expand Up @@ -704,18 +705,20 @@ describe("ChatFeed", () => {
expect(agentPost!.getAttribute("data-rule")).toBeNull();

expect(userOne!.getAttribute("data-author-kind")).toBe("user");
expect(userOne!.className).toContain("bg-primary/[0.06]");
expect(userOne!.className).toContain(POST_TINT.user);
// No accent bar: it competed with the sidebar's connected-agent border.
expect(userOne!.className).not.toContain("before:w-0.5");
expect(userOne!.getAttribute("data-group-start")).toBe("true");
expect(userOne!.getAttribute("data-rule")).toBe("true");
expect(userOne!.className).toContain("border-t");
// A grouped row keeps the tint (one block) but no boundary of its own.
expect(userTwo!.className).toContain("bg-primary/[0.06]");
expect(userTwo!.className).toContain(POST_TINT.user);
expect(userTwo!.getAttribute("data-group-start")).toBeNull();
expect(userTwo!.getAttribute("data-rule")).toBeNull();
expect(userTwo!.className).not.toContain("border-t");

expect(peer.getAttribute("data-author-kind")).toBe("peer");
expect(peer.className).toContain("bg-violet-500/[0.06]");
expect(peer.className).toContain(POST_TINT.peer);
expect(peer.getAttribute("data-rule")).toBe("true");

// Bodies stop at a reading measure; the row itself spans the pane.
Expand Down Expand Up @@ -1140,7 +1143,7 @@ describe("ChatFeed", () => {
expect(post.getAttribute("data-side")).toBe("true");
expect(post.className).toContain(SIDE_POST_INDENT);
expect(post.className).not.toContain("px-4");
expect(post.className).toContain("bg-violet-500/[0.06]");
expect(post.className).toContain(POST_TINT.peer);
const body = Array.from(post.querySelectorAll("div")).find((el) =>
el.className.includes(POST_BODY_MEASURE)
);
Expand Down
2 changes: 1 addition & 1 deletion docs/chat-surface-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ changes; the flag, routes and tools are unchanged.
share a parent, otherwise **agent** — also the fallback when the sender is
no longer in the list (generic bot icon). `agentRelation` in
`lib/agent-lineage.ts`; the same chip sits on the Messages panel's thread
headers so the two agree. The violet tint and the outgoing "to <name>"
headers so the two agree. The muted side-conversation treatment and the outgoing "to <name>"
line stay.
- Entries that arrive after the feed first rendered — new posts, status
lines, media, and posts edited in place — fade in (200 ms opacity with a
Expand Down
Loading