fix(tui): prevent duplicate renderable IDs (#32110)
This commit is contained in:
@@ -202,7 +202,6 @@ function match<T extends PanelEntry>(query: string, entries: T[]) {
|
||||
}
|
||||
|
||||
function PanelShell(props: {
|
||||
id: string
|
||||
title: string
|
||||
countVisible?: boolean
|
||||
query: string
|
||||
@@ -279,7 +278,7 @@ function PanelShell(props: {
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<box id={props.id} width="100%" flexDirection="column" border={false} backgroundColor="transparent" flexShrink={0}>
|
||||
<box width="100%" flexDirection="column" border={false} backgroundColor="transparent" flexShrink={0}>
|
||||
{minimal() ? (
|
||||
<box width="100%" flexDirection="column" border={false} backgroundColor="transparent" flexShrink={0}>
|
||||
{content}
|
||||
@@ -299,7 +298,6 @@ function PanelShell(props: {
|
||||
)}
|
||||
{minimal() ? (
|
||||
<box
|
||||
id={`${props.id}-bottom`}
|
||||
width="100%"
|
||||
height={1}
|
||||
border={false}
|
||||
@@ -317,7 +315,6 @@ function PanelShell(props: {
|
||||
</box>
|
||||
) : (
|
||||
<box
|
||||
id={`${props.id}-bottom`}
|
||||
width="100%"
|
||||
height={1}
|
||||
border={["left"]}
|
||||
@@ -549,7 +546,6 @@ export function RunCommandMenuBody(props: {
|
||||
|
||||
return (
|
||||
<PanelShell
|
||||
id="run-direct-footer-command-panel"
|
||||
title="Commands"
|
||||
countVisible={false}
|
||||
query={query()}
|
||||
@@ -565,7 +561,6 @@ export function RunCommandMenuBody(props: {
|
||||
chrome="minimal"
|
||||
>
|
||||
<RunFooterMenu
|
||||
id="run-direct-footer-command-list"
|
||||
theme={props.theme}
|
||||
items={items}
|
||||
selected={menu.selected}
|
||||
@@ -649,7 +644,6 @@ export function RunSubagentSelectBody(props: {
|
||||
|
||||
return (
|
||||
<PanelShell
|
||||
id="run-direct-footer-subagent-panel"
|
||||
title="Select subagent"
|
||||
query={query()}
|
||||
count={items().length}
|
||||
@@ -664,7 +658,6 @@ export function RunSubagentSelectBody(props: {
|
||||
chrome="minimal"
|
||||
>
|
||||
<RunFooterMenu
|
||||
id="run-direct-footer-subagent-list"
|
||||
theme={props.theme}
|
||||
items={items}
|
||||
selected={menu.selected}
|
||||
@@ -748,7 +741,6 @@ export function RunQueuedPromptSelectBody(props: {
|
||||
|
||||
return (
|
||||
<PanelShell
|
||||
id="run-direct-footer-queued-panel"
|
||||
title="Queued prompts"
|
||||
query={query()}
|
||||
count={items().length}
|
||||
@@ -763,7 +755,6 @@ export function RunQueuedPromptSelectBody(props: {
|
||||
chrome="minimal"
|
||||
>
|
||||
<RunFooterMenu
|
||||
id="run-direct-footer-queued-list"
|
||||
theme={props.theme}
|
||||
items={items}
|
||||
selected={menu.selected}
|
||||
@@ -827,7 +818,6 @@ export function RunSkillSelectBody(props: {
|
||||
|
||||
return (
|
||||
<PanelShell
|
||||
id="run-direct-footer-skill-panel"
|
||||
title="Skills"
|
||||
query={query()}
|
||||
count={items().length}
|
||||
@@ -842,7 +832,6 @@ export function RunSkillSelectBody(props: {
|
||||
chrome="minimal"
|
||||
>
|
||||
<RunFooterMenu
|
||||
id="run-direct-footer-skill-list"
|
||||
theme={props.theme}
|
||||
items={items}
|
||||
selected={menu.selected}
|
||||
@@ -927,7 +916,6 @@ export function RunVariantSelectBody(props: {
|
||||
|
||||
return (
|
||||
<PanelShell
|
||||
id="run-direct-footer-variant-panel"
|
||||
title="Select variant"
|
||||
query={query()}
|
||||
count={items().length}
|
||||
@@ -942,7 +930,6 @@ export function RunVariantSelectBody(props: {
|
||||
chrome="minimal"
|
||||
>
|
||||
<RunFooterMenu
|
||||
id="run-direct-footer-variant-list"
|
||||
theme={props.theme}
|
||||
items={items}
|
||||
selected={menu.selected}
|
||||
@@ -1050,7 +1037,6 @@ export function RunModelSelectBody(props: {
|
||||
|
||||
return (
|
||||
<PanelShell
|
||||
id="run-direct-footer-model-panel"
|
||||
title="Select model"
|
||||
query={query()}
|
||||
count={items().length}
|
||||
@@ -1065,7 +1051,6 @@ export function RunModelSelectBody(props: {
|
||||
chrome="minimal"
|
||||
>
|
||||
<RunFooterMenu
|
||||
id="run-direct-footer-model-list"
|
||||
theme={props.theme}
|
||||
items={items}
|
||||
selected={menu.selected}
|
||||
|
||||
@@ -115,7 +115,6 @@ export function createFooterMenuState(input: { count: Accessor<number>; limit?:
|
||||
}
|
||||
|
||||
export function RunFooterMenu(props: {
|
||||
id?: string
|
||||
theme: Accessor<RunFooterTheme>
|
||||
items: Accessor<RunFooterMenuItem[]>
|
||||
selected: Accessor<number>
|
||||
@@ -226,7 +225,6 @@ export function RunFooterMenu(props: {
|
||||
}
|
||||
return (
|
||||
<box
|
||||
id={props.id ?? "run-direct-footer-menu"}
|
||||
width="100%"
|
||||
height={props.rows()}
|
||||
backgroundColor={props.background ? props.theme().shade : transparent}
|
||||
|
||||
@@ -96,7 +96,6 @@ export function RejectField(props: {
|
||||
|
||||
return (
|
||||
<textarea
|
||||
id="run-direct-footer-permission-reject"
|
||||
width="100%"
|
||||
minHeight={1}
|
||||
maxHeight={3}
|
||||
@@ -259,14 +258,12 @@ export function RunPermissionBody(props: {
|
||||
|
||||
return (
|
||||
<box
|
||||
id="run-direct-footer-permission-body"
|
||||
width="100%"
|
||||
height="100%"
|
||||
flexDirection="column"
|
||||
backgroundColor={props.theme.surface}
|
||||
>
|
||||
<box
|
||||
id="run-direct-footer-permission-head"
|
||||
flexDirection="column"
|
||||
gap={1}
|
||||
paddingLeft={1}
|
||||
@@ -303,7 +300,6 @@ export function RunPermissionBody(props: {
|
||||
fallback={
|
||||
<box width="100%" flexGrow={1} flexShrink={1} justifyContent="flex-end">
|
||||
<box
|
||||
id="run-direct-footer-permission-reject-bar"
|
||||
flexDirection={narrow() ? "column" : "row"}
|
||||
flexShrink={0}
|
||||
backgroundColor={props.theme.line}
|
||||
@@ -433,7 +429,6 @@ export function RunPermissionBody(props: {
|
||||
</box>
|
||||
|
||||
<box
|
||||
id="run-direct-footer-permission-actions"
|
||||
flexDirection={narrow() ? "column" : "row"}
|
||||
flexShrink={0}
|
||||
backgroundColor={props.theme.pane}
|
||||
|
||||
@@ -250,10 +250,9 @@ export function RunPromptBody(props: {
|
||||
})
|
||||
|
||||
return (
|
||||
<box id="run-direct-footer-prompt" width="100%">
|
||||
<box id="run-direct-footer-input-shell" paddingTop={1} paddingBottom={1} paddingRight={2}>
|
||||
<box width="100%">
|
||||
<box paddingTop={1} paddingBottom={1} paddingRight={2}>
|
||||
<textarea
|
||||
id="run-direct-footer-composer"
|
||||
width="100%"
|
||||
minHeight={TEXTAREA_MIN_ROWS}
|
||||
maxHeight={TEXTAREA_MAX_ROWS}
|
||||
|
||||
@@ -268,9 +268,8 @@ export function RunQuestionBody(props: {
|
||||
})
|
||||
|
||||
return (
|
||||
<box id="run-direct-footer-question-body" width="100%" height="100%" flexDirection="column">
|
||||
<box width="100%" height="100%" flexDirection="column">
|
||||
<box
|
||||
id="run-direct-footer-question-panel"
|
||||
flexDirection="column"
|
||||
gap={1}
|
||||
paddingLeft={1}
|
||||
@@ -281,14 +280,13 @@ export function RunQuestionBody(props: {
|
||||
backgroundColor={props.theme.surface}
|
||||
>
|
||||
<Show when={!single()}>
|
||||
<box id="run-direct-footer-question-tabs" flexDirection="row" gap={1} paddingLeft={1} flexShrink={0}>
|
||||
<box flexDirection="row" gap={1} paddingLeft={1} flexShrink={0}>
|
||||
<For each={props.request.questions}>
|
||||
{(item, index) => {
|
||||
const active = () => state().tab === index()
|
||||
const answered = () => (state().answers[index()]?.length ?? 0) > 0
|
||||
return (
|
||||
<box
|
||||
id={`run-direct-footer-question-tab-${index()}`}
|
||||
paddingLeft={1}
|
||||
paddingRight={1}
|
||||
backgroundColor={active() ? props.theme.highlight : props.theme.surface}
|
||||
@@ -304,7 +302,6 @@ export function RunQuestionBody(props: {
|
||||
}}
|
||||
</For>
|
||||
<box
|
||||
id="run-direct-footer-question-tab-confirm"
|
||||
paddingLeft={1}
|
||||
paddingRight={1}
|
||||
backgroundColor={confirm() ? props.theme.highlight : props.theme.surface}
|
||||
@@ -382,7 +379,6 @@ export function RunQuestionBody(props: {
|
||||
const hit = () => state().answers[state().tab]?.includes(item.label) ?? false
|
||||
return (
|
||||
<box
|
||||
id={`run-direct-footer-question-option-${index()}`}
|
||||
flexDirection="column"
|
||||
gap={0}
|
||||
onMouseOver={() => {
|
||||
@@ -428,7 +424,6 @@ export function RunQuestionBody(props: {
|
||||
|
||||
<Show when={questionCustom(props.request, state())}>
|
||||
<box
|
||||
id="run-direct-footer-question-option-custom"
|
||||
flexDirection="column"
|
||||
gap={0}
|
||||
onMouseOver={() => {
|
||||
@@ -480,7 +475,6 @@ export function RunQuestionBody(props: {
|
||||
>
|
||||
<box paddingLeft={3}>
|
||||
<textarea
|
||||
id="run-direct-footer-question-custom"
|
||||
width="100%"
|
||||
minHeight={1}
|
||||
maxHeight={4}
|
||||
@@ -518,7 +512,6 @@ export function RunQuestionBody(props: {
|
||||
</box>
|
||||
|
||||
<box
|
||||
id="run-direct-footer-question-actions"
|
||||
flexDirection={narrow() ? "column" : "row"}
|
||||
flexShrink={0}
|
||||
gap={1}
|
||||
|
||||
@@ -120,7 +120,6 @@ export function RunFooterSubagentBody(props: {
|
||||
|
||||
return (
|
||||
<box
|
||||
id="run-direct-footer-subagent"
|
||||
width="100%"
|
||||
height="100%"
|
||||
flexDirection="column"
|
||||
|
||||
@@ -621,7 +621,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
|
||||
return (
|
||||
<box
|
||||
id="run-direct-footer-shell"
|
||||
width="100%"
|
||||
height="100%"
|
||||
border={false}
|
||||
@@ -631,7 +630,7 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
padding={0}
|
||||
>
|
||||
<Show when={panel() || inspecting()}>
|
||||
<box id="run-direct-footer-panel-spacer" width="100%" height={1} flexShrink={0} backgroundColor="transparent" />
|
||||
<box width="100%" height={1} flexShrink={0} backgroundColor="transparent" />
|
||||
</Show>
|
||||
|
||||
<Show
|
||||
@@ -641,7 +640,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
<For each={[promptView()]}>
|
||||
{() => (
|
||||
<box
|
||||
id="run-direct-footer-composer-frame"
|
||||
width="100%"
|
||||
flexShrink={0}
|
||||
border={panel() || prompt() ? false : ["left"]}
|
||||
@@ -656,7 +654,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
}
|
||||
>
|
||||
<box
|
||||
id="run-direct-footer-composer-area"
|
||||
width="100%"
|
||||
flexGrow={1}
|
||||
paddingLeft={0}
|
||||
@@ -666,7 +663,7 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
backgroundColor={panel() || prompt() ? "transparent" : theme().surface}
|
||||
gap={0}
|
||||
>
|
||||
<box id="run-direct-footer-body" width="100%" flexGrow={1} flexShrink={1} flexDirection="column">
|
||||
<box width="100%" flexGrow={1} flexShrink={1} flexDirection="column">
|
||||
<Switch>
|
||||
<Match when={active().type === "prompt" && route().type === "composer"}>
|
||||
<RunPromptBody
|
||||
@@ -804,7 +801,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
|
||||
<Show when={!panel() && menu()}>
|
||||
<RunFooterMenu
|
||||
id="run-direct-footer-complete"
|
||||
theme={theme}
|
||||
items={composer.options}
|
||||
selected={composer.selected}
|
||||
@@ -818,7 +814,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
|
||||
<Show when={!panel() && !menu()}>
|
||||
<box
|
||||
id="run-direct-footer-statusline"
|
||||
width="100%"
|
||||
height={1}
|
||||
flexDirection="row"
|
||||
@@ -827,7 +822,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
backgroundColor={statuslineBackground()}
|
||||
>
|
||||
<box
|
||||
id="run-direct-footer-statusline-mode"
|
||||
paddingLeft={1}
|
||||
paddingRight={1}
|
||||
backgroundColor={theme().statusAccent}
|
||||
@@ -839,7 +833,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
</box>
|
||||
|
||||
<box
|
||||
id="run-direct-footer-statusline-main"
|
||||
flexDirection="row"
|
||||
gap={1}
|
||||
flexGrow={1}
|
||||
@@ -850,13 +843,12 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
backgroundColor="transparent"
|
||||
>
|
||||
<Show when={busy() && !exiting()}>
|
||||
<box id="run-direct-footer-status-spinner" flexShrink={0}>
|
||||
<box flexShrink={0}>
|
||||
<spinner color={spin().color} frames={spin().frames} interval={40} />
|
||||
</box>
|
||||
</Show>
|
||||
|
||||
<text
|
||||
id="run-direct-footer-statusline-text"
|
||||
fg={statusColor()}
|
||||
wrapMode="none"
|
||||
truncate
|
||||
@@ -874,7 +866,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
|
||||
<Show when={activityMeta().length > 0}>
|
||||
<box
|
||||
id="run-direct-footer-statusline-meta"
|
||||
paddingRight={1}
|
||||
backgroundColor="transparent"
|
||||
flexShrink={1}
|
||||
@@ -888,7 +879,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
<Show when={responsive().statusline.showModel && modelStatus()}>
|
||||
{(info) => (
|
||||
<box
|
||||
id="run-direct-footer-statusline-model"
|
||||
paddingRight={1}
|
||||
backgroundColor="transparent"
|
||||
flexShrink={0}
|
||||
@@ -913,7 +903,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
<For each={contextHints()}>
|
||||
{(hint, index) => (
|
||||
<box
|
||||
id={`run-direct-footer-statusline-${hint.kind}`}
|
||||
paddingRight={1}
|
||||
backgroundColor="transparent"
|
||||
flexShrink={0}
|
||||
@@ -933,7 +922,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
<Show when={commandHint()}>
|
||||
{(hint) => (
|
||||
<box
|
||||
id="run-direct-footer-statusline-hint"
|
||||
paddingRight={1}
|
||||
backgroundColor="transparent"
|
||||
flexShrink={0}
|
||||
@@ -955,7 +943,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
}
|
||||
>
|
||||
<box
|
||||
id="run-direct-footer-subagent-frame"
|
||||
width="100%"
|
||||
flexGrow={1}
|
||||
flexShrink={1}
|
||||
|
||||
@@ -34,8 +34,6 @@ type ActiveEntry = {
|
||||
rendered: boolean
|
||||
}
|
||||
|
||||
let nextId = 0
|
||||
|
||||
function commitMarkdownBlocks(input: {
|
||||
surface: ScrollbackSurface
|
||||
renderable: MarkdownRenderable
|
||||
@@ -152,12 +150,10 @@ export class RunScrollbackStream {
|
||||
const surface = this.renderer.createScrollbackSurface({
|
||||
startOnNewLine: entryFlags(commit).startOnNewLine,
|
||||
})
|
||||
const id = `run-scrollback-entry-${nextId++}`
|
||||
const style = entryLook(commit, this.theme.entry)
|
||||
const renderable =
|
||||
body.type === "text"
|
||||
? new TextRenderable(surface.renderContext, {
|
||||
id,
|
||||
content: "",
|
||||
width: "100%",
|
||||
wrapMode: "word",
|
||||
@@ -166,7 +162,6 @@ export class RunScrollbackStream {
|
||||
})
|
||||
: body.type === "code"
|
||||
? new CodeRenderable(surface.renderContext, {
|
||||
id,
|
||||
content: "",
|
||||
filetype: body.filetype,
|
||||
syntaxStyle: entrySyntax(commit, this.theme),
|
||||
@@ -178,7 +173,6 @@ export class RunScrollbackStream {
|
||||
treeSitterClient: this.treeSitterClient,
|
||||
})
|
||||
: new MarkdownRenderable(surface.renderContext, {
|
||||
id,
|
||||
content: "",
|
||||
syntaxStyle: entrySyntax(commit, this.theme),
|
||||
width: "100%",
|
||||
|
||||
@@ -322,7 +322,6 @@ export function entryWriter(input: {
|
||||
export function spacerWriter(): ScrollbackWriter {
|
||||
return (ctx: ScrollbackRenderContext) => ({
|
||||
root: new TextRenderable(ctx.renderContext, {
|
||||
id: "run-scrollback-spacer",
|
||||
width: Math.max(1, Math.trunc(ctx.width)),
|
||||
height: 1,
|
||||
content: "",
|
||||
|
||||
@@ -45,8 +45,6 @@ type Cell = {
|
||||
mark: "text" | "full" | "mix" | "top"
|
||||
}
|
||||
|
||||
let id = 0
|
||||
|
||||
function cells(line: string): Cell[] {
|
||||
const list: Cell[] = []
|
||||
for (const char of line) {
|
||||
@@ -117,7 +115,6 @@ function write(
|
||||
|
||||
root.add(
|
||||
new TextRenderable(ctx.renderContext, {
|
||||
id: `run-direct-splash-line-${id++}`,
|
||||
position: "absolute",
|
||||
left: line.left,
|
||||
top: line.top,
|
||||
@@ -246,7 +243,6 @@ function build(input: SplashWriterInput, kind: "entry" | "exit", ctx: Scrollback
|
||||
}
|
||||
|
||||
const root = new BoxRenderable(ctx.renderContext, {
|
||||
id: `run-direct-splash-${kind}-${id++}`,
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
|
||||
Reference in New Issue
Block a user