diff options
| author | Adam <[email protected]> | 2026-02-12 09:49:14 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-12 09:49:14 -0600 |
| commit | ff4414bb152acfddb5c0eb073c38bedc1df4ae14 (patch) | |
| tree | 78381c67d21ef6f089647f6b19e7aa2976840dbc /packages/app/src/components/prompt-input | |
| parent | 56ad2db02055955f926fda0e4a89055b22ead6f9 (diff) | |
| download | opencode-ff4414bb152acfddb5c0eb073c38bedc1df4ae14.tar.gz opencode-ff4414bb152acfddb5c0eb073c38bedc1df4ae14.zip | |
chore: refactor packages/app files (#13236)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Frank <[email protected]>
Diffstat (limited to 'packages/app/src/components/prompt-input')
4 files changed, 114 insertions, 96 deletions
diff --git a/packages/app/src/components/prompt-input/context-items.tsx b/packages/app/src/components/prompt-input/context-items.tsx index a843e109d..b575c3961 100644 --- a/packages/app/src/components/prompt-input/context-items.tsx +++ b/packages/app/src/components/prompt-input/context-items.tsx @@ -20,61 +20,68 @@ export const PromptContextItems: Component<ContextItemsProps> = (props) => { <Show when={props.items.length > 0}> <div class="flex flex-nowrap items-start gap-2 p-2 overflow-x-auto no-scrollbar"> <For each={props.items}> - {(item) => ( - <Tooltip - value={ - <span class="flex max-w-[300px]"> - <span class="text-text-invert-base truncate-start [unicode-bidi:plaintext] min-w-0"> - {getDirectory(item.path)} + {(item) => { + const directory = getDirectory(item.path) + const filename = getFilename(item.path) + const label = getFilenameTruncated(item.path, 14) + const selected = props.active(item) + + return ( + <Tooltip + value={ + <span class="flex max-w-[300px]"> + <span class="text-text-invert-base truncate-start [unicode-bidi:plaintext] min-w-0"> + {directory} + </span> + <span class="shrink-0">{filename}</span> </span> - <span class="shrink-0">{getFilename(item.path)}</span> - </span> - } - placement="top" - openDelay={2000} - > - <div - classList={{ - "group shrink-0 flex flex-col rounded-[6px] pl-2 pr-1 py-1 max-w-[200px] h-12 transition-all transition-transform shadow-xs-border hover:shadow-xs-border-hover": true, - "cursor-pointer hover:bg-surface-interactive-weak": !!item.commentID && !props.active(item), - "cursor-pointer bg-surface-interactive-hover hover:bg-surface-interactive-hover shadow-xs-border-hover": - props.active(item), - "bg-background-stronger": !props.active(item), - }} - onClick={() => props.openComment(item)} + } + placement="top" + openDelay={2000} > - <div class="flex items-center gap-1.5"> - <FileIcon node={{ path: item.path, type: "file" }} class="shrink-0 size-3.5" /> - <div class="flex items-center text-11-regular min-w-0 font-medium"> - <span class="text-text-strong whitespace-nowrap">{getFilenameTruncated(item.path, 14)}</span> - <Show when={item.selection}> - {(sel) => ( - <span class="text-text-weak whitespace-nowrap shrink-0"> - {sel().startLine === sel().endLine - ? `:${sel().startLine}` - : `:${sel().startLine}-${sel().endLine}`} - </span> - )} - </Show> + <div + classList={{ + "group shrink-0 flex flex-col rounded-[6px] pl-2 pr-1 py-1 max-w-[200px] h-12 transition-all transition-transform shadow-xs-border hover:shadow-xs-border-hover": true, + "cursor-pointer hover:bg-surface-interactive-weak": !!item.commentID && !selected, + "cursor-pointer bg-surface-interactive-hover hover:bg-surface-interactive-hover shadow-xs-border-hover": + selected, + "bg-background-stronger": !selected, + }} + onClick={() => props.openComment(item)} + > + <div class="flex items-center gap-1.5"> + <FileIcon node={{ path: item.path, type: "file" }} class="shrink-0 size-3.5" /> + <div class="flex items-center text-11-regular min-w-0 font-medium"> + <span class="text-text-strong whitespace-nowrap">{label}</span> + <Show when={item.selection}> + {(sel) => ( + <span class="text-text-weak whitespace-nowrap shrink-0"> + {sel().startLine === sel().endLine + ? `:${sel().startLine}` + : `:${sel().startLine}-${sel().endLine}`} + </span> + )} + </Show> + </div> + <IconButton + type="button" + icon="close-small" + variant="ghost" + class="ml-auto size-3.5 text-text-weak hover:text-text-strong transition-all" + onClick={(e) => { + e.stopPropagation() + props.remove(item) + }} + aria-label={props.t("prompt.context.removeFile")} + /> </div> - <IconButton - type="button" - icon="close-small" - variant="ghost" - class="ml-auto size-3.5 text-text-weak hover:text-text-strong transition-all" - onClick={(e) => { - e.stopPropagation() - props.remove(item) - }} - aria-label={props.t("prompt.context.removeFile")} - /> + <Show when={item.comment}> + {(comment) => <div class="text-12-regular text-text-strong ml-5 pr-1 truncate">{comment()}</div>} + </Show> </div> - <Show when={item.comment}> - {(comment) => <div class="text-12-regular text-text-strong ml-5 pr-1 truncate">{comment()}</div>} - </Show> - </div> - </Tooltip> - )} + </Tooltip> + ) + }} </For> </div> </Show> diff --git a/packages/app/src/components/prompt-input/drag-overlay.tsx b/packages/app/src/components/prompt-input/drag-overlay.tsx index e05b47d7c..41962ce53 100644 --- a/packages/app/src/components/prompt-input/drag-overlay.tsx +++ b/packages/app/src/components/prompt-input/drag-overlay.tsx @@ -6,12 +6,17 @@ type PromptDragOverlayProps = { label: string } +const kindToIcon = { + image: "photo", + "@mention": "link", +} as const + export const PromptDragOverlay: Component<PromptDragOverlayProps> = (props) => { return ( <Show when={props.type !== null}> <div class="absolute inset-0 z-10 flex items-center justify-center bg-surface-raised-stronger-non-alpha/90 pointer-events-none"> <div class="flex flex-col items-center gap-2 text-text-weak"> - <Icon name={props.type === "@mention" ? "link" : "photo"} class="size-8" /> + <Icon name={props.type ? kindToIcon[props.type] : kindToIcon.image} class="size-8" /> <span class="text-14-regular">{props.label}</span> </div> </div> diff --git a/packages/app/src/components/prompt-input/image-attachments.tsx b/packages/app/src/components/prompt-input/image-attachments.tsx index ba3addf0a..835fddc30 100644 --- a/packages/app/src/components/prompt-input/image-attachments.tsx +++ b/packages/app/src/components/prompt-input/image-attachments.tsx @@ -9,6 +9,13 @@ type PromptImageAttachmentsProps = { removeLabel: string } +const fallbackClass = "size-16 rounded-md bg-surface-base flex items-center justify-center border border-border-base" +const imageClass = + "size-16 rounded-md object-cover border border-border-base hover:border-border-strong-base transition-colors" +const removeClass = + "absolute -top-1.5 -right-1.5 size-5 rounded-full bg-surface-raised-stronger-non-alpha border border-border-base flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity hover:bg-surface-raised-base-hover" +const nameClass = "absolute bottom-0 left-0 right-0 px-1 py-0.5 bg-black/50 rounded-b-md" + export const PromptImageAttachments: Component<PromptImageAttachmentsProps> = (props) => { return ( <Show when={props.attachments.length > 0}> @@ -19,7 +26,7 @@ export const PromptImageAttachments: Component<PromptImageAttachmentsProps> = (p <Show when={attachment.mime.startsWith("image/")} fallback={ - <div class="size-16 rounded-md bg-surface-base flex items-center justify-center border border-border-base"> + <div class={fallbackClass}> <Icon name="folder" class="size-6 text-text-weak" /> </div> } @@ -27,19 +34,19 @@ export const PromptImageAttachments: Component<PromptImageAttachmentsProps> = (p <img src={attachment.dataUrl} alt={attachment.filename} - class="size-16 rounded-md object-cover border border-border-base hover:border-border-strong-base transition-colors" + class={imageClass} onClick={() => props.onOpen(attachment)} /> </Show> <button type="button" onClick={() => props.onRemove(attachment.id)} - class="absolute -top-1.5 -right-1.5 size-5 rounded-full bg-surface-raised-stronger-non-alpha border border-border-base flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity hover:bg-surface-raised-base-hover" + class={removeClass} aria-label={props.removeLabel} > <Icon name="close" class="size-3 text-text-weak" /> </button> - <div class="absolute bottom-0 left-0 right-0 px-1 py-0.5 bg-black/50 rounded-b-md"> + <div class={nameClass}> <span class="text-10-regular text-white truncate block">{attachment.filename}</span> </div> </div> diff --git a/packages/app/src/components/prompt-input/slash-popover.tsx b/packages/app/src/components/prompt-input/slash-popover.tsx index b97bb6752..554a15bb7 100644 --- a/packages/app/src/components/prompt-input/slash-popover.tsx +++ b/packages/app/src/components/prompt-input/slash-popover.tsx @@ -52,47 +52,46 @@ export const PromptPopover: Component<PromptPopoverProps> = (props) => { fallback={<div class="text-text-weak px-2 py-1">{props.t("prompt.popover.emptyResults")}</div>} > <For each={props.atFlat.slice(0, 10)}> - {(item) => ( - <button - classList={{ - "w-full flex items-center gap-x-2 rounded-md px-2 py-0.5": true, - "bg-surface-raised-base-hover": props.atActive === props.atKey(item), - }} - onClick={() => props.onAtSelect(item)} - onMouseEnter={() => props.setAtActive(props.atKey(item))} - > - <Show - when={item.type === "agent"} - fallback={ - <> - <FileIcon - node={{ path: item.type === "file" ? item.path : "", type: "file" }} - class="shrink-0 size-4" - /> - <div class="flex items-center text-14-regular min-w-0"> - <span class="text-text-weak whitespace-nowrap truncate min-w-0"> - {item.type === "file" - ? item.path.endsWith("/") - ? item.path - : getDirectory(item.path) - : ""} - </span> - <Show when={item.type === "file" && !item.path.endsWith("/")}> - <span class="text-text-strong whitespace-nowrap"> - {item.type === "file" ? getFilename(item.path) : ""} - </span> - </Show> - </div> - </> - } + {(item) => { + const active = props.atActive === props.atKey(item) + const shared = { + "w-full flex items-center gap-x-2 rounded-md px-2 py-0.5": true, + "bg-surface-raised-base-hover": active, + } + + if (item.type === "agent") { + return ( + <button + classList={shared} + onClick={() => props.onAtSelect(item)} + onMouseEnter={() => props.setAtActive(props.atKey(item))} + > + <Icon name="brain" size="small" class="text-icon-info-active shrink-0" /> + <span class="text-14-regular text-text-strong whitespace-nowrap">@{item.name}</span> + </button> + ) + } + + const isDirectory = item.path.endsWith("/") + const directory = isDirectory ? item.path : getDirectory(item.path) + const filename = isDirectory ? "" : getFilename(item.path) + + return ( + <button + classList={shared} + onClick={() => props.onAtSelect(item)} + onMouseEnter={() => props.setAtActive(props.atKey(item))} > - <Icon name="brain" size="small" class="text-icon-info-active shrink-0" /> - <span class="text-14-regular text-text-strong whitespace-nowrap"> - @{item.type === "agent" ? item.name : ""} - </span> - </Show> - </button> - )} + <FileIcon node={{ path: item.path, type: "file" }} class="shrink-0 size-4" /> + <div class="flex items-center text-14-regular min-w-0"> + <span class="text-text-weak whitespace-nowrap truncate min-w-0">{directory}</span> + <Show when={!isDirectory}> + <span class="text-text-strong whitespace-nowrap">{filename}</span> + </Show> + </div> + </button> + ) + }} </For> </Show> </Match> |
