This commit is contained in:
Ruben Fiszel
2026-05-01 14:50:41 +00:00
parent 1710b8a77b
commit 621375bc40
4 changed files with 63 additions and 45 deletions
@@ -9,15 +9,12 @@
import { importFlowStore } from '$lib/components/flows/flowStore.svelte'
import { importScriptStore } from '$lib/components/scripts/scriptStore.svelte'
import Modal from '$lib/components/common/modal/Modal.svelte'
import Toggle from '$lib/components/Toggle.svelte'
import Tabs from '$lib/components/common/tabs/Tabs.svelte'
import Tab from '$lib/components/common/tabs/Tab.svelte'
import { PythonIcon, TypeScriptIcon } from '$lib/components/common/languageIcons'
import { Code2, Loader2, Plus } from 'lucide-svelte'
import { Code2, Loader2, NetworkIcon, Plus } from 'lucide-svelte'
import YAML from 'yaml'
const SKIP_FLOW_MODAL_KEY = 'windmill_skip_flow_modal'
let drawer: Drawer | undefined = $state(undefined)
let wacDrawer: Drawer | undefined = $state(undefined)
let pendingRaw: string | undefined = $state(undefined)
@@ -26,9 +23,6 @@
let wacImportType: 'yaml' | 'json' = $state('yaml')
let flowModalOpen = $state(false)
let wacHovered = $state(false)
let skipModal = $state(
typeof localStorage !== 'undefined' && localStorage.getItem(SKIP_FLOW_MODAL_KEY) === 'true'
)
async function importRaw() {
$importFlowStore =
@@ -46,11 +40,7 @@
}
function handleFlowClick() {
if (skipModal) {
goto(`${base}/flows/add?nodraft=true`)
} else {
flowModalOpen = true
}
flowModalOpen = true
}
function selectFlowEditor() {
@@ -68,9 +58,9 @@
goto(`${base}/scripts/add?nodraft=true&wac=typescript`)
}
function toggleSkipModal() {
skipModal = !skipModal
localStorage.setItem(SKIP_FLOW_MODAL_KEY, String(skipModal))
function selectPipeline() {
flowModalOpen = false
goto(`${base}/pipeline`)
}
</script>
@@ -113,9 +103,13 @@
</div>
<!-- Flow Type Selection Modal -->
<Modal bind:open={flowModalOpen} title="Create a new flow">
<!-- max-w-4xl widens the modal to fit three tiles comfortably; default
`max-w-lg` (~512px) was sized for two tiles and squeezed the grid.
kind="X" replaces the bottom Cancel button with a top-right close (X)
so the action area is purely the three tile buttons. -->
<Modal bind:open={flowModalOpen} title="Create a new flow" class="sm:max-w-4xl" kind="X">
<div class="flex flex-col gap-4 pr-4">
<div class="grid grid-cols-2 gap-8">
<div class="grid grid-cols-3 gap-6">
<!-- Flow Editor option -->
<button
class="flex flex-col items-center gap-3 p-6 rounded-lg border-2 border-gray-200 dark:border-gray-700 hover:border-blue-500 dark:hover:border-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-all cursor-pointer group"
@@ -142,13 +136,6 @@
onmouseenter={() => (wacHovered = true)}
onmouseleave={() => (wacHovered = false)}
>
<!-- Alpha badge -->
<div
class="absolute top-2 right-2 z-10 px-2 py-0.5 rounded-full bg-purple-500 text-white text-2xs font-bold uppercase tracking-wide"
>
Alpha
</div>
<!-- Default state -->
<div
class="flex flex-col items-center gap-3 p-6 absolute inset-0 transition-all duration-300"
@@ -196,11 +183,31 @@
</button>
</div>
</div>
</div>
<div class="flex items-center gap-2 px-1">
<Toggle size="xs" checked={skipModal} on:change={toggleSkipModal} />
<span class="text-2xs text-tertiary">Always use the Flow editor (skip this modal)</span>
<!-- Pipeline option -->
<button
class="relative flex flex-col items-center gap-3 p-6 rounded-lg border-2 border-gray-200 dark:border-gray-700 hover:border-emerald-500 dark:hover:border-emerald-400 hover:bg-emerald-50 dark:hover:bg-emerald-900/20 transition-all cursor-pointer group"
onclick={selectPipeline}
>
<!-- Alpha badge — pipeline is the newest of the three; same
positioning the WAC tile used so it reads consistently. -->
<div
class="absolute top-2 right-2 z-10 px-2 py-0.5 rounded-full bg-emerald-500 text-white text-2xs font-bold uppercase tracking-wide"
>
Alpha
</div>
<div
class="w-32 h-32 rounded-xl bg-emerald-100 dark:bg-emerald-900/40 flex items-center justify-center group-hover:bg-emerald-200 dark:group-hover:bg-emerald-800/50 transition-colors"
>
<NetworkIcon size={32} class="text-emerald-600 dark:text-emerald-400" />
</div>
<div class="text-center">
<h3 class="font-semibold text-primary">Pipeline</h3>
<p class="text-xs text-tertiary mt-1">
Asset-driven DAG of scripts. Trigger on schedule, asset change, or external event.
</p>
</div>
</button>
</div>
</div>
</Modal>
@@ -45,6 +45,8 @@
import { getContext, untrack } from 'svelte'
import { triggerableByAI } from '$lib/actions/triggerableByAI.svelte'
import TextInput from '../text_input/TextInput.svelte'
import { NetworkIcon } from 'lucide-svelte'
import { base } from '$lib/base'
interface Props {
filter?: string
subtab?: 'flow' | 'script' | 'app'
@@ -594,6 +596,27 @@
/>
{:else}
<div class="border rounded-md bg-surface-tertiary">
<!-- One pipeline row per folder that has pipeline-member scripts.
Mirrors the entry the tree view shows under each folder, so
users in flat view get the same affordance — clicking opens
the pipeline editor for that folder. Hidden when an
active filter is set (filter operates on items, not on
folder-level entries). -->
{#if filter === ''}
{#each [...pipelineFolders].sort() as folder (folder)}
<!-- px-4/py-3 + gap-4 mirrors common/table/Row.svelte so the
row is the same height as the script/flow/app items
beneath it; without it the single-line pipeline row
was visibly shorter than the surrounding rows. -->
<a
href="{base}/pipeline/{encodeURIComponent(folder)}"
class="w-full inline-flex items-center gap-4 px-4 py-3 border-b last:border-b-0 hover:bg-surface-hover transition-colors text-sm first-of-type:rounded-t-md"
>
<NetworkIcon size={16} class="text-emerald-600 dark:text-emerald-400" />
<span class="text-xs font-medium text-emphasis truncate">Pipeline · f/{folder}</span>
</a>
{/each}
{/if}
{#each (items ?? []).slice(0, nbDisplayed) as item (item.type + '/' + item.path + (item.hash ? '/' + item.hash : ''))}
<Item
{item}
@@ -82,16 +82,16 @@
{#if opened || isSearching}
<div>
{#if hasPipeline && isFolder(item)}
<!-- py-3 matches common/table/Row.svelte so this row sits at
the same height as the script/flow/app rows that follow
it under the same folder; py-2 was visibly shorter. -->
<a
href="{base}/pipeline/{encodeURIComponent(item.folderName)}"
class="flex items-center gap-4 px-4 py-2 border-b text-sm hover:bg-surface-hover transition-colors"
class="flex items-center gap-4 px-4 py-3 border-b text-sm hover:bg-surface-hover transition-colors"
style="padding-left: {(depth + 1) * 16}px;"
>
<NetworkIcon size={16} class="text-emerald-600 dark:text-emerald-400" />
<div class="flex flex-col">
<span class="text-xs font-medium text-emphasis">Pipeline</span>
<span class="text-2xs text-secondary">Open pipeline editor</span>
</div>
<span class="text-xs font-medium text-emphasis">Pipeline</span>
</a>
{/if}
{#each item.items.slice(0, showMax) as subItem, index ((subItem['path'] ? subItem['type'] + '__' + subItem['path'] + '__' + index : undefined) ?? 'folder__' + subItem['folderName'] + '__' + index)}
@@ -22,7 +22,6 @@
Loader2,
Code,
LayoutDashboard,
NetworkIcon,
PlugZap
} from 'lucide-svelte'
import { hubBaseUrlStore } from '$lib/stores'
@@ -302,17 +301,6 @@
{#if !$userStore?.operator && showCreateButtons}
<CreateActionsScript aiId="create-script-button" aiDescription="Creates a new script" />
{#if HOME_SHOW_CREATE_FLOW}<CreateActionsFlow />{/if}
<Button
id="pipeline-button"
aiId="pipeline-button"
aiDescription="Opens the pipeline editor"
unifiedSize="lg"
startIcon={{ icon: NetworkIcon }}
href="{base}/pipeline"
variant="accent"
>
Pipeline
</Button>
{#if HOME_SHOW_CREATE_APP}<CreateActionsApp />{/if}
{/if}
</PageHeader>