Skip to content

Commit 53c2716

Browse files
feat(blocks): surface deprecated block and model warnings on canvas (#5785)
* feat(blocks): surface deprecated block and model warnings on canvas * improvement(blocks): simplify deprecation derivation, drop unused getModelReplacement * fix(blocks): make deprecation badge keyboard-accessible
1 parent 70a7bf0 commit 53c2716

41 files changed

Lines changed: 298 additions & 6 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,10 +458,11 @@ export const Panel = memo(function Panel() {
458458

459459
useEffect(() => {
460460
const handler = (e: Event) => {
461-
const message = (e as CustomEvent<MothershipSendMessageDetail>).detail?.message
462-
if (!message) return
461+
const detail = (e as CustomEvent<MothershipSendMessageDetail>).detail
462+
if (!detail?.message) return
463+
e.preventDefault()
463464
setActiveTab('copilot')
464-
copilotSendMessage(message)
465+
copilotSendMessage(detail.message, undefined, detail.contexts)
465466
}
466467
window.addEventListener(MOTHERSHIP_SEND_MESSAGE_EVENT, handler)
467468
return () => window.removeEventListener(MOTHERSHIP_SEND_MESSAGE_EVENT, handler)

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ import { createLogger } from '@sim/logger'
33
import { SubBlockRowView, WorkflowBlockView } from '@sim/workflow-renderer'
44
import { isEqual } from 'es-toolkit'
55
import { useParams } from 'next/navigation'
6+
import { usePostHog } from 'posthog-js/react'
67
import { type NodeProps, useUpdateNodeInternals } from 'reactflow'
78
import { useStoreWithEqualityFn } from 'zustand/traditional'
89
import { getBaseUrl } from '@/lib/core/utils/urls'
910
import { createMcpToolId } from '@/lib/mcp/shared'
11+
import { sendMothershipMessage } from '@/lib/mothership/events'
1012
import { getProviderIdFromServiceId } from '@/lib/oauth'
13+
import { captureEvent } from '@/lib/posthog/client'
1114
import { calculateWorkflowBlockDimensions } from '@/lib/workflows/blocks/deterministic-dimensions'
1215
import { getConditionRows, getRouterRows } from '@/lib/workflows/dynamic-handle-topology'
1316
import {
@@ -45,7 +48,12 @@ import {
4548
import { useBlockVisual } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks'
4649
import { useBlockDimensions } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-block-dimensions'
4750
import { useCustomBlockOverlayVersion } from '@/blocks/custom/client-overlay'
48-
import { SELECTOR_TYPES_HYDRATION_REQUIRED, type SubBlockConfig } from '@/blocks/types'
51+
import { getBlock } from '@/blocks/registry'
52+
import {
53+
type BlockConfig,
54+
SELECTOR_TYPES_HYDRATION_REQUIRED,
55+
type SubBlockConfig,
56+
} from '@/blocks/types'
4957
import { getDependsOnFields } from '@/blocks/utils'
5058
import { useKnowledgeBase } from '@/hooks/kb/use-knowledge'
5159
import { useCustomTools } from '@/hooks/queries/custom-tools'
@@ -58,6 +66,7 @@ import { useTablesList } from '@/hooks/queries/tables'
5866
import { useWorkflowMap } from '@/hooks/queries/workflows'
5967
import { useReactiveConditions } from '@/hooks/use-reactive-conditions'
6068
import { useSelectorDisplayName } from '@/hooks/use-selector-display-name'
69+
import { isModelDeprecated } from '@/providers/models'
6170
import { useVariablesStore } from '@/stores/variables/store'
6271
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
6372
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
@@ -72,6 +81,48 @@ const EMPTY_SUBBLOCK_VALUES = {} as Record<string, any>
7281
/** Stable empty map for rows that never resolve MCP tool names */
7382
const EMPTY_MCP_TOOL_NAMES: ReadonlyMap<string, string> = new Map()
7483

84+
interface BlockDeprecation {
85+
kind: 'block' | 'model'
86+
tooltip: string
87+
prompt: string
88+
}
89+
90+
/**
91+
* Deprecation state for a placed block: the block type itself (via
92+
* `config.deprecated.replacedBy`) or its selected model. `null` when neither
93+
* applies or in diff mode. Drives the canvas badge + click-to-fix prompt.
94+
*/
95+
function getBlockDeprecation(
96+
config: BlockConfig,
97+
name: string,
98+
model: unknown,
99+
isDiffMode: boolean
100+
): BlockDeprecation | null {
101+
if (isDiffMode) return null
102+
103+
const replacedBy = config.deprecated?.replacedBy
104+
if (replacedBy) {
105+
const target = getBlock(replacedBy)
106+
if (!target) return null
107+
const hasModel = config.subBlocks?.some((sub) => sub.id === 'model')
108+
return {
109+
kind: 'block',
110+
tooltip: 'This block is deprecated. Click to upgrade',
111+
prompt: `The "${name}" block is deprecated. Migrate it to the current ${target.name} block: change the block type, then set the new block's required inputs as a separate edit (inputs are validated against the old type when sent in the same edit), or delete it and re-add ${target.name} and rewire the connections.${hasModel ? ' Also pick a current, non-deprecated model.' : ''}`,
112+
}
113+
}
114+
115+
if (typeof model === 'string' && isModelDeprecated(model)) {
116+
return {
117+
kind: 'model',
118+
tooltip: `${model} is deprecated. Click to upgrade`,
119+
prompt: `The "${name}" block uses the deprecated model "${model}". Switch it to the latest equivalent model.`,
120+
}
121+
}
122+
123+
return null
124+
}
125+
75126
interface SubBlockRowProps {
76127
title: string
77128
value?: string
@@ -479,6 +530,28 @@ export const WorkflowBlock = memo(function WorkflowBlock({
479530
),
480531
isEqual
481532
)
533+
534+
const posthog = usePostHog()
535+
536+
const deprecation = getBlockDeprecation(
537+
config,
538+
name,
539+
blockSubBlockValues.model,
540+
currentWorkflow.isDiffMode
541+
)
542+
543+
const onFixDeprecation = () => {
544+
if (!deprecation) return
545+
captureEvent(posthog, 'deprecated_block_fix_clicked', {
546+
block_type: type,
547+
workflow_id: currentWorkflowId,
548+
kind: deprecation.kind,
549+
})
550+
sendMothershipMessage(deprecation.prompt, [
551+
{ kind: 'workflow_block', workflowId: currentWorkflowId, blockId: id, label: name },
552+
])
553+
}
554+
482555
const canonicalIndex = useMemo(() => buildCanonicalIndex(config.subBlocks), [config.subBlocks])
483556
const canonicalModeOverrides = currentStoreBlock?.data?.canonicalModes
484557

@@ -798,6 +871,9 @@ export const WorkflowBlock = memo(function WorkflowBlock({
798871
deployChildWorkflow({ workflowId: childWorkflowId })
799872
}
800873
}}
874+
deprecationTooltip={deprecation?.tooltip}
875+
canFixDeprecation={canEditWorkflow}
876+
onFixDeprecation={onFixDeprecation}
801877
shouldShowScheduleBadge={shouldShowScheduleBadge}
802878
scheduleIsDisabled={Boolean(scheduleInfo?.isDisabled)}
803879
onReactivateSchedule={() => {

apps/sim/blocks/blocks/api_trigger.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const ApiTriggerBlock: BlockConfig = {
1515
`,
1616
category: 'triggers',
1717
hideFromToolbar: true,
18+
deprecated: { replacedBy: 'start_trigger' },
1819
bgColor: '#2F55FF',
1920
icon: ApiIcon,
2021
subBlocks: [

apps/sim/blocks/blocks/chat_trigger.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const ChatTriggerBlock: BlockConfig = {
1616
`,
1717
category: 'triggers',
1818
hideFromToolbar: true,
19+
deprecated: { replacedBy: 'start_trigger' },
1920
bgColor: '#6F3DFA',
2021
icon: ChatTriggerIcon,
2122
subBlocks: [],

apps/sim/blocks/blocks/confluence.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
1212
name: 'Confluence (Legacy)',
1313
description: 'Interact with Confluence',
1414
hideFromToolbar: true,
15+
deprecated: { replacedBy: 'confluence_v2' },
1516
authMode: AuthMode.OAuth,
1617
longDescription:
1718
'Integrate Confluence into the workflow. Can read, create, update, delete pages, manage comments, attachments, labels, and search content.',
@@ -360,6 +361,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
360361

361362
export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
362363
...ConfluenceBlock,
364+
deprecated: undefined,
363365
type: 'confluence_v2',
364366
name: 'Confluence',
365367
hideFromToolbar: false,

apps/sim/blocks/blocks/cursor.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const CursorBlock: BlockConfig<CursorResponse> = {
1717
icon: CursorIcon,
1818
authMode: AuthMode.ApiKey,
1919
hideFromToolbar: true,
20+
deprecated: { replacedBy: 'cursor_v2' },
2021
subBlocks: [
2122
{
2223
id: 'operation',
@@ -223,6 +224,7 @@ export const CursorBlock: BlockConfig<CursorResponse> = {
223224

224225
export const CursorV2Block: BlockConfig<CursorResponse> = {
225226
...CursorBlock,
227+
deprecated: undefined,
226228
type: 'cursor_v2',
227229
name: 'Cursor',
228230
description: 'Launch and manage Cursor cloud agents to work on GitHub repositories',

apps/sim/blocks/blocks/extend.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const ExtendBlock: BlockConfig<ExtendParserOutput> = {
1414
name: 'Extend',
1515
description: 'Parse and extract content from documents',
1616
hideFromToolbar: true,
17+
deprecated: { replacedBy: 'extend_v2' },
1718
authMode: AuthMode.ApiKey,
1819
longDescription:
1920
'Integrate Extend AI into the workflow. Parse and extract structured content from documents including PDFs, images, and Office files.',
@@ -165,6 +166,7 @@ const extendV2SubBlocks = (ExtendBlock.subBlocks || []).flatMap((subBlock) => {
165166

166167
export const ExtendV2Block: BlockConfig<ExtendParserOutput> = {
167168
...ExtendBlock,
169+
deprecated: undefined,
168170
type: 'extend_v2',
169171
name: 'Extend',
170172
hideFromToolbar: false,

apps/sim/blocks/blocks/file.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export const FileBlock: BlockConfig<FileParserOutput> = {
7878
bgColor: '#40916C',
7979
icon: DocumentIcon,
8080
hideFromToolbar: true,
81+
deprecated: { replacedBy: 'file_v5' },
8182
subBlocks: [
8283
{
8384
id: 'inputMethod',
@@ -185,6 +186,7 @@ export const FileV2Block: BlockConfig<FileParserOutput> = {
185186
name: 'File (Legacy)',
186187
description: 'Read and parse multiple files',
187188
hideFromToolbar: true,
189+
deprecated: { replacedBy: 'file_v5' },
188190
subBlocks: [
189191
{
190192
id: 'file',
@@ -278,6 +280,7 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
278280
bgColor: '#40916C',
279281
icon: DocumentIcon,
280282
hideFromToolbar: true,
283+
deprecated: { replacedBy: 'file_v5' },
281284
subBlocks: [
282285
{
283286
id: 'operation',
@@ -568,6 +571,7 @@ export const FileV4Block: BlockConfig<FileParserV3Output> = {
568571
longDescription:
569572
'Read workspace files by picker or canonical ID, fetch and parse files from URLs with optional headers, write new workspace files, or append content to existing files.',
570573
hideFromToolbar: true,
574+
deprecated: { replacedBy: 'file_v5' },
571575
bestPractices: `
572576
- Use Read when you need an existing workspace file object by picker selection or canonical file ID.
573577
- Use Fetch for external file URLs. Add headers for authenticated downloads, for example Slack private file URLs require an Authorization Bearer token.
@@ -820,6 +824,7 @@ export const FileV4Block: BlockConfig<FileParserV3Output> = {
820824

821825
export const FileV5Block: BlockConfig<FileParserV3Output> = {
822826
...FileV4Block,
827+
deprecated: undefined,
823828
type: 'file_v5',
824829
name: 'File',
825830
description:

apps/sim/blocks/blocks/fireflies.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const FirefliesBlock: BlockConfig<FirefliesResponse> = {
1111
name: 'Fireflies (Legacy)',
1212
description: 'Interact with Fireflies.ai meeting transcripts and recordings',
1313
hideFromToolbar: true,
14+
deprecated: { replacedBy: 'fireflies_v2' },
1415
authMode: AuthMode.ApiKey,
1516
triggerAllowed: true,
1617
longDescription:
@@ -646,6 +647,7 @@ const firefliesV2Inputs = FirefliesBlock.inputs
646647

647648
export const FirefliesV2Block: BlockConfig<FirefliesResponse> = {
648649
...FirefliesBlock,
650+
deprecated: undefined,
649651
type: 'fireflies_v2',
650652
name: 'Fireflies',
651653
description: 'Interact with Fireflies.ai meeting transcripts and recordings',

apps/sim/blocks/blocks/github.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
2020
icon: GithubIcon,
2121
triggerAllowed: true,
2222
hideFromToolbar: true,
23+
deprecated: { replacedBy: 'github_v2' },
2324
subBlocks: [
2425
{
2526
id: 'operation',
@@ -2102,6 +2103,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
21022103

21032104
export const GitHubV2Block: BlockConfig<GitHubResponse> = {
21042105
...GitHubBlock,
2106+
deprecated: undefined,
21052107
type: 'github_v2',
21062108
name: 'GitHub',
21072109
hideFromToolbar: false,

0 commit comments

Comments
 (0)