mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-10-21 05:06:37 +00:00
fix(timeline): space every marker evenly (#1062)
The rail doubled the gap wherever a tool marker started a different message, a collapsed tool group ended or a prompt followed an assistant with tools: a V1 notion of tools belonging to their assistant message. OpenCode V2 records each model call as its own message, so almost every tool started a new group while tools called together stayed tight, giving irregular, pronounced gaps that meant nothing to the reader. Every visible marker now follows the previous one by one uniform gap, and the message-tool bookkeeping is removed. Validation: render-cost checks the exact 10,000-marker extent with uniform gaps; timeline-tool-filters, history-navigation and system-messages browser tests and the UI typecheck pass.
This commit is contained in:
parent
da0dc279a7
commit
00f39ada16
4 changed files with 7 additions and 55 deletions
|
|
@ -101,7 +101,7 @@ The timeline uses the same standard native scrollbar as the transcript, with one
|
|||
ordinary gutter. Marker rectangles give up width to it; fixed-size icons and
|
||||
vertical geometry never scale with available width. `timeline-virtual-list.tsx`
|
||||
measures the marker height and gap primitives, computes exact offsets (including
|
||||
group spacing), and mounts only the visible range. Hidden tools are excluded from
|
||||
the uniform gaps between markers), and mounts only the visible range. Hidden tools are excluded from
|
||||
the layout. Browsing offscreen rows cannot change the scrollbar extent.
|
||||
|
||||
Manual rail browsing suppresses active-marker reveals until an explicit transcript
|
||||
|
|
|
|||
|
|
@ -446,15 +446,6 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
|
|||
onCleanup(() => { observer.disconnect(); window.removeEventListener("resize", clearHoverPreview) })
|
||||
})
|
||||
|
||||
// Messages with at least one tool segment, for the group spacing below.
|
||||
const messagesWithTools = createMemo(() => {
|
||||
const set = new Set<string>()
|
||||
for (const s of props.segments) {
|
||||
if (s.type === "tool") set.add(s.messageId)
|
||||
}
|
||||
return set
|
||||
})
|
||||
|
||||
const segmentStates = createMemo(() => {
|
||||
const resolvedStore = store()
|
||||
const result = new Map<string, TimelineSegmentState>()
|
||||
|
|
@ -507,46 +498,9 @@ const MessageTimeline: Component<MessageTimelineProps> = (props) => {
|
|||
const messageId = messageBySegmentId().get(id)
|
||||
return messageId === undefined ? null : firstVisibleByMessage().get(messageId) ?? null
|
||||
})
|
||||
const segmentSpacerHeights = createMemo(() => {
|
||||
const states = segmentStates()
|
||||
const result = new Map<string, number>()
|
||||
let previousVisible: TimelineSegment | null = null
|
||||
|
||||
for (let index = 0; index < props.segments.length; index += 1) {
|
||||
const segment = props.segments[index]
|
||||
const state = states.get(segment.id)
|
||||
|
||||
if (state?.hidden) {
|
||||
result.set(segment.id, 0)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!previousVisible) {
|
||||
result.set(segment.id, 0)
|
||||
previousVisible = segment
|
||||
continue
|
||||
}
|
||||
|
||||
const previousRaw = index > 0 ? props.segments[index - 1] : null
|
||||
const startsVisibleToolGroup = segment.type === "tool"
|
||||
&& (previousVisible.type !== "tool" || previousVisible.messageId !== segment.messageId)
|
||||
const startsCollapsedToolGroup = segment.type === "assistant"
|
||||
&& previousVisible.messageId !== segment.messageId
|
||||
&& messagesWithTools().has(segment.messageId)
|
||||
&& previousRaw?.type === "tool"
|
||||
&& previousRaw.messageId === segment.messageId
|
||||
const followsVisibleGroupParent = (segment.type === "user" || segment.type === "compaction")
|
||||
&& previousVisible.type === "assistant"
|
||||
&& messagesWithTools().has(previousVisible.messageId)
|
||||
|
||||
const gapUnits = 1 + (startsVisibleToolGroup || startsCollapsedToolGroup || followsVisibleGroupParent ? 1 : 0)
|
||||
result.set(segment.id, gapUnits)
|
||||
|
||||
previousVisible = segment
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
// One uniform gap between visible markers, none before the first. V2 records
|
||||
// each model call as its own message, so message-based group gaps were noise.
|
||||
const segmentSpacerHeights = createMemo(() => new Map(visibleSegments().map((segment, index) => [segment.id, index ? 1 : 0])))
|
||||
|
||||
return (
|
||||
<div class="message-timeline-container">
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export interface TimelineListHandle {
|
|||
|
||||
/** Timeline markers have known geometry, unlike transcript messages. Measure
|
||||
* the two CSS primitives once and give the native scrollbar the exact extent,
|
||||
* including offscreen group gaps. No estimated or zero-height hidden rows. */
|
||||
* including offscreen gaps. No estimated or zero-height hidden rows. */
|
||||
export default function TimelineVirtualList<T>(props: {
|
||||
items: T[]
|
||||
gap: (item: T) => number
|
||||
|
|
|
|||
|
|
@ -414,10 +414,8 @@ test("timeline exact extent, bounded DOM and active highlight updates do not mou
|
|||
const marker = document.querySelector<HTMLElement>(".timeline-measure button")!
|
||||
const gap = document.querySelector<HTMLElement>(".timeline-measure div")!
|
||||
const markerHeight = parseFloat(getComputedStyle(marker).height), gapHeight = parseFloat(getComputedStyle(gap).height)
|
||||
// Each 3-marker group has a double gap before its tool and following user.
|
||||
let units = 0
|
||||
for (let index = 1; index < 10000; index++) units += index % 3 === 2 ? 1 : 2
|
||||
return { extent: parseFloat(content.style.height), expected: markerHeight * 10000 + gapHeight * units, mounted: document.querySelectorAll(".timeline-virtual-row").length }
|
||||
// One uniform gap between consecutive markers, whatever their message.
|
||||
return { extent: parseFloat(content.style.height), expected: markerHeight * 10000 + gapHeight * 9999, mounted: document.querySelectorAll(".timeline-virtual-row").length }
|
||||
})
|
||||
console.log("timeline-geometry", geometry)
|
||||
// CSS serialization and native scroll extents round at this magnitude.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue