fix(tui): prevent duplicate renderable IDs (#32110)

This commit is contained in:
Sebastian 2026-06-13 00:51:32 +02:00 committed by GitHub
commit fff0ec294c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 98 additions and 95 deletions

View file

@ -784,7 +784,6 @@ function DiffViewer(props: { api: TuiPluginApi }) {
<Panel flexGrow={1} minHeight={0} border="none">
<Separator axis="x" start={showFileTree() ? "edge-out" : undefined} />
<scrollbox
id="diff-viewer-patches"
ref={(element: ScrollBoxRenderable) => (scroll = element)}
flexGrow={1}
minHeight={0}
@ -824,7 +823,6 @@ function DiffViewer(props: { api: TuiPluginApi }) {
{(patch) => (
<box border={patchLeftBorder()} borderColor={theme().border}>
<diff
id={`diff-viewer-patch-${entry.fileIndex}`}
ref={(element: DiffRenderable) => diffNodeByFileIndex.set(entry.fileIndex, element)}
diff={patch()}
view={view()}

View file

@ -1607,7 +1607,13 @@ function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: Ass
return (
<Show when={content()}>
<box id={"text-" + props.part.id} paddingLeft={3} marginTop={1} flexDirection="column" flexShrink={0}>
<box
id={`text-${props.part.messageID}-${props.part.id}`}
paddingLeft={3}
marginTop={1}
flexDirection="column"
flexShrink={0}
>
<box onMouseUp={toggle}>
<ReasoningHeader
toggleable={inMinimal()}
@ -1684,7 +1690,7 @@ function TextPart(props: { last: boolean; part: TextPart; message: AssistantMess
const { theme, syntax } = useTheme()
return (
<Show when={props.part.text.trim()}>
<box id={"text-" + props.part.id} paddingLeft={3} marginTop={1} flexShrink={0}>
<box id={`text-${props.part.messageID}-${props.part.id}`} paddingLeft={3} marginTop={1} flexShrink={0}>
<markdown
syntaxStyle={syntax()}
streaming={true}
@ -1875,7 +1881,7 @@ function InlineTool(props: {
return (
<InlineToolRow
id={`tool-inline-${props.subagent ? "subagent-" : ""}${props.part.id}`}
id={`tool-inline-${props.subagent ? "subagent-" : ""}${props.part.messageID}-${props.part.id}`}
icon={props.icon}
iconColor={props.iconColor}
color={fg()}
@ -2007,7 +2013,7 @@ function BlockTool(props: {
const error = createMemo(() => (props.part?.state.status === "error" ? props.part.state.error : undefined))
return (
<box
id={props.part ? "tool-block-" + props.part.id : undefined}
id={props.part ? `tool-block-${props.part.messageID}-${props.part.id}` : undefined}
border={["left"]}
paddingTop={1}
paddingBottom={1}
@ -2174,7 +2180,7 @@ function Read(props: ToolProps) {
</InlineTool>
<For each={loaded()}>
{(filepath, index) => (
<box id={`tool-inline-loaded-${props.part.id}-${index()}`} paddingLeft={3}>
<box id={`tool-inline-loaded-${props.part.messageID}-${props.part.id}-${index()}`} paddingLeft={3}>
<text paddingLeft={3} fg={theme.textMuted}>
Loaded {pathFormatter.format(filepath)}
</text>

View file

@ -238,9 +238,19 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
const option = selected()
if (option) props.onMove?.(option)
if (!scroll) return
const target = scroll.getChildren().find((child: { id?: string }) => {
return child.id === JSON.stringify(selected()?.value)
})
let remaining = store.selected
let index = 0
// Locate the row by position because a unique renderable ID cannot currently be ensured.
for (const [category, options] of grouped()) {
if (category) index++
if (remaining < options.length) {
index += remaining
break
}
index += options.length
remaining -= options.length
}
const target = scroll.getChildren()[index]
if (!target) return
const y = target.y - scroll.y
if (center) {
@ -553,7 +563,6 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
const current = createMemo(() => isDeepEqual(option.value, props.current))
return (
<box
id={JSON.stringify(option.value)}
flexDirection="column"
position="relative"
onMouseMove={() => {