Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions packages/app/src/components/prompt-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ 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"
import { isCodeFile } from "@opencode-ai/util/path"

interface PromptInputProps {
class?: string
Expand Down Expand Up @@ -148,7 +149,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
}

const openComment = (item: { path: string; commentID?: string; commentOrigin?: "review" | "file" }) => {
if (!item.commentID) return
if (!item.commentID) {
if (!isCodeFile(item.path)) return
const tab = files.tab(item.path)
tabs().open(tab)
files.load(item.path)
return
}

const focus = { file: item.path, id: item.commentID }
comments.setActive(focus)
Expand Down Expand Up @@ -425,10 +432,30 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
if (part.type === "agent") pill.setAttribute("data-name", part.name)
pill.setAttribute("contenteditable", "false")
pill.style.userSelect = "text"
pill.style.cursor = "default"

if (part.type === "file" && isCodeFile(part.path)) {
pill.classList.add("prompt-pill--code")
}

return pill
}

const handleEditorClick = (e: MouseEvent) => {
const target = e.target as HTMLElement
const pill = target.closest('[data-type="file"]') as HTMLElement | null
if (!pill) return

const path = pill.dataset.path
if (!path || !isCodeFile(path)) return

if (!view().reviewPanel.opened()) view().reviewPanel.open()
layout.fileTree.open()
layout.fileTree.setTab("all")
const tab = files.tab(path)
tabs().open(tab)
files.load(path)
}

const isNormalizedEditor = () =>
Array.from(editorRef.childNodes).every((node) => {
if (node.nodeType === Node.TEXT_NODE) {
Expand Down Expand Up @@ -988,12 +1015,18 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
onCompositionStart={() => setComposing(true)}
onCompositionEnd={() => setComposing(false)}
onKeyDown={handleKeyDown}
onClick={handleEditorClick}
classList={{
"select-text": true,
"w-full p-3 pr-12 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
"[&_[data-type=file]]:text-syntax-property": true,
"[&_[data-type=agent]]:text-syntax-type": true,
"font-mono!": store.mode === "shell",
"[&_.prompt-pill--code]:cursor-pointer": true,
"[&_.prompt-pill--code]:rounded": true,
"[&_.prompt-pill--code]:px-1": true,
"[&_.prompt-pill--code]:hover:text-syntax-property/70": true,
"[&_.prompt-pill--code]:transition-colors": true,
}}
/>
<Show when={!prompt.dirty()}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const PromptContextItems: Component<ContextItemsProps> = (props) => {
<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 hover:bg-surface-interactive-weak": item.type === "file" && !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),
Expand Down
34 changes: 34 additions & 0 deletions packages/util/src/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,37 @@ export function truncateMiddle(text: string, maxLength: number = 20) {
const end = Math.floor(available / 2)
return text.slice(0, start) + "…" + text.slice(-end)
}

const MEDIA_EXTENSIONS = new Set([
"png",
"jpg",
"jpeg",
"gif",
"webp",
"svg",
"bmp",
"ico",
"mp4",
"mov",
"avi",
"webm",
"mp3",
"wav",
"ogg",
"pdf",
"zip",
"tar",
"gz",
"rar",
"7z",
"exe",
"dll",
"so",
"dylib",
])

export const isCodeFile = (path: string) => {
const ext = path.split(".").pop()?.toLowerCase()
if (!ext) return false
return !MEDIA_EXTENSIONS.has(ext)
}
Loading