feat(walkthrough): stabilize navigation footer (#2002)

This commit is contained in:
Carlos Florêncio
2026-07-28 16:38:56 +01:00
committed by GitHub
parent adecad7f30
commit e9bb4e20e2
13 changed files with 471 additions and 19 deletions
@@ -236,13 +236,13 @@ export function WalkthroughFloatingWindow({
data-testid="walkthrough-floating"
data-mobile-variant="bottom-sheet"
className={cn(
"fixed inset-x-2 bottom-[calc(0.5rem+var(--app-status-bar-height))] z-[43] max-h-[calc(100dvh-1rem)] overflow-y-auto rounded-xl",
"fixed inset-x-2 bottom-[calc(0.5rem+var(--app-status-bar-height)+env(safe-area-inset-bottom,0px))] z-[43] max-h-[calc(100dvh-1rem-env(safe-area-inset-bottom,0px))] overflow-hidden rounded-xl",
"sm:inset-x-auto sm:bottom-auto sm:w-[400px] sm:max-w-[calc(100vw-2rem)]",
)}
style={isDesktop && position ? { left: position.x, top: position.y } : undefined}
onPointerDown={onPointerDown}
>
<WalkthroughStepInner onClose={onClose} onSelectFile={openFile} />
<WalkthroughStepInner onClose={onClose} onSelectFile={openFile} layout="floating" />
</div>
</>
);
@@ -20,6 +20,7 @@ import {
import type { TaskWalkthrough, WalkthroughStep } from "@/lib/types/http";
import type { WalkthroughComment } from "@/lib/state/slices/comments";
import { CommentForm } from "./comment-form";
import { cn } from "@kandev/ui/lib/utils";
export const WALKTHROUGH_STEP_BODY_CLASS =
"prose prose-sm dark:prose-invert max-w-none text-left text-sm leading-6 [overflow-wrap:anywhere] [text-align:left] [&_li]:text-left [&_p]:my-2 [&_p]:text-left";
@@ -103,7 +104,10 @@ function StepNavigation({
onNext: () => void;
}) {
return (
<div className="flex items-center gap-2 px-4 py-2 border-t border-border">
<div
className="flex shrink-0 items-center gap-2 border-t border-border px-4 py-2"
data-testid="walkthrough-navigation"
>
<Button
variant="outline"
size="sm"
@@ -205,9 +209,11 @@ function useWalkthroughStepFeedback(params: {
export function WalkthroughStepInner({
onClose,
onSelectFile,
layout = "inline",
}: {
onClose: () => void;
onSelectFile?: OpenFileFn;
layout?: "inline" | "floating";
}) {
const activeTaskId = useAppStore((s) => s.tasks.activeTaskId);
const sessionId = useAppStore((s) => s.tasks.activeSessionId);
@@ -236,10 +242,25 @@ export function WalkthroughStepInner({
}, [activeTaskId, walkthrough, markSeen]);
if (!activeTaskId || !walkthrough) return null;
if (!step) return null;
const isFloating = layout === "floating";
const lineLabel = step.line_end ? `Lines ${step.line}–${step.line_end}` : `Line ${step.line}`;
const navigation = (
<StepNavigation
activeStep={activeStep}
stepCount={stepCount}
onPrevious={() => setActiveStep(activeTaskId, activeStep - 1)}
onNext={() => setActiveStep(activeTaskId, activeStep + 1)}
/>
);
return (
<div className="flex max-h-[calc(100dvh-2rem)] flex-col rounded-xl border-l-2 border-primary/60 border border-border bg-card shadow-lg sm:max-h-[min(78vh,720px)]">
<div
className={cn(
"flex max-h-[calc(100dvh-2rem)] flex-col rounded-xl border border-border border-l-2 border-primary/60 bg-card shadow-lg sm:max-h-[min(78vh,720px)]",
isFloating &&
"h-[calc(100dvh-1rem-var(--app-status-bar-height)-env(safe-area-inset-bottom,0px))] overflow-hidden sm:h-[min(78vh,720px)]",
)}
>
<StepHeader
activeStep={activeStep}
stepCount={stepCount}
@@ -250,13 +271,8 @@ export function WalkthroughStepInner({
step={step}
onOpenFile={() => revealFileAtLine(openFile, step.file, step.line, step.repo)}
/>
<StepNavigation
activeStep={activeStep}
stepCount={stepCount}
onPrevious={() => setActiveStep(activeTaskId, activeStep - 1)}
onNext={() => setActiveStep(activeTaskId, activeStep + 1)}
/>
<div className="px-4 pb-3 pt-1">
{!isFloating ? navigation : null}
<div className="shrink-0 px-4 pb-3 pt-1">
<CommentForm
key={`${walkthrough.id}:${activeStep}`}
onSubmit={addWalkthroughFeedback}
@@ -265,6 +281,7 @@ export function WalkthroughStepInner({
autoFocus={false}
/>
</div>
{isFloating ? navigation : null}
</div>
);
}