From 8a83a74439abe953c86d061bdc9afb411c72f500 Mon Sep 17 00:00:00 2001 From: Lucy Macartney Date: Mon, 22 Jun 2026 15:23:46 +0100 Subject: [PATCH 01/18] Add AI-first landing screen for new workflow creation (#4856) When a user navigates to /w/new, show a full-screen overlay before they reach the empty canvas. The overlay presents three entry points: - Build with AI (textarea + submit, shown when AI assistant is enabled) - Browse Templates - Import from YAML The action handlers are stubs for now, wired up in follow-on issues #4857 (Build with AI), #4858 (Browse Templates), and #4859 (Import YAML). Implementation notes: - Adds showLandingScreen boolean to UIStore, initialised to true when isNewWorkflow is true, with a dismissLandingScreen() action - Hides the breadcrumb/header bar while the landing screen is visible - Guards the existing empty-canvas placeholder with !showLandingScreen so they don't render simultaneously (TODO: remove placeholder in #4856 once landing screen is fully stable) - Adds CSS design tokens for border-subtle, border-strong, surface-subtle, and semantic-success used by the landing screen UI --- assets/css/app.css | 14 ++ .../CollaborativeEditor.tsx | 76 +++++-- .../components/LandingScreen.tsx | 141 ++++++++++++ .../components/WorkflowEditor.tsx | 13 +- .../contexts/StoreProvider.tsx | 2 +- assets/js/collaborative-editor/hooks/useUI.ts | 12 + .../stores/createUIStore.ts | 11 +- assets/js/collaborative-editor/types/ui.ts | 6 + .../__helpers__/storeMocks.ts | 2 + .../CollaborativeEditor.keyboard.test.tsx | 1 + .../components/LandingScreen.test.tsx | 165 ++++++++++++++ .../components/WorkflowEditor.test.tsx | 1 + .../createUIStore.landing-screen.test.ts | 52 +++++ assets/test/e2e/pages/workflow-edit.page.ts | 7 + .../workflows/workflow-landing-screen.spec.ts | 215 ++++++++++++++++++ 15 files changed, 687 insertions(+), 31 deletions(-) create mode 100644 assets/js/collaborative-editor/components/LandingScreen.tsx create mode 100644 assets/test/collaborative-editor/components/LandingScreen.test.tsx create mode 100644 assets/test/collaborative-editor/stores/createUIStore.landing-screen.test.ts create mode 100644 assets/test/e2e/specs/workflows/workflow-landing-screen.spec.ts diff --git a/assets/css/app.css b/assets/css/app.css index 87eece11dd3..40dd1c98109 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -95,6 +95,20 @@ --color-warning-800: var(--color-yellow-800); --color-warning-900: var(--color-yellow-900); --color-warning-950: var(--color-yellow-950); + + /* NEW DESIGN SYSTEM - TODO-AI-FIRST extend? */ + --gray-light-200: #eae9e8; + --gray-light-300: #d6d5d4; + --gray-light-400: #cccbca; + + --color-border-strong: var(--gray-light-400); + --color-border-subtle: var(--gray-light-200); + + /* Surface tokens */ + --color-surface-subtle: #EBF5F2; + + /* Semantic tokens */ + --color-semantic-success: #006840; } @layer base { diff --git a/assets/js/collaborative-editor/CollaborativeEditor.tsx b/assets/js/collaborative-editor/CollaborativeEditor.tsx index a86a7030972..9400166277d 100644 --- a/assets/js/collaborative-editor/CollaborativeEditor.tsx +++ b/assets/js/collaborative-editor/CollaborativeEditor.tsx @@ -2,14 +2,15 @@ import { useMemo, useRef } from 'react'; import { useURLState } from '#/react/lib/use-url-state'; +import { PickerButton } from '../picker/PickerButton'; import { SocketProvider } from '../react/contexts/SocketProvider'; import type { WithActionProps } from '../react/lib/with-props'; import { AIAssistantPanelWrapper } from './components/AIAssistantPanelWrapper'; import { BreadcrumbLink, BreadcrumbText } from './components/Breadcrumbs'; -import { PickerButton } from '../picker/PickerButton'; import type { MonacoHandle } from './components/CollaborativeMonaco'; import { Header } from './components/Header'; +import { LandingScreen } from './components/LandingScreen'; import { LoadingBoundary } from './components/LoadingBoundary'; import { Toaster } from './components/ui/Toaster'; import { VersionDebugLogger } from './components/VersionDebugLogger'; @@ -24,7 +25,7 @@ import { useLatestSnapshotLockVersion, useProject, } from './hooks/useSessionContext'; -import { useIsRunPanelOpen } from './hooks/useUI'; +import { useIsRunPanelOpen, useShowLandingScreen } from './hooks/useUI'; import { useVersionSelect } from './hooks/useVersionSelect'; import { useWorkflowState } from './hooks/useWorkflow'; import { KeyboardProvider } from './keyboard'; @@ -152,10 +153,10 @@ function BreadcrumbContent({ projectColor, projectEnv, currentWorkflowName, - workflowId, workflowFromStore?.lock_version, latestSnapshotLockVersion, handleVersionSelect, + isNewWorkflow, ]); return ( @@ -172,6 +173,26 @@ function BreadcrumbContent({ ); } +function LandingScreenWrapper({ + aiAssistantEnabled, +}: { + aiAssistantEnabled: boolean; +}) { + const showLandingScreen = useShowLandingScreen(); + if (!showLandingScreen) return null; + return ( + <> + {/* TODO-AI-FIRST Stubs — wired up in Issues #4857 (Build with AI), #4858 (Browse Templates), #4859 (Import YAML) */} + {}} + onBrowseTemplates={() => {}} + onImportYAML={() => {}} + /> + + ); +} + export const CollaborativeEditor: WithActionProps< CollaborativeEditorDataProps > = props => { @@ -219,28 +240,30 @@ export const CollaborativeEditor: WithActionProps<
- + {!isNewWorkflow && ( + + )}
@@ -251,6 +274,9 @@ export const CollaborativeEditor: WithActionProps<
+
void; + testId: string; +} + +interface LandingScreenProps { + aiAssistantEnabled: boolean; + onBuildWithAI: (prompt: string) => void; + onBrowseTemplates: () => void; + onImportYAML: () => void; +} + +export function LandingScreen({ + aiAssistantEnabled, + onBuildWithAI, + onBrowseTemplates, + onImportYAML, +}: LandingScreenProps) { + const [prompt, setPrompt] = useState(''); + const isValid = prompt.trim().length > 0; + + const handleSubmit = () => { + if (!isValid) return; + onBuildWithAI(prompt); + }; + + return ( +
+
+

+ Where would you like to start today? +

+ + {aiAssistantEnabled && ( +
+
+ + + + + + Recommended + +
+
+