diff options
| author | Adam <[email protected]> | 2026-02-20 09:13:13 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-02-20 09:13:17 -0600 |
| commit | 9c5bbba6eab4466621028e3cf3467119051423fb (patch) | |
| tree | 31ecb3ff0886ecae74fe03769e8f68ae996dc8d2 /packages/ui/src | |
| parent | ce17f9dd9451feae8b80f7d8054be5b72ce8d1fa (diff) | |
| download | opencode-9c5bbba6eab4466621028e3cf3467119051423fb.tar.gz opencode-9c5bbba6eab4466621028e3cf3467119051423fb.zip | |
fix(app): patch tool renders like edit tool
Diffstat (limited to 'packages/ui/src')
| -rw-r--r-- | packages/ui/src/components/message-part.tsx | 237 |
1 files changed, 145 insertions, 92 deletions
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index f3a60b09e..6421985e0 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -1603,6 +1603,12 @@ ToolRegistry.register({ const i18n = useI18n() const diffComponent = useDiffComponent() const files = createMemo(() => (props.metadata.files ?? []) as ApplyPatchFile[]) + const pending = createMemo(() => props.status === "pending" || props.status === "running") + const single = createMemo(() => { + const list = files() + if (list.length !== 1) return + return list[0] + }) const [expanded, setExpanded] = createSignal<string[]>([]) let seeded = false @@ -1621,100 +1627,147 @@ ToolRegistry.register({ }) return ( - <div data-component="apply-patch-tool"> - <BasicTool - {...props} - icon="code-lines" - defer - trigger={{ - title: i18n.t("ui.tool.patch"), - subtitle: subtitle(), - }} - > - <Show when={files().length > 0}> - <Accordion - multiple - data-scope="apply-patch" - style={{ "--sticky-accordion-offset": "40px" }} - value={expanded()} - onChange={(value) => setExpanded(Array.isArray(value) ? value : value ? [value] : [])} + <Show + when={single()} + fallback={ + <div data-component="apply-patch-tool"> + <BasicTool + {...props} + icon="code-lines" + defer + trigger={{ + title: i18n.t("ui.tool.patch"), + subtitle: subtitle(), + }} > - <For each={files()}> - {(file) => { - const active = createMemo(() => expanded().includes(file.filePath)) - const [visible, setVisible] = createSignal(false) - - createEffect(() => { - if (!active()) { - setVisible(false) - return - } - - requestAnimationFrame(() => { - if (!active()) return - setVisible(true) - }) - }) - - return ( - <Accordion.Item value={file.filePath} data-type={file.type}> - <StickyAccordionHeader> - <Accordion.Trigger> - <div data-slot="apply-patch-trigger-content"> - <div data-slot="apply-patch-file-info"> - <FileIcon node={{ path: file.relativePath, type: "file" }} /> - <div data-slot="apply-patch-file-name-container"> - <Show when={file.relativePath.includes("/")}> - <span data-slot="apply-patch-directory">{`\u202A${getDirectory(file.relativePath)}\u202C`}</span> - </Show> - <span data-slot="apply-patch-filename">{getFilename(file.relativePath)}</span> + <Show when={files().length > 0}> + <Accordion + multiple + data-scope="apply-patch" + style={{ "--sticky-accordion-offset": "40px" }} + value={expanded()} + onChange={(value) => setExpanded(Array.isArray(value) ? value : value ? [value] : [])} + > + <For each={files()}> + {(file) => { + const active = createMemo(() => expanded().includes(file.filePath)) + const [visible, setVisible] = createSignal(false) + + createEffect(() => { + if (!active()) { + setVisible(false) + return + } + + requestAnimationFrame(() => { + if (!active()) return + setVisible(true) + }) + }) + + return ( + <Accordion.Item value={file.filePath} data-type={file.type}> + <StickyAccordionHeader> + <Accordion.Trigger> + <div data-slot="apply-patch-trigger-content"> + <div data-slot="apply-patch-file-info"> + <FileIcon node={{ path: file.relativePath, type: "file" }} /> + <div data-slot="apply-patch-file-name-container"> + <Show when={file.relativePath.includes("/")}> + <span data-slot="apply-patch-directory">{`\u202A${getDirectory(file.relativePath)}\u202C`}</span> + </Show> + <span data-slot="apply-patch-filename">{getFilename(file.relativePath)}</span> + </div> + </div> + <div data-slot="apply-patch-trigger-actions"> + <Switch> + <Match when={file.type === "add"}> + <span data-slot="apply-patch-change" data-type="added"> + {i18n.t("ui.patch.action.created")} + </span> + </Match> + <Match when={file.type === "delete"}> + <span data-slot="apply-patch-change" data-type="removed"> + {i18n.t("ui.patch.action.deleted")} + </span> + </Match> + <Match when={file.type === "move"}> + <span data-slot="apply-patch-change" data-type="modified"> + {i18n.t("ui.patch.action.moved")} + </span> + </Match> + <Match when={true}> + <DiffChanges changes={{ additions: file.additions, deletions: file.deletions }} /> + </Match> + </Switch> + <Icon name="chevron-grabber-vertical" size="small" /> + </div> </div> - </div> - <div data-slot="apply-patch-trigger-actions"> - <Switch> - <Match when={file.type === "add"}> - <span data-slot="apply-patch-change" data-type="added"> - {i18n.t("ui.patch.action.created")} - </span> - </Match> - <Match when={file.type === "delete"}> - <span data-slot="apply-patch-change" data-type="removed"> - {i18n.t("ui.patch.action.deleted")} - </span> - </Match> - <Match when={file.type === "move"}> - <span data-slot="apply-patch-change" data-type="modified"> - {i18n.t("ui.patch.action.moved")} - </span> - </Match> - <Match when={true}> - <DiffChanges changes={{ additions: file.additions, deletions: file.deletions }} /> - </Match> - </Switch> - <Icon name="chevron-grabber-vertical" size="small" /> - </div> - </div> - </Accordion.Trigger> - </StickyAccordionHeader> - <Accordion.Content> - <Show when={visible()}> - <div data-component="apply-patch-file-diff"> - <Dynamic - component={diffComponent} - before={{ name: file.filePath, contents: file.before }} - after={{ name: file.movePath ?? file.filePath, contents: file.after }} - /> - </div> - </Show> - </Accordion.Content> - </Accordion.Item> - ) - }} - </For> - </Accordion> - </Show> - </BasicTool> - </div> + </Accordion.Trigger> + </StickyAccordionHeader> + <Accordion.Content> + <Show when={visible()}> + <div data-component="apply-patch-file-diff"> + <Dynamic + component={diffComponent} + before={{ name: file.filePath, contents: file.before }} + after={{ name: file.movePath ?? file.filePath, contents: file.after }} + /> + </div> + </Show> + </Accordion.Content> + </Accordion.Item> + ) + }} + </For> + </Accordion> + </Show> + </BasicTool> + </div> + } + > + {(file) => ( + <BasicTool + {...props} + icon="code-lines" + defer + trigger={ + <div data-component="edit-trigger"> + <div data-slot="message-part-title-area"> + <div data-slot="message-part-title"> + <span data-slot="message-part-title-text"> + <Show when={pending()} fallback={i18n.t("ui.tool.patch")}> + <TextShimmer text={i18n.t("ui.tool.patch")} /> + </Show> + </span> + <Show when={!pending()}> + <span data-slot="message-part-title-filename">{getFilename(file().relativePath)}</span> + </Show> + </div> + <Show when={!pending() && file().relativePath.includes("/")}> + <div data-slot="message-part-path"> + <span data-slot="message-part-directory">{getDirectory(file().relativePath)}</span> + </div> + </Show> + </div> + <div data-slot="message-part-actions"> + <Show when={!pending()}> + <DiffChanges changes={{ additions: file().additions, deletions: file().deletions }} /> + </Show> + </div> + </div> + } + > + <div data-component="edit-content"> + <Dynamic + component={diffComponent} + before={{ name: file().filePath, contents: file().before }} + after={{ name: file().movePath ?? file().filePath, contents: file().after }} + /> + </div> + </BasicTool> + )} + </Show> ) }, }) |
