summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-20 09:13:13 -0600
committerAdam <[email protected]>2026-02-20 09:13:17 -0600
commit9c5bbba6eab4466621028e3cf3467119051423fb (patch)
tree31ecb3ff0886ecae74fe03769e8f68ae996dc8d2 /packages/ui/src
parentce17f9dd9451feae8b80f7d8054be5b72ce8d1fa (diff)
downloadopencode-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.tsx237
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>
)
},
})