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 App
created: 2026-10-15T03:38:04Z
norm: '1'
sig: 063d0f00fea02511
body_hash: baf242069dc38c51
raw_hash: 04ec83f8b4b4da88
vouched: 2026-10-22T14:33:26Z
body_hash: a9cafa59e02070dc
raw_hash: df133116ed1fd81f
vouched: 2026-10-22T14:56:07Z
lines: 235-824
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: function Dashboard
created: 2026-10-19T20:51:03Z
norm: '1'
sig: 4bfb45497157c0e0
body_hash: 3dc6d02ecfe40716
raw_hash: d2e36ee5c66a68da
vouched: 2026-10-22T14:33:26Z
body_hash: 3517529f5c766be4
raw_hash: 40843a80b4f5c8da
vouched: 2026-10-22T14:56:08Z
lines: 256-606
---

Expand Down
1 change: 0 additions & 1 deletion apps/app/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,6 @@ export function App() {
user={hello.user}
people={people}
presence={presence[current.id]}
attention={attention}
local={local}
connected={connected}
onSetup={!local && current.repo ? () => startSetup(current) : undefined}
Expand Down
54 changes: 2 additions & 52 deletions apps/app/src/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,17 @@ import {
} from "@ace/ui";
import {
IconChevronDown,
IconCircleX,
IconCloud,
IconExternal,
IconGlobe,
IconHash,
IconLaptop,
IconMessage,
IconPlus,
IconRotate,
} from "@ace/ui/icons";
import type { GithubList, ModelRef } from "@ace/channel/protocol";
import type { Listing, People, Presence } from "@ace/host/protocol";

import type { Outcome } from "./attention";
import { Notice } from "./composer";
import { desktop, here } from "./desktop";
import { prompt, StateIcon } from "./github";
Expand All @@ -37,7 +34,7 @@ import { host } from "./host";
import { InitGit } from "./open-project";
import type { AppProject } from "./projects";
import { pulse, type Teammate } from "./pulse";
import { byBranch, needsYou, pickBackUp, type Work } from "./work";
import { byBranch, pickBackUp, type Work } from "./work";

/**
* A project's dashboard draft and history follow its identity, so setting up a GitHub project here
Expand Down Expand Up @@ -123,9 +120,6 @@ function Section({ title, action, children }: {
);
}

const ROW =
"flex min-w-0 items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm outline-none hover:bg-muted/60 focus-visible:ring-2 focus-visible:ring-ring/30";

function ChannelLink({ channel, onChannel }: {
channel: Listing;
onChannel: (channel: Listing) => void;
Expand Down Expand Up @@ -262,7 +256,6 @@ export function Dashboard(
user,
people,
presence,
attention,
local,
connected,
onSetup,
Expand All @@ -282,7 +275,6 @@ export function Dashboard(
user: string;
people: People;
presence: Presence[] | undefined;
attention: Map<string, Outcome>;
local: boolean;
connected: boolean;
/** Offered for a GitHub project with no checkout on this host. */
Expand Down Expand Up @@ -322,12 +314,10 @@ export function Dashboard(
: undefined;
// `@me` is this host's GitHub CLI account, the only one Ace reads GitHub with.
const authored = useGithub<GithubList>(list("prs", "author:@me"));
const reviews = useGithub<GithubList>(list("prs", "review-requested:@me"));
const assigned = useGithub<GithubList>(list("issues", "assignee:@me"));
const github = [authored, reviews, assigned];
const github = [authored, assigned];
const branches = byBranch(channels, user);
const work = pickBackUp(authored.value?.items || [], assigned.value?.items || [], branches);
const waiting = needsYou(channels, attention, reviews.value?.items || [], branches);
const team = pulse(channels, presence, people, user, since, now);
const owner = (channel?: Listing) =>
channel && channel.owner !== user ? people[channel.owner] || channel.owner : undefined;
Expand Down Expand Up @@ -458,46 +448,6 @@ export function Dashboard(
)}
</div>
)}
{waiting.length > 0 && (
<Section title="Needs you">
<ul className="flex flex-col gap-1">
{waiting.map((value) =>
value.kind === "review"
? (
<WorkRow
key={value.work.item.url}
work={value.work}
owner={owner(value.work.channel)}
busy={busy}
onChannel={onChannel}
/>
)
: (
<li key={value.channel.id} className="flex">
<button
type="button"
className={`${ROW} flex-1`}
onClick={() => onChannel(value.channel)}
>
{value.outcome === "failed"
? <IconCircleX className="size-4 shrink-0 text-destructive" aria-hidden />
: <IconMessage className="size-4 shrink-0 text-accent" aria-hidden />}
<span className="min-w-0 flex-1">
<span className="block truncate">{value.channel.name}</span>
<span className="mt-0.5 block truncate text-xs text-muted-foreground">
{value.outcome === "failed"
? "Your run failed"
: "The agent is waiting for your answer"}
{owner(value.channel) && ` · ${owner(value.channel)}’s channel`}
</span>
</span>
</button>
</li>
)
)}
</ul>
</Section>
)}
{project.repo && (
<Section
title="Pick back up"
Expand Down
27 changes: 0 additions & 27 deletions apps/app/src/work.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import type { GithubItem } from "@ace/channel/protocol";
import type { Listing } from "@ace/host/protocol";

import type { Outcome } from "./attention";

/** A pull request or issue for the viewer, with the channel already on it. */
export type Work = { item: GithubItem; reason: string; channel?: Listing };

export type NeedsYou =
| { kind: "channel"; channel: Listing; outcome: Exclude<Outcome, "done"> }
| { kind: "review"; work: Work };

const ROWS = 10;

/**
Expand Down Expand Up @@ -58,24 +52,3 @@ export function pickBackUp(
a.rank - b.rank || b.work.item.updated.localeCompare(a.work.item.updated)
).slice(0, ROWS).map(({ work }) => work);
}

/** The viewer's failed runs and unanswered questions, newest first, then requested reviews. */
export function needsYou(
channels: Listing[],
attention: Map<string, Outcome>,
reviews: GithubItem[],
branches: Map<string, Listing>,
): NeedsYou[] {
const waiting: NeedsYou[] = channels.flatMap((channel) => {
const outcome = attention.get(channel.id);
if (!outcome || outcome === "done" || channel.state === "archived") return [];
return [{ kind: "channel" as const, channel, outcome }];
}).sort((a, b) => b.channel.ran!.at - a.channel.ran!.at);
return [
...waiting,
...reviews.map((item): NeedsYou => ({
kind: "review",
work: { item, channel: onBranch(item, branches), reason: "Review requested" },
})),
];
}