diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
index 605eb2bf..33a5d816 100644
--- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
@@ -7,6 +7,7 @@ import { Locale } from "@/util/locale"
import { Keybind } from "@/util/keybind"
import { useTheme } from "../context/theme"
import { useSDK } from "../context/sdk"
+import { DialogSessionRename } from "./dialog-session-rename"
export function DialogSessionList() {
const dialog = useDialog()
@@ -74,6 +75,13 @@ export function DialogSessionList() {
setToDelete(option.value)
},
},
+ {
+ keybind: Keybind.parse("r")[0],
+ title: "rename",
+ onTrigger: async (option) => {
+ dialog.replace(() => )
+ },
+ },
]}
/>
)
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-rename.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-rename.tsx
new file mode 100644
index 00000000..aaf03320
--- /dev/null
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-rename.tsx
@@ -0,0 +1,35 @@
+import { DialogPrompt } from "@tui/ui/dialog-prompt"
+import { useDialog } from "@tui/ui/dialog"
+import { useSync } from "@tui/context/sync"
+import { createMemo } from "solid-js"
+import { useSDK } from "../context/sdk"
+
+interface DialogSessionRenameProps {
+ session: string
+}
+
+export function DialogSessionRename(props: DialogSessionRenameProps) {
+ const dialog = useDialog()
+ const sync = useSync()
+ const sdk = useSDK()
+ const session = createMemo(() => sync.session.get(props.session))
+
+ return (
+ {
+ sdk.client.session.update({
+ path: {
+ id: props.session,
+ },
+ body: {
+ title: value,
+ },
+ })
+ dialog.clear()
+ }}
+ onCancel={() => dialog.clear()}
+ />
+ )
+}
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
index da9caa5a..92798b94 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
@@ -222,6 +222,11 @@ export function Autocomplete(props: {
description: "unshare a session",
onSelect: () => command.trigger("session.unshare"),
},
+ {
+ display: "/rename",
+ description: "rename session",
+ onSelect: () => command.trigger("session.rename"),
+ },
)
}
results.push(
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
index baa63d37..78b1ed4a 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
@@ -63,6 +63,7 @@ import { Sidebar } from "./sidebar"
import { LANGUAGE_EXTENSIONS } from "@/lsp/language"
import parsers from "../../../../../../parsers-config.ts"
import { Toast } from "../../ui/toast"
+import { DialogSessionRename } from "../../component/dialog-session-rename"
addDefaultParsers(parsers.parsers)
@@ -370,6 +371,15 @@ export function Session() {
dialog.clear()
},
},
+ {
+ title: "Rename session",
+ value: "session.rename",
+ keybind: "session_rename",
+ category: "Session",
+ onSelect: (dialog) => {
+ dialog.replace(() => )
+ },
+ },
])
const revert = createMemo(() => {
diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx
new file mode 100644
index 00000000..a77727aa
--- /dev/null
+++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-prompt.tsx
@@ -0,0 +1,65 @@
+import { TextareaRenderable, TextAttributes } from "@opentui/core"
+import { useTheme } from "../context/theme"
+import { useDialog, type DialogContext } from "./dialog"
+import { onMount } from "solid-js"
+
+export type DialogPromptProps = {
+ title: string
+ value?: string
+ onConfirm?: (value: string) => void
+ onCancel?: () => void
+}
+
+export function DialogPrompt(props: DialogPromptProps) {
+ const dialog = useDialog()
+ const { theme } = useTheme()
+ let textarea: TextareaRenderable
+
+ onMount(() => {
+ dialog.setSize("large")
+ setTimeout(() => {
+ textarea.focus()
+ }, 1)
+ textarea.gotoLineEnd()
+ })
+
+ return (
+
+
+ {props.title}
+ esc
+
+
+
+
+ Press enter to confirm, esc to cancel
+
+
+ )
+}
+
+DialogPrompt.show = (dialog: DialogContext, title: string, value?: string) => {
+ return new Promise((resolve) => {
+ dialog.replace(
+ () => (
+ resolve(value)}
+ onCancel={() => resolve(null)}
+ />
+ ),
+ () => resolve(null),
+ )
+ })
+}
diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
index 7689b377..3c25c4a8 100644
--- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
+++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
@@ -138,7 +138,10 @@ export function DialogSelect(props: DialogSelectProps) {
for (const item of props.keybind ?? []) {
if (Keybind.match(item.keybind, keybind.parse(evt))) {
const s = selected()
- if (s) item.onTrigger(s)
+ if (s) {
+ evt.preventDefault()
+ item.onTrigger(s)
+ }
}
}
})