Browse Source

feat: add WorkflowBrowserModal and wire up Header open-project button

Header's folder icon now opens the workflow browser modal instead of
the OS directory picker directly. The modal wraps WorkflowBrowserView
in the standard overlay pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 4 months ago
parent
commit
89adec32c2
  1. 44
      src/components/Header.tsx
  2. 36
      src/components/WorkflowBrowserModal.tsx

44
src/components/Header.tsx

@ -1,11 +1,12 @@
"use client";
import { useState, useMemo, useCallback } from "react";
import { useWorkflowStore, WorkflowFile } from "@/store/workflowStore";
import { useWorkflowStore } from "@/store/workflowStore";
import { useShallow } from "zustand/shallow";
import { ProjectSetupModal } from "./ProjectSetupModal";
import { CostIndicator } from "./CostIndicator";
import { KeyboardShortcutsDialog } from "./KeyboardShortcutsDialog";
import { WorkflowBrowserModal } from "./WorkflowBrowserModal";
function CommentsNavigationIcon() {
// Subscribe to nodes so we re-render when comments change
@ -93,6 +94,7 @@ export function Header() {
const [showProjectModal, setShowProjectModal] = useState(false);
const [projectModalMode, setProjectModalMode] = useState<"new" | "settings">("new");
const [showWorkflowBrowser, setShowWorkflowBrowser] = useState(false);
const isProjectConfigured = !!workflowName;
const canSave = !!(workflowId && workflowName && saveDirectoryPath);
@ -114,36 +116,8 @@ export function Header() {
setShowProjectModal(true);
};
const handleOpenFile = async () => {
try {
// Open native OS directory picker
const browseRes = await fetch("/api/browse-directory");
const browseResult = await browseRes.json();
if (!browseResult.success || browseResult.cancelled || !browseResult.path) {
if (!browseResult.success && !browseResult.cancelled) {
alert(browseResult.error || "Failed to open directory picker");
}
return;
}
const dirPath = browseResult.path;
// Load workflow JSON from that directory
const loadRes = await fetch(`/api/workflow?path=${encodeURIComponent(dirPath)}&load=true`);
const loadResult = await loadRes.json();
if (!loadResult.success) {
alert(loadResult.error || "No workflow file found in directory");
return;
}
const workflow = loadResult.workflow as WorkflowFile;
await loadWorkflow(workflow, dirPath);
} catch (error) {
console.error("Failed to open workflow:", error);
alert("Failed to open workflow. Please try again.");
}
const handleOpenFile = () => {
setShowWorkflowBrowser(true);
};
const handleProjectSave = async (id: string, name: string, path: string) => {
@ -230,6 +204,14 @@ export function Header() {
onSave={handleProjectSave}
mode={projectModalMode}
/>
<WorkflowBrowserModal
isOpen={showWorkflowBrowser}
onClose={() => setShowWorkflowBrowser(false)}
onWorkflowLoaded={async (workflow, dirPath) => {
setShowWorkflowBrowser(false);
await loadWorkflow(workflow, dirPath);
}}
/>
<header className="h-11 bg-neutral-900 border-b border-neutral-800 flex items-center justify-between px-4 shrink-0">
<div className="flex items-center gap-2">
<button

36
src/components/WorkflowBrowserModal.tsx

@ -0,0 +1,36 @@
"use client";
import { WorkflowFile } from "@/store/workflowStore";
import { WorkflowBrowserView } from "./quickstart/WorkflowBrowserView";
interface WorkflowBrowserModalProps {
isOpen: boolean;
onClose: () => void;
onWorkflowLoaded: (workflow: WorkflowFile, directoryPath: string) => void;
}
export function WorkflowBrowserModal({
isOpen,
onClose,
onWorkflowLoaded,
}: WorkflowBrowserModalProps) {
if (!isOpen) return null;
return (
<div
className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60"
onWheelCapture={(e) => e.stopPropagation()}
onClick={onClose}
>
<div
className="w-full max-w-2xl mx-4 bg-neutral-800 rounded-xl border border-neutral-700 shadow-2xl overflow-clip max-h-[85vh] flex flex-col"
onClick={(e) => e.stopPropagation()}
>
<WorkflowBrowserView
onWorkflowLoaded={onWorkflowLoaded}
onClose={onClose}
/>
</div>
</div>
);
}
Loading…
Cancel
Save