mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-18 00:03:11 +00:00
feat: replace flow summary card with breadcrumb + frame the flow as a chat-owned card
- FlowBuilder: replace the SummaryPathDisplay box (path / summary stack)
with an inline breadcrumb (Workflow icon + "Flows" / folder /
summary-or-leaf, separated by ChevronRight). The breadcrumb derives
$pathStore segments via $derived (breadcrumbParts/Folder/Leaf) so it
can be referenced from the template without {@const}, which is not
allowed as a direct child of <div>. Drops the now-unused
SummaryPathDisplay import.
- design-sandbox: position the right pane as a contained "chat-owned"
workspace — p-2 pl-0 padding plus an inner rounded-xl border / shadow-sm
card wrapping FlowWrapper. Drop the left pane's right border so the
chat reads as full-bleed primary content. Add a chat-side header
(h-12 to match FlowBuilder's max-h-12 row) with a MessageSquare icon
and a conversationTitle derived from the first user message in the
current chat (truncated to 60 chars). Hide the splitpanes divider
visually via a scoped :global(.design-sandbox-splitpanes ...) rule
while keeping the hit area so the split is still resizable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
63197f682d
commit
b28b814e95
@@ -59,7 +59,9 @@
|
||||
CheckCircle,
|
||||
RefreshCw,
|
||||
CheckCheck,
|
||||
Disc
|
||||
Disc,
|
||||
ChevronRight,
|
||||
Workflow
|
||||
} from 'lucide-svelte'
|
||||
import Awareness from './Awareness.svelte'
|
||||
import { getAllModules } from './flows/flowExplorer'
|
||||
@@ -71,7 +73,6 @@
|
||||
import { resetAllTodos, skipAllTodos } from '$lib/tutorialUtils'
|
||||
import { tutorialsToDo } from '$lib/stores'
|
||||
import { getTutorialIndex } from '$lib/tutorials/config'
|
||||
import SummaryPathDisplay from './SummaryPathDisplay.svelte'
|
||||
import type { FlowBuilderWhitelabelCustomUi } from './custom_ui'
|
||||
import FlowYamlEditor from './flows/header/FlowYamlEditor.svelte'
|
||||
import { type TriggerContext, type ScheduleTrigger } from './triggers'
|
||||
@@ -622,6 +623,15 @@
|
||||
const flowEditorDrawer = writable<FlowEditorDrawer | undefined>(undefined)
|
||||
const history = initHistory(untrack(() => flowStore).val)
|
||||
const pathStore = writable<string>(untrack(() => pathStoreInit) ?? initialPath)
|
||||
|
||||
// Breadcrumb derivations for the flow header (replaces SummaryPathDisplay).
|
||||
const breadcrumbParts = $derived(($pathStore ?? '').split('/').filter(Boolean))
|
||||
const breadcrumbFolder = $derived(
|
||||
breadcrumbParts.length > 1 ? breadcrumbParts.slice(0, -1).join('/') : ''
|
||||
)
|
||||
const breadcrumbLeaf = $derived(
|
||||
breadcrumbParts.length > 0 ? breadcrumbParts[breadcrumbParts.length - 1] : 'untitled'
|
||||
)
|
||||
const captureOn = writable<boolean>(false)
|
||||
const showCaptureHint = writable<boolean | undefined>(undefined)
|
||||
const flowInputEditorStateStore = writable<FlowInputEditorState>({
|
||||
@@ -1150,13 +1160,20 @@
|
||||
<div
|
||||
class="justify-between flex flex-row items-center pl-2 pr-4 space-x-4 scrollbar-hidden overflow-x-auto max-h-12 h-full relative"
|
||||
>
|
||||
<div class="flex w-full gap-8 items-center min-w-0">
|
||||
<SummaryPathDisplay
|
||||
bind:summary={flowStore.val.summary}
|
||||
bind:path={$pathStore}
|
||||
kind="flow"
|
||||
editable
|
||||
/>
|
||||
<div class="flex w-full gap-1.5 items-center min-w-0 text-xs">
|
||||
<Workflow class="w-3.5 h-3.5 text-secondary shrink-0" />
|
||||
<span class="text-secondary shrink-0">Flows</span>
|
||||
{#if breadcrumbFolder}
|
||||
<ChevronRight class="w-3 h-3 text-secondary opacity-60 shrink-0" />
|
||||
<span class="text-secondary truncate font-mono">{breadcrumbFolder}</span>
|
||||
{/if}
|
||||
<ChevronRight class="w-3 h-3 text-secondary opacity-60 shrink-0" />
|
||||
<span
|
||||
class="font-medium text-primary truncate"
|
||||
title={flowStore.val.summary || breadcrumbLeaf}
|
||||
>
|
||||
{flowStore.val.summary || breadcrumbLeaf}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="gap-4 flex-row hidden md:flex whitespace-nowrap">
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
previewModalState,
|
||||
closePreviewModal
|
||||
} from '$lib/components/copilot/chat/previewModalState.svelte'
|
||||
import { MessageSquare } from 'lucide-svelte'
|
||||
|
||||
// Fake conversation: user asks the assistant to build a 3-step user-engagement flow.
|
||||
const fakeMessages: DisplayMessage[] = [
|
||||
@@ -203,23 +204,49 @@
|
||||
let customUi: FlowBuilderWhitelabelCustomUi = {
|
||||
aiAgent: true
|
||||
}
|
||||
|
||||
// Conversation title shown in the chat topbar. Derived from the first user
|
||||
// message in the current chat, truncated. Falls back to a placeholder while
|
||||
// the chat is empty.
|
||||
const conversationTitle = $derived.by(() => {
|
||||
const firstUser = aiChatManager.displayMessages.find((m) => m.role === 'user')
|
||||
if (!firstUser) return 'New conversation'
|
||||
const text = firstUser.content.trim()
|
||||
const truncated = text.length > 60 ? text.slice(0, 60).trimEnd() + '…' : text
|
||||
return truncated
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="h-full w-full flex flex-col min-h-0">
|
||||
<Splitpanes horizontal={false} class="flex-1 min-h-0">
|
||||
<Pane size={50} minSize={20} class="flex flex-col min-h-0 border-r border-light">
|
||||
<AIChat />
|
||||
<Splitpanes horizontal={false} class="design-sandbox-splitpanes flex-1 min-h-0">
|
||||
<Pane size={50} minSize={20} class="flex flex-col min-h-0">
|
||||
<div class="flex flex-row items-center gap-2 px-3 h-12 bg-surface shrink-0">
|
||||
<MessageSquare class="w-3.5 h-3.5 text-secondary shrink-0" />
|
||||
<span class="text-xs font-medium text-primary truncate" title={conversationTitle}>
|
||||
{conversationTitle}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex-1 min-h-0">
|
||||
<AIChat />
|
||||
</div>
|
||||
</Pane>
|
||||
<Pane size={50} minSize={30} class="flex flex-col min-h-0">
|
||||
<FlowWrapper
|
||||
disableAi
|
||||
pathStoreInit="u/admin/sandbox_flow"
|
||||
{customUi}
|
||||
selectedId={undefined}
|
||||
newFlow
|
||||
{flowStore}
|
||||
{flowStateStore}
|
||||
/>
|
||||
<!-- The right pane is a contained, padded card to make it visually
|
||||
clear that the flow view "belongs to" the chat on the left rather
|
||||
than being its own root view. -->
|
||||
<Pane size={50} minSize={30} class="flex flex-col min-h-0 p-2 pl-0">
|
||||
<div
|
||||
class="flex-1 min-h-0 rounded-xl border border-light bg-surface overflow-hidden shadow-sm"
|
||||
>
|
||||
<FlowWrapper
|
||||
disableAi
|
||||
pathStoreInit="u/admin/sandbox_flow"
|
||||
{customUi}
|
||||
selectedId={undefined}
|
||||
newFlow
|
||||
{flowStore}
|
||||
{flowStateStore}
|
||||
/>
|
||||
</div>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</div>
|
||||
@@ -245,3 +272,17 @@
|
||||
<ScheduleEditorInner bind:this={scheduleEditor} useDrawer={false} />
|
||||
</div>
|
||||
</Modal2>
|
||||
|
||||
<style>
|
||||
/* Hide the visible splitpanes divider while keeping the hit area so the
|
||||
panel is still resizable. Scoped to this page via the wrapper class. */
|
||||
:global(.design-sandbox-splitpanes > .splitpanes__splitter) {
|
||||
background-color: transparent !important;
|
||||
background-image: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
:global(.design-sandbox-splitpanes > .splitpanes__splitter::before),
|
||||
:global(.design-sandbox-splitpanes > .splitpanes__splitter::after) {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user