diff --git a/apps/web/src/components/common/review-panel.tsx b/apps/web/src/components/common/review-panel.tsx index a0c38f76..b0bde0f7 100644 --- a/apps/web/src/components/common/review-panel.tsx +++ b/apps/web/src/components/common/review-panel.tsx @@ -1,7 +1,15 @@ import { TOOLS } from "@snapotter/shared"; -import { ArrowRight, ChevronDown, ChevronRight, Download, FileImage, Undo2 } from "lucide-react"; +import { + ArrowRight, + ChevronDown, + ChevronRight, + Download, + FileImage, + PenTool, + Undo2, +} from "lucide-react"; import { useMemo, useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { formatFileSize, triggerDownload } from "@/lib/download"; import { ICON_MAP } from "@/lib/icon-map"; import { getSuggestedTools } from "@/lib/suggested-tools"; @@ -103,6 +111,15 @@ export function ReviewPanel({ + {/* Open in Editor */} + + + Open in Editor + + {/* Suggested tools */} {suggestedTools.length > 0 && (
diff --git a/apps/web/src/components/editor/common/loading-overlay.tsx b/apps/web/src/components/editor/common/loading-overlay.tsx new file mode 100644 index 00000000..21faa990 --- /dev/null +++ b/apps/web/src/components/editor/common/loading-overlay.tsx @@ -0,0 +1,55 @@ +// apps/web/src/components/editor/common/loading-overlay.tsx +import { X } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { useEditorStore } from "@/stores/editor-store"; + +export function LoadingOverlay() { + const loadingState = useEditorStore((s) => s.loadingState); + const setLoadingState = useEditorStore((s) => s.setLoadingState); + + if (!loadingState) return null; + + return ( +
+
+ {/* Operation name */} +

{loadingState.operation}

+ + {/* Progress bar */} + {loadingState.progress !== null && ( +
+
= 0 + ? { width: `${Math.min(100, loadingState.progress)}%` } + : undefined + } + /> +
+ )} + + {/* Indeterminate spinner when no progress */} + {loadingState.progress === null && ( +
+ )} + + {/* Cancel button */} + {loadingState.cancellable && ( + + )} +
+
+ ); +} diff --git a/apps/web/src/components/editor/editor-canvas.tsx b/apps/web/src/components/editor/editor-canvas.tsx index bbf86fbf..11ae7dfb 100644 --- a/apps/web/src/components/editor/editor-canvas.tsx +++ b/apps/web/src/components/editor/editor-canvas.tsx @@ -21,6 +21,7 @@ import { useEditorShortcuts } from "@/hooks/use-editor-shortcuts"; import { useEditorStore } from "@/stores/editor-store"; import type { CanvasObject } from "@/types/editor"; import { BrushCursorOverlay, useEditorCursor } from "./common/custom-cursor"; +import { LoadingOverlay } from "./common/loading-overlay"; import { useBrushTool } from "./tools/brush-tool"; import { useEraserTool } from "./tools/eraser-tool"; import { useFillTool } from "./tools/fill-tool"; @@ -391,6 +392,7 @@ export function EditorCanvas() { )} +
); } diff --git a/apps/web/src/components/editor/editor-right-panel.tsx b/apps/web/src/components/editor/editor-right-panel.tsx index 1d36c51d..7b307955 100644 --- a/apps/web/src/components/editor/editor-right-panel.tsx +++ b/apps/web/src/components/editor/editor-right-panel.tsx @@ -40,11 +40,13 @@ export function EditorRightPanel() { {sourceImageUrl && } {/* Tabs */} -
+
{TABS.map((tab) => (