Keep active task transcripts in memory for instant task switching

This commit is contained in:
Ian Gulliver
2026-10-16 10:45:38 -07:00
parent 8200ffd17e
commit 91d95d0581
17 changed files with 252 additions and 84 deletions
+10 -1
View File
@@ -4,7 +4,7 @@ import { markdown } from "./markdown.js";
import { previewURL } from "./preview.js";
import { findingCount, reviewCounts, severityCounts, unignoredFindings } from "./reviews.js";
import { composerText, endSend, flushDrafts, forgetLostImages, lostImageCount, setComposerImages, setComposerText, startSend } from "./drafts.js";
import { currentSession, findTask, focusTab, focusTask, on, onEach, pickReview, pinSnapshot, setFocus, showReview, state, type Topic } from "./state.js";
import { currentSession, findTask, focusTab, focusTask, on, onEach, pickReview, pinSnapshot, setFocus, showReview, state, transcriptLoading, type Topic } from "./state.js";
import { taskStyle } from "./taskcolor.js";
import { fillTodoLinks } from "./todolink.js";
@@ -15,6 +15,7 @@ type Review = Schemas["Review"];
export type ChatSource = {
key: () => string | undefined;
events: () => ByreEvent[];
loading: () => boolean;
session: () => AgentSession | undefined;
root: () => string;
taskId: () => string | undefined;
@@ -22,6 +23,7 @@ export type ChatSource = {
refreshTopics: Topic[];
transcriptTopic: Topic;
streamTopic: Topic;
readyTopic: Topic;
empty: string;
canSend: (sessionState: string) => boolean;
placeholder: string;
@@ -84,6 +86,7 @@ type Ctx = {
const taskSource: ChatSource = {
key: () => state.selectedTaskId,
events: () => state.transcript,
loading: transcriptLoading,
session: () => (state.selectedTaskId ? currentSession(state.selectedTaskId) : undefined),
root: () => (state.selectedTaskId ? (findTask(state.selectedTaskId)?.worktree_path ?? "") : ""),
taskId: () => state.selectedTaskId,
@@ -91,6 +94,7 @@ const taskSource: ChatSource = {
refreshTopics: ["tasks", "sessions"],
transcriptTopic: "transcript",
streamTopic: "stream",
readyTopic: "ready",
empty: "No task selected. Ask your lead, or pick one with ⌘K.",
canSend: () => true,
placeholder: "Message the agent. Paste or drop images to attach them.",
@@ -235,6 +239,7 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
const headImpl = makeHead(() => renderHead());
const scanner = h("div", { class: "scanner", hidden: true });
const loading = h("div", { class: "empty" }, "loading…");
const setRunning = (running: boolean) => {
transcript.classList.toggle("running", running);
scanner.hidden = !running;
@@ -541,6 +546,9 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
return;
}
replace(transcript, scanner);
if (src.loading()) {
transcript.prepend(loading);
}
appendEvents(src.events());
renderComposer();
};
@@ -553,6 +561,7 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
}),
onEach([src.transcriptTopic], (fresh) => appendEvents(fresh as ByreEvent[])),
onEach([src.streamTopic], (ev) => appendDelta(ev as ByreEvent)),
onEach([src.readyTopic], () => loading.remove()),
onEach(["tasks", "sessions"], () => fillTodoLinks(transcript, models[0]!)),
];