mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-10-29 18:52:45 +08:00
perf(agent-manager): coalesce timeline auto-scroll
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Keep the Agent Manager task timeline in place while reviewing earlier activity, and resume following updates from the latest bar.
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { sizes, MAX_HEIGHT } from "../../webview-ui/src/utils/timeline/sizes"
|
||||
import { sizes, pinned, MAX_HEIGHT } from "../../webview-ui/src/utils/timeline/sizes"
|
||||
import type { Part, TextPart, ToolPart, StepFinishPart } from "../../webview-ui/src/types/messages"
|
||||
|
||||
function mkText(text: string): TextPart {
|
||||
@@ -89,4 +89,10 @@ describe("timeline sizes", () => {
|
||||
expect(Number.isInteger(bar.height)).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it("detects whether scrolling should follow new bars", () => {
|
||||
expect(pinned({ scrollLeft: 100, scrollWidth: 200, clientWidth: 100 })).toBe(true)
|
||||
expect(pinned({ scrollLeft: 88, scrollWidth: 200, clientWidth: 100 })).toBe(true)
|
||||
expect(pinned({ scrollLeft: 87, scrollWidth: 200, clientWidth: 100 })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Component, Index, Show, createMemo, createEffect, createSignal, on, onC
|
||||
import { Portal } from "solid-js/web"
|
||||
import { useSession } from "../../context/session"
|
||||
import { color, label } from "../../utils/timeline/colors"
|
||||
import { sizes, MAX_HEIGHT } from "../../utils/timeline/sizes"
|
||||
import { sizes, pinned, MAX_HEIGHT } from "../../utils/timeline/sizes"
|
||||
import type { Part, Message } from "../../types/messages"
|
||||
|
||||
export interface TimelineBar {
|
||||
@@ -73,19 +73,31 @@ export const TaskTimeline: Component = () => {
|
||||
const bars = createMemo(() => collect(messages(), allParts()))
|
||||
const busy = () => session.status() === "busy"
|
||||
|
||||
// Auto-scroll to the latest bar when new bars appear
|
||||
// Auto-scroll to the latest bar when new bars appear while preserving manual scroll position.
|
||||
let prev = 0
|
||||
let frame: number | undefined
|
||||
let follow = true
|
||||
const onScroll = () => {
|
||||
if (ref) follow = pinned(ref)
|
||||
}
|
||||
createEffect(
|
||||
on(
|
||||
() => bars().length,
|
||||
(len) => {
|
||||
if (len > prev && ref) {
|
||||
ref.scrollLeft = ref.scrollWidth
|
||||
if (len > prev && ref && follow && frame === undefined) {
|
||||
frame = requestAnimationFrame(() => {
|
||||
frame = undefined
|
||||
if (!ref || !follow) return
|
||||
ref.scrollLeft = ref.scrollWidth
|
||||
})
|
||||
}
|
||||
prev = len
|
||||
},
|
||||
),
|
||||
)
|
||||
onCleanup(() => {
|
||||
if (frame !== undefined) cancelAnimationFrame(frame)
|
||||
})
|
||||
|
||||
const hideTip = () => {
|
||||
tipBar = undefined
|
||||
@@ -161,6 +173,7 @@ export const TaskTimeline: Component = () => {
|
||||
onPointerUp={onPointerUp}
|
||||
onPointerCancel={onPointerUp}
|
||||
onPointerLeave={hideTip}
|
||||
onScroll={onScroll}
|
||||
>
|
||||
<Index each={bars()}>
|
||||
{(bar) => {
|
||||
|
||||
@@ -20,6 +20,16 @@ export interface BarSize {
|
||||
content: number
|
||||
}
|
||||
|
||||
export interface TimelineScroll {
|
||||
scrollLeft: number
|
||||
scrollWidth: number
|
||||
clientWidth: number
|
||||
}
|
||||
|
||||
export function pinned(scroll: TimelineScroll, slack = BAR_W): boolean {
|
||||
return scroll.scrollWidth - scroll.clientWidth - scroll.scrollLeft <= slack
|
||||
}
|
||||
|
||||
// ── Content length ───────────────────────────────────────────────────
|
||||
|
||||
function content(part: Part): number {
|
||||
|
||||
Reference in New Issue
Block a user