Compare commits

...

4 Commits

Author SHA1 Message Date
Shoubhit Dash d8c3d8d884 highlight clickable inline tools 2026-03-05 19:07:49 +05:30
Shoubhit Dash 66ba6c4d34 Merge branch 'dev' into fix/subagent-navigation-inline-click 2026-03-05 19:05:30 +05:30
Shoubhit Dash c4c3a020f9 refactor(tui): use renderer.hasSelection guards 2026-03-04 17:39:44 +05:30
Shoubhit Dash 002b81cd67 fix(tui): keep inline task navigation and fix nested child jump 2026-03-04 17:06:10 +05:30
@@ -128,6 +128,11 @@ export function Session() {
.filter((x) => x.parentID === parentID || x.id === parentID) .filter((x) => x.parentID === parentID || x.id === parentID)
.toSorted((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) .toSorted((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
}) })
const subagents = createMemo(() => {
return sync.data.session
.filter((x) => x.parentID === route.sessionID)
.toSorted((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
})
const messages = createMemo(() => sync.data.message[route.sessionID] ?? []) const messages = createMemo(() => sync.data.message[route.sessionID] ?? [])
const permissions = createMemo(() => { const permissions = createMemo(() => {
if (session()?.parentID) return [] if (session()?.parentID) return []
@@ -316,20 +321,14 @@ export function Session() {
const local = useLocal() const local = useLocal()
function moveFirstChild() { function moveFirstChild() {
if (children().length === 1) return const next = subagents()[0]
const next = children().find((x) => !!x.parentID) if (!next) return
if (next) { navigate({ type: "session", sessionID: next.id })
navigate({
type: "session",
sessionID: next.id,
})
}
} }
function moveChild(direction: number) { function moveChild(direction: number) {
if (children().length === 1) return
const sessions = children().filter((x) => !!x.parentID) const sessions = children().filter((x) => !!x.parentID)
if (sessions.length === 0) return
let next = sessions.findIndex((x) => x.id === session()?.id) + direction let next = sessions.findIndex((x) => x.id === session()?.id) + direction
if (next >= sessions.length) next = 0 if (next >= sessions.length) next = 0
@@ -1124,7 +1123,7 @@ export function Session() {
<UserMessage <UserMessage
index={index()} index={index()}
onMouseUp={() => { onMouseUp={() => {
if (renderer.getSelection()?.getSelectedText()) return if (renderer.hasSelection) return
dialog.replace(() => ( dialog.replace(() => (
<DialogMessage <DialogMessage
messageID={message.id} messageID={message.id}
@@ -1623,6 +1622,7 @@ function InlineTool(props: {
complete: any complete: any
pending: string pending: string
spinner?: boolean spinner?: boolean
onClick?: () => void
children: JSX.Element children: JSX.Element
part: ToolPart part: ToolPart
}) { }) {
@@ -1630,6 +1630,8 @@ function InlineTool(props: {
const { theme } = useTheme() const { theme } = useTheme()
const ctx = use() const ctx = use()
const sync = useSync() const sync = useSync()
const renderer = useRenderer()
const [hover, setHover] = createSignal(false)
const permission = createMemo(() => { const permission = createMemo(() => {
const callID = sync.data.permission[ctx.sessionID]?.at(0)?.tool?.callID const callID = sync.data.permission[ctx.sessionID]?.at(0)?.tool?.callID
@@ -1678,13 +1680,23 @@ function InlineTool(props: {
return return
} }
}} }}
onMouseOver={() => props.onClick && setHover(true)}
onMouseOut={() => setHover(false)}
onMouseUp={() => {
if (renderer.hasSelection) return
props.onClick?.()
}}
> >
<Switch> <Switch>
<Match when={props.spinner}> <Match when={props.spinner}>
<Spinner color={fg()} children={props.children} /> <Spinner color={hover() ? theme.text : fg()} children={props.children} />
</Match> </Match>
<Match when={true}> <Match when={true}>
<text paddingLeft={3} fg={fg()} attributes={denied() ? TextAttributes.STRIKETHROUGH : undefined}> <text
paddingLeft={3}
fg={hover() ? theme.text : fg()}
attributes={denied() ? TextAttributes.STRIKETHROUGH : undefined}
>
<Show fallback={<>~ {props.pending}</>} when={props.complete}> <Show fallback={<>~ {props.pending}</>} when={props.complete}>
<span style={{ fg: props.iconColor }}>{props.icon}</span> {props.children} <span style={{ fg: props.iconColor }}>{props.icon}</span> {props.children}
</Show> </Show>
@@ -1723,7 +1735,7 @@ function BlockTool(props: {
onMouseOver={() => props.onClick && setHover(true)} onMouseOver={() => props.onClick && setHover(true)}
onMouseOut={() => setHover(false)} onMouseOut={() => setHover(false)}
onMouseUp={() => { onMouseUp={() => {
if (renderer.getSelection()?.getSelectedText()) return if (renderer.hasSelection) return
props.onClick?.() props.onClick?.()
}} }}
> >
@@ -1943,10 +1955,7 @@ function WebSearch(props: ToolProps<any>) {
} }
function Task(props: ToolProps<typeof TaskTool>) { function Task(props: ToolProps<typeof TaskTool>) {
const { theme } = useTheme()
const keybind = useKeybind()
const { navigate } = useRoute() const { navigate } = useRoute()
const local = useLocal()
const sync = useSync() const sync = useSync()
onMount(() => { onMount(() => {
@@ -1999,6 +2008,15 @@ function Task(props: ToolProps<typeof TaskTool>) {
complete={props.input.description} complete={props.input.description}
pending="Delegating..." pending="Delegating..."
part={props.part} part={props.part}
onClick={
props.metadata.sessionId
? () => {
const id = props.metadata.sessionId
if (!id) return
navigate({ type: "session", sessionID: id })
}
: undefined
}
> >
{content()} {content()}
</InlineTool> </InlineTool>