summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/app/src/components/session/session-header.tsx69
-rw-r--r--packages/app/src/pages/directory-layout.tsx14
-rw-r--r--packages/ui/src/components/basic-tool.css10
-rw-r--r--packages/ui/src/components/basic-tool.tsx8
-rw-r--r--packages/ui/src/components/message-part.tsx9
-rw-r--r--packages/ui/src/context/data.tsx10
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,
}
},
})