diff --git a/frontend/src/app/pages/Workflows/WorkflowCard.tsx b/frontend/src/app/pages/Workflows/WorkflowCard.tsx index d434c4bb..c89fd0d7 100644 --- a/frontend/src/app/pages/Workflows/WorkflowCard.tsx +++ b/frontend/src/app/pages/Workflows/WorkflowCard.tsx @@ -43,7 +43,7 @@ import InlineEditableTitle from '@/app/components/InlineEditableTitle'; import { Typewriter } from '@/app/components/feedback/Animated'; import StopRounded from '@mui/icons-material/StopRounded'; import PauseRounded from '@mui/icons-material/PauseRounded'; -import { StatusDot, RunSparkline, LastFiredHint, isStaleSinceLastRun } from './workflowVisuals'; +import { StatusDot, RunSparkline, LastFiredHint, isStaleSinceLastRun, isRealTitle } from './workflowVisuals'; import { store } from '@/shared/state/store'; import { getAgentWorkTime, fmtSeconds } from '@/shared/agentWorkTime'; @@ -75,14 +75,6 @@ const HANDLE_DEFS: { dir: ResizeDir; sx: Record }[] = [ { dir: 'se', sx: { bottom: -EDGE_THICKNESS / 2, right: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, ]; -// Placeholder titles the backend uses before auto-naming kicks in. The title -// Typewriter only animates once the title is a real (generated/user) name, so -// the card doesn't animate on mount or while still showing a placeholder. -const PLACEHOLDER_TITLES = new Set(['', 'New workflow', 'Untitled workflow', 'Scheduled workflow']); -function isRealTitle(title?: string | null): boolean { - return !!title && !PLACEHOLDER_TITLES.has(title.trim()); -} - interface Props { workflowId: string; cardX: number; diff --git a/frontend/src/app/pages/Workflows/WorkflowsHubCard.tsx b/frontend/src/app/pages/Workflows/WorkflowsHubCard.tsx index 996dfbb6..3bbe53ac 100644 --- a/frontend/src/app/pages/Workflows/WorkflowsHubCard.tsx +++ b/frontend/src/app/pages/Workflows/WorkflowsHubCard.tsx @@ -29,6 +29,8 @@ import { useEffect } from 'react'; import ScheduleCalendar from './ScheduleCalendar'; import AddToSchedulePopover from './AddToSchedulePopover'; import { WEEKDAY_LABEL, addDays, sameDay, startOfMonthGrid, isWorkflowSchedulable } from './scheduleUtils'; +import { isRealTitle } from './workflowVisuals'; +import { Typewriter } from '@/app/components/feedback/Animated'; type ResizeDir = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw'; @@ -680,7 +682,14 @@ function SidebarSection({ title, items, onPick, scheduled, onContext, onSchedule )} - {w.title} + {/* Retype the title letter-by-letter when it auto-renames, matching + the workflow card. Gated on a real (non-placeholder) title so it + never animates on first appearance or for already-named rows. */} + + {(t) => ( + {t} + )} + {scheduled && !w.schedule.enabled && ( Paused )} diff --git a/frontend/src/app/pages/Workflows/workflowVisuals.tsx b/frontend/src/app/pages/Workflows/workflowVisuals.tsx index c2e1d808..acfcbeeb 100644 --- a/frontend/src/app/pages/Workflows/workflowVisuals.tsx +++ b/frontend/src/app/pages/Workflows/workflowVisuals.tsx @@ -31,6 +31,16 @@ import { useClaudeTokens } from '@/shared/styles/ThemeContext'; import type { Workflow, WorkflowRun, ScheduleConfig, PermissionTier } from '@/shared/state/workflowsSlice'; import { formatTime, WEEKDAY_LABEL, isScheduleConfigured } from './scheduleUtils'; +// ---------- Title placeholders ---------- + +// Placeholder titles the backend uses before auto-naming kicks in. The title +// Typewriter only animates once the title is a real (generated/user) name, so +// the UI doesn't animate on mount or while still showing a placeholder. +const PLACEHOLDER_TITLES = new Set(['', 'New workflow', 'Untitled workflow', 'Scheduled workflow']); +export function isRealTitle(title?: string | null): boolean { + return !!title && !PLACEHOLDER_TITLES.has(title.trim()); +} + // ---------- Status colors ---------- export type LastRunStatus = NonNullable;