From 384262e3093082b0530b404d139cf129543113eb Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:34:08 +0000 Subject: [PATCH] I have refactored `ExtractorDialog` to use `DialogFooter` and moved the stage actions. Here is a summary of the changes I made: * I added `DialogFooter` to `ExtractorDialog`. * I moved the action buttons (Start, Cancel, and the finish button) from `AnalyzeStage`, `SelectionStage`, and `RefineStage` to the `ExtractorDialog` footer. * I lifted the `selectedIndices` state from `SelectionStage` to `ExtractorDialog`. * I updated `AnalyzeStage` to show the status in the primary action button. * I enabled cancellation during analysis. * I ensured that `onSuccess` is called only when the user clicks the finish button in the Refine stage. --- src/ui/components/ExtractorDialog.tsx | 116 ++++++++++++++---- src/ui/components/extractor/AnalyzeStage.tsx | 35 +----- src/ui/components/extractor/RefineStage.tsx | 13 +- .../components/extractor/SelectionStage.tsx | 50 ++------ verification/extractor_dialog_analyze.png | Bin 0 -> 61798 bytes verification/extractor_dialog_upload.png | Bin 0 -> 72350 bytes verification/verify_analyze.py | 52 ++++++++ verification/verify_extractor.py | 48 ++++++++ 8 files changed, 204 insertions(+), 110 deletions(-) create mode 100644 verification/extractor_dialog_analyze.png create mode 100644 verification/extractor_dialog_upload.png create mode 100644 verification/verify_analyze.py create mode 100644 verification/verify_extractor.py diff --git a/src/ui/components/ExtractorDialog.tsx b/src/ui/components/ExtractorDialog.tsx index cc41273..e8625a6 100644 --- a/src/ui/components/ExtractorDialog.tsx +++ b/src/ui/components/ExtractorDialog.tsx @@ -1,3 +1,4 @@ +import { Loader2, Play, RefreshCw, Sparkles } from 'lucide-react' import React, { useEffect, useRef, useState } from 'react' import { toast } from 'sonner' import { client } from '../client' @@ -6,7 +7,8 @@ import { AnalyzeStage } from './extractor/AnalyzeStage' import { RefineStage } from './extractor/RefineStage' import { SelectionStage } from './extractor/SelectionStage' import { UploadStage } from './extractor/UploadStage' -import { Dialog, DialogContent, DialogTitle } from './ui/dialog' +import { Button } from './ui/button' +import { Dialog, DialogContent, DialogFooter, DialogTitle } from './ui/dialog' interface ExtractorDialogProps { open: boolean @@ -54,8 +56,6 @@ export const ExtractorDialog: React.FC = ({ useEffect(() => { if (!open) { // Small delay to allow animation to finish if needed, or just reset immediately - // resetting immediately might flash content if closing animation plays - // But for now, we reset on close to ensure fresh state next open const t = setTimeout(resetState, 300) return () => clearTimeout(t) } @@ -105,7 +105,9 @@ export const ExtractorDialog: React.FC = ({ if (event === 'status') { setStatusMessage(data.message) } else if (event === 'complete') { - setCandidates(data.assets as CandidateAsset[]) + const newCandidates = data.assets as CandidateAsset[] + setCandidates(newCandidates) + setSelectedIndices(newCandidates.map((_, i) => i)) setStage('selection') } else if (event === 'error') { throw new Error(data.message) @@ -122,10 +124,16 @@ export const ExtractorDialog: React.FC = ({ } } - const handleRefineConfirm = (indices: number[]) => { - setSelectedIndices(indices) + const toggleSelection = (index: number) => { + setSelectedIndices((prev) => + prev.includes(index) ? prev.filter((i) => i !== index) : [...prev, index], + ) + } + + const handleRefineConfirm = () => { + if (selectedIndices.length === 0) return setStage('refine') - handleRefine(indices) + handleRefine(selectedIndices) } const handleRefine = async (indices: number[]) => { @@ -170,7 +178,6 @@ export const ExtractorDialog: React.FC = ({ setResults((prev) => [...prev, data.asset]) } else if (event === 'complete') { setIsRefineComplete(true) - onSuccess(data.assets) } else if (event === 'error') { throw new Error(data.message) } @@ -182,18 +189,24 @@ export const ExtractorDialog: React.FC = ({ console.error(e) const err = e instanceof Error ? e : new Error(String(e)) toast.error(err.message || 'Refinement failed') - // Don't change stage automatically on error here, let user see what happened or retry? - // For now, staying on refine stage allows them to see partial results. - // But we might want a 'retry' button or back. } } + const handleClose = () => onOpenChange(false) + + const handleDone = () => { + onSuccess(results) + handleClose() + } + + const isAnalyzing = stage === 'analyze' && !!statusMessage + return ( Extractor - {/* Top Navigation / Branding Bar (Optional, matches App feel) */} + {/* Top Navigation / Branding Bar */}

Extractor

@@ -201,23 +214,18 @@ export const ExtractorDialog: React.FC = ({
{/* Content Area */} -
+
{stage === 'upload' && } {stage === 'analyze' && preview && ( - + )} {stage === 'selection' && ( setStage('upload')} + selectedIndices={selectedIndices} + onToggleSelection={toggleSelection} /> )} @@ -226,10 +234,74 @@ export const ExtractorDialog: React.FC = ({ selectedCandidates={candidates.filter((_, i) => selectedIndices.includes(i))} results={results} isComplete={isRefineComplete} - onDone={resetState} /> )}
+ + {/* Footer */} + + {stage === 'upload' && ( + + )} + + {stage === 'analyze' && ( + <> + + + + )} + + {stage === 'selection' && ( + <> + + + + )} + + {stage === 'refine' && ( + <> + {isRefineComplete ? ( + <> + + + + ) : ( + + )} + + )} +
) diff --git a/src/ui/components/extractor/AnalyzeStage.tsx b/src/ui/components/extractor/AnalyzeStage.tsx index 3c1d857..af3a926 100644 --- a/src/ui/components/extractor/AnalyzeStage.tsx +++ b/src/ui/components/extractor/AnalyzeStage.tsx @@ -1,20 +1,11 @@ -import { BrainCircuit, Play } from 'lucide-react' import React from 'react' -import { Button } from '../ui/button' interface AnalyzeStageProps { imageSrc: string - statusMessage: string isAnalyzing: boolean - onStart: () => void } -export const AnalyzeStage: React.FC = ({ - imageSrc, - statusMessage, - isAnalyzing, - onStart, -}) => { +export const AnalyzeStage: React.FC = ({ imageSrc, isAnalyzing }) => { return (
{/* Background Frosted Glass Layer */} @@ -49,30 +40,6 @@ export const AnalyzeStage: React.FC = ({
- - {/* Status Text or Action Button */} -
- {isAnalyzing ? ( -
- - {statusMessage} -
-
-
-
-
-
- ) : ( - - )} -