Watch: snooze finished threads, name the files a file approval would edit, Settle on finished notifications, an instant launch from the saved feed, plain Loading states, and no more cancelled errors
Gateway / check (push) Successful in 9s
Gateway / image (push) Successful in 22s

Gateway: an approval push for a file change says which files it would edit
This commit is contained in:
gib committed 2026-11-02 14:45:34 -05:00
1 parent 4dc4e57dac
commit edc4b88990
35 files changed
+1162 -144

No files matched your search

+25 -5
View File
@@ -4,7 +4,7 @@
// a host loads its first snapshot, so a restart never replays old news.
import { threadPhase, type Phase } from "./awareness.ts";
import type { PushCategory, PushPayload } from "./contract.ts";
import type { PendingRequest, PushCategory, PushPayload } from "./contract.ts";
import type { HostRow } from "./db.ts";
import type { T3Host, ThreadChange } from "./t3/host.ts";
import { trimText, type SummaryCache, type ThreadSummary } from "./views.ts";
@@ -139,12 +139,32 @@ function category(phase: Phase): PushCategory {
}
}
/**
* An approval push's body: the files a file-change approval would edit, as "Wants to edit
* compose.yml +9 −6" or "Wants to edit 3 files: a.ts, b.ts, c.ts", or else the command it wants to
* run. The watch's cards word it the same way.
*/
export function approvalBody(request: PendingRequest | null): string {
const files = request?.kind === "file-change" ? request.files : [];
const [file] = files;
if (files.length === 1 && file) {
const counts = [
file.additions === null ? null : `+${file.additions}`,
file.deletions === null ? null : `\u2212${file.deletions}`,
].filter((count) => count !== null);
return trimText(["Wants to edit", file.name, ...counts].join(" "), BODY_CHARS);
}
if (files.length > 1) {
return trimText(`Wants to edit ${files.length} files: ${files.map((file) => file.name).join(", ")}`, BODY_CHARS);
}
const prompt = request?.prompt;
return prompt ? `Wants to run: ${trimText(prompt, BODY_CHARS)}` : "Needs your approval.";
}
function body(phase: Phase, lastError: string | null, summary: ThreadSummary | undefined): string {
switch (phase) {
case "waiting_for_approval": {
const prompt = summary?.request?.prompt;
return prompt ? `Wants to run: ${trimText(prompt, BODY_CHARS)}` : "Needs your approval.";
}
case "waiting_for_approval":
return approvalBody(summary?.request ?? null);
case "waiting_for_input": {
const question = summary?.request?.questions[0]?.question;
return question ? trimText(question, BODY_CHARS) : "Has a question for you.";