diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 55cfaa490f..4ab6aab64c 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -52,7 +52,6 @@ import { import { createPromptSubmit, type FollowupDraft } from "./prompt-input/submit" import { PromptPopover, type AtOption, type SlashCommand } from "./prompt-input/slash-popover" import { PromptContextItems } from "./prompt-input/context-items" -import { PromptImageAttachments } from "./prompt-input/image-attachments" import { PromptDragOverlay } from "./prompt-input/drag-overlay" import { promptPlaceholder } from "./prompt-input/placeholder" import { ImagePreview } from "@opencode-ai/ui/image-preview" @@ -1291,6 +1290,7 @@ export const PromptInput: Component = (props) => { /> { const active = comments.active() return !!item.commentID && item.commentID === active?.id && item.path === active?.file @@ -1300,15 +1300,12 @@ export const PromptInput: Component = (props) => { if (item.commentID) comments.remove(item.path, item.commentID) prompt.context.remove(item.key) }} - t={(key) => language.t(key as Parameters[0])} - /> - + openImage={(attachment) => dialog.show(() => ) } - onRemove={removeAttachment} - removeLabel={language.t("prompt.attachment.remove")} + removeImage={removeAttachment} + imageRemoveLabel={language.t("prompt.attachment.remove")} + t={(key) => language.t(key as Parameters[0])} />
boolean openComment: (item: PromptContextItem) => void remove: (item: PromptContextItem) => void + openImage: (attachment: ImageAttachmentPart) => void + removeImage: (id: string) => void + imageRemoveLabel: string t: (key: string) => string } export const PromptContextItems: Component = (props) => { + const seen = new Map() + let seq = 0 + + const rows = createMemo(() => { + const all = [ + ...props.items.map((item) => ({ type: "ctx" as const, key: `ctx:${item.key}`, item })), + ...props.images.map((attachment) => ({ type: "img" as const, key: `img:${attachment.id}`, attachment })), + ] + + for (const row of all) { + if (seen.has(row.key)) continue + seen.set(row.key, seq) + seq += 1 + } + + return all.slice().sort((a, b) => (seen.get(a.key) ?? 0) - (seen.get(b.key) ?? 0)) + }) + return ( - 0}> + 0}>
- - {(item) => { - const directory = getDirectory(item.path) - const filename = getFilename(item.path) - const label = getFilenameTruncated(item.path, 14) - const selected = props.active(item) + + {(row) => { + if (row.type === "img") { + return ( + + ) + } + + const directory = getDirectory(row.item.path) + const filename = getFilename(row.item.path) + const label = getFilenameTruncated(row.item.path, 14) + const selected = props.active(row.item) return ( = (props) => { } placement="top" openDelay={2000} + class="shrink-0" >
props.openComment(item)} + onClick={() => props.openComment(row.item)} >
- +
{label} - + {(sel) => ( {sel().startLine === sel().endLine @@ -69,12 +104,12 @@ export const PromptContextItems: Component = (props) => { class="ml-auto size-3.5 text-text-weak hover:text-text-strong transition-all" onClick={(e) => { e.stopPropagation() - props.remove(item) + props.remove(row.item) }} aria-label={props.t("prompt.context.removeFile")} />
- + {(comment) =>
{comment()}
}
diff --git a/packages/app/src/components/prompt-input/image-attachments.tsx b/packages/app/src/components/prompt-input/image-attachments.tsx index 366e36bd42..fd25a7fabd 100644 --- a/packages/app/src/components/prompt-input/image-attachments.tsx +++ b/packages/app/src/components/prompt-input/image-attachments.tsx @@ -1,4 +1,4 @@ -import { Component, For, Show } from "solid-js" +import { Component, Show } from "solid-js" import { Icon } from "@opencode-ai/ui/icon" import { Tooltip } from "@opencode-ai/ui/tooltip" import type { ImageAttachmentPart } from "@/context/prompt" @@ -10,6 +10,13 @@ type PromptImageAttachmentsProps = { removeLabel: string } +type PromptImageAttachmentProps = { + attachment: ImageAttachmentPart + onOpen: (attachment: ImageAttachmentPart) => void + onRemove: (id: string) => void + removeLabel: string +} + const fallbackClass = "size-12 rounded-[6px] bg-background-stronger flex items-center justify-center shadow-xs-border cursor-default" const imageClass = "size-12 rounded-[6px] object-cover shadow-xs-border" @@ -19,39 +26,48 @@ const removeClass = export const PromptImageAttachments: Component = (props) => { return ( 0}> -
- - {(attachment) => ( - -
- - -
- } - > - {attachment.filename} props.onOpen(attachment)} - /> - - -
- - )} - -
+ <> + {props.attachments.map((attachment) => ( + + ))} + ) } + +export const PromptImageAttachment: Component = (props) => { + return ( + +
+ + +
+ } + > + {props.attachment.filename} props.onOpen(props.attachment)} + /> + + +
+ + ) +}