From befd0f16362678dcd99cd9118cbcb044997c9511 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:05:40 -0600 Subject: [PATCH] feat(app): new session layout --- packages/app/src/pages/layout.tsx | 53 +++++----- packages/app/src/pages/session.tsx | 4 +- .../enterprise/src/routes/share/[shareID].tsx | 4 +- packages/ui/src/components/session-turn.css | 28 ++---- packages/ui/src/components/session-turn.tsx | 96 ++++++++++++------- 5 files changed, 107 insertions(+), 78 deletions(-) diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 2f71570f4..5312ff0a2 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -1296,7 +1296,13 @@ export default function Layout(props: ParentProps) { ) } - const SessionItem = (props: { session: Session; slug: string; mobile?: boolean; dense?: boolean }): JSX.Element => { + const SessionItem = (props: { + session: Session + slug: string + mobile?: boolean + dense?: boolean + popover?: boolean + }): JSX.Element => { const notification = useNotification() const notifications = createMemo(() => notification.session.unseen(props.session.id)) const hasError = createMemo(() => notifications().some((n) => n.type === "error")) @@ -1335,6 +1341,7 @@ export default function Layout(props: ParentProps) { ) const hoverReady = createMemo(() => sessionStore.message[props.session.id] !== undefined) const hoverAllowed = createMemo(() => !props.mobile && layout.sidebar.opened()) + const hoverEnabled = createMemo(() => (props.popover ?? true) && hoverAllowed()) const isActive = createMemo(() => props.session.id === params.id) const messageLabel = (message: Message) => { @@ -1370,23 +1377,14 @@ export default function Layout(props: ParentProps) { - - props.session.title} - onSave={(next) => renameSession(props.session, next)} - class="text-14-regular text-text-strong grow-1 min-w-0 overflow-hidden text-ellipsis truncate" - displayClass="text-14-regular text-text-strong grow-1 min-w-0 overflow-hidden text-ellipsis truncate" - stopPropagation - /> - + props.session.title} + onSave={(next) => renameSession(props.session, next)} + class="text-14-regular text-text-strong grow-1 min-w-0 overflow-hidden text-ellipsis truncate" + displayClass="text-14-regular text-text-strong grow-1 min-w-0 overflow-hidden text-ellipsis truncate" + stopPropagation + /> {(summary) => (
@@ -1396,7 +1394,7 @@ export default function Layout(props: ParentProps) {
- )) + ) return (
+ {item} + + } > Loading messages…
}> @@ -1730,6 +1732,7 @@ export default function Layout(props: ParentProps) { slug={base64Encode(props.project.worktree)} dense mobile={props.mobile} + popover={false} /> )} @@ -1746,7 +1749,13 @@ export default function Layout(props: ParentProps) { {(session) => ( - + )} diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index b1c844f0c..5f282ac85 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -1233,6 +1233,7 @@ export default function Page() { > diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx index d657ddc12..483db4d93 100644 --- a/packages/enterprise/src/routes/share/[shareID].tsx +++ b/packages/enterprise/src/routes/share/[shareID].tsx @@ -295,13 +295,13 @@ export default function () { {(message) => ( setStore("expandedSteps", message.id, (v) => !v)} classes={{ root: "min-w-0 w-full relative", - content: - "flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]", + content: "flex flex-col justify-between !overflow-visible", container: "px-4", }} /> diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css index 1e3cc0b29..f7ab97179 100644 --- a/packages/ui/src/components/session-turn.css +++ b/packages/ui/src/components/session-turn.css @@ -29,23 +29,6 @@ gap: 28px; overflow-anchor: none; - [data-slot="session-turn-user-badges"] { - position: absolute; - right: 0; - display: flex; - gap: 6px; - padding-left: 16px; - background: linear-gradient(to right, transparent, var(--background-stronger) 12px); - opacity: 0; - transition: opacity 0.15s ease; - pointer-events: none; - } - - &:hover [data-slot="session-turn-user-badges"] { - opacity: 1; - pointer-events: auto; - } - [data-slot="session-turn-badge"] { display: inline-flex; align-items: center; @@ -71,7 +54,7 @@ [data-slot="session-turn-response-trigger"] { position: sticky; - top: 32px; + top: calc(var(--sticky-header-height, 0px)); background-color: var(--background-stronger); z-index: 20; width: calc(100% + 9px); @@ -88,10 +71,17 @@ } [data-slot="session-turn-message-content"] { - margin-top: -18px; + margin-top: 0; max-width: 100%; } + [data-slot="session-turn-user-badges"] { + display: flex; + align-items: center; + gap: 6px; + padding-left: 16px; + } + [data-slot="session-turn-message-title"] { width: 100%; font-size: var(--font-size-large); diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index ae1321bac..8b807af82 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -119,6 +119,7 @@ function AssistantMessageItem(props: { export function SessionTurn( props: ParentProps<{ sessionID: string + sessionTitle?: string messageID: string lastUserMessageID?: string stepsExpanded?: boolean @@ -330,7 +331,9 @@ export function SessionTurn( const response = createMemo(() => lastTextPart()?.text) const responsePartId = createMemo(() => lastTextPart()?.id) - const hasDiffs = createMemo(() => message()?.summary?.diffs?.length) + const sessionInfo = createMemo(() => data.store.session.find((item) => item.id === props.sessionID)) + const sessionTitle = createMemo(() => props.sessionTitle ?? sessionInfo()?.title) + const hasDiffs = createMemo(() => (data.store.session_diff?.[props.sessionID]?.length ?? 0) > 0) const hideResponsePart = createMemo(() => !working() && !!responsePartId()) const [responseCopied, setResponseCopied] = createSignal(false) @@ -376,6 +379,7 @@ export function SessionTurn( diffLimit: diffInit, status: rawStatus(), duration: duration(), + titleShown: false, }) createEffect( @@ -389,6 +393,18 @@ export function SessionTurn( ), ) + createEffect(() => { + if (!sessionTitle()) { + setStore("titleShown", false) + return + } + if (store.titleShown) return + const first = allMessages().find((item) => item?.role === "user") + if (!first) return + if (first.id !== props.messageID) return + setStore("titleShown", true) + }) + createEffect(() => { const r = retry() if (!r) { @@ -482,40 +498,53 @@ export function SessionTurn( - {/* Title (sticky) */} -
setStore("stickyTitleRef", el)} data-slot="session-turn-sticky-title"> -
-
- - - - - -

{msg().summary?.title}

-
-
-
-
- - {(msg() as UserMessage).agent} - - - - - {(msg() as UserMessage).model?.modelID} - - - {(msg() as UserMessage).variant || "default"} + +
setStore("stickyTitleRef", el)} data-slot="session-turn-sticky-title"> +
+
+ + + + + +

{sessionTitle()}

+
+
+
-
+ + + +
+ + {(msg() as UserMessage).agent} + + + + + {(msg() as UserMessage).model?.modelID} + + + + {(msg() as UserMessage).variant} + +
+
{/* User Message */}
+ {/* Trigger (sticky) */}
setStore("stickyTriggerRef", el)} data-slot="session-turn-response-trigger"> @@ -612,7 +641,7 @@ export function SessionTurn( setStore("diffsOpen", value) }} > - + {(diff) => ( @@ -658,13 +687,13 @@ export function SessionTurn( )} - store.diffLimit}> + store.diffLimit}>