[aidan] ux/workflows: open New workflow in agent build chat instead of empty card

When creating a new workflow from the hub, open it in edit_agent view (with the
agent builder chat) instead of a preview card. The workflow is created on the
backend first so the embedded session has a real ID.
This commit is contained in:
abccodes
2026-06-16 23:49:34 -07:00
parent 3a2dad47ff
commit a32c1240e5
9 changed files with 65 additions and 10 deletions
@@ -10,7 +10,7 @@ export interface SelectedElement {
computedStyles: Record<string, string>;
screenshot?: string;
boundingRect: { x: number; y: number; width: number; height: number };
semanticType?: 'agent-card' | 'message' | 'tool-call' | 'tool-group' | 'view-card' | 'browser-card' | 'dom-element';
semanticType?: 'agent-card' | 'message' | 'tool-call' | 'tool-group' | 'view-card' | 'browser-card' | 'workflow-card' | 'workflows-hub-card' | 'dom-element';
semanticLabel?: string;
semanticData?: Record<string, any>;
}
@@ -6,7 +6,7 @@ const SELECT_ATTR = 'data-select-type';
const SELECT_ID_ATTR = 'data-select-id';
const SELECT_META_ATTR = 'data-select-meta';
const DRAG_SELECT_TYPES = ['agent-card', 'view-card', 'browser-card'] as const;
const DRAG_SELECT_TYPES = ['agent-card', 'view-card', 'browser-card', 'workflow-card', 'workflows-hub-card'] as const;
const DRAG_SELECTOR = DRAG_SELECT_TYPES.map((t) => `[${SELECT_ATTR}="${t}"]`).join(',');
export interface OverlayState {
@@ -38,6 +38,8 @@ const SEMANTIC_LABELS: Record<string, string> = {
'tool-group': 'Tool Group',
'view-card': 'View',
'browser-card': 'Browser',
'workflow-card': 'Workflow',
'workflows-hub-card': 'Workflows',
};
function findSelectableAncestor(target: Element, excludeId?: string | null): Element | null {
@@ -289,6 +289,8 @@ const DashboardCanvas: React.FC<DashboardCanvasProps> = ({
cards={cards}
viewCards={viewCards}
browserCards={browserCards}
workflowCards={workflowCards}
workflowsHub={workflowsHub}
focusedCardId={focusedCardId}
shakeDirection={shakeDirection}
neighborDirections={neighborDirections}
@@ -273,6 +273,14 @@ const DashboardCardLayer: React.FC<DashboardCardLayerProps> = ({
zoom={zoom}
panX={panX}
panY={panY}
isSelected={selection.isSelected('workflows-hub')}
isHighlighted={highlightedCardId === 'workflows-hub'}
multiDragDelta={selection.isSelected('workflows-hub') ? multiDragDelta : null}
onCardSelect={onCardSelect}
onDragStart={onDragStart}
onDragMove={onDragMove}
onDragEnd={onDragEnd}
onBringToFront={onBringToFront}
/>
)}
{Object.values(workflowCards).map((wc) => (
@@ -9,6 +9,8 @@ import type {
CardPosition,
ViewCardPosition,
BrowserCardPosition,
WorkflowCardPosition,
WorkflowsHubPosition,
} from '@/shared/state/dashboardLayoutSlice';
import type { useCanvasControls } from '../hooks/interaction/useCanvasControls';
@@ -23,6 +25,8 @@ interface DashboardOverlaysProps {
cards: Record<string, CardPosition>;
viewCards: Record<string, ViewCardPosition>;
browserCards: Record<string, BrowserCardPosition>;
workflowCards: Record<string, WorkflowCardPosition>;
workflowsHub: WorkflowsHubPosition | null;
focusedCardId: string | null;
shakeDirection: Direction | null;
neighborDirections: NeighborDirections;
@@ -52,6 +56,8 @@ const DashboardOverlays: React.FC<DashboardOverlaysProps> = ({
cards,
viewCards,
browserCards,
workflowCards,
workflowsHub,
focusedCardId,
shakeDirection,
neighborDirections,
@@ -121,6 +127,8 @@ const DashboardOverlays: React.FC<DashboardOverlaysProps> = ({
cards,
viewCards,
browserCards,
workflowCards,
workflowsHub,
}}
onMinimapPan={(px, py) => canvas.actions.setState({ panX: px, panY: py, zoom: canvas.zoom })}
/>
@@ -1,6 +1,6 @@
import React, { useRef, useCallback, useMemo } from 'react';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import type { CardPosition, ViewCardPosition, BrowserCardPosition } from '@/shared/state/dashboardLayoutSlice';
import type { CardPosition, ViewCardPosition, BrowserCardPosition, WorkflowCardPosition, WorkflowsHubPosition } from '@/shared/state/dashboardLayoutSlice';
const MINIMAP_W = 200;
const MINIMAP_H = 140;
@@ -14,6 +14,8 @@ export interface MinimapProps {
cards: Record<string, CardPosition>;
viewCards: Record<string, ViewCardPosition>;
browserCards: Record<string, BrowserCardPosition>;
workflowCards: Record<string, WorkflowCardPosition>;
workflowsHub: WorkflowsHubPosition | null;
onPan: (panX: number, panY: number) => void;
}
@@ -22,12 +24,12 @@ interface CardRect {
y: number;
width: number;
height: number;
type: 'agent' | 'view' | 'browser';
type: 'agent' | 'view' | 'browser' | 'workflow' | 'workflows-hub';
}
const Minimap: React.FC<MinimapProps> = ({
panX, panY, zoom, viewportRef,
cards, viewCards, browserCards,
cards, viewCards, browserCards, workflowCards, workflowsHub,
onPan,
}) => {
const c = useClaudeTokens();
@@ -45,8 +47,20 @@ const Minimap: React.FC<MinimapProps> = ({
for (const bc of Object.values(browserCards)) {
result.push({ x: bc.x, y: bc.y, width: bc.width, height: bc.height, type: 'browser' });
}
for (const wc of Object.values(workflowCards)) {
result.push({ x: wc.x, y: wc.y, width: wc.width, height: wc.height, type: 'workflow' });
}
if (workflowsHub) {
result.push({
x: workflowsHub.x,
y: workflowsHub.y,
width: workflowsHub.width,
height: workflowsHub.height,
type: 'workflows-hub',
});
}
return result;
}, [cards, viewCards, browserCards]);
}, [cards, viewCards, browserCards, workflowCards, workflowsHub]);
const layout = useMemo(() => {
const vp = viewportRef.current;
@@ -128,11 +142,13 @@ const Minimap: React.FC<MinimapProps> = ({
window.addEventListener('mouseup', onUp);
}, [minimapToCanvas]);
const typeColor = (type: 'agent' | 'view' | 'browser') => {
const typeColor = (type: CardRect['type']) => {
switch (type) {
case 'agent': return c.accent.primary;
case 'view': return c.status.info;
case 'browser': return c.status.success;
case 'workflow': return c.status.warning;
case 'workflows-hub': return c.status.warning;
}
};
@@ -27,6 +27,10 @@ export function getCardRect(id: string, type: CardType):
const wc = layoutState.workflowCards[id];
if (!wc) return undefined;
return { x: wc.x, y: wc.y, width: wc.width, height: wc.height };
} else if (type === 'workflows-hub') {
const hub = layoutState.workflowsHub;
if (!hub) return undefined;
return { x: hub.x, y: hub.y, width: hub.width, height: hub.height };
}
return undefined;
}
@@ -55,6 +55,7 @@ export function useDashboardController(dashboardId: string, isActive: boolean) {
browserCards,
notes,
workflowCards,
workflowsHub,
);
const {
toolbarRef, toolbarOpen, setToolbarOpen, searchPaletteOpen, setSearchPaletteOpen,
@@ -1,5 +1,5 @@
import { useState, useCallback, useRef, useEffect, RefObject } from 'react';
import type { CardPosition, ViewCardPosition, BrowserCardPosition, NotePosition, WorkflowCardPosition } from '@/shared/state/dashboardLayoutSlice';
import type { CardPosition, ViewCardPosition, BrowserCardPosition, NotePosition, WorkflowCardPosition, WorkflowsHubPosition } from '@/shared/state/dashboardLayoutSlice';
export type { CardType } from '@/shared/state/dashboardLayoutSlice';
import type { CardType } from '@/shared/state/dashboardLayoutSlice';
@@ -44,6 +44,7 @@ export function useDashboardSelection(
browserCards: Record<string, BrowserCardPosition> = {},
notes: Record<string, NotePosition> = {},
workflowCards: Record<string, WorkflowCardPosition> = {},
workflowsHub: WorkflowsHubPosition | null = null,
) {
const [selectedIds, setSelectedIds] = useState<Map<string, CardType>>(new Map());
const [marquee, setMarquee] = useState<MarqueeRect | null>(null);
@@ -79,8 +80,9 @@ export function useDashboardSelection(
for (const bc of Object.values(browserCards)) next.set(bc.browser_id, 'browser');
for (const n of Object.values(notes)) next.set(n.note_id, 'note');
for (const wc of Object.values(workflowCards)) next.set(wc.workflow_id, 'workflow');
if (workflowsHub) next.set('workflows-hub', 'workflows-hub');
setSelectedIds(next);
}, [cards, viewCards, browserCards, notes, workflowCards]);
}, [cards, viewCards, browserCards, notes, workflowCards, workflowsHub]);
const selectCard = useCallback(
(id: string, type: CardType, shiftKey: boolean) => {
@@ -176,6 +178,18 @@ export function useDashboardSelection(
}
}
if (
workflowsHub &&
rectsIntersect(rect, {
x: workflowsHub.x,
y: workflowsHub.y,
width: workflowsHub.width,
height: workflowsHub.height,
})
) {
intersecting.set('workflows-hub', 'workflows-hub');
}
if (shiftKey) {
const base = selectionBeforeMarqueeRef.current;
const next = new Map(base);
@@ -191,7 +205,7 @@ export function useDashboardSelection(
return intersecting;
},
[cards, viewCards, browserCards, notes, workflowCards],
[cards, viewCards, browserCards, notes, workflowCards, workflowsHub],
);
const handleCanvasMouseDown = useCallback(