tui: add session rename functionality with /rename command

- Add /rename command to autocomplete when a session is active
- Add rename dialog component for changing session names
- Add rename option to session list dialog with 'r' keybind
- Add session rename command to command registry
This commit is contained in:
Dax Raad
2025-10-31 18:44:27 -04:00
parent d473d4ffc8
commit 30f9fa12d9
6 changed files with 127 additions and 1 deletions

View File

@@ -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(() => <DialogSessionRename session={option.value} />)
},
},
]}
/>
)

View File

@@ -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 (
<DialogPrompt
title="Rename Session"
value={session()?.title}
onConfirm={(value) => {
sdk.client.session.update({
path: {
id: props.session,
},
body: {
title: value,
},
})
dialog.clear()
}}
onCancel={() => dialog.clear()}
/>
)
}

View File

@@ -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(

View File

@@ -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(() => <DialogSessionRename session={route.sessionID} />)
},
},
])
const revert = createMemo(() => {

View File

@@ -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 (
<box paddingLeft={2} paddingRight={2} gap={1}>
<box flexDirection="row" justifyContent="space-between">
<text attributes={TextAttributes.BOLD}>{props.title}</text>
<text fg={theme.textMuted}>esc</text>
</box>
<box>
<textarea
onSubmit={() => {
props.onConfirm?.(textarea.plainText)
dialog.clear()
}}
keyBindings={[{ name: "return", action: "submit" }]}
ref={(val: TextareaRenderable) => (textarea = val)}
initialValue={props.value}
placeholder="Enter text"
/>
</box>
<box paddingBottom={1}>
<text fg={theme.textMuted}>Press enter to confirm, esc to cancel</text>
</box>
</box>
)
}
DialogPrompt.show = (dialog: DialogContext, title: string, value?: string) => {
return new Promise<string | null>((resolve) => {
dialog.replace(
() => (
<DialogPrompt
title={title}
value={value}
onConfirm={(value) => resolve(value)}
onCancel={() => resolve(null)}
/>
),
() => resolve(null),
)
})
}

View File

@@ -138,7 +138,10 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
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)
}
}
}
})