feat(opencode): Add PDF attachment Drag and Drop (#16926)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com>pull/21204/head^2
parent
3ea6413407
commit
3c96bf8468
|
|
@ -2,6 +2,7 @@ import { BoxRenderable, TextareaRenderable, MouseEvent, PasteEvent, decodePasteB
|
||||||
import { createEffect, createMemo, onMount, createSignal, onCleanup, on, Show, Switch, Match } from "solid-js"
|
import { createEffect, createMemo, onMount, createSignal, onCleanup, on, Show, Switch, Match } from "solid-js"
|
||||||
import "opentui-spinner/solid"
|
import "opentui-spinner/solid"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
import { fileURLToPath } from "url"
|
||||||
import { Filesystem } from "@/util/filesystem"
|
import { Filesystem } from "@/util/filesystem"
|
||||||
import { useLocal } from "@tui/context/local"
|
import { useLocal } from "@tui/context/local"
|
||||||
import { useTheme } from "@tui/context/theme"
|
import { useTheme } from "@tui/context/theme"
|
||||||
|
|
@ -248,7 +249,7 @@ export function Prompt(props: PromptProps) {
|
||||||
onSelect: async () => {
|
onSelect: async () => {
|
||||||
const content = await Clipboard.read()
|
const content = await Clipboard.read()
|
||||||
if (content?.mime.startsWith("image/")) {
|
if (content?.mime.startsWith("image/")) {
|
||||||
await pasteImage({
|
await pasteAttachment({
|
||||||
filename: "clipboard",
|
filename: "clipboard",
|
||||||
mime: content.mime,
|
mime: content.mime,
|
||||||
content: content.data,
|
content: content.data,
|
||||||
|
|
@ -771,11 +772,16 @@ export function Prompt(props: PromptProps) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pasteImage(file: { filename?: string; content: string; mime: string }) {
|
async function pasteAttachment(file: { filename?: string; filepath?: string; content: string; mime: string }) {
|
||||||
const currentOffset = input.visualCursor.offset
|
const currentOffset = input.visualCursor.offset
|
||||||
const extmarkStart = currentOffset
|
const extmarkStart = currentOffset
|
||||||
const count = store.prompt.parts.filter((x) => x.type === "file" && x.mime.startsWith("image/")).length
|
const pdf = file.mime === "application/pdf"
|
||||||
const virtualText = `[Image ${count + 1}]`
|
const count = store.prompt.parts.filter((x) => {
|
||||||
|
if (x.type !== "file") return false
|
||||||
|
if (pdf) return x.mime === "application/pdf"
|
||||||
|
return x.mime.startsWith("image/")
|
||||||
|
}).length
|
||||||
|
const virtualText = pdf ? `[PDF ${count + 1}]` : `[Image ${count + 1}]`
|
||||||
const extmarkEnd = extmarkStart + virtualText.length
|
const extmarkEnd = extmarkStart + virtualText.length
|
||||||
const textToInsert = virtualText + " "
|
const textToInsert = virtualText + " "
|
||||||
|
|
||||||
|
|
@ -796,7 +802,7 @@ export function Prompt(props: PromptProps) {
|
||||||
url: `data:${file.mime};base64,${file.content}`,
|
url: `data:${file.mime};base64,${file.content}`,
|
||||||
source: {
|
source: {
|
||||||
type: "file",
|
type: "file",
|
||||||
path: file.filename ?? "",
|
path: file.filepath ?? file.filename ?? "",
|
||||||
text: {
|
text: {
|
||||||
start: extmarkStart,
|
start: extmarkStart,
|
||||||
end: extmarkEnd,
|
end: extmarkEnd,
|
||||||
|
|
@ -926,7 +932,7 @@ export function Prompt(props: PromptProps) {
|
||||||
const content = await Clipboard.read()
|
const content = await Clipboard.read()
|
||||||
if (content?.mime.startsWith("image/")) {
|
if (content?.mime.startsWith("image/")) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
await pasteImage({
|
await pasteAttachment({
|
||||||
filename: "clipboard",
|
filename: "clipboard",
|
||||||
mime: content.mime,
|
mime: content.mime,
|
||||||
content: content.data,
|
content: content.data,
|
||||||
|
|
@ -1012,9 +1018,16 @@ export function Prompt(props: PromptProps) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// trim ' from the beginning and end of the pasted content. just
|
const filepath = iife(() => {
|
||||||
// ' and nothing else
|
const raw = pastedContent.replace(/^['"]+|['"]+$/g, "")
|
||||||
const filepath = pastedContent.replace(/^'+|'+$/g, "").replace(/\\ /g, " ")
|
if (raw.startsWith("file://")) {
|
||||||
|
try {
|
||||||
|
return fileURLToPath(raw)
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
if (process.platform === "win32") return raw
|
||||||
|
return raw.replace(/\\(.)/g, "$1")
|
||||||
|
})
|
||||||
const isUrl = /^(https?):\/\//.test(filepath)
|
const isUrl = /^(https?):\/\//.test(filepath)
|
||||||
if (!isUrl) {
|
if (!isUrl) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1029,14 +1042,15 @@ export function Prompt(props: PromptProps) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mime.startsWith("image/")) {
|
if (mime.startsWith("image/") || mime === "application/pdf") {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const content = await Filesystem.readArrayBuffer(filepath)
|
const content = await Filesystem.readArrayBuffer(filepath)
|
||||||
.then((buffer) => Buffer.from(buffer).toString("base64"))
|
.then((buffer) => Buffer.from(buffer).toString("base64"))
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
if (content) {
|
if (content) {
|
||||||
await pasteImage({
|
await pasteAttachment({
|
||||||
filename,
|
filename,
|
||||||
|
filepath,
|
||||||
mime,
|
mime,
|
||||||
content,
|
content,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ const TIPS = [
|
||||||
"Use {highlight}/undo{/highlight} to revert the last message and file changes",
|
"Use {highlight}/undo{/highlight} to revert the last message and file changes",
|
||||||
"Use {highlight}/redo{/highlight} to restore previously undone messages and file changes",
|
"Use {highlight}/redo{/highlight} to restore previously undone messages and file changes",
|
||||||
"Run {highlight}/share{/highlight} to create a public link to your conversation at opencode.ai",
|
"Run {highlight}/share{/highlight} to create a public link to your conversation at opencode.ai",
|
||||||
"Drag and drop images into the terminal to add them as context",
|
"Drag and drop images or PDFs into the terminal to add them as context",
|
||||||
"Press {highlight}Ctrl+V{/highlight} to paste images from your clipboard into the prompt",
|
"Press {highlight}Ctrl+V{/highlight} to paste images from your clipboard into the prompt",
|
||||||
"Press {highlight}Ctrl+X E{/highlight} or {highlight}/editor{/highlight} to compose messages in your external editor",
|
"Press {highlight}Ctrl+X E{/highlight} or {highlight}/editor{/highlight} to compose messages in your external editor",
|
||||||
"Run {highlight}/init{/highlight} to auto-generate project rules based on your codebase",
|
"Run {highlight}/init{/highlight} to auto-generate project rules based on your codebase",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue