diff --git a/src/app.tsx b/src/app.tsx index 9674e59..d85d47d 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -12,6 +12,7 @@ import { createSession, killSession, browse9p, + renameSession, } from './lib/api' import type { IdxEntry } from './lib/api' import { parseChat } from './lib/chat' @@ -122,6 +123,13 @@ export function App() { setSessions(s) }, [selectedId]) + const handleRename = useCallback(async (id: string, newName: string) => { + await renameSession(id, newName) + if (selectedId === id) setSelectedId(newName) + const s = await listSessions() + setSessions(s) + }, [selectedId]) + return (
setShowNew(true)} onKill={handleKill} + onRename={handleRename} themes={getThemes()} currentTheme={themeName} onThemeChange={handleSetTheme} @@ -144,6 +153,7 @@ export function App() { browseData={browseData} onBrowse={handleBrowse} onSelectSession={handleSelect} + onRename={handleRename} /> {showNew && ( setShowNew(false)} onCreate={handleCreate} /> diff --git a/src/components/ChatPane.tsx b/src/components/ChatPane.tsx index f73eb48..3a679d5 100644 --- a/src/components/ChatPane.tsx +++ b/src/components/ChatPane.tsx @@ -17,6 +17,7 @@ interface Props { browseData: any onBrowse: (path: string) => void onSelectSession: (id: string) => void + onRename: (id: string, newName: string) => void } function stateClass(state: string) { @@ -25,7 +26,7 @@ function stateClass(state: string) { return 'running' } -export function ChatPane({ session, cfg, messages, onSend, onStop, browsePath, browseData, onBrowse, onSelectSession }: Props) { +export function ChatPane({ session, cfg, messages, onSend, onStop, browsePath, browseData, onBrowse, onSelectSession, onRename }: Props) { const listRef = useRef(null) const [atBottom, setAtBottom] = useState(true) @@ -55,15 +56,35 @@ export function ChatPane({ session, cfg, messages, onSend, onStop, browsePath, b ) } - const sid = shortId(session.id) + const sid = session.id const state = cfg.state ?? 'stopped' + const [renaming, setRenaming] = useState(false) + const [renameVal, setRenameVal] = useState('') + + const startRename = () => { setRenaming(true); setRenameVal(sid) } + const commitRename = () => { + setRenaming(false) + const name = renameVal.trim() + if (name && name !== sid) onRename(sid, name) + } return (
onBrowse('/mnt/s/')}>↑{' '} - /s/{sid}/chat + /s/{renaming ? ( + setRenameVal((e.target as HTMLInputElement).value)} + onBlur={() => commitRename()} + onKeyDown={e => { if (e.key === 'Enter') commitRename(); if (e.key === 'Escape') setRenaming(false) }} + autoFocus + /> + ) : ( + {sid} + )}/chat
@@ -87,6 +108,8 @@ export function ChatPane({ session, cfg, messages, onSend, onStop, browsePath, b
u: {msg.content}
) : msg.role === 'tool' ? ( + ) : msg.role === 'info' ? ( +
{msg.content}
) : (
s.code ?? s.tool ?? JSON.stringify(s)).join('\n') - return { name: m[1], body: lines } - } - return { name: m[1], body: JSON.stringify(obj, null, 2) } + // Extract a useful detail: tool name or language + let detail = '' + if (obj.steps?.[0]?.tool) detail = obj.steps[0].tool + else if (obj.steps?.[0]?.code) detail = obj.language ?? 'bash' + return { name, detail, body: JSON.stringify(obj, null, 2), output } } catch { - return { name: m[1], body: m[2] } + return { name, detail: '', body: m[2], output } } } function ToolCall({ content }: { content: string }) { - const { name, body } = formatToolContent(content) + const { name, detail, body, output } = formatToolContent(content) + const [open, setOpen] = useState(false) return (
-
- 🖥️ - {name && {name}} -
-
{body}
+
setOpen((e.target as HTMLDetailsElement).open)}> + + 🖥️ + {name && {name}} + {detail && {detail}} + +
{body}
+
+ {output &&
{output}
}
) } diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 712b060..4fdc73c 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,6 +1,7 @@ +import { useState } from 'preact/hooks' import type { IdxEntry } from '../lib/api' import type { Theme } from '../lib/theme' -import { shortId, truncate } from '../lib/chat' +import { truncate } from '../lib/chat' interface Props { sessions: IdxEntry[] @@ -8,6 +9,7 @@ interface Props { onSelect: (id: string | null) => void onNew: () => void onKill: (id: string) => void + onRename: (id: string, newName: string) => void themes: Theme[] currentTheme: string onThemeChange: (name: string) => void @@ -19,7 +21,21 @@ function stateClass(state: string) { return 'running' // thinking, calling: , etc. } -export function Sidebar({ sessions, selectedId, onSelect, onNew, onKill, themes, currentTheme, onThemeChange }: Props) { +export function Sidebar({ sessions, selectedId, onSelect, onNew, onKill, onRename, themes, currentTheme, onThemeChange }: Props) { + const [editingId, setEditingId] = useState(null) + const [editValue, setEditValue] = useState('') + + const startRename = (e: MouseEvent, id: string) => { + e.stopPropagation() + setEditingId(id) + setEditValue(id) + } + + const commitRename = (oldId: string) => { + const name = editValue.trim() + setEditingId(null) + if (name && name !== oldId) onRename(oldId, name) + } return (