Browse Source

refactor: 移除欢迎弹窗相关的所有代码和状态

彻底删除了 Quickstart 欢迎弹窗功能,包括对应的 store 状态、国际化文案、测试用例和组件代码,简化了项目代码结构。
feature/group
huangmin 1 month ago
parent
commit
3955996fe8
  1. 5
      src/app/canvas/page.tsx
  2. 11
      src/components/Header.tsx
  3. 41
      src/components/WorkflowCanvas.tsx
  4. 299
      src/components/__tests__/WelcomeModal.test.tsx
  5. 62
      src/components/__tests__/WorkflowCanvas.test.tsx
  6. 94
      src/components/quickstart/WelcomeModal.tsx
  7. 1
      src/components/quickstart/index.ts
  8. 4
      src/i18n/index.tsx
  9. 9
      src/store/workflowStore.ts

5
src/app/canvas/page.tsx

@ -22,7 +22,6 @@ function CanvasPageContent() {
(state) => state.initializeAutoSave
);
const cleanupAutoSave = useWorkflowStore((state) => state.cleanupAutoSave);
const setShowQuickstart = useWorkflowStore((state) => state.setShowQuickstart);
const initializePopiModels = useModelStore((state) => state.initializePopiModels);
const [showFTUX, setShowFTUX] = useState(false);
@ -57,7 +56,6 @@ function CanvasPageContent() {
const isPopiaiEmbed = new URLSearchParams(window.location.search).get("embed") === "popiai";
if (isPopiaiEmbed) {
setShowFTUX(false);
setShowQuickstart(false);
useFTUXStore.getState().resetTutorial();
return;
}
@ -65,7 +63,7 @@ function CanvasPageContent() {
if (!getFTUXCompleted()) {
setShowFTUX(true);
}
}, [setShowQuickstart]);
}, []);
const handleFTUXComplete = () => {
setShowFTUX(false);
@ -75,7 +73,6 @@ function CanvasPageContent() {
const handleStartTutorial = () => {
setShowFTUX(false);
setFTUXCompleted(true);
setShowQuickstart(false); // Close WelcomeModal if open
useFTUXStore.getState().startTutorial();
};

11
src/components/Header.tsx

@ -1,6 +1,7 @@
"use client";
import { useState, useMemo, useCallback, useEffect, type ReactNode } from "react";
import Link from "next/link";
import { Button } from "antd";
import { useWorkflowStore } from "@/store/workflowStore";
import { useCanvasWorkflowStore } from "@/store/canvasWorkflowStore";
@ -106,7 +107,6 @@ export function Header() {
revertToSnapshot,
shortcutsDialogOpen,
setShortcutsDialogOpen,
setShowQuickstart,
} = useWorkflowStore(useShallow((state) => ({
workflowName: state.workflowName,
workflowId: state.workflowId,
@ -120,7 +120,6 @@ export function Header() {
revertToSnapshot: state.revertToSnapshot,
shortcutsDialogOpen: state.shortcutsDialogOpen,
setShortcutsDialogOpen: state.setShortcutsDialogOpen,
setShowQuickstart: state.setShowQuickstart,
})));
const [showProjectModal, setShowProjectModal] = useState(false);
@ -288,16 +287,12 @@ export function Header() {
)}
<header className="h-14 bg-neutral-950/95 border-b border-neutral-900 flex items-center justify-between px-5 shrink-0">
<div className="flex items-center gap-2">
<button
onClick={() => setShowQuickstart(true)}
className="flex items-center gap-2 hover:opacity-80 transition-opacity"
title={t("header.openWelcome")}
>
<Link href="/" className="flex items-center gap-2 transition-opacity hover:opacity-80">
<img src="/banana_icon.png" alt="" aria-hidden="true" className="w-6 h-6" />
<h1 className="text-xl font-semibold text-neutral-100 tracking-tight">
Popi.TV
</h1>
</button>
</Link>
<div className="flex items-center gap-2 ml-4 pl-4 border-l border-neutral-700">
{isProjectConfigured ? (

41
src/components/WorkflowCanvas.tsx

@ -88,8 +88,6 @@ async function readImageDimensions(src: string): Promise<{ width: number; height
img.src = src;
});
}
import { WelcomeModal } from "./quickstart";
import { ProjectSetupModal } from "./ProjectSetupModal";
import { ChatPanel, type ChatRunAction } from "./ChatPanel";
import { GenerationComposer } from "./composer/GenerationComposer";
import { EditOperation } from "@/lib/chat/editOperations";
@ -459,13 +457,12 @@ export const isDraggingNodeRef = { current: false };
export function WorkflowCanvas() {
const { t } = useI18n();
const popiProviderMode = isPopiProviderMode();
const { nodes, edges, groups, isModalOpen, showQuickstart, navigationTarget, canvasNavigationSettings, dimmedNodeIds, skippedNodeIds, hoveredNodeId, providerSettings, providerEnvStatus, isRunning } =
const { nodes, edges, groups, isModalOpen, navigationTarget, canvasNavigationSettings, dimmedNodeIds, skippedNodeIds, hoveredNodeId, providerSettings, providerEnvStatus, isRunning } =
useWorkflowStore(useShallow((state) => ({
nodes: state.nodes,
edges: state.edges,
groups: state.groups,
isModalOpen: state.isModalOpen,
showQuickstart: state.showQuickstart,
navigationTarget: state.navigationTarget,
canvasNavigationSettings: state.canvasNavigationSettings,
dimmedNodeIds: state.dimmedNodeIds,
@ -486,14 +483,11 @@ export function WorkflowCanvas() {
const setNodeGroupId = useWorkflowStore((state) => state.setNodeGroupId);
const executeWorkflow = useWorkflowStore((state) => state.executeWorkflow);
const executeSelectedNodes = useWorkflowStore((state) => state.executeSelectedNodes);
const setShowQuickstart = useWorkflowStore((state) => state.setShowQuickstart);
const setNavigationTarget = useWorkflowStore((state) => state.setNavigationTarget);
const captureSnapshot = useWorkflowStore((state) => state.captureSnapshot);
const applyEditOperations = useWorkflowStore((state) => state.applyEditOperations);
const setWorkflowMetadata = useWorkflowStore((state) => state.setWorkflowMetadata);
const setShortcutsDialogOpen = useWorkflowStore((state) => state.setShortcutsDialogOpen);
const regenerateNode = useWorkflowStore((state) => state.regenerateNode);
const clearWorkflow = useWorkflowStore((state) => state.clearWorkflow);
const selectSingleNode = useWorkflowStore((state) => state.selectSingleNode);
const setHoveredNodeId = useWorkflowStore((state) => state.setHoveredNodeId);
const updateCanvasNavigationSettings = useWorkflowStore((state) => state.updateCanvasNavigationSettings);
@ -507,7 +501,6 @@ export function WorkflowCanvas() {
const [multiAngleDialog, setMultiAngleDialog] = useState<MultiAngleDialogState | null>(null);
const [isSplitting, setIsSplitting] = useState(false);
const [isBuildingWorkflow, setIsBuildingWorkflow] = useState(false);
const [showNewProjectSetup, setShowNewProjectSetup] = useState(false);
const [isMacOSClient, setIsMacOSClient] = useState(false);
useEffect(() => {
@ -2582,38 +2575,6 @@ export function WorkflowCanvas() {
</div>
)}
{/* Welcome Modal */}
{showQuickstart && (
<WelcomeModal
onWorkflowGenerated={async (workflow, directoryPath) => {
await loadWorkflow(workflow, directoryPath);
setShowQuickstart(false);
}}
onClose={() => setShowQuickstart(false)}
onNewProject={() => {
clearWorkflow();
setShowQuickstart(false);
setShowNewProjectSetup(true);
}}
/>
)}
{/* New Project Setup Modal */}
{showNewProjectSetup && (
<ProjectSetupModal
isOpen={showNewProjectSetup}
mode="new"
onSave={(id, name, directoryPath) => {
setWorkflowMetadata(id, name, directoryPath);
setShowNewProjectSetup(false);
}}
onClose={() => {
setShowNewProjectSetup(false);
setShowQuickstart(true);
}}
/>
)}
<ReactFlow
nodes={allNodes}
edges={edges}

299
src/components/__tests__/WelcomeModal.test.tsx

@ -1,299 +0,0 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react";
import { WelcomeModal } from "@/components/quickstart/WelcomeModal";
import { WorkflowFile } from "@/store/workflowStore";
// Mock fetch
const mockFetch = vi.fn();
global.fetch = mockFetch;
// Mock WorkflowBrowserView (Load workflow now navigates to this view)
vi.mock("@/components/quickstart/WorkflowBrowserView", () => ({
WorkflowBrowserView: ({
onBack,
onWorkflowLoaded,
onClose,
}: {
onBack: () => void;
onWorkflowLoaded: (w: WorkflowFile, p: string) => void;
onClose: () => void;
}) => (
<div data-testid="workflow-browser-view">
<button onClick={onBack}>Back</button>
<button data-testid="load-workflow-btn" onClick={() => onWorkflowLoaded({ version: 1, nodes: [], edges: [], name: "Test" } as unknown as WorkflowFile, "/test/dir")}>
Load
</button>
<button data-testid="close-browser-btn" onClick={onClose}>
Close
</button>
</div>
),
}));
// Mock templates
vi.mock("@/lib/quickstart/templates", () => {
const template = {
id: "product-shot",
name: "Product Shot",
description: "Place product in a new scene or environment",
icon: "M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4",
category: "product",
tags: ["Gemini"],
workflow: {
name: "Product Shot",
nodes: [{ id: "1", type: "imageInput", position: { x: 0, y: 0 }, data: {} }],
edges: [],
},
};
return {
getAllPresets: () => [template],
PRESET_TEMPLATES: [template],
getPresetTemplate: (id: string) => (id === "product-shot" ? { ...template, id: `workflow-${Date.now()}` } : null),
getTemplateContent: () => ({ prompts: {}, images: {} }),
};
});
describe("WelcomeModal", () => {
const mockOnWorkflowGenerated = vi.fn();
const mockOnClose = vi.fn();
const mockOnNewProject = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
// Setup default fetch mock for community workflows
mockFetch.mockImplementation((url: string) => {
if (url === "/api/community-workflows") {
return Promise.resolve({
ok: true,
json: () => Promise.resolve({ success: true, workflows: [] }),
});
}
return Promise.resolve({
ok: true,
json: () => Promise.resolve({ success: true }),
});
});
});
afterEach(() => {
vi.restoreAllMocks();
});
describe("Basic Rendering", () => {
it("should render welcome modal with initial view by default", () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
expect(screen.getByText("Popi.TV")).toBeInTheDocument();
expect(screen.getByText("New project")).toBeInTheDocument();
expect(screen.getByText("Templates")).toBeInTheDocument();
expect(screen.getByText("Prompt a workflow")).toBeInTheDocument();
});
it("should render modal overlay with backdrop", () => {
const { container } = render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
const backdrop = container.querySelector(".bg-black\\/60");
expect(backdrop).toBeInTheDocument();
});
});
describe("Initial View Navigation", () => {
it("should call onNewProject when 'New project' is clicked", () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
fireEvent.click(screen.getByText("New project"));
expect(mockOnNewProject).toHaveBeenCalled();
});
it("should navigate to templates view when 'Templates' is clicked", async () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
await act(async () => {
fireEvent.click(screen.getByText("Templates"));
});
await waitFor(() => {
expect(screen.getByText("Template Explorer")).toBeInTheDocument();
expect(screen.getByText("Quick Start")).toBeInTheDocument();
});
});
it("should navigate to vibe view when 'Prompt a workflow' is clicked", () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
fireEvent.click(screen.getByText("Prompt a workflow"));
expect(screen.getByText("Prompt a Workflow")).toBeInTheDocument();
expect(screen.getByText("Describe your workflow")).toBeInTheDocument();
});
});
describe("View Transitions", () => {
it("should navigate back to initial view from templates view", async () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
// Navigate to templates
await act(async () => {
fireEvent.click(screen.getByText("Templates"));
});
await waitFor(() => {
expect(screen.getByText("Template Explorer")).toBeInTheDocument();
});
// Click back
await act(async () => {
fireEvent.click(screen.getByText("Back"));
});
expect(screen.getByText("Popi.TV")).toBeInTheDocument();
expect(screen.getByText("New project")).toBeInTheDocument();
});
it("should navigate back to initial view from prompt view", () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
// Navigate to prompt view
fireEvent.click(screen.getByText("Prompt a workflow"));
expect(screen.getByText("Prompt a Workflow")).toBeInTheDocument();
// Click back
fireEvent.click(screen.getByText("Back"));
expect(screen.getByText("Popi.TV")).toBeInTheDocument();
});
});
describe("Load Workflow via Browser View", () => {
it("should show WorkflowBrowserView when 'Load workflow' is clicked", () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
fireEvent.click(screen.getByText("Load workflow"));
expect(screen.getByTestId("workflow-browser-view")).toBeInTheDocument();
});
it("should navigate back to initial view from browse view", () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
fireEvent.click(screen.getByText("Load workflow"));
expect(screen.getByTestId("workflow-browser-view")).toBeInTheDocument();
fireEvent.click(screen.getByText("Back"));
expect(screen.getByText("Popi.TV")).toBeInTheDocument();
});
it("should call onWorkflowGenerated when a workflow is loaded from browser", () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
fireEvent.click(screen.getByText("Load workflow"));
fireEvent.click(screen.getByTestId("load-workflow-btn"));
expect(mockOnWorkflowGenerated).toHaveBeenCalledWith(
expect.objectContaining({ version: 1, nodes: [], edges: [] }),
"/test/dir"
);
});
});
describe("Workflow Selection from Child Views", () => {
it("should call onWorkflowGenerated when workflow is generated from templates view", async () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
// Navigate to templates
await act(async () => {
fireEvent.click(screen.getByText("Templates"));
});
await waitFor(() => {
expect(screen.getByText("Template Explorer")).toBeInTheDocument();
});
// Verify templates view is showing - the actual workflow selection is tested in QuickstartTemplatesView tests
expect(screen.getByText("Quick Start")).toBeInTheDocument();
});
it("should show prompt view when navigating to vibe", () => {
render(
<WelcomeModal
onWorkflowGenerated={mockOnWorkflowGenerated}
onClose={mockOnClose}
onNewProject={mockOnNewProject}
/>
);
// Navigate to vibe/prompt view
fireEvent.click(screen.getByText("Prompt a workflow"));
expect(screen.getByText("Prompt a Workflow")).toBeInTheDocument();
expect(screen.getByText("Generate Workflow")).toBeInTheDocument();
});
});
});

62
src/components/__tests__/WorkflowCanvas.test.tsx

@ -17,7 +17,6 @@ const mockExecuteWorkflow = vi.fn();
const mockCopySelectedNodes = vi.fn();
const mockPasteNodes = vi.fn();
const mockClearClipboard = vi.fn();
const mockSetShowQuickstart = vi.fn();
const mockSetHoveredNodeId = vi.fn();
const mockRegenerateNode = vi.fn();
const mockExecuteSelectedNodes = vi.fn();
@ -109,10 +108,6 @@ vi.mock("@/components/GroupsOverlay", () => ({
GroupControlsOverlay: () => null,
}));
vi.mock("@/components/quickstart", () => ({
WelcomeModal: () => <div data-testid="welcome-modal" />,
}));
vi.mock("@/utils/gridSplitter", () => ({
detectAndSplitGrid: vi.fn(),
}));
@ -164,8 +159,6 @@ const createDefaultState = (overrides = {}) => ({
executeSelectedNodes: mockExecuteSelectedNodes,
isRunning: false,
isModalOpen: false,
showQuickstart: false,
setShowQuickstart: mockSetShowQuickstart,
copySelectedNodes: mockCopySelectedNodes,
pasteNodes: mockPasteNodes,
clearClipboard: mockClearClipboard,
@ -555,61 +548,6 @@ describe("WorkflowCanvas", () => {
});
});
describe("Welcome Modal", () => {
it("should show welcome modal when canvas is empty and showQuickstart is true", () => {
mockUseWorkflowStore.mockImplementation((selector) => {
return selector(createDefaultState({
nodes: [],
showQuickstart: true,
}));
});
render(
<TestWrapper>
<WorkflowCanvas />
</TestWrapper>
);
expect(screen.getByTestId("welcome-modal")).toBeInTheDocument();
});
it("should not show welcome modal when canvas has nodes", () => {
// When a workflow with nodes is loaded, the store sets showQuickstart to false.
// The component renders the modal based solely on showQuickstart state.
mockUseWorkflowStore.mockImplementation((selector) => {
return selector(createDefaultState({
nodes: [createMockNode("node-1", "prompt")],
showQuickstart: false,
}));
});
render(
<TestWrapper>
<WorkflowCanvas />
</TestWrapper>
);
expect(screen.queryByTestId("welcome-modal")).not.toBeInTheDocument();
});
it("should not show welcome modal when showQuickstart is false", () => {
mockUseWorkflowStore.mockImplementation((selector) => {
return selector(createDefaultState({
nodes: [],
showQuickstart: false,
}));
});
render(
<TestWrapper>
<WorkflowCanvas />
</TestWrapper>
);
expect(screen.queryByTestId("welcome-modal")).not.toBeInTheDocument();
});
});
describe("Node Types Registration", () => {
it("should register all required node types for the canvas", () => {
// We verify the canvas renders with the node types object defined

94
src/components/quickstart/WelcomeModal.tsx

@ -1,94 +0,0 @@
"use client";
import { useState, useCallback } from "react";
import { WorkflowFile } from "@/store/workflowStore";
import { QuickstartView } from "@/types/quickstart";
import { QuickstartInitialView } from "./QuickstartInitialView";
import { TemplateExplorerView } from "./TemplateExplorerView";
import { PromptWorkflowView } from "./PromptWorkflowView";
import { WorkflowBrowserView } from "./WorkflowBrowserView";
interface WelcomeModalProps {
onWorkflowGenerated: (workflow: WorkflowFile, directoryPath?: string) => void;
onClose: () => void;
onNewProject: () => void;
}
export function WelcomeModal({
onWorkflowGenerated,
onClose,
onNewProject,
}: WelcomeModalProps) {
const [currentView, setCurrentView] = useState<QuickstartView>("initial");
const handleNewProject = useCallback(() => {
onNewProject();
}, [onNewProject]);
const handleSelectTemplates = useCallback(() => {
setCurrentView("templates");
}, []);
const handleSelectVibe = useCallback(() => {
setCurrentView("vibe");
}, []);
const handleSelectLoad = useCallback(() => {
setCurrentView("browse");
}, []);
const handleBack = useCallback(() => {
setCurrentView("initial");
}, []);
const handleWorkflowSelected = useCallback(
(workflow: WorkflowFile) => {
onWorkflowGenerated(workflow);
},
[onWorkflowGenerated]
);
// Template explorer needs more width for two-column layout
const dialogWidth = currentView === "templates" ? "max-w-6xl" : "max-w-2xl";
const dialogHeight = currentView === "templates" || currentView === "browse" ? "max-h-[85vh]" : "max-h-[80vh]";
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 ${dialogWidth} mx-4 bg-neutral-800 rounded-xl border border-neutral-700 shadow-2xl overflow-clip ${dialogHeight} flex flex-col`} onClick={(e) => e.stopPropagation()}>
{currentView === "initial" && (
<QuickstartInitialView
onNewProject={handleNewProject}
onSelectTemplates={handleSelectTemplates}
onSelectVibe={handleSelectVibe}
onSelectLoad={handleSelectLoad}
/>
)}
{currentView === "templates" && (
<TemplateExplorerView
onBack={handleBack}
onWorkflowSelected={handleWorkflowSelected}
/>
)}
{currentView === "vibe" && (
<PromptWorkflowView
onBack={handleBack}
onWorkflowGenerated={handleWorkflowSelected}
/>
)}
{currentView === "browse" && (
<WorkflowBrowserView
onBack={handleBack}
onWorkflowLoaded={(workflow, dirPath) =>
onWorkflowGenerated(workflow, dirPath)
}
onClose={onClose}
/>
)}
</div>
</div>
);
}

1
src/components/quickstart/index.ts

@ -1,4 +1,3 @@
export { WelcomeModal } from "./WelcomeModal";
export { QuickstartBackButton } from "./QuickstartBackButton";
export { QuickstartInitialView } from "./QuickstartInitialView";
export { QuickstartTemplatesView } from "./QuickstartTemplatesView";

4
src/i18n/index.tsx

@ -108,7 +108,6 @@ const en = {
"error.providerOverloaded": "{model}: provider is temporarily overloaded. Please retry in a moment or switch models.",
"node.optionalTitle": "This input is optional - empty inputs will be skipped",
"node.markOptionalTitle": "Mark as optional - empty inputs will skip this branch",
"header.openWelcome": "Open welcome screen",
"header.untitled": "Untitled",
"header.saving": "Saving...",
"header.savedAt": "Saved {time}",
@ -1071,7 +1070,6 @@ const zhCN: Dictionary = {
"error.providerOverloaded": "{model}:服务商暂时过载。请稍后重试,或切换到其他模型。",
"node.optionalTitle": "此输入为可选,空输入会被跳过",
"node.markOptionalTitle": "标记为可选,空输入会跳过这个分支",
"header.openWelcome": "打开欢迎页",
"header.untitled": "未命名",
"header.saving": "保存中...",
"header.savedAt": "已保存 {time}",
@ -1879,7 +1877,6 @@ const zhTW: Dictionary = {
"error.providerOverloaded": "{model}:服務商暫時過載。請稍後重試,或切換到其他模型。",
"node.optionalTitle": "此輸入為可選,空輸入會被略過",
"node.markOptionalTitle": "標記為可選,空輸入會略過這個分支",
"header.openWelcome": "開啟歡迎頁",
"header.untitled": "未命名",
"header.saving": "儲存中...",
"header.savedAt": "已儲存 {time}",
@ -2488,7 +2485,6 @@ const ja: Dictionary = {
"error.providerOverloaded": "{model}: プロバイダーが一時的に混雑しています。少し待って再試行するか、別のモデルに切り替えてください。",
"node.optionalTitle": "この入力は任意です。空の入力はスキップされます",
"node.markOptionalTitle": "任意に設定します。空の入力はこの分岐をスキップします",
"header.openWelcome": "ウェルカム画面を開く",
"header.untitled": "無題",
"header.saving": "保存中...",
"header.savedAt": "{time} に保存済み",

9
src/store/workflowStore.ts

@ -293,11 +293,9 @@ interface WorkflowStore {
// UI State
openModalCount: number;
isModalOpen: boolean;
showQuickstart: boolean;
hoveredNodeId: string | null;
incrementModalCount: () => void;
decrementModalCount: () => void;
setShowQuickstart: (show: boolean) => void;
setHoveredNodeId: (id: string | null) => void;
// Execution
@ -754,7 +752,6 @@ const workflowStoreImpl: StateCreator<WorkflowStore> = (set, get) => ({
groups: {},
openModalCount: 0,
isModalOpen: false,
showQuickstart: false,
hoveredNodeId: null,
isRunning: false,
currentNodeIds: [], // Changed from currentNodeId for parallel execution
@ -883,10 +880,6 @@ const workflowStoreImpl: StateCreator<WorkflowStore> = (set, get) => ({
});
},
setShowQuickstart: (show: boolean) => {
set({ showQuickstart: show });
},
setHoveredNodeId: (id: string | null) => {
if (hoverRafId !== null) cancelAnimationFrame(hoverRafId);
hoverRafId = requestAnimationFrame(() => {
@ -2634,8 +2627,6 @@ const workflowStoreImpl: StateCreator<WorkflowStore> = (set, get) => ({
useExternalImageStorage: savedConfig?.useExternalImageStorage ?? true,
// Reset viewed comments when loading new workflow
viewedCommentNodeIds: new Set<string>(),
// Dismiss welcome modal after loading a workflow
showQuickstart: false,
hasUnsavedChanges: promotedModels.changed || restoredExecutionState.changed,
});

Loading…
Cancel
Save