tui: refactor command system and add session management commands

This commit is contained in:
Dax Raad 2025-09-23 00:31:02 -04:00
parent 127241d53f
commit 3d80e36549
5 changed files with 51 additions and 45 deletions

View file

@ -16,16 +16,25 @@ const ctx = createContext<Context>()
function init() {
const [registrations, setRegistrations] = createSignal<Accessor<DialogSelectOption[]>[]>([])
const dialog = useDialog()
const options = createMemo(() => {
return registrations().flatMap((x) => x())
})
return {
trigger(name: string) {
for (const option of options()) {
if (option.value === name) {
option.onSelect?.(dialog)
return
}
}
},
register(cb: () => DialogSelectOption[]) {
const results = createMemo(cb)
setRegistrations((x) => [...x, results])
setRegistrations((arr) => [results, ...arr])
onCleanup(() => {
setRegistrations((x) => x.filter((x) => x !== results))
setRegistrations((arr) => arr.filter((x) => x !== results))
})
},
get options() {

View file

@ -14,6 +14,7 @@ import type { FilePart } from "@opencode-ai/sdk"
import fuzzysort from "fuzzysort"
import { DialogModel } from "./dialog-model"
import { DialogAgent } from "./dialog-agent"
import { useCommandDialog } from "./dialog-command"
export type PromptProps = {
sessionID?: string
@ -254,6 +255,7 @@ function Autocomplete(props: {
const sdk = useSDK()
const local = useLocal()
const sync = useSync()
const command = useCommandDialog()
const [store, setStore] = createStore({
index: 0,
@ -310,12 +312,10 @@ function Autocomplete(props: {
},
)
const route = useRoute()
const session = createMemo(() => (props.sessionID ? sync.session.get(props.sessionID) : undefined))
const commands = createMemo((): AutocompleteOption[] => {
const results: AutocompleteOption[] = []
const s = session()
const dialog = useDialog()
for (const command of sync.data.command) {
results.push({
display: "/" + command.name,
@ -341,41 +341,19 @@ function Autocomplete(props: {
{
display: "/compact",
description: "compact the session",
onSelect: () => {
sdk.session.summarize({
path: {
id: s.id,
},
body: {
modelID: local.model.current().modelID,
providerID: local.model.current().providerID,
},
})
},
onSelect: () => command.trigger("session.compact"),
},
{
display: "/share",
disabled: !!s.share?.url,
description: "share a session",
onSelect: () => {
sdk.session.share({
path: {
id: s.id,
},
})
},
onSelect: () => command.trigger("session.share"),
},
{
display: "/unshare",
disabled: !s.share,
description: "unshare a session",
onSelect: () => {
sdk.session.unshare({
path: {
id: s.id,
},
})
},
onSelect: () => command.trigger("session.unshare"),
},
)
}
@ -383,25 +361,17 @@ function Autocomplete(props: {
{
display: "/new",
description: "create a new session",
onSelect: () => {
route.navigate({
type: "home",
})
},
onSelect: () => command.trigger("session.new"),
},
{
display: "/models",
description: "list models",
onSelect: () => {
dialog.replace(() => <DialogModel />)
},
onSelect: () => command.trigger("model.list"),
},
{
display: "/agents",
description: "list agents",
onSelect: () => {
dialog.replace(() => <DialogAgent />)
},
onSelect: () => command.trigger("agent.list"),
},
)
const max = firstBy(results, [(x) => x.display.length, "desc"])?.display.length

View file

@ -49,8 +49,27 @@ export function Session() {
})
})
const local = useLocal()
const command = useCommandDialog()
command.register(() => [
{
title: "Compact session",
value: "session.compact",
category: "Session",
onSelect: (dialog) => {
sdk.session.summarize({
path: {
id: route.sessionID,
},
body: {
modelID: local.model.current().modelID,
providerID: local.model.current().providerID,
},
})
dialog.clear()
},
},
{
title: "Share session",
value: "session.share",
@ -79,6 +98,12 @@ export function Session() {
dialog.clear()
},
},
{
title: "Rename session",
value: "session.rename",
category: "Session",
onSelect: () => {},
},
])
return (

View file

@ -137,7 +137,7 @@ function App() {
command.register(() => [
{
title: "Switch session",
value: "switch-session",
value: "session.list",
category: "Session",
onSelect: () => {
dialog.replace(() => <DialogSessionList />)
@ -145,7 +145,7 @@ function App() {
},
{
title: "New session",
value: "new-session",
value: "session.new",
category: "Session",
onSelect: () => {
route.navigate({
@ -156,7 +156,7 @@ function App() {
},
{
title: "Switch model",
value: "switch-model",
value: "model.list",
category: "Agent",
onSelect: () => {
dialog.replace(() => <DialogModel />)
@ -164,7 +164,7 @@ function App() {
},
{
title: "Switch agent",
value: "switch-agent",
value: "agent.list",
category: "Agent",
onSelect: () => {
dialog.replace(() => <DialogAgent />)

View file

@ -155,7 +155,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
)}
</For>
</scrollbox>
<box paddingRight={2} paddingLeft={3} paddingBottom={1} flexDirection="row">
<box paddingRight={2} paddingLeft={3} flexDirection="row">
{/*
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
n
</text>
@ -164,6 +165,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
{" "}r
</text>
<text fg={Theme.textMuted}> rename</text>
*/}
</box>
</box>
)