Skip to content

Commit 3c96bf8

Browse files
feat(opencode): Add PDF attachment Drag and Drop (anomalyco#16926)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
1 parent 3ea6413 commit 3c96bf8

2 files changed

Lines changed: 26 additions & 12 deletions

File tree

packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { BoxRenderable, TextareaRenderable, MouseEvent, PasteEvent, decodePasteB
22
import { createEffect, createMemo, onMount, createSignal, onCleanup, on, Show, Switch, Match } from "solid-js"
33
import "opentui-spinner/solid"
44
import path from "path"
5+
import { fileURLToPath } from "url"
56
import { Filesystem } from "@/util/filesystem"
67
import { useLocal } from "@tui/context/local"
78
import { useTheme } from "@tui/context/theme"
@@ -248,7 +249,7 @@ export function Prompt(props: PromptProps) {
248249
onSelect: async () => {
249250
const content = await Clipboard.read()
250251
if (content?.mime.startsWith("image/")) {
251-
await pasteImage({
252+
await pasteAttachment({
252253
filename: "clipboard",
253254
mime: content.mime,
254255
content: content.data,
@@ -771,11 +772,16 @@ export function Prompt(props: PromptProps) {
771772
)
772773
}
773774

774-
async function pasteImage(file: { filename?: string; content: string; mime: string }) {
775+
async function pasteAttachment(file: { filename?: string; filepath?: string; content: string; mime: string }) {
775776
const currentOffset = input.visualCursor.offset
776777
const extmarkStart = currentOffset
777-
const count = store.prompt.parts.filter((x) => x.type === "file" && x.mime.startsWith("image/")).length
778-
const virtualText = `[Image ${count + 1}]`
778+
const pdf = file.mime === "application/pdf"
779+
const count = store.prompt.parts.filter((x) => {
780+
if (x.type !== "file") return false
781+
if (pdf) return x.mime === "application/pdf"
782+
return x.mime.startsWith("image/")
783+
}).length
784+
const virtualText = pdf ? `[PDF ${count + 1}]` : `[Image ${count + 1}]`
779785
const extmarkEnd = extmarkStart + virtualText.length
780786
const textToInsert = virtualText + " "
781787

@@ -796,7 +802,7 @@ export function Prompt(props: PromptProps) {
796802
url: `data:${file.mime};base64,${file.content}`,
797803
source: {
798804
type: "file",
799-
path: file.filename ?? "",
805+
path: file.filepath ?? file.filename ?? "",
800806
text: {
801807
start: extmarkStart,
802808
end: extmarkEnd,
@@ -926,7 +932,7 @@ export function Prompt(props: PromptProps) {
926932
const content = await Clipboard.read()
927933
if (content?.mime.startsWith("image/")) {
928934
e.preventDefault()
929-
await pasteImage({
935+
await pasteAttachment({
930936
filename: "clipboard",
931937
mime: content.mime,
932938
content: content.data,
@@ -1012,9 +1018,16 @@ export function Prompt(props: PromptProps) {
10121018
return
10131019
}
10141020

1015-
// trim ' from the beginning and end of the pasted content. just
1016-
// ' and nothing else
1017-
const filepath = pastedContent.replace(/^'+|'+$/g, "").replace(/\\ /g, " ")
1021+
const filepath = iife(() => {
1022+
const raw = pastedContent.replace(/^['"]+|['"]+$/g, "")
1023+
if (raw.startsWith("file://")) {
1024+
try {
1025+
return fileURLToPath(raw)
1026+
} catch {}
1027+
}
1028+
if (process.platform === "win32") return raw
1029+
return raw.replace(/\\(.)/g, "$1")
1030+
})
10181031
const isUrl = /^(https?):\/\//.test(filepath)
10191032
if (!isUrl) {
10201033
try {
@@ -1029,14 +1042,15 @@ export function Prompt(props: PromptProps) {
10291042
return
10301043
}
10311044
}
1032-
if (mime.startsWith("image/")) {
1045+
if (mime.startsWith("image/") || mime === "application/pdf") {
10331046
event.preventDefault()
10341047
const content = await Filesystem.readArrayBuffer(filepath)
10351048
.then((buffer) => Buffer.from(buffer).toString("base64"))
10361049
.catch(() => {})
10371050
if (content) {
1038-
await pasteImage({
1051+
await pasteAttachment({
10391052
filename,
1053+
filepath,
10401054
mime,
10411055
content,
10421056
})

packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const TIPS = [
5555
"Use {highlight}/undo{/highlight} to revert the last message and file changes",
5656
"Use {highlight}/redo{/highlight} to restore previously undone messages and file changes",
5757
"Run {highlight}/share{/highlight} to create a public link to your conversation at opencode.ai",
58-
"Drag and drop images into the terminal to add them as context",
58+
"Drag and drop images or PDFs into the terminal to add them as context",
5959
"Press {highlight}Ctrl+V{/highlight} to paste images from your clipboard into the prompt",
6060
"Press {highlight}Ctrl+X E{/highlight} or {highlight}/editor{/highlight} to compose messages in your external editor",
6161
"Run {highlight}/init{/highlight} to auto-generate project rules based on your codebase",

0 commit comments

Comments
 (0)