diff options
| -rw-r--r-- | packages/app/src/components/session/session-header.tsx | 69 | ||||
| -rw-r--r-- | packages/app/src/pages/directory-layout.tsx | 14 | ||||
| -rw-r--r-- | packages/ui/src/components/basic-tool.css | 10 | ||||
| -rw-r--r-- | packages/ui/src/components/basic-tool.tsx | 8 | ||||
| -rw-r--r-- | packages/ui/src/components/message-part.tsx | 9 | ||||
| -rw-r--r-- | packages/ui/src/context/data.tsx | 10 |
6 files changed, 105 insertions, 15 deletions
diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 6c102e4bd..ded5d9d0f 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -35,7 +35,12 @@ export function SessionHeader() { const projectDirectory = createMemo(() => base64Decode(params.dir ?? "")) const sessions = createMemo(() => (sync.data.session ?? []).filter((s) => !s.parentID)) - const currentSession = createMemo(() => sessions().find((s) => s.id === params.id)) + const currentSession = createMemo(() => sync.data.session.find((s) => s.id === params.id)) + const parentSession = createMemo(() => { + const current = currentSession() + if (!current?.parentID) return undefined + return sync.data.session.find((s) => s.id === current.parentID) + }) const shareEnabled = createMemo(() => sync.data.config.share !== "disabled") const worktrees = createMemo(() => layout.projects.list().map((p) => p.worktree), [], { equals: same }) @@ -45,6 +50,8 @@ export function SessionHeader() { function navigateToSession(session: Session | undefined) { if (!session) return + // Only navigate if we're actually changing to a different session + if (session.id === params.id) return navigate(`/${params.dir}/session/${session.id}`) } @@ -79,18 +86,56 @@ export function SessionHeader() { </Select> <div class="text-text-weaker">/</div> </div> - <Select - options={sessions()} - current={currentSession()} - placeholder="New session" - label={(x) => x.title} - value={(x) => x.id} - onSelect={navigateToSession} - class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md" - variant="ghost" - /> + <Show + when={parentSession()} + fallback={ + <> + <Select + options={sessions()} + current={currentSession()} + placeholder="New session" + label={(x) => x.title} + value={(x) => x.id} + onSelect={navigateToSession} + class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md" + variant="ghost" + /> + </> + } + > + <div class="flex items-center gap-2 min-w-0"> + <Select + options={sessions()} + current={parentSession()} + placeholder="Back to parent session" + label={(x) => x.title} + value={(x) => x.id} + onSelect={(session) => { + // Only navigate if selecting a different session than current parent + const currentParent = parentSession() + if (session && currentParent && session.id !== currentParent.id) { + navigateToSession(session) + } + }} + class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md" + variant="ghost" + /> + <div class="text-text-weaker">/</div> + <div class="flex items-center gap-1.5 min-w-0"> + <Tooltip value="Back to parent session"> + <button + type="button" + class="flex items-center justify-center gap-1 p-1 rounded hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active transition-colors flex-shrink-0" + onClick={() => navigateToSession(parentSession())} + > + <Icon name="arrow-left" size="small" class="text-icon-base" /> + </button> + </Tooltip> + </div> + </div> + </Show> </div> - <Show when={currentSession()}> + <Show when={currentSession() && !parentSession()}> <TooltipKeybind class="hidden xl:block" title="New session" keybind={command.keybind("session.new")}> <IconButton as={A} href={`/${params.dir}/session`} icon="edit-small-2" variant="ghost" /> </TooltipKeybind> diff --git a/packages/app/src/pages/directory-layout.tsx b/packages/app/src/pages/directory-layout.tsx index 6aac3c384..39124637c 100644 --- a/packages/app/src/pages/directory-layout.tsx +++ b/packages/app/src/pages/directory-layout.tsx @@ -1,5 +1,5 @@ import { createMemo, Show, type ParentProps } from "solid-js" -import { useParams } from "@solidjs/router" +import { useNavigate, useParams } from "@solidjs/router" import { SDKProvider, useSDK } from "@/context/sdk" import { SyncProvider, useSync } from "@/context/sync" import { LocalProvider } from "@/context/local" @@ -10,6 +10,7 @@ import { iife } from "@opencode-ai/util/iife" export default function Layout(props: ParentProps) { const params = useParams() + const navigate = useNavigate() const directory = createMemo(() => { return base64Decode(params.dir!) }) @@ -26,8 +27,17 @@ export default function Layout(props: ParentProps) { response: "once" | "always" | "reject" }) => sdk.client.permission.respond(input) + const navigateToSession = (sessionID: string) => { + navigate(`/${params.dir}/session/${sessionID}`) + } + return ( - <DataProvider data={sync.data} directory={directory()} onPermissionRespond={respond}> + <DataProvider + data={sync.data} + directory={directory()} + onPermissionRespond={respond} + onNavigateToSession={navigateToSession} + > <LocalProvider>{props.children}</LocalProvider> </DataProvider> ) diff --git a/packages/ui/src/components/basic-tool.css b/packages/ui/src/components/basic-tool.css index a5f1ca557..2c6bfeb67 100644 --- a/packages/ui/src/components/basic-tool.css +++ b/packages/ui/src/components/basic-tool.css @@ -68,6 +68,16 @@ line-height: var(--line-height-large); letter-spacing: var(--letter-spacing-normal); color: var(--text-weak); + + &.clickable { + cursor: pointer; + text-decoration: underline; + transition: color 0.15s ease; + + &:hover { + color: var(--text-base); + } + } } [data-slot="basic-tool-tool-arg"] { diff --git a/packages/ui/src/components/basic-tool.tsx b/packages/ui/src/components/basic-tool.tsx index 67720955d..15b5d4867 100644 --- a/packages/ui/src/components/basic-tool.tsx +++ b/packages/ui/src/components/basic-tool.tsx @@ -25,6 +25,7 @@ export interface BasicToolProps { hideDetails?: boolean defaultOpen?: boolean forceOpen?: boolean + onSubtitleClick?: () => void } export function BasicTool(props: BasicToolProps) { @@ -59,6 +60,13 @@ export function BasicTool(props: BasicToolProps) { data-slot="basic-tool-tool-subtitle" classList={{ [trigger().subtitleClass ?? ""]: !!trigger().subtitleClass, + clickable: !!props.onSubtitleClick, + }} + onClick={(e) => { + if (props.onSubtitleClick) { + e.stopPropagation() + props.onSubtitleClick() + } }} > {trigger().subtitle} diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index cb50cf245..8102c2ce7 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -759,6 +759,13 @@ ToolRegistry.register({ }) } + const handleSubtitleClick = () => { + const sessionId = childSessionId() + if (sessionId && data.navigateToSession) { + data.navigateToSession(sessionId) + } + } + const renderChildToolPart = () => { const toolData = childToolPart() if (!toolData) return null @@ -797,6 +804,7 @@ ToolRegistry.register({ titleClass: "capitalize", subtitle: props.input.description, }} + onSubtitleClick={handleSubtitleClick} /> } > @@ -826,6 +834,7 @@ ToolRegistry.register({ titleClass: "capitalize", subtitle: props.input.description, }} + onSubtitleClick={handleSubtitleClick} > <div ref={autoScroll.scrollRef} diff --git a/packages/ui/src/context/data.tsx b/packages/ui/src/context/data.tsx index 9f7ec813f..acab99fe8 100644 --- a/packages/ui/src/context/data.tsx +++ b/packages/ui/src/context/data.tsx @@ -30,9 +30,16 @@ export type PermissionRespondFn = (input: { response: "once" | "always" | "reject" }) => void +export type NavigateToSessionFn = (sessionID: string) => void + export const { use: useData, provider: DataProvider } = createSimpleContext({ name: "Data", - init: (props: { data: Data; directory: string; onPermissionRespond?: PermissionRespondFn }) => { + init: (props: { + data: Data + directory: string + onPermissionRespond?: PermissionRespondFn + onNavigateToSession?: NavigateToSessionFn + }) => { return { get store() { return props.data @@ -41,6 +48,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ return props.directory }, respondToPermission: props.onPermissionRespond, + navigateToSession: props.onNavigateToSession, } }, }) |
