mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
tui: improve clipboard handling and keybind display
- Add cross-platform clipboard utility with image and text support - Fix keybind display to properly show leader key combinations - Refactor logo rendering for better maintainability - Update prompt component to use new clipboard functionality
This commit is contained in:
parent
09b65ff1a7
commit
68e76b8d49
6 changed files with 86 additions and 20 deletions
|
|
@ -14,7 +14,7 @@ import type { FilePart } from "@opencode-ai/sdk"
|
|||
import fuzzysort from "fuzzysort"
|
||||
import { useCommandDialog } from "./dialog-command"
|
||||
import { useKeybind } from "../context/keybind"
|
||||
import clipboard from "clipboardy"
|
||||
import { Clipboard } from "../../../../util/clipboard"
|
||||
|
||||
export type PromptProps = {
|
||||
sessionID?: string
|
||||
|
|
@ -71,9 +71,11 @@ export function Prompt(props: PromptProps) {
|
|||
<box paddingTop={1} paddingBottom={2} backgroundColor={Theme.backgroundElement} flexGrow={1}>
|
||||
<input
|
||||
onPaste={async function (text) {
|
||||
const data = (await clipboard.read().catch(() => {})) ?? text
|
||||
console.log(data)
|
||||
this.insertText(data)
|
||||
const content = await Clipboard.read()
|
||||
console.log(content)
|
||||
if (!content) {
|
||||
this.insertText(text)
|
||||
}
|
||||
}}
|
||||
onInput={(value) => {
|
||||
let diff = value.length - store.input.length
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ export function init() {
|
|||
print(key: keyof KeybindsConfig) {
|
||||
const first = keybinds()[key]?.at(0)
|
||||
if (!first) return ""
|
||||
return Keybind.toString(first)
|
||||
const result = Keybind.toString(first)
|
||||
return result.replace("<leader>", Keybind.toString(keybinds().leader![0]!))
|
||||
},
|
||||
}
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Installation } from "../../../installation"
|
|||
import { useTheme } from "./context/theme"
|
||||
import { TextAttributes } from "@opentui/core"
|
||||
import { Prompt } from "./component/prompt"
|
||||
import { For } from "solid-js"
|
||||
|
||||
export function Home() {
|
||||
const { currentTheme } = useTheme()
|
||||
|
|
@ -46,23 +47,33 @@ function HelpRow(props: { children: string; slash: string; theme: any }) {
|
|||
)
|
||||
}
|
||||
|
||||
const LOGO_LEFT = [
|
||||
` `,
|
||||
`█▀▀█ █▀▀█ █▀▀█ █▀▀▄`,
|
||||
`█░░█ █░░█ █▀▀▀ █░░█`,
|
||||
`▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`,
|
||||
]
|
||||
|
||||
const LOGO_RIGHT = [
|
||||
` ▄ `,
|
||||
`█▀▀▀ █▀▀█ █▀▀█ █▀▀█`,
|
||||
`█░░░ █░░█ █░░█ █▀▀▀`,
|
||||
`▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`,
|
||||
]
|
||||
|
||||
function Logo(props: { theme: any }) {
|
||||
return (
|
||||
<box>
|
||||
<box flexDirection="row">
|
||||
<text fg={props.theme.textMuted}>{"█▀▀█ █▀▀█ █▀▀ █▀▀▄"}</text>
|
||||
<text fg={props.theme.text} attributes={TextAttributes.BOLD}>
|
||||
{" █▀▀ █▀▀█ █▀▀▄ █▀▀"}
|
||||
</text>
|
||||
</box>
|
||||
<box flexDirection="row">
|
||||
<text fg={props.theme.textMuted}>{`█░░█ █░░█ █▀▀ █░░█`}</text>
|
||||
<text fg={props.theme.text}>{` █░░ █░░█ █░░█ █▀▀`}</text>
|
||||
</box>
|
||||
<box flexDirection="row">
|
||||
<text fg={props.theme.textMuted}>{`▀▀▀▀ █▀▀▀ ▀▀▀ ▀ ▀`}</text>
|
||||
<text fg={props.theme.text}>{` ▀▀▀ ▀▀▀▀ ▀▀▀ ▀▀▀`}</text>
|
||||
</box>
|
||||
<For each={LOGO_LEFT}>
|
||||
{(line, index) => (
|
||||
<box flexDirection="row" gap={1}>
|
||||
<text fg={props.theme.textMuted}>{line}</text>
|
||||
<text fg={props.theme.text} attributes={TextAttributes.BOLD}>
|
||||
{LOGO_RIGHT[index()]}
|
||||
</text>
|
||||
</box>
|
||||
)}
|
||||
</For>
|
||||
<box flexDirection="row" justifyContent="flex-end">
|
||||
<text fg={props.theme.textMuted}>{Installation.VERSION}</text>
|
||||
</box>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
export namespace Clipboard {
|
||||
export function copy() {}
|
||||
}
|
||||
49
packages/opencode/src/util/clipboard.ts
Normal file
49
packages/opencode/src/util/clipboard.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { $ } from "bun"
|
||||
import { platform } from "os"
|
||||
import clipboardy from "clipboardy"
|
||||
|
||||
export namespace Clipboard {
|
||||
export interface Content {
|
||||
data: string
|
||||
mime: string
|
||||
}
|
||||
|
||||
export async function read(): Promise<Content | undefined> {
|
||||
const os = platform()
|
||||
|
||||
if (os === "darwin") {
|
||||
const imageBuffer = await $`osascript -e 'try' -e 'the clipboard as «class PNGf»' -e 'end try'`.nothrow().text()
|
||||
if (imageBuffer) {
|
||||
return { data: Buffer.from(imageBuffer).toString("base64url"), mime: "image/png" }
|
||||
}
|
||||
}
|
||||
|
||||
if (os === "linux") {
|
||||
const wayland = await $`wl-paste -t image/png`.nothrow().text()
|
||||
if (wayland) {
|
||||
return { data: Buffer.from(wayland).toString("base64url"), mime: "image/png" }
|
||||
}
|
||||
const x11 = await $`xclip -selection clipboard -t image/png -o`.nothrow().text()
|
||||
if (x11) {
|
||||
return { data: Buffer.from(x11).toString("base64url"), mime: "image/png" }
|
||||
}
|
||||
}
|
||||
|
||||
if (os === "win32") {
|
||||
const script =
|
||||
"Add-Type -AssemblyName System.Windows.Forms; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); [System.Convert]::ToBase64String($ms.ToArray()) }"
|
||||
const base64 = await $`powershell -command "${script}"`.nothrow().text()
|
||||
if (base64) {
|
||||
const imageBuffer = Buffer.from(base64.trim(), "base64")
|
||||
if (imageBuffer.length > 0) {
|
||||
return { data: imageBuffer.toString("base64url"), mime: "image/png" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const text = await clipboardy.read().catch(() => {})
|
||||
if (text) {
|
||||
return { data: text, mime: "text/plain" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ export namespace Keybind {
|
|||
let result = parts.join("+")
|
||||
|
||||
if (info.leader) {
|
||||
result = `<leader>${result}`
|
||||
result = `<leader>,${result}`
|
||||
}
|
||||
|
||||
return result
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue