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) => (