fix: filter web timeline assistant marks

This commit is contained in:
2026-07-22 16:08:45 +09:00
parent ad93047a25
commit c7c838b4e5
5 changed files with 106 additions and 3 deletions
@@ -3,7 +3,9 @@ import {
createConsoleProjector,
projectConsole,
segmentsToText,
selectConsoleTimelineLines,
workerConsoleHref,
type ConsoleLine,
} from "./model.ts";
declare const Deno: {
@@ -24,6 +26,16 @@ function assertEquals<T>(actual: T, expected: T): void {
}
}
function consoleLine(id: string, kind: ConsoleLine["kind"]): ConsoleLine {
return {
id,
kind,
title: kind,
body: id,
source: "event",
};
}
Deno.test("workerConsoleHref encodes runtime and worker target authority", () => {
assert(
workerConsoleHref({
@@ -857,3 +869,20 @@ Deno.test("projectConsole reseeds visible rows from segment rotation", () => {
["user:after rotation seed"],
);
});
Deno.test("selectConsoleTimelineLines keeps all users and only last assistant per turn", () => {
const items = [
consoleLine("u1", "user"),
consoleLine("a1", "assistant"),
consoleLine("tool1", "tool"),
consoleLine("a2", "assistant"),
consoleLine("u2", "user"),
consoleLine("a3", "assistant"),
consoleLine("a4", "assistant"),
];
assertEquals(
selectConsoleTimelineLines(items).map(({ item, index }) => `${index}:${item.id}`),
["0:u1", "3:a2", "4:u2", "6:a4"],
);
});
@@ -64,6 +64,39 @@ export type ConsoleProjection = {
lastEventId: string | null;
};
export type ConsoleTimelineLineSelection = {
item: ConsoleLine;
index: number;
};
export function selectConsoleTimelineLines(
items: ConsoleLine[],
): ConsoleTimelineLineSelection[] {
const selected: ConsoleTimelineLineSelection[] = [];
let lastAssistant: ConsoleTimelineLineSelection | null = null;
const flushAssistant = () => {
if (lastAssistant) {
selected.push(lastAssistant);
lastAssistant = null;
}
};
items.forEach((item, index) => {
if (item.kind === "user") {
flushAssistant();
selected.push({ item, index });
return;
}
if (item.kind === "assistant") {
lastAssistant = { item, index };
}
});
flushAssistant();
return selected;
}
export type WorkerTarget = {
runtime_id: string;
worker_id: string;
@@ -17,6 +17,7 @@
import { fitTextarea } from "$lib/workspace/console/textarea-fit";
import {
createConsoleProjector,
selectConsoleTimelineLines,
type ConsoleEventInput,
type ConsoleLine,
type ConsoleProjection,
@@ -643,8 +644,10 @@
metrics: ScrollMetrics,
): TimelineLayout {
const denominator = Math.max(items.length - 1, 1);
const rawMarks = items
.map((item, index) => timelineMarkForLine(item, index, denominator))
const rawMarks = selectConsoleTimelineLines(items)
.map(({ item, index }) =>
timelineMarkForLine(item, index, denominator)
)
.filter((mark): mark is TimelineMark => mark !== null);
const trackHeight = timelineTrackHeight(metrics);
const positioned = positionTimelineMarks(rawMarks, trackHeight);