summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-03-24 13:03:55 -0500
committerAdam <[email protected]>2026-03-24 13:04:32 -0500
commit42a773481e4d50a59784d514d81257330de38ca9 (patch)
tree3faa9617e0f7ff979833ec56427623eb6ac8d944
parent539b01f20fc3677155b3bdbb428c69423a805578 (diff)
downloadopencode-42a773481e4d50a59784d514d81257330de38ca9.tar.gz
opencode-42a773481e4d50a59784d514d81257330de38ca9.zip
fix(app): sidebar truncation
-rw-r--r--packages/app/src/pages/layout/sidebar-items.tsx179
1 files changed, 92 insertions, 87 deletions
diff --git a/packages/app/src/pages/layout/sidebar-items.tsx b/packages/app/src/pages/layout/sidebar-items.tsx
index a9627c5db..75dada05f 100644
--- a/packages/app/src/pages/layout/sidebar-items.tsx
+++ b/packages/app/src/pages/layout/sidebar-items.tsx
@@ -104,7 +104,7 @@ const SessionRow = (props: {
}): JSX.Element => (
<A
href={`/${props.slug}/session/${props.session.id}`}
- class={`flex items-center justify-between gap-3 min-w-0 text-left w-full focus:outline-none transition-[padding] ${props.mobile ? "pr-7" : ""} group-hover/session:pr-7 group-focus-within/session:pr-7 group-active/session:pr-7 ${props.dense ? "py-0.5" : "py-1"}`}
+ class={`flex items-center gap-1 min-w-0 w-full text-left focus:outline-none ${props.dense ? "py-0.5" : "py-1"}`}
onPointerDown={props.warmPress}
onPointerEnter={props.warmHover}
onPointerLeave={props.cancelHoverPrefetch}
@@ -115,30 +115,26 @@ const SessionRow = (props: {
props.clearHoverProjectSoon()
}}
>
- <div class="flex items-center gap-1 w-full">
- <div
- class="shrink-0 size-6 flex items-center justify-center"
- style={{ color: props.tint() ?? "var(--icon-interactive-base)" }}
- >
- <Switch fallback={<Icon name="dash" size="small" class="text-icon-weak" />}>
- <Match when={props.isWorking()}>
- <Spinner class="size-[15px]" />
- </Match>
- <Match when={props.hasPermissions()}>
- <div class="size-1.5 rounded-full bg-surface-warning-strong" />
- </Match>
- <Match when={props.hasError()}>
- <div class="size-1.5 rounded-full bg-text-diff-delete-base" />
- </Match>
- <Match when={props.unseenCount() > 0}>
- <div class="size-1.5 rounded-full bg-text-interactive-base" />
- </Match>
- </Switch>
- </div>
- <span class="text-14-regular text-text-strong grow-1 min-w-0 overflow-hidden text-ellipsis truncate">
- {props.session.title}
- </span>
+ <div
+ class="shrink-0 size-6 flex items-center justify-center"
+ style={{ color: props.tint() ?? "var(--icon-interactive-base)" }}
+ >
+ <Switch fallback={<Icon name="dash" size="small" class="text-icon-weak" />}>
+ <Match when={props.isWorking()}>
+ <Spinner class="size-[15px]" />
+ </Match>
+ <Match when={props.hasPermissions()}>
+ <div class="size-1.5 rounded-full bg-surface-warning-strong" />
+ </Match>
+ <Match when={props.hasError()}>
+ <div class="size-1.5 rounded-full bg-text-diff-delete-base" />
+ </Match>
+ <Match when={props.unseenCount() > 0}>
+ <div class="size-1.5 rounded-full bg-text-interactive-base" />
+ </Match>
+ </Switch>
</div>
+ <span class="text-14-regular text-text-strong min-w-0 flex-1 truncate">{props.session.title}</span>
</A>
)
@@ -167,7 +163,11 @@ const SessionHoverPreview = (props: {
placement="right-start"
gutter={16}
shift={-2}
- trigger={<div ref={ref}>{props.trigger}</div>}
+ trigger={
+ <div ref={ref} class="min-w-0 w-full">
+ {props.trigger}
+ </div>
+ }
open={props.hoverSession() === props.session.id}
onOpenChange={(open) => {
if (!open) {
@@ -309,62 +309,71 @@ export const SessionItem = (props: SessionItemProps): JSX.Element => {
return (
<div
data-session-id={props.session.id}
- class="group/session relative w-full rounded-md cursor-default pl-2 pr-3 transition-colors
+ class="group/session relative w-full min-w-0 rounded-md cursor-default pl-2 pr-3 transition-colors
hover:bg-surface-raised-base-hover [&:has(:focus-visible)]:bg-surface-raised-base-hover has-[[data-expanded]]:bg-surface-raised-base-hover has-[.active]:bg-surface-base-active"
>
- <Show
- when={hoverEnabled()}
- fallback={
- <Tooltip placement={props.mobile ? "bottom" : "right"} value={props.session.title} gutter={10}>
- {item}
- </Tooltip>
- }
- >
- <SessionHoverPreview
- mobile={props.mobile}
- nav={props.nav}
- hoverSession={props.hoverSession}
- session={props.session}
- sidebarHovering={props.sidebarHovering}
- hoverReady={hoverReady}
- hoverMessages={hoverMessages}
- language={language}
- isActive={isActive}
- slug={props.slug}
- setHoverSession={props.setHoverSession}
- messageLabel={messageLabel}
- onMessageSelect={(message) => {
- if (!isActive())
- layout.pendingMessage.set(`${base64Encode(props.session.directory)}/${props.session.id}`, message.id)
+ <div class="flex min-w-0 items-center gap-1">
+ <div class="min-w-0 flex-1">
+ <Show
+ when={hoverEnabled()}
+ fallback={
+ <Tooltip
+ placement={props.mobile ? "bottom" : "right"}
+ value={props.session.title}
+ gutter={10}
+ class="min-w-0 w-full"
+ >
+ {item}
+ </Tooltip>
+ }
+ >
+ <SessionHoverPreview
+ mobile={props.mobile}
+ nav={props.nav}
+ hoverSession={props.hoverSession}
+ session={props.session}
+ sidebarHovering={props.sidebarHovering}
+ hoverReady={hoverReady}
+ hoverMessages={hoverMessages}
+ language={language}
+ isActive={isActive}
+ slug={props.slug}
+ setHoverSession={props.setHoverSession}
+ messageLabel={messageLabel}
+ onMessageSelect={(message) => {
+ if (!isActive())
+ layout.pendingMessage.set(`${base64Encode(props.session.directory)}/${props.session.id}`, message.id)
- navigate(`${props.slug}/session/${props.session.id}#message-${message.id}`)
- }}
- trigger={item}
- />
- </Show>
+ navigate(`${props.slug}/session/${props.session.id}#message-${message.id}`)
+ }}
+ trigger={item}
+ />
+ </Show>
+ </div>
- <div
- class={`absolute ${props.dense ? "top-0.5 right-0.5" : "top-1 right-1"} flex items-center gap-0.5 transition-opacity`}
- classList={{
- "opacity-100 pointer-events-auto": !!props.mobile,
- "opacity-0 pointer-events-none": !props.mobile,
- "group-hover/session:opacity-100 group-hover/session:pointer-events-auto": true,
- "group-focus-within/session:opacity-100 group-focus-within/session:pointer-events-auto": true,
- }}
- >
- <Tooltip value={language.t("common.archive")} placement="top">
- <IconButton
- icon="archive"
- variant="ghost"
- class="size-6 rounded-md"
- aria-label={language.t("common.archive")}
- onClick={(event) => {
- event.preventDefault()
- event.stopPropagation()
- void props.archiveSession(props.session)
- }}
- />
- </Tooltip>
+ <div
+ class="shrink-0 overflow-hidden transition-[width,opacity]"
+ classList={{
+ "w-6 opacity-100 pointer-events-auto": !!props.mobile,
+ "w-0 opacity-0 pointer-events-none": !props.mobile,
+ "group-hover/session:w-6 group-hover/session:opacity-100 group-hover/session:pointer-events-auto": true,
+ "group-focus-within/session:w-6 group-focus-within/session:opacity-100 group-focus-within/session:pointer-events-auto": true,
+ }}
+ >
+ <Tooltip value={language.t("common.archive")} placement="top">
+ <IconButton
+ icon="archive"
+ variant="ghost"
+ class="size-6 rounded-md"
+ aria-label={language.t("common.archive")}
+ onClick={(event) => {
+ event.preventDefault()
+ event.stopPropagation()
+ void props.archiveSession(props.session)
+ }}
+ />
+ </Tooltip>
+ </div>
</div>
</div>
)
@@ -386,30 +395,26 @@ export const NewSessionItem = (props: {
<A
href={`/${props.slug}/session`}
end
- class={`flex items-center justify-between gap-3 min-w-0 text-left w-full focus:outline-none ${props.dense ? "py-0.5" : "py-1"}`}
+ class={`flex items-center gap-1 min-w-0 w-full text-left focus:outline-none ${props.dense ? "py-0.5" : "py-1"}`}
onClick={() => {
props.setHoverSession(undefined)
if (layout.sidebar.opened()) return
props.clearHoverProjectSoon()
}}
>
- <div class="flex items-center gap-1 w-full">
- <div class="shrink-0 size-6 flex items-center justify-center">
- <Icon name="new-session" size="small" class="text-icon-weak" />
- </div>
- <span class="text-14-regular text-text-strong grow-1 min-w-0 overflow-hidden text-ellipsis truncate">
- {label}
- </span>
+ <div class="shrink-0 size-6 flex items-center justify-center">
+ <Icon name="new-session" size="small" class="text-icon-weak" />
</div>
+ <span class="text-14-regular text-text-strong min-w-0 flex-1 truncate">{label}</span>
</A>
)
return (
- <div class="group/session relative w-full rounded-md cursor-default transition-colors pl-2 pr-3 hover:bg-surface-raised-base-hover [&:has(:focus-visible)]:bg-surface-raised-base-hover has-[.active]:bg-surface-base-active">
+ <div class="group/session relative w-full min-w-0 rounded-md cursor-default transition-colors pl-2 pr-3 hover:bg-surface-raised-base-hover [&:has(:focus-visible)]:bg-surface-raised-base-hover has-[.active]:bg-surface-base-active">
<Show
when={!tooltip()}
fallback={
- <Tooltip placement={props.mobile ? "bottom" : "right"} value={label} gutter={10}>
+ <Tooltip placement={props.mobile ? "bottom" : "right"} value={label} gutter={10} class="min-w-0 w-full">
{item}
</Tooltip>
}