Просмотр исходного кода

tui: add copy last assistant message to session menu

Dax Raad 3 месяцев назад
Родитель
Сommit
0da1ed3fc8

+ 3 - 6
packages/opencode/src/cli/cmd/tui/app.tsx

@@ -177,17 +177,14 @@ function App() {
   const exit = useExit()
 
   useKeyboard(async (evt) => {
+    if (!Installation.isLocal()) return
     if (evt.meta && evt.name === "t") {
-      if (process.env.DEBUG) {
-        renderer.toggleDebugOverlay()
-      }
+      renderer.toggleDebugOverlay()
       return
     }
 
     if (evt.meta && evt.name === "d") {
-      if (process.env.DEBUG) {
-        renderer.console.toggle()
-      }
+      renderer.console.toggle()
       return
     }
   })

+ 82 - 5
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx

@@ -64,7 +64,6 @@ import { LANGUAGE_EXTENSIONS } from "@/lsp/language"
 import parsers from "../../../../../../parsers-config.ts"
 import { Clipboard } from "../../util/clipboard"
 import { Toast, useToast } from "../../ui/toast"
-import { DialogSessionRename } from "../../component/dialog-session-rename"
 import { useKV } from "../../context/kv.tsx"
 
 addDefaultParsers(parsers.parsers)
@@ -401,12 +400,49 @@ export function Session() {
       },
     },
     {
-      title: "Rename session",
-      value: "session.rename",
-      keybind: "session_rename",
+      title: "Copy last assistant message",
+      value: "messages.copy",
+      keybind: "messages_copy",
       category: "Session",
       onSelect: (dialog) => {
-        dialog.replace(() => <DialogSessionRename session={route.sessionID} />)
+        const lastAssistantMessage = messages().findLast((msg) => msg.role === "assistant")
+        if (!lastAssistantMessage) {
+          toast.show({ message: "No assistant messages found", variant: "error" })
+          dialog.clear()
+          return
+        }
+
+        const parts = sync.data.part[lastAssistantMessage.id] ?? []
+        const textParts = parts.filter((part) => part.type === "text")
+        if (textParts.length === 0) {
+          toast.show({ message: "No text parts found in last assistant message", variant: "error" })
+          dialog.clear()
+          return
+        }
+
+        const text = textParts
+          .map((part) => part.text)
+          .join("\n")
+          .trim()
+        if (!text) {
+          toast.show({
+            message: "No text content found in last assistant message",
+            variant: "error",
+          })
+          dialog.clear()
+          return
+        }
+
+        console.log(text)
+        const base64 = Buffer.from(text).toString("base64")
+        const osc52 = `\x1b]52;c;${base64}\x07`
+        const finalOsc52 = process.env["TMUX"] ? `\x1bPtmux;\x1b${osc52}\x1b\\` : osc52
+        /* @ts-expect-error */
+        renderer.writeOut(finalOsc52)
+        Clipboard.copy(text)
+          .then(() => toast.show({ message: "Message copied to clipboard!", variant: "success" }))
+          .catch(() => toast.show({ message: "Failed to copy to clipboard", variant: "error" }))
+        dialog.clear()
       },
     },
     {
@@ -431,6 +467,47 @@ export function Session() {
         dialog.clear()
       },
     },
+    {
+      title: "Copy last assistant message",
+      value: "messages.copy",
+      keybind: "messages_copy",
+      category: "Session",
+      onSelect: (dialog) => {
+        const lastAssistantMessage = messages().findLast((msg) => msg.role === "assistant")
+        if (lastAssistantMessage) {
+          const parts = sync.data.part[lastAssistantMessage.id] ?? []
+          const textParts = parts.filter((part) => part.type === "text")
+          if (textParts.length > 0) {
+            const text = textParts
+              .map((part) => part.text)
+              .join("\n")
+              .trim()
+            if (text) {
+              Clipboard.copy(text)
+                .then(() =>
+                  toast.show({ message: "Message copied to clipboard!", variant: "success" }),
+                )
+                .catch(() =>
+                  toast.show({ message: "Failed to copy to clipboard", variant: "error" }),
+                )
+            } else {
+              toast.show({
+                message: "No text content found in last assistant message",
+                variant: "error",
+              })
+            }
+          } else {
+            toast.show({
+              message: "No text parts found in last assistant message",
+              variant: "error",
+            })
+          }
+        } else {
+          toast.show({ message: "No assistant messages found", variant: "error" })
+        }
+        dialog.clear()
+      },
+    },
   ])
 
   const revert = createMemo(() => {

+ 4 - 1
packages/opencode/src/file/index.ts

@@ -320,7 +320,10 @@ export namespace File {
     log.info("search", { query: input.query })
     const limit = input.limit ?? 100
     const result = await state().then((x) => x.files())
-    if (!input.query) return input.dirs !== false ? result.dirs.toSorted().slice(0, limit) : []
+    if (!input.query)
+      return input.dirs !== false
+        ? result.dirs.toSorted().slice(0, limit)
+        : result.files.slice(0, limit)
     const items = input.dirs !== false ? [...result.files, ...result.dirs] : result.files
     const sorted = fuzzysort.go(input.query, items, { limit: limit }).map((r) => r.target)
     log.info("search", { query: input.query, results: sorted.length })