From aa91619bb67b7ae0cbd3a3848b6171d36967bd1f Mon Sep 17 00:00:00 2001 From: Guilhem Date: Wed, 5 Aug 2026 09:28:13 +0200 Subject: [PATCH] fix: flow step picker layout and single hover/keyboard highlight (#10488) * fix: keep flow step picker rows on one line and highlight only one Co-Authored-By: Claude Opus 5 (1M context) * fix: restore hover on standalone picker rows and drop phantom ai slots Co-Authored-By: Claude Opus 5 (1M context) * fix: drop inert picker resize and align hub rows with workspace rows The step picker popover carried `!resize` but computes `overflow: visible`, so CSS `resize` never applied and the handle did nothing. Dropping it also pins the inner height at 464px, keeping `displayPath` off everywhere except the content-sized trigger picker. Hub rows there rendered summary and path inside a fixed 28px button; give them the same `h-auto min-h-7 py-1` the workspace rows got. Guard `hover:bg-transparent` on `onHover` in both pickers so all three agree, and drop the unconditional `title` on TopLevelNode, which put a native tooltip on every kind button. Co-Authored-By: Claude Opus 5 (1M context) * fix: keep GenAiQuick's CSS hover when it is not wired into the shared index Co-Authored-By: Claude Opus 5 (1M context) --------- Co-authored-by: Claude Opus 5 (1M context) --- .../flows/content/FlowInputsQuick.svelte | 51 ++++++++++++++----- .../flows/content/GenAiQuick.svelte | 20 +++++--- .../flows/map/InsertModuleInner.svelte | 25 ++++----- .../flows/map/InsertModulePopover.svelte | 2 +- .../pickers/FlowScriptPickerQuick.svelte | 8 +-- .../flows/pickers/FlowToplevelNode.svelte | 9 ++-- .../flows/pickers/PickHubScriptQuick.svelte | 25 ++++++--- .../flows/pickers/TopLevelNode.svelte | 16 ++++-- .../pickers/WorkspaceScriptPickerQuick.svelte | 25 ++++++--- .../components/home/ListFiltersQuick.svelte | 3 +- 10 files changed, 125 insertions(+), 59 deletions(-) diff --git a/frontend/src/lib/components/flows/content/FlowInputsQuick.svelte b/frontend/src/lib/components/flows/content/FlowInputsQuick.svelte index 7a55ca92fc..89943056e2 100644 --- a/frontend/src/lib/components/flows/content/FlowInputsQuick.svelte +++ b/frontend/src/lib/components/flows/content/FlowInputsQuick.svelte @@ -217,6 +217,13 @@ } } + // Mouse and keyboard share this one index, so rows report hover on mousemove rather than + // mouseenter: arrow keys scroll the list, which slides a row under a stationary cursor and + // fires mouseenter, which would otherwise hijack the selection mid-navigation. + function hover(index: number) { + selectedByKeyboard = index + } + onMount(() => { $insertButtonOpen = true }) @@ -247,9 +254,24 @@ preFilter untrack(() => onPrefilterChange(preFilter)) }) - let aiLength = $derived( - funcDesc?.length > 0 && !disableAi && selectedKind != 'flow' && preFilter == 'all' ? 2 : 0 + // Gates the two AI rows and their slots in the index space; both must agree or arrow keys land + // on indices that render nothing. + let showAiRows = $derived( + !disableAi && + funcDesc?.length > 0 && + kind != 'failure' && + kind != 'preprocessor' && + (selectedKind == 'script' || selectedKind == 'trigger') && + preFilter == 'all' ) + let aiLength = $derived(showAiRows ? 2 : 0) + + // Every result row lives in one keyboard index space, and hovering a row moves that index, so + // mouse and keyboard can never highlight two different rows. Offsets follow the render order. + let inlineOffset = $derived(topLevelNodes.length) + let aiOffset = $derived(inlineOffset + (inlineScripts?.length ?? 0)) + let workspaceOffset = $derived(aiOffset + aiLength) + let hubOffset = $derived(workspaceOffset + (filteredWorkspaceItems?.length ?? 0)) @@ -281,8 +303,9 @@ icon: owner.startsWith('f/') ? Folder : User, props: { width: 14, height: 14 } }} + title={owner.slice(2)} > - {owner.slice(2)} + {owner.slice(2)} {/each} @@ -355,6 +378,7 @@ }} {label} selected={selectedByKeyboard === i} + onHover={() => hover(i)} /> {/each} {/if} @@ -395,7 +419,8 @@ {#each inlineScripts as [label, lang], i (lang)} hover(i + inlineOffset)} {enterpriseLangs} {label} lang={lang == 'docker' ? 'bash' : lang} @@ -419,13 +444,14 @@ {/each} {/if} - {#if !disableAi && funcDesc?.length > 0 && kind != 'failure' && kind != 'preprocessor' && (selectedKind == 'script' || selectedKind == 'trigger') && preFilter == 'all'} + {#if showAiRows}
  • hover(aiOffset)} on:click={() => { lang = 'bun' onGenerate() @@ -436,7 +462,8 @@ hover(aiOffset + 1)} on:click={() => { lang = 'python3' onGenerate() @@ -463,11 +490,12 @@ bind:filteredWithOwner={filteredWorkspaceItems} {filter} kind={selectedKind} - selected={selectedByKeyboard - inlineScripts?.length - aiLength - topLevelNodes.length} + selected={selectedByKeyboard - workspaceOffset} on:pickScript on:pickFlow {displayPath} {refreshCount} + onHover={(i) => hover(workspaceOffset + i)} /> {/await}
    @@ -511,15 +539,12 @@ } appFilter={selected?.name} kind={selectedKind} - selected={selectedByKeyboard - - inlineScripts?.length - - aiLength - - filteredWorkspaceItems?.length - - topLevelNodes.length} + selected={selectedByKeyboard - hubOffset} on:pickScript bind:loading {displayPath} {refreshCount} + onHover={(i) => hover(hubOffset + i)} /> {/await} {/if} diff --git a/frontend/src/lib/components/flows/content/GenAiQuick.svelte b/frontend/src/lib/components/flows/content/GenAiQuick.svelte index e0e681d568..5d3814fb63 100644 --- a/frontend/src/lib/components/flows/content/GenAiQuick.svelte +++ b/frontend/src/lib/components/flows/content/GenAiQuick.svelte @@ -1,17 +1,18 @@
    @@ -84,7 +85,7 @@
    {#if kind === 'script'} -
    +
    {/if} {#if customUi?.aiSandbox != false} - { - selectedKind = 'aisandbox' - }} - /> - {/if} + { + selectedKind = 'aisandbox' + }} + /> + {/if} {/if}
    {/if} diff --git a/frontend/src/lib/components/flows/map/InsertModulePopover.svelte b/frontend/src/lib/components/flows/map/InsertModulePopover.svelte index 0b29a8c9dd..2e319c91f7 100644 --- a/frontend/src/lib/components/flows/map/InsertModulePopover.svelte +++ b/frontend/src/lib/components/flows/map/InsertModulePopover.svelte @@ -36,7 +36,7 @@ void } let { @@ -18,7 +19,8 @@ lang = undefined, selected = false, eeRestricted, - enterpriseLangs = [] + enterpriseLangs = [], + onHover = undefined }: Props = $props() const dispatch = createEventDispatcher() @@ -44,12 +46,12 @@
    {/each}