Centralize tool call scroll helpers
This commit is contained in:
@@ -9,7 +9,14 @@ import type { DiffViewMode } from "../stores/preferences"
|
|||||||
import { sendPermissionResponse } from "../stores/instances"
|
import { sendPermissionResponse } from "../stores/instances"
|
||||||
import type { TextPart, RenderCache } from "../types/message"
|
import type { TextPart, RenderCache } from "../types/message"
|
||||||
import { resolveToolRenderer } from "./tool-call/renderers"
|
import { resolveToolRenderer } from "./tool-call/renderers"
|
||||||
import type { DiffPayload, DiffRenderOptions, MarkdownRenderOptions, ToolCallPart, ToolRendererContext } from "./tool-call/types"
|
import type {
|
||||||
|
DiffPayload,
|
||||||
|
DiffRenderOptions,
|
||||||
|
MarkdownRenderOptions,
|
||||||
|
ToolCallPart,
|
||||||
|
ToolRendererContext,
|
||||||
|
ToolScrollHelpers,
|
||||||
|
} from "./tool-call/types"
|
||||||
import { getRelativePath, getToolIcon, getToolName, isToolStateCompleted, isToolStateError, isToolStateRunning } from "./tool-call/utils"
|
import { getRelativePath, getToolIcon, getToolName, isToolStateCompleted, isToolStateError, isToolStateRunning } from "./tool-call/utils"
|
||||||
import { getLogger } from "../lib/logger"
|
import { getLogger } from "../lib/logger"
|
||||||
|
|
||||||
@@ -419,7 +426,20 @@ export default function ToolCall(props: ToolCallProps) {
|
|||||||
persistScrollSnapshot(event.currentTarget)
|
persistScrollSnapshot(event.currentTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const scrollHelpers: ToolScrollHelpers = {
|
||||||
|
registerContainer: (element, options) => {
|
||||||
|
if (options?.disableTracking) return
|
||||||
|
initializeScrollContainer(element)
|
||||||
|
},
|
||||||
|
handleScroll: handleScrollEvent,
|
||||||
|
renderSentinel: (options) => {
|
||||||
|
if (options?.disableTracking) return null
|
||||||
|
return <div ref={setBottomSentinel} aria-hidden="true" class="tool-call-scroll-sentinel" style={{ height: "1px" }} />
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
|
||||||
const container = scrollContainer()
|
const container = scrollContainer()
|
||||||
if (!container) return
|
if (!container) return
|
||||||
|
|
||||||
@@ -565,11 +585,8 @@ export default function ToolCall(props: ToolCallProps) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class="message-text tool-call-markdown tool-call-markdown-large tool-call-diff-shell"
|
class="message-text tool-call-markdown tool-call-markdown-large tool-call-diff-shell"
|
||||||
ref={(element) => {
|
ref={(element) => scrollHelpers.registerContainer(element, { disableTracking: options?.disableScrollTracking })}
|
||||||
if (options?.disableScrollTracking) return
|
onScroll={options?.disableScrollTracking ? undefined : scrollHelpers.handleScroll}
|
||||||
initializeScrollContainer(element)
|
|
||||||
}}
|
|
||||||
onScroll={options?.disableScrollTracking ? undefined : handleScrollEvent}
|
|
||||||
>
|
>
|
||||||
<div class="tool-call-diff-toolbar" role="group" aria-label="Diff view mode">
|
<div class="tool-call-diff-toolbar" role="group" aria-label="Diff view mode">
|
||||||
<span class="tool-call-diff-toolbar-label">{toolbarLabel}</span>
|
<span class="tool-call-diff-toolbar-label">{toolbarLabel}</span>
|
||||||
@@ -601,9 +618,7 @@ export default function ToolCall(props: ToolCallProps) {
|
|||||||
cacheEntryParams={cacheHandle.params()}
|
cacheEntryParams={cacheHandle.params()}
|
||||||
onRendered={handleDiffRendered}
|
onRendered={handleDiffRendered}
|
||||||
/>
|
/>
|
||||||
<Show when={!options?.disableScrollTracking}>
|
{scrollHelpers.renderSentinel({ disableTracking: options?.disableScrollTracking })}
|
||||||
<div ref={setBottomSentinel} aria-hidden="true" class="tool-call-scroll-sentinel" style={{ height: "1px" }} />
|
|
||||||
</Show>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -630,14 +645,14 @@ export default function ToolCall(props: ToolCallProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={messageClass} ref={(element) => initializeScrollContainer(element)} onScroll={handleScrollEvent}>
|
<div class={messageClass} ref={(element) => scrollHelpers.registerContainer(element)} onScroll={scrollHelpers.handleScroll}>
|
||||||
<Markdown
|
<Markdown
|
||||||
part={markdownPart}
|
part={markdownPart}
|
||||||
isDark={isDark()}
|
isDark={isDark()}
|
||||||
disableHighlight={disableHighlight}
|
disableHighlight={disableHighlight}
|
||||||
onRendered={handleMarkdownRendered}
|
onRendered={handleMarkdownRendered}
|
||||||
/>
|
/>
|
||||||
<div ref={setBottomSentinel} aria-hidden="true" class="tool-call-scroll-sentinel" style={{ height: "1px" }} />
|
{scrollHelpers.renderSentinel()}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -654,6 +669,7 @@ export default function ToolCall(props: ToolCallProps) {
|
|||||||
partVersion: partVersionAccessor,
|
partVersion: partVersionAccessor,
|
||||||
renderMarkdown: renderMarkdownContent,
|
renderMarkdown: renderMarkdownContent,
|
||||||
renderDiff: renderDiffContent,
|
renderDiff: renderDiffContent,
|
||||||
|
scrollHelpers,
|
||||||
}
|
}
|
||||||
|
|
||||||
let previousPartVersion: number | undefined
|
let previousPartVersion: number | undefined
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const taskRenderer: ToolRenderer = {
|
|||||||
}
|
}
|
||||||
return base
|
return base
|
||||||
},
|
},
|
||||||
renderBody({ toolState, toolCall, messageVersion, partVersion }) {
|
renderBody({ toolState, toolCall, messageVersion, partVersion, scrollHelpers }) {
|
||||||
const items = createMemo(() => {
|
const items = createMemo(() => {
|
||||||
// Track the reactive change points so we only recompute when the part/message changes
|
// Track the reactive change points so we only recompute when the part/message changes
|
||||||
messageVersion?.()
|
messageVersion?.()
|
||||||
@@ -63,7 +63,11 @@ export const taskRenderer: ToolRenderer = {
|
|||||||
if (items().length === 0) return null
|
if (items().length === 0) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="message-text tool-call-markdown tool-call-task-container">
|
<div
|
||||||
|
class="message-text tool-call-markdown tool-call-task-container"
|
||||||
|
ref={(element) => scrollHelpers?.registerContainer(element)}
|
||||||
|
onScroll={scrollHelpers ? (event) => scrollHelpers.handleScroll(event as Event & { currentTarget: HTMLDivElement }) : undefined}
|
||||||
|
>
|
||||||
<div class="tool-call-task-summary">
|
<div class="tool-call-task-summary">
|
||||||
<For each={items()}>
|
<For each={items()}>
|
||||||
{(item) => {
|
{(item) => {
|
||||||
@@ -78,7 +82,9 @@ export const taskRenderer: ToolRenderer = {
|
|||||||
}}
|
}}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
|
{scrollHelpers?.renderSentinel?.()}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ export interface DiffRenderOptions {
|
|||||||
label?: string
|
label?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ToolScrollHelpers {
|
||||||
|
registerContainer(element: HTMLDivElement | null, options?: { disableTracking?: boolean }): void
|
||||||
|
handleScroll(event: Event & { currentTarget: HTMLDivElement }): void
|
||||||
|
renderSentinel(options?: { disableTracking?: boolean }): JSXElement | null
|
||||||
|
}
|
||||||
|
|
||||||
export interface ToolRendererContext {
|
export interface ToolRendererContext {
|
||||||
toolCall: Accessor<ToolCallPart>
|
toolCall: Accessor<ToolCallPart>
|
||||||
toolState: Accessor<ToolState | undefined>
|
toolState: Accessor<ToolState | undefined>
|
||||||
@@ -29,6 +35,7 @@ export interface ToolRendererContext {
|
|||||||
partVersion?: Accessor<number | undefined>
|
partVersion?: Accessor<number | undefined>
|
||||||
renderMarkdown(options: MarkdownRenderOptions): JSXElement | null
|
renderMarkdown(options: MarkdownRenderOptions): JSXElement | null
|
||||||
renderDiff(payload: DiffPayload, options?: DiffRenderOptions): JSXElement | null
|
renderDiff(payload: DiffPayload, options?: DiffRenderOptions): JSXElement | null
|
||||||
|
scrollHelpers?: ToolScrollHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolRenderer {
|
export interface ToolRenderer {
|
||||||
|
|||||||
Reference in New Issue
Block a user