feat: enable diff and preview panels on smaller screens (#1524)

On screens < 1280px, users can now access Preview and Diffs views
via toggle buttons in the header, creating a tab-like experience.

Changes:
- Remove isXL check from AttemptHeaderActions toggle buttons
- Simplify mobile layout to switch between attempt/aux content based on mode
- Keyboard shortcuts (Cmd/Ctrl+Enter) now cycle through views on mobile

Closes #1523
This commit is contained in:
Stephan Fitzpatrick
2025-12-17 04:02:01 -08:00
committed by GitHub
parent 4e158df3d0
commit 20352f2893
2 changed files with 19 additions and 47 deletions

View File

@@ -279,53 +279,27 @@ export function TasksLayout({
const desktopKey = isPanelOpen ? 'desktop-with-panel' : 'kanban-only'; const desktopKey = isPanelOpen ? 'desktop-with-panel' : 'kanban-only';
if (isMobile) { if (isMobile) {
const columns = isPanelOpen ? ['0fr', '1fr', '0fr'] : ['1fr', '0fr', '0fr']; // When panel is open and mode is set, show aux content (preview/diffs)
const gridTemplateColumns = `minmax(0, ${columns[0]}) minmax(0, ${columns[1]}) minmax(0, ${columns[2]})`; // Otherwise show attempt content
const isKanbanVisible = columns[0] !== '0fr'; const showAux = isPanelOpen && mode !== null;
const isAttemptVisible = columns[1] !== '0fr';
const isAuxVisible = columns[2] !== '0fr';
return ( return (
<div <div className="h-full min-h-0 flex flex-col">
className="h-full min-h-0 grid" {/* Header is visible when panel is open */}
style={{ {isPanelOpen && rightHeader && (
gridTemplateColumns, <div className="shrink-0 sticky top-0 z-20 bg-background border-b">
transition: 'grid-template-columns 250ms cubic-bezier(0.2, 0, 0, 1)', {rightHeader}
}} </div>
> )}
<div
className="min-w-0 min-h-0 overflow-hidden"
aria-hidden={!isKanbanVisible}
aria-label="Kanban board"
role="region"
style={{ pointerEvents: isKanbanVisible ? 'auto' : 'none' }}
>
{kanban}
</div>
<div <div className="flex-1 min-h-0">
className="min-w-0 min-h-0 overflow-hidden border-l flex flex-col" {!isPanelOpen ? (
aria-hidden={!isAttemptVisible} kanban
aria-label="Details" ) : showAux ? (
role="region" <AuxRouter mode={mode} aux={aux} />
style={{ pointerEvents: isAttemptVisible ? 'auto' : 'none' }} ) : (
> attempt
{rightHeader && (
<div className="shrink-0 sticky top-0 z-20 bg-background border-b">
{rightHeader}
</div>
)} )}
<div className="flex-1 min-h-0">{attempt}</div>
</div>
<div
className="min-w-0 min-h-0 overflow-hidden border-l"
aria-hidden={!isAuxVisible}
aria-label={mode === 'preview' ? 'Preview' : 'Diffs'}
role="region"
style={{ pointerEvents: isAuxVisible ? 'auto' : 'none' }}
>
{aux}
</div> </div>
</div> </div>
); );

View File

@@ -12,7 +12,6 @@ import type { LayoutMode } from '../layout/TasksLayout';
import type { TaskAttempt, TaskWithAttemptStatus } from 'shared/types'; import type { TaskAttempt, TaskWithAttemptStatus } from 'shared/types';
import { ActionsDropdown } from '../ui/actions-dropdown'; import { ActionsDropdown } from '../ui/actions-dropdown';
import { usePostHog } from 'posthog-js/react'; import { usePostHog } from 'posthog-js/react';
import { useMediaQuery } from '@/hooks/useMediaQuery';
import type { SharedTaskRecord } from '@/hooks/useProjectTasks'; import type { SharedTaskRecord } from '@/hooks/useProjectTasks';
interface AttemptHeaderActionsProps { interface AttemptHeaderActionsProps {
@@ -34,11 +33,10 @@ export const AttemptHeaderActions = ({
}: AttemptHeaderActionsProps) => { }: AttemptHeaderActionsProps) => {
const { t } = useTranslation('tasks'); const { t } = useTranslation('tasks');
const posthog = usePostHog(); const posthog = usePostHog();
const isXL = useMediaQuery('(min-width: 1280px)');
return ( return (
<> <>
{typeof mode !== 'undefined' && onModeChange && isXL && ( {typeof mode !== 'undefined' && onModeChange && (
<TooltipProvider> <TooltipProvider>
<ToggleGroup <ToggleGroup
type="single" type="single"
@@ -106,7 +104,7 @@ export const AttemptHeaderActions = ({
</ToggleGroup> </ToggleGroup>
</TooltipProvider> </TooltipProvider>
)} )}
{typeof mode !== 'undefined' && onModeChange && isXL && ( {typeof mode !== 'undefined' && onModeChange && (
<div className="h-4 w-px bg-border" /> <div className="h-4 w-px bg-border" />
)} )}
<ActionsDropdown task={task} attempt={attempt} sharedTask={sharedTask} /> <ActionsDropdown task={task} attempt={attempt} sharedTask={sharedTask} />