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
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: function usePull
created: 2026-10-16T22:11:37Z
norm: '1'
sig: a9da5d18c3817c7c
body_hash: 745f3ff60655c30b
raw_hash: e2e769d80fa9b661
vouched: 2026-10-22T16:53:07Z
body_hash: 64fb1617a63f2e44
raw_hash: e9ed5de066837849
vouched: 2026-10-23T12:33:55Z
lines: 191-218
---

Expand Down
4 changes: 3 additions & 1 deletion apps/app/src/channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ const pulls = new Map<string, GithubPull | null>();
/**
* The newest pull request from the chat's branch, read again while its channel is shown, since the
* chat surfaces failed checks with the details sidebar closed. Opening a pull request doesn't move
* the head, so a run ending reads it again too.
* the head, so a run ending reads it again too, as does the window regaining focus.
*/
function usePull(
repo: string | undefined,
Expand All @@ -221,9 +221,11 @@ function usePull(
);
void read();
const timer = setInterval(read, settling ? SETTLING : PULL);
window.addEventListener("focus", read);
return () => {
active = false;
clearInterval(timer);
window.removeEventListener("focus", read);
};
}, [repo, branch, head, busy, settling]);
return key ? pulls.get(key) : undefined;
Expand Down
52 changes: 48 additions & 4 deletions packages/ui/src/components/session-details/session-details.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { type ComponentType, type ReactNode, useId, useState } from "react";
import {
type ComponentType,
type ReactNode,
useId,
useLayoutEffect,
useRef,
useState,
} from "react";

import {
IconBranch,
Expand Down Expand Up @@ -246,6 +253,45 @@ function CreatePr({ creating, onCreate, onDraft }: {
);
}

/** Clamped to four lines, with Show more only while the text doesn't fit. */
function Summary({ text }: { text: string }) {
const [open, setOpen] = useState(false);
const [long, setLong] = useState(false);
const ref = useRef<HTMLParagraphElement>(null);
useLayoutEffect(() => {
const node = ref.current;
if (!node || open) return;
const measure = () => setLong(node.scrollHeight > node.clientHeight);
measure();
const observer = new ResizeObserver(measure);
observer.observe(node);
return () => observer.disconnect();
}, [text, open]);
return (
<>
<p
ref={ref}
className={cn(
"text-xs leading-snug whitespace-pre-wrap break-words text-foreground",
!open && "line-clamp-4",
)}
>
{text}
</p>
{(long || open) && (
<button
type="button"
aria-expanded={open}
className="self-start rounded-sm text-xs text-muted-foreground outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/30"
onClick={() => setOpen(!open)}
>
{open ? "Show less" : "Show more"}
</button>
)}
</>
);
}

function Pull({ pull }: { pull: DetailsPull }) {
const [Icon, color] = PULL_ICONS[pull.state];
const failed = pull.checks.some((check) => check.state === "failed");
Expand Down Expand Up @@ -316,9 +362,7 @@ export function SessionDetailsView(
<IconX aria-hidden />
</Button>
</div>
<p className="line-clamp-4 text-xs leading-snug whitespace-pre-wrap break-words text-foreground">
{summary || "No summary yet."}
</p>
<Summary text={summary || "No summary yet."} />
</section>

{(changes || branch || pull !== undefined) && (
Expand Down