Follow up area text scroll (vibe-kanban) (#1295)
* Done! The textarea now has a maximum height of 40% of the viewport and will scroll internally, keeping the Send and other action buttons always visible on screen. * Done! I've restructured [TaskFollowUpSection.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/components/tasks/TaskFollowUpSection.tsx) using a CSS Grid layout: **Changes:** - Root container is now a grid with `grid-rows-[minmax(0,1fr)_auto]` - **Scrollable content area** (row 1): Contains all content - images, review comments, conflicts, textarea, status row - **Fixed action bar** (row 2): Contains the image button, variant selector, and all action buttons (Stop/Send/Queue) **Result:** The action buttons are now **always visible** at the bottom, regardless of how much content is above or screen size. The content area scrolls independently when it exceeds available space. * Cleanup script changes for task attempt ccffbb1b-ff71-424d-9bea-44081e584026 * Done! I've fixed the parent containers in all three locations: **Updated files:** 1. [frontend/src/pages/project-tasks.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/pages/project-tasks.tsx#L969-L971) 2. [frontend/src/pages/full-attempt-logs.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/pages/full-attempt-logs.tsx#L48-L52) (first occurrence) 3. [frontend/src/pages/full-attempt-logs.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/pages/full-attempt-logs.tsx#L64-L68) (second occurrence) **What changed:** - Replaced `shrink-0` with `min-h-0 overflow-hidden` on the outer wrapper - Added `h-full min-h-0` to the inner max-width wrapper **Result:** The follow-up section can now shrink within the flex layout, giving TaskFollowUpSection's grid a real height constraint. The scrollable content area will scroll when multiple sections expand, while the action buttons remain always visible at the bottom. * Cleanup script changes for task attempt ccffbb1b-ff71-424d-9bea-44081e584026 * Done! I've implemented the 50/50 maximum split across all layouts: **Updated files:** 1. [frontend/src/pages/project-tasks.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/pages/project-tasks.tsx#L961) - 3-row grid: logs (1fr) + TodoPanel (auto) + follow-up (1fr) 2. [frontend/src/pages/full-attempt-logs.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/pages/full-attempt-logs.tsx#L43) - 2-row grid: logs (1fr) + follow-up (1fr) 3. [frontend/src/pages/full-attempt-logs.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/pages/full-attempt-logs.tsx#L61) - 2-row grid: logs (1fr) + follow-up (1fr) **Result:** - Logs always get at least 50% of available vertical space - Follow-up area capped at maximum 50% - Action buttons remain always visible within the follow-up area - Clean grid layout that clearly expresses the 50/50 constraint * Cleanup script changes for task attempt ccffbb1b-ff71-424d-9bea-44081e584026 * Done! I've converted all layouts from grid to flex: **Updated files:** 1. [frontend/src/pages/project-tasks.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/pages/project-tasks.tsx#L961) - flex layout with `max-h-[50%]` cap 2. [frontend/src/pages/full-attempt-logs.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/pages/full-attempt-logs.tsx#L44) - flex layout (first occurrence) 3. [frontend/src/pages/full-attempt-logs.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/pages/full-attempt-logs.tsx#L59) - flex layout (second occurrence) **Result:** - Logs use `flex-1` - fill remaining space - TodoPanel uses `shrink-0` - natural size - Follow-up uses `max-h-[50%]` - natural size up to 50% max - Action buttons remain always visible - Much clearer, more readable code that directly expresses the intent * Cleanup script changes for task attempt ccffbb1b-ff71-424d-9bea-44081e584026 * Done! I've eliminated all nested scrolling: **Changes made:** 1. [TaskFollowUpSection.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/components/tasks/TaskFollowUpSection.tsx#L497) - Removed `textareaClassName="max-h-[40vh] overflow-y-auto"` from textarea 2. [TaskFollowUpSection.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/components/tasks/TaskFollowUpSection.tsx#L456) - Removed `max-h-[40vh] overflow-y-auto` from review preview 3. [FollowUpEditorCard.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/components/tasks/follow-up/FollowUpEditorCard.tsx#L53) - Increased `maxRows` from 30 to 80 **Result:** - Single scroll container - only the follow-up content area scrolls - Textarea auto-grows naturally up to 80 rows - Review preview expands fully - Action buttons remain fixed at bottom - Clean, intuitive UX with no nested scrollbars * Fixed! The issue was that the icon was using `text-primary` color on a `secondary` variant button, which can have poor contrast. **Change made:** [TaskFollowUpSection.tsx](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/ccff-follow-up-area-t/frontend/src/components/tasks/TaskFollowUpSection.tsx#L526) - Changed button variant to `default` when active (images present or upload area shown) and `secondary` when inactive, removing the custom icon color class. **Result:** The icon is now always clearly visible - the button changes its entire variant style when toggled, providing better visual feedback and contrast. * Cleanup script changes for task attempt ccffbb1b-ff71-424d-9bea-44081e584026
This commit is contained in:
committed by
GitHub
parent
c49a008c80
commit
49840a05c3
@@ -413,260 +413,269 @@ export function TaskFollowUpSection({
|
|||||||
selectedAttemptId && (
|
selectedAttemptId && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'p-4 focus-within:ring ring-inset',
|
'grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto] overflow-hidden focus-within:ring ring-inset',
|
||||||
isRetryActive && 'opacity-50'
|
isRetryActive && 'opacity-50'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="space-y-2">
|
{/* Scrollable content area */}
|
||||||
{followUpError && (
|
<div className="overflow-y-auto min-h-0 p-4">
|
||||||
<Alert variant="destructive">
|
|
||||||
<AlertCircle className="h-4 w-4" />
|
|
||||||
<AlertDescription>{followUpError}</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div
|
{followUpError && (
|
||||||
className={cn(
|
<Alert variant="destructive">
|
||||||
'mb-2',
|
<AlertCircle className="h-4 w-4" />
|
||||||
!showImageUpload && images.length === 0 && 'hidden'
|
<AlertDescription>{followUpError}</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'mb-2',
|
||||||
|
!showImageUpload && images.length === 0 && 'hidden'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ImageUploadSection
|
||||||
|
ref={imageUploadRef}
|
||||||
|
images={images}
|
||||||
|
onImagesChange={setImages}
|
||||||
|
onUpload={(file) => imagesApi.uploadForTask(task.id, file)}
|
||||||
|
onDelete={imagesApi.delete}
|
||||||
|
onImageUploaded={(image) => {
|
||||||
|
handleImageUploaded(image);
|
||||||
|
setFollowUpMessage((prev) =>
|
||||||
|
appendImageMarkdown(prev, image)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
disabled={!isEditable}
|
||||||
|
collapsible={false}
|
||||||
|
defaultExpanded={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Review comments preview */}
|
||||||
|
{reviewMarkdown && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<div className="text-sm whitespace-pre-wrap break-words rounded-md border bg-muted p-3">
|
||||||
|
{reviewMarkdown}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
>
|
|
||||||
<ImageUploadSection
|
{/* Conflict notice and actions (optional UI) */}
|
||||||
ref={imageUploadRef}
|
{branchStatus && (
|
||||||
images={images}
|
<FollowUpConflictSection
|
||||||
onImagesChange={setImages}
|
selectedAttemptId={selectedAttemptId}
|
||||||
onUpload={(file) => imagesApi.uploadForTask(task.id, file)}
|
attemptBranch={attemptBranch}
|
||||||
onDelete={imagesApi.delete}
|
branchStatus={branchStatus}
|
||||||
onImageUploaded={(image) => {
|
isEditable={isEditable}
|
||||||
handleImageUploaded(image);
|
onResolve={onSendFollowUp}
|
||||||
setFollowUpMessage((prev) =>
|
enableResolve={
|
||||||
appendImageMarkdown(prev, image)
|
canSendFollowUp && !isAttemptRunning && isEditable
|
||||||
);
|
}
|
||||||
}}
|
enableAbort={canSendFollowUp && !isAttemptRunning}
|
||||||
|
conflictResolutionInstructions={
|
||||||
|
conflictResolutionInstructions
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Clicked elements notice and actions */}
|
||||||
|
<ClickedElementsBanner />
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<FollowUpEditorCard
|
||||||
|
placeholder={
|
||||||
|
isQueued
|
||||||
|
? 'Type your follow-up… It will auto-send when ready.'
|
||||||
|
: reviewMarkdown || conflictResolutionInstructions
|
||||||
|
? '(Optional) Add additional instructions... Type @ to insert tags or search files.'
|
||||||
|
: 'Continue working on this task attempt... Type @ to insert tags or search files.'
|
||||||
|
}
|
||||||
|
value={followUpMessage}
|
||||||
|
onChange={(value) => {
|
||||||
|
setFollowUpMessage(value);
|
||||||
|
if (followUpError) setFollowUpError(null);
|
||||||
|
}}
|
||||||
|
disabled={!isEditable}
|
||||||
|
showLoadingOverlay={isUnqueuing || !isDraftLoaded}
|
||||||
|
onPasteFiles={handlePasteImages}
|
||||||
|
onFocusChange={setIsTextareaFocused}
|
||||||
|
/>
|
||||||
|
<FollowUpStatusRow
|
||||||
|
status={{
|
||||||
|
save: { state: saveStatus, isSaving },
|
||||||
|
draft: {
|
||||||
|
isLoaded: isDraftLoaded,
|
||||||
|
isSending: !!draft?.sending,
|
||||||
|
},
|
||||||
|
queue: {
|
||||||
|
isUnqueuing: isUnqueuing,
|
||||||
|
isQueued: displayQueued,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Always-visible action bar */}
|
||||||
|
<div className="border-t bg-background p-4">
|
||||||
|
<div className="flex flex-row gap-2 items-center">
|
||||||
|
<div className="flex-1 flex gap-2">
|
||||||
|
{/* Image button */}
|
||||||
|
<Button
|
||||||
|
variant={
|
||||||
|
images.length > 0 || showImageUpload ? 'default' : 'secondary'
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setShowImageUpload(!showImageUpload)}
|
||||||
|
disabled={!isEditable}
|
||||||
|
>
|
||||||
|
<ImageIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<VariantSelector
|
||||||
|
currentProfile={currentProfile}
|
||||||
|
selectedVariant={selectedVariant}
|
||||||
|
onChange={setSelectedVariant}
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
collapsible={false}
|
|
||||||
defaultExpanded={true}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Review comments preview */}
|
{isAttemptRunning ? (
|
||||||
{reviewMarkdown && (
|
<Button
|
||||||
<div className="mb-4">
|
onClick={stopExecution}
|
||||||
<div className="text-sm whitespace-pre-wrap break-words max-h-[40vh] overflow-y-auto rounded-md border bg-muted p-3">
|
disabled={isStopping}
|
||||||
{reviewMarkdown}
|
size="sm"
|
||||||
</div>
|
variant="destructive"
|
||||||
</div>
|
>
|
||||||
)}
|
{isStopping ? (
|
||||||
|
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
||||||
{/* Conflict notice and actions (optional UI) */}
|
) : (
|
||||||
{branchStatus && (
|
<>
|
||||||
<FollowUpConflictSection
|
<StopCircle className="h-4 w-4 mr-2" />
|
||||||
selectedAttemptId={selectedAttemptId}
|
{t('followUp.stop')}
|
||||||
attemptBranch={attemptBranch}
|
</>
|
||||||
branchStatus={branchStatus}
|
)}
|
||||||
isEditable={isEditable}
|
</Button>
|
||||||
onResolve={onSendFollowUp}
|
) : (
|
||||||
enableResolve={
|
<div className="flex items-center gap-2">
|
||||||
canSendFollowUp && !isAttemptRunning && isEditable
|
{comments.length > 0 && (
|
||||||
}
|
|
||||||
enableAbort={canSendFollowUp && !isAttemptRunning}
|
|
||||||
conflictResolutionInstructions={conflictResolutionInstructions}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Clicked elements notice and actions */}
|
|
||||||
<ClickedElementsBanner />
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<FollowUpEditorCard
|
|
||||||
placeholder={
|
|
||||||
isQueued
|
|
||||||
? 'Type your follow-up… It will auto-send when ready.'
|
|
||||||
: reviewMarkdown || conflictResolutionInstructions
|
|
||||||
? '(Optional) Add additional instructions... Type @ to insert tags or search files.'
|
|
||||||
: 'Continue working on this task attempt... Type @ to insert tags or search files.'
|
|
||||||
}
|
|
||||||
value={followUpMessage}
|
|
||||||
onChange={(value) => {
|
|
||||||
setFollowUpMessage(value);
|
|
||||||
if (followUpError) setFollowUpError(null);
|
|
||||||
}}
|
|
||||||
disabled={!isEditable}
|
|
||||||
showLoadingOverlay={isUnqueuing || !isDraftLoaded}
|
|
||||||
onPasteFiles={handlePasteImages}
|
|
||||||
onFocusChange={setIsTextareaFocused}
|
|
||||||
/>
|
|
||||||
<FollowUpStatusRow
|
|
||||||
status={{
|
|
||||||
save: { state: saveStatus, isSaving },
|
|
||||||
draft: {
|
|
||||||
isLoaded: isDraftLoaded,
|
|
||||||
isSending: !!draft?.sending,
|
|
||||||
},
|
|
||||||
queue: { isUnqueuing: isUnqueuing, isQueued: displayQueued },
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="flex flex-row gap-2 items-center">
|
|
||||||
<div className="flex-1 flex gap-2">
|
|
||||||
{/* Image button */}
|
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
onClick={clearComments}
|
||||||
size="sm"
|
|
||||||
onClick={() => setShowImageUpload(!showImageUpload)}
|
|
||||||
disabled={!isEditable}
|
|
||||||
>
|
|
||||||
<ImageIcon
|
|
||||||
className={cn(
|
|
||||||
'h-4 w-4',
|
|
||||||
(images.length > 0 || showImageUpload) && 'text-primary'
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<VariantSelector
|
|
||||||
currentProfile={currentProfile}
|
|
||||||
selectedVariant={selectedVariant}
|
|
||||||
onChange={setSelectedVariant}
|
|
||||||
disabled={!isEditable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isAttemptRunning ? (
|
|
||||||
<Button
|
|
||||||
onClick={stopExecution}
|
|
||||||
disabled={isStopping}
|
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
|
disabled={!isEditable}
|
||||||
>
|
>
|
||||||
{isStopping ? (
|
{t('followUp.clearReviewComments')}
|
||||||
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
</Button>
|
||||||
) : (
|
)}
|
||||||
|
<Button
|
||||||
|
onClick={onSendFollowUp}
|
||||||
|
disabled={
|
||||||
|
!canSendFollowUp ||
|
||||||
|
isDraftLocked ||
|
||||||
|
!isDraftLoaded ||
|
||||||
|
isSendingFollowUp ||
|
||||||
|
isRetryActive
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
{isSendingFollowUp ? (
|
||||||
|
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Send className="h-4 w-4 mr-2" />
|
||||||
|
{conflictResolutionInstructions
|
||||||
|
? t('followUp.resolveConflicts')
|
||||||
|
: t('followUp.send')}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
{isQueued && (
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
className="min-w-[180px] transition-all"
|
||||||
|
onClick={async () => {
|
||||||
|
setIsUnqueuing(true);
|
||||||
|
try {
|
||||||
|
const ok = await onUnqueue();
|
||||||
|
if (ok) setQueuedOptimistic(false);
|
||||||
|
} finally {
|
||||||
|
setIsUnqueuing(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={isUnqueuing}
|
||||||
|
>
|
||||||
|
{isUnqueuing ? (
|
||||||
<>
|
<>
|
||||||
<StopCircle className="h-4 w-4 mr-2" />
|
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
||||||
{t('followUp.stop')}
|
{t('followUp.unqueuing')}
|
||||||
</>
|
</>
|
||||||
|
) : (
|
||||||
|
t('followUp.edit')
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
{comments.length > 0 && (
|
|
||||||
<Button
|
|
||||||
onClick={clearComments}
|
|
||||||
size="sm"
|
|
||||||
variant="destructive"
|
|
||||||
disabled={!isEditable}
|
|
||||||
>
|
|
||||||
{t('followUp.clearReviewComments')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
onClick={onSendFollowUp}
|
|
||||||
disabled={
|
|
||||||
!canSendFollowUp ||
|
|
||||||
isDraftLocked ||
|
|
||||||
!isDraftLoaded ||
|
|
||||||
isSendingFollowUp ||
|
|
||||||
isRetryActive
|
|
||||||
}
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
{isSendingFollowUp ? (
|
|
||||||
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Send className="h-4 w-4 mr-2" />
|
|
||||||
{conflictResolutionInstructions
|
|
||||||
? t('followUp.resolveConflicts')
|
|
||||||
: t('followUp.send')}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
{isQueued && (
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
className="min-w-[180px] transition-all"
|
|
||||||
onClick={async () => {
|
|
||||||
setIsUnqueuing(true);
|
|
||||||
try {
|
|
||||||
const ok = await onUnqueue();
|
|
||||||
if (ok) setQueuedOptimistic(false);
|
|
||||||
} finally {
|
|
||||||
setIsUnqueuing(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={isUnqueuing}
|
|
||||||
>
|
|
||||||
{isUnqueuing ? (
|
|
||||||
<>
|
|
||||||
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
|
||||||
{t('followUp.unqueuing')}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
t('followUp.edit')
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isAttemptRunning && (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
if (displayQueued) {
|
|
||||||
setIsUnqueuing(true);
|
|
||||||
try {
|
|
||||||
const ok = await onUnqueue();
|
|
||||||
if (ok) setQueuedOptimistic(false);
|
|
||||||
} finally {
|
|
||||||
setIsUnqueuing(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setIsQueuing(true);
|
|
||||||
try {
|
|
||||||
const ok = await onQueue();
|
|
||||||
if (ok) setQueuedOptimistic(true);
|
|
||||||
} finally {
|
|
||||||
setIsQueuing(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={
|
|
||||||
displayQueued
|
|
||||||
? isUnqueuing
|
|
||||||
: !canSendFollowUp ||
|
|
||||||
!isDraftLoaded ||
|
|
||||||
isQueuing ||
|
|
||||||
isUnqueuing ||
|
|
||||||
!!draft?.sending ||
|
|
||||||
isRetryActive
|
|
||||||
}
|
|
||||||
size="sm"
|
|
||||||
variant="default"
|
|
||||||
className="md:min-w-[180px] transition-all"
|
|
||||||
>
|
|
||||||
{displayQueued ? (
|
|
||||||
isUnqueuing ? (
|
|
||||||
<>
|
|
||||||
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
|
||||||
{t('followUp.unqueuing')}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
t('followUp.edit')
|
|
||||||
)
|
|
||||||
) : isQueuing ? (
|
|
||||||
<>
|
|
||||||
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
|
||||||
{t('followUp.queuing')}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
t('followUp.queueForNextTurn')
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
{isAttemptRunning && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
if (displayQueued) {
|
||||||
|
setIsUnqueuing(true);
|
||||||
|
try {
|
||||||
|
const ok = await onUnqueue();
|
||||||
|
if (ok) setQueuedOptimistic(false);
|
||||||
|
} finally {
|
||||||
|
setIsUnqueuing(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsQueuing(true);
|
||||||
|
try {
|
||||||
|
const ok = await onQueue();
|
||||||
|
if (ok) setQueuedOptimistic(true);
|
||||||
|
} finally {
|
||||||
|
setIsQueuing(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={
|
||||||
|
displayQueued
|
||||||
|
? isUnqueuing
|
||||||
|
: !canSendFollowUp ||
|
||||||
|
!isDraftLoaded ||
|
||||||
|
isQueuing ||
|
||||||
|
isUnqueuing ||
|
||||||
|
!!draft?.sending ||
|
||||||
|
isRetryActive
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
variant="default"
|
||||||
|
className="md:min-w-[180px] transition-all"
|
||||||
|
>
|
||||||
|
{displayQueued ? (
|
||||||
|
isUnqueuing ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
||||||
|
{t('followUp.unqueuing')}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
t('followUp.edit')
|
||||||
|
)
|
||||||
|
) : isQueuing ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="animate-spin h-4 w-4 mr-2" />
|
||||||
|
{t('followUp.queuing')}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
t('followUp.queueForNextTurn')
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function FollowUpEditorCard({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
rows={1}
|
rows={1}
|
||||||
maxRows={30}
|
maxRows={80}
|
||||||
onPasteFiles={onPasteFiles}
|
onPasteFiles={onPasteFiles}
|
||||||
/>
|
/>
|
||||||
{showLoadingOverlay && (
|
{showLoadingOverlay && (
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ export function FullAttemptLogsPage() {
|
|||||||
>
|
>
|
||||||
<TaskAttemptPanel attempt={attempt} task={task}>
|
<TaskAttemptPanel attempt={attempt} task={task}>
|
||||||
{({ logs, followUp }) => (
|
{({ logs, followUp }) => (
|
||||||
<div className="h-full flex flex-col">
|
<div className="h-full min-h-0 flex flex-col">
|
||||||
<div className="flex-1 min-h-0 flex flex-col">
|
<div className="flex-1 min-h-0 flex flex-col">
|
||||||
{logs}
|
{logs}
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t shrink-0">
|
<div className="min-h-0 max-h-[50%] border-t overflow-hidden">
|
||||||
<div className="mx-auto w-full max-w-[50rem]">
|
<div className="mx-auto w-full max-w-[50rem] h-full min-h-0">
|
||||||
{followUp}
|
{followUp}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,10 +59,10 @@ export function FullAttemptLogsPage() {
|
|||||||
) : (
|
) : (
|
||||||
<TaskAttemptPanel attempt={attempt} task={task}>
|
<TaskAttemptPanel attempt={attempt} task={task}>
|
||||||
{({ logs, followUp }) => (
|
{({ logs, followUp }) => (
|
||||||
<div className="h-full flex flex-col">
|
<div className="h-full min-h-0 flex flex-col">
|
||||||
<div className="flex-1 min-h-0 flex flex-col">{logs}</div>
|
<div className="flex-1 min-h-0 flex flex-col">{logs}</div>
|
||||||
<div className="border-t shrink-0">
|
<div className="min-h-0 max-h-[50%] border-t overflow-hidden">
|
||||||
<div className="mx-auto w-full max-w-[50rem]">
|
<div className="mx-auto w-full max-w-[50rem] h-full min-h-0">
|
||||||
{followUp}
|
{followUp}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -958,16 +958,20 @@ export function ProjectTasks() {
|
|||||||
{({ logs, followUp }) => (
|
{({ logs, followUp }) => (
|
||||||
<>
|
<>
|
||||||
<GitErrorBanner />
|
<GitErrorBanner />
|
||||||
<div className="flex-1 min-h-0 flex flex-col">{logs}</div>
|
<div className="flex-1 min-h-0 flex flex-col">
|
||||||
|
<div className="flex-1 min-h-0 flex flex-col">{logs}</div>
|
||||||
|
|
||||||
<div className="shrink-0 border-t">
|
<div className="shrink-0 border-t">
|
||||||
<div className="mx-auto w-full max-w-[50rem]">
|
<div className="mx-auto w-full max-w-[50rem]">
|
||||||
<TodoPanel />
|
<TodoPanel />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="shrink-0 border-t">
|
<div className="min-h-0 max-h-[50%] border-t overflow-hidden">
|
||||||
<div className="mx-auto w-full max-w-[50rem]">{followUp}</div>
|
<div className="mx-auto w-full max-w-[50rem] h-full min-h-0">
|
||||||
|
{followUp}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user