Unify issue rows with GitHub-style mobile layout across app

On mobile, all issue rows now show title first (up to 2 lines),
with metadata (icons, identifier, timestamp) on a second line below.
Desktop layout is preserved as single-line rows.

Affected locations: Inbox recent issues, Inbox stale work,
Dashboard recent tasks, and IssuesList.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-03-07 20:01:16 -06:00
parent 7661fae4b3
commit 6e86f69f95
3 changed files with 180 additions and 161 deletions

View File

@@ -313,26 +313,28 @@ export function Dashboard() {
<Link
key={issue.id}
to={`/issues/${issue.identifier ?? issue.id}`}
className="px-4 py-2 text-sm cursor-pointer hover:bg-accent/50 transition-colors no-underline text-inherit block"
className="px-4 py-2.5 text-sm cursor-pointer hover:bg-accent/50 transition-colors no-underline text-inherit block"
>
<div className="flex gap-3">
<div className="flex items-start gap-2 min-w-0 flex-1">
<div className="flex items-center gap-2 shrink-0 mt-0.5">
<PriorityIcon priority={issue.priority} />
<StatusIcon status={issue.status} />
</div>
<p className="min-w-0 flex-1 truncate">
<span>{issue.title}</span>
{issue.assigneeAgentId && (() => {
const name = agentName(issue.assigneeAgentId);
return name
? <span className="hidden sm:inline"><Identity name={name} size="sm" className="ml-2 inline-flex" /></span>
: null;
})()}
</p>
</div>
<span className="text-xs text-muted-foreground shrink-0 pt-0.5">
{timeAgo(issue.updatedAt)}
<div className="flex flex-col gap-1 sm:flex-row sm:items-center sm:gap-3">
<span className="line-clamp-2 text-sm sm:order-2 sm:flex-1 sm:min-w-0 sm:line-clamp-none sm:truncate">
{issue.title}
</span>
<span className="flex items-center gap-2 sm:order-1 sm:shrink-0">
<PriorityIcon priority={issue.priority} />
<StatusIcon status={issue.status} />
<span className="text-xs font-mono text-muted-foreground">
{issue.identifier ?? issue.id.slice(0, 8)}
</span>
{issue.assigneeAgentId && (() => {
const name = agentName(issue.assigneeAgentId);
return name
? <span className="hidden sm:inline-flex"><Identity name={name} size="sm" /></span>
: null;
})()}
<span className="text-xs text-muted-foreground sm:hidden">&middot;</span>
<span className="text-xs text-muted-foreground shrink-0 sm:order-last">
{timeAgo(issue.updatedAt)}
</span>
</span>
</div>
</Link>

View File

@@ -841,38 +841,39 @@ export function Inbox() {
{staleIssues.map((issue) => (
<div
key={issue.id}
className="group/stale relative flex items-center gap-3 overflow-hidden px-4 py-3 transition-colors hover:bg-accent/50"
className="group/stale relative flex items-start gap-3 overflow-hidden px-4 py-3 transition-colors hover:bg-accent/50"
>
<Clock className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground sm:mt-0" />
<Link
to={`/issues/${issue.identifier ?? issue.id}`}
className="flex min-w-0 flex-1 cursor-pointer items-center gap-3 no-underline text-inherit"
className="flex min-w-0 flex-1 cursor-pointer flex-col gap-1 no-underline text-inherit sm:flex-row sm:items-center sm:gap-3"
>
<Clock className="h-4 w-4 shrink-0 text-muted-foreground" />
<PriorityIcon priority={issue.priority} />
<StatusIcon status={issue.status} />
<span className="shrink-0 text-xs font-mono text-muted-foreground">
{issue.identifier ?? issue.id.slice(0, 8)}
<span className="line-clamp-2 text-sm sm:order-2 sm:flex-1 sm:min-w-0 sm:line-clamp-none sm:truncate">
{issue.title}
</span>
<span className="min-w-0 flex-1 truncate text-sm">{issue.title}</span>
{issue.assigneeAgentId &&
(() => {
const name = agentName(issue.assigneeAgentId);
return name ? (
<Identity name={name} size="sm" />
) : (
<span className="shrink-0 font-mono text-xs text-muted-foreground">
{issue.assigneeAgentId.slice(0, 8)}
</span>
);
})()}
<span className="shrink-0 text-xs text-muted-foreground">
updated {timeAgo(issue.updatedAt)}
<span className="flex items-center gap-2 sm:order-1 sm:shrink-0">
<PriorityIcon priority={issue.priority} />
<StatusIcon status={issue.status} />
<span className="shrink-0 text-xs font-mono text-muted-foreground">
{issue.identifier ?? issue.id.slice(0, 8)}
</span>
{issue.assigneeAgentId &&
(() => {
const name = agentName(issue.assigneeAgentId);
return name ? (
<span className="hidden sm:inline-flex"><Identity name={name} size="sm" /></span>
) : null;
})()}
<span className="text-xs text-muted-foreground sm:hidden">&middot;</span>
<span className="shrink-0 text-xs text-muted-foreground sm:order-last">
updated {timeAgo(issue.updatedAt)}
</span>
</span>
</Link>
<button
type="button"
onClick={() => dismiss(`stale:${issue.id}`)}
className="rounded-md p-1 text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-foreground group-hover/stale:opacity-100"
className="mt-0.5 rounded-md p-1 text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-foreground group-hover/stale:opacity-100 sm:mt-0"
aria-label="Dismiss"
>
<X className="h-3.5 w-3.5" />
@@ -900,7 +901,7 @@ export function Inbox() {
key={issue.id}
className="flex items-start gap-3 px-4 py-3 transition-colors hover:bg-accent/50"
>
<span className="flex h-5 w-4 shrink-0 items-center justify-center">
<span className="flex h-5 w-4 shrink-0 items-center justify-center mt-0.5 sm:mt-0">
{(isUnread || isFading) && (
<button
type="button"
@@ -922,21 +923,26 @@ export function Inbox() {
</span>
<Link
to={`/issues/${issue.identifier ?? issue.id}`}
className="flex flex-1 min-w-0 cursor-pointer flex-wrap items-center gap-x-3 gap-y-0.5 no-underline text-inherit sm:flex-nowrap"
className="flex flex-1 min-w-0 cursor-pointer flex-col gap-1 no-underline text-inherit sm:flex-row sm:items-center sm:gap-3"
>
<PriorityIcon priority={issue.priority} />
<StatusIcon status={issue.status} />
<span className="text-xs font-mono text-muted-foreground">
{issue.identifier ?? issue.id.slice(0, 8)}
</span>
<span className="ml-auto shrink-0 text-xs text-muted-foreground sm:order-last">
{issue.lastExternalCommentAt
? `commented ${timeAgo(issue.lastExternalCommentAt)}`
: `updated ${timeAgo(issue.updatedAt)}`}
</span>
<span className="w-full line-clamp-2 text-sm sm:w-0 sm:flex-1 sm:line-clamp-none sm:truncate">
<span className="line-clamp-2 text-sm sm:order-2 sm:flex-1 sm:min-w-0 sm:line-clamp-none sm:truncate">
{issue.title}
</span>
<span className="flex items-center gap-2 sm:order-1 sm:shrink-0">
<PriorityIcon priority={issue.priority} />
<StatusIcon status={issue.status} />
<span className="text-xs font-mono text-muted-foreground">
{issue.identifier ?? issue.id.slice(0, 8)}
</span>
<span className="text-xs text-muted-foreground sm:hidden">
&middot;
</span>
<span className="text-xs text-muted-foreground sm:order-last">
{issue.lastExternalCommentAt
? `commented ${timeAgo(issue.lastExternalCommentAt)}`
: `updated ${timeAgo(issue.updatedAt)}`}
</span>
</span>
</Link>
</div>
);