Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions frontend/src/components/repo/RepoList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,12 +308,12 @@ export function RepoList() {
switch (true) {
case isLoading && !repos:
return (
<div className="px-0 md:p-4 h-full flex flex-col">
<div className="px-0 md:px-4 md:pt-4 h-full flex flex-col">
<div className="px-2 md:px-0">
<div className="h-10 bg-muted/50 animate-pulse rounded w-full" />
</div>
<div className="mx-2 md:mx-0 flex-1 min-h-0">
<div className="h-full overflow-y-auto pt-4 pb-2 md:pb-0">
<div className="h-full overflow-y-auto pt-4 pb-2 md:pb-4">
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 gap-3 md:gap-4 w-full">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="md:pl-8">
Expand Down Expand Up @@ -388,7 +388,7 @@ export function RepoList() {

return (
<>
<div className="px-0 md:p-4 h-full flex flex-col">
<div className="px-0 md:px-4 md:pt-4 h-full flex flex-col">
<RepoListControls
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
Expand Down Expand Up @@ -416,7 +416,7 @@ export function RepoList() {
/>

<div className="mx-2 md:mx-0 flex-1 min-h-0">
<div className="h-full overflow-y-auto pt-4 md:pb-0 [mask-image:linear-gradient(to_bottom,transparent,black_16px,black)]">
<div className="h-full overflow-y-auto pt-4 md:pb-4 [mask-image:linear-gradient(to_bottom,transparent,black_16px,black)]">
{(() => {
switch (true) {
case sortedViewModels.length === 0:
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/servers/RemoteProjectList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function RemoteProjectListLoading() {
<div className="h-10 bg-muted/50 animate-pulse rounded w-full" />
</div>
<div className="mx-2 md:mx-0 flex-1 min-h-0">
<div className="h-full overflow-y-auto pt-4 pb-2 md:pb-0">
<div className="h-full overflow-y-auto pt-4 pb-2 md:pb-4">
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 gap-3 md:gap-4 w-full">
{Array.from({ length: 6 }).map((_, index) => (
<RepoCardSkeleton key={index} />
Expand Down Expand Up @@ -117,7 +117,7 @@ function RemoteProjectListContent({ serverId }: { serverId: string }) {
const openSettings = () => openTab('servers')

return (
<div className="flex h-full flex-col px-0 md:p-4">
<div className="flex h-full flex-col px-0 md:px-4 md:pt-4">
{!serversLoading && !server ? (
<LinkedServerStateCard
icon={Server}
Expand Down Expand Up @@ -181,7 +181,7 @@ function RemoteProjectListContent({ serverId }: { serverId: string }) {
<OpenDirectoryTrigger onClick={() => setOpenDirectoryOpen(true)} />
</div>
<div className="mx-2 flex min-h-0 flex-1 md:mx-0">
<div className="h-full w-full overflow-y-auto pt-4 md:pb-0 [mask-image:linear-gradient(to_bottom,transparent,black_16px,black)]">
<div className="h-full w-full overflow-y-auto pt-4 md:pb-4 [mask-image:linear-gradient(to_bottom,transparent,black_16px,black)]">
{filteredProjects.length === 0 ? (
<div className="text-center p-12">
<Search className="w-12 h-12 mx-auto mb-4 text-muted-foreground" />
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/session/FormPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,10 @@ export function FormPrompt({ form, onReply, onCancel, onMinimize, onAskSideQuest

return (
<div
className="flex flex-1 min-h-0 flex-col overflow-hidden rounded-t-[22px] border-t border-primary/40 bg-background bg-gradient-to-b from-primary/15 to-card mb-1 md:flex-none md:rounded-xl md:border-2 md:border-primary/40 md:bg-gradient-to-br md:from-primary/10 md:to-primary/20 md:shadow-lg md:shadow-primary/20"
className="flex flex-1 min-h-0 flex-col overflow-hidden rounded-t-[22px] border-t border-primary/40 bg-background bg-gradient-to-b from-primary/15 to-card mb-1 md:flex-initial md:rounded-xl md:border-2 md:border-primary/40 md:bg-gradient-to-br md:from-primary/10 md:to-primary/20 md:shadow-lg md:shadow-primary/20"
style={isMobile ? swipeStyles : undefined}
>
<div ref={mobileHeaderRef} className="shrink-0 md:hidden">
<div ref={mobileHeaderRef} className="shrink-0 border-b border-primary/25 bg-primary/10 md:hidden">
<button
type="button"
onClick={handleMinimize}
Expand All @@ -349,7 +349,7 @@ export function FormPrompt({ form, onReply, onCancel, onMinimize, onAskSideQuest
>
Dismiss
</button>
<span className="min-w-0 flex-1 truncate text-center text-[13px] font-semibold uppercase tracking-wide text-muted-foreground">
<span className="min-w-0 flex-1 truncate text-center text-[13px] font-semibold uppercase tracking-wide text-primary">
{form.title || 'Form'}
</span>
<button
Expand Down Expand Up @@ -387,7 +387,7 @@ export function FormPrompt({ form, onReply, onCancel, onMinimize, onAskSideQuest
</div>

{!isMinimized && (
<div className="flex-1 min-h-0 overflow-y-auto overflow-x-hidden overscroll-contain px-4 pt-2 pb-6 space-y-7 md:flex-none md:p-3 md:max-h-[70vh] md:space-y-3 md:bg-background/60 dark:md:bg-background/30">
<div className="flex-1 min-h-0 overflow-y-auto overflow-x-hidden overscroll-contain px-4 pt-4 pb-6 space-y-7 bg-background/60 dark:bg-background/30 md:flex-initial md:p-3 md:max-h-[70vh] md:space-y-3">
{renderFields.map((field) =>
field.type === 'external' ? (
<ExternalFieldCard key={field.key} field={field} />
Expand Down
15 changes: 9 additions & 6 deletions frontend/src/pages/SessionDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@ export function SessionDetail() {
const { getForSession: getFormForSession, reply: replyToForm, cancel: cancelForm, syncForSession: syncFormsForSession } = useForms();
const currentForm = sessionId ? getFormForSession(sessionId) : null;
const minimizedForm = currentForm && currentForm.id === minimizedFormId ? currentForm : null;
const isFormSheetOpen = isMobile && !!currentForm && !minimizedForm;
const isFormOpen = !!currentForm && !minimizedForm;
const isFormSheetOpen = isMobile && isFormOpen;

const lastAssistantMessage = messages.filter(m => m.type === 'assistant').at(-1);
const lastAssistantText = getAssistantText(lastAssistantMessage);
Expand Down Expand Up @@ -876,18 +877,20 @@ export function SessionDetail() {
</div>
{sessionDirectory && !isEditingMessage && (
<div
ref={promptOverlayRef}
data-testid="prompt-overlay"
className={cn(
"absolute left-0 right-0 flex justify-center",
isFormSheetOpen && "top-0 flex-col items-center justify-start",
isFormOpen && "top-0 flex-col items-center justify-end pointer-events-none md:pt-3",
)}
style={{ bottom: inputBottomOffset }}
>
<div
ref={promptOverlayRef}
className={cn(
"relative",
isFormSheetOpen ? "flex w-full flex-1 min-h-0 flex-col" : "w-[94%] md:max-w-4xl",
"relative pointer-events-auto",
isFormSheetOpen
? "flex w-full flex-1 min-h-0 flex-col"
: cn("w-[94%] md:max-w-4xl", isFormOpen && "flex max-h-full min-h-0 flex-col"),
)}
>
{!isFormSheetOpen && (
Expand Down Expand Up @@ -945,7 +948,7 @@ export function SessionDetail() {
onAskSideQuestion={() => handleAskSideQuestion('')}
/>
)}
<div className={cn(isFormSheetOpen && "shrink-0 px-[3%]")}>
<div className={cn(isFormSheetOpen && "shrink-0 bg-background px-[3%]")}>
<SessionSendErrorBanner sessionId={sessionId} isConnected={streamConnected} isReconnecting={streamReconnecting} />
<SessionGoalBar sessionID={sessionId} />
<BackgroundWorkBar
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/pages/__tests__/SessionDetail.form-prompt.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,18 @@ describe('SessionDetail form prompt session scoping', () => {
expect(screen.getByTestId('prompt-overlay')).toHaveClass('top-0')
})

it('bounds the form card by the space above the prompt on desktop so its header stays visible', async () => {
mocks.useMobile.mockReturnValue(false)
formsState.bySession[VIEWED_SESSION_ID] = viewedSessionForm

render(renderTree())

await waitFor(() => expect(screen.getAllByText('Continue A')[0]).toBeInTheDocument())
const overlay = screen.getByTestId('prompt-overlay')
expect(overlay).toHaveClass('top-0', 'justify-end', 'pointer-events-none')
expect(overlay.firstElementChild).toHaveClass('max-h-full', 'min-h-0', 'pointer-events-auto')
})

it('keeps the prompt overlay anchored without an open form or once the form is minimized', async () => {
mocks.useMobile.mockReturnValue(true)

Expand Down
Loading