Skip to content

Commit 257087a

Browse files
committed
v2
1 parent 5e75863 commit 257087a

8 files changed

Lines changed: 82 additions & 115 deletions

File tree

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

Lines changed: 52 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,66 @@
11
import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
22
import ReactMarkdown from 'react-markdown'
33
import remarkGfm from 'remark-gfm'
4-
import { useUpdateNodeInternals, type NodeProps } from 'reactflow'
4+
import { type NodeProps, useUpdateNodeInternals } from 'reactflow'
55
import { cn } from '@/lib/utils'
66
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
7+
import { usePanelEditorStore } from '@/stores/panel-new/editor/store'
8+
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
9+
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
10+
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
711
import { useCurrentWorkflow } from '../../hooks'
812
import { ActionBar } from '../workflow-block/components'
913
import { useBlockState } from '../workflow-block/hooks'
1014
import type { WorkflowBlockProps } from '../workflow-block/types'
11-
import { usePanelEditorStore } from '@/stores/panel-new/editor/store'
12-
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
13-
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
14-
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
1515

1616
interface NoteBlockNodeData extends WorkflowBlockProps {}
1717

1818
const NOTE_MIN_WIDTH = 220
1919
const NOTE_MIN_HEIGHT = 140
2020

21-
const MarkdownContent = memo(function MarkdownContent({ content }: { content: string }) {
21+
/**
22+
* Extract string value from subblock value object or primitive
23+
*/
24+
function extractFieldValue(rawValue: unknown): string | undefined {
25+
if (typeof rawValue === 'string') return rawValue
26+
if (rawValue && typeof rawValue === 'object' && 'value' in rawValue) {
27+
const candidate = (rawValue as { value?: unknown }).value
28+
return typeof candidate === 'string' ? candidate : undefined
29+
}
30+
return undefined
31+
}
32+
33+
/**
34+
* Compact markdown renderer for note blocks with tight spacing
35+
*/
36+
const NoteMarkdown = memo(function NoteMarkdown({ content }: { content: string }) {
2237
return (
2338
<ReactMarkdown
2439
remarkPlugins={[remarkGfm]}
2540
components={{
26-
p: ({ children }) => (
27-
<p className='mb-2 text-sm leading-relaxed text-[#E5E5E5] last:mb-0'>{children}</p>
28-
),
29-
strong: ({ children }) => <strong className='font-semibold text-white'>{children}</strong>,
30-
em: ({ children }) => <em className='text-[#B8B8B8]'>{children}</em>,
31-
h1: ({ children }) => (
32-
<h1 className='mt-0 mb-[6px] text-lg font-semibold leading-snug text-[#E5E5E5]'>
33-
{children}
34-
</h1>
35-
),
36-
h2: ({ children }) => (
37-
<h2 className='mt-0 mb-[4px] text-base font-semibold leading-snug text-[#E5E5E5]'>
38-
{children}
39-
</h2>
40-
),
41-
h3: ({ children }) => (
42-
<h3 className='mt-0 mb-[4px] text-sm font-semibold leading-snug text-[#E5E5E5]'>
43-
{children}
44-
</h3>
45-
),
46-
a: ({ href, children }) => (
47-
<a
48-
href={href}
49-
target='_blank'
50-
rel='noopener noreferrer'
51-
className='font-medium text-[#33B4FF] underline-offset-2 hover:underline'
52-
>
53-
{children}
54-
</a>
55-
),
56-
ul: ({ children }) => (
57-
<ul className='mb-2 ml-5 list-disc text-sm leading-relaxed text-[#E5E5E5]'>{children}</ul>
58-
),
59-
ol: ({ children }) => (
60-
<ol className='mb-2 ml-5 list-decimal text-sm leading-relaxed text-[#E5E5E5]'>{children}</ol>
61-
),
62-
li: ({ children }) => <li className='mb-1 last:mb-0'>{children}</li>,
41+
p: ({ children }) => <p className='mb-0 text-sm text-[#E5E5E5]'>{children}</p>,
42+
h1: ({ children }) => <h1 className='mt-0 mb-[-2px] text-lg font-semibold text-[#E5E5E5]'>{children}</h1>,
43+
h2: ({ children }) => <h2 className='mt-0 mb-[-2px] text-base font-semibold text-[#E5E5E5]'>{children}</h2>,
44+
h3: ({ children }) => <h3 className='mt-0 mb-[-2px] text-sm font-semibold text-[#E5E5E5]'>{children}</h3>,
45+
h4: ({ children }) => <h4 className='mt-0 mb-[-2px] text-xs font-semibold text-[#E5E5E5]'>{children}</h4>,
46+
ul: ({ children }) => <ul className='-mt-[2px] mb-0 list-disc pl-4 text-sm text-[#E5E5E5]'>{children}</ul>,
47+
ol: ({ children }) => <ol className='-mt-[2px] mb-0 list-decimal pl-4 text-sm text-[#E5E5E5]'>{children}</ol>,
48+
li: ({ children }) => <li className='mb-0'>{children}</li>,
6349
code: ({ inline, children }: any) =>
6450
inline ? (
6551
<code className='rounded bg-[#393939] px-1 py-0.5 text-xs text-[#F59E0B]'>{children}</code>
6652
) : (
6753
<code className='block rounded bg-[#1A1A1A] p-2 text-xs text-[#E5E5E5]'>{children}</code>
6854
),
69-
blockquote: ({ children }) => (
70-
<blockquote className='mb-3 border-l-2 border-[#F59E0B] pl-3 italic text-[#B8B8B8]'>
55+
a: ({ href, children }) => (
56+
<a href={href} target='_blank' rel='noopener noreferrer' className='text-[#33B4FF] underline-offset-2 hover:underline'>
7157
{children}
72-
</blockquote>
58+
</a>
59+
),
60+
strong: ({ children }) => <strong className='font-semibold text-white'>{children}</strong>,
61+
em: ({ children }) => <em className='text-[#B8B8B8]'>{children}</em>,
62+
blockquote: ({ children }) => (
63+
<blockquote className='m-0 border-l-2 border-[#F59E0B] pl-3 italic text-[#B8B8B8]'>{children}</blockquote>
7364
),
7465
}}
7566
>
@@ -90,7 +81,11 @@ export const NoteBlock = memo(function NoteBlock({ id, data }: NodeProps<NoteBlo
9081
const isFocused = currentBlockId === id
9182

9283
const currentWorkflow = useCurrentWorkflow()
93-
const { isEnabled, isActive, diffStatus, isDeletedBlock } = useBlockState(id, currentWorkflow, data)
84+
const { isEnabled, isActive, diffStatus, isDeletedBlock } = useBlockState(
85+
id,
86+
currentWorkflow,
87+
data
88+
)
9489

9590
const activeWorkflowId = useWorkflowRegistry((state) => state.activeWorkflowId)
9691
const storedValues = useSubBlockStore(
@@ -105,45 +100,25 @@ export const NoteBlock = memo(function NoteBlock({ id, data }: NodeProps<NoteBlo
105100

106101
const noteValues = useMemo(() => {
107102
if (data.isPreview && data.subBlockValues) {
108-
const previewFormatState = data.subBlockValues.format
109-
const previewContentState = data.subBlockValues.content
110-
111-
const extractedPreviewFormat =
112-
typeof previewFormatState === 'object' && previewFormatState !== null
113-
? (previewFormatState as { value?: unknown }).value
114-
: previewFormatState
115-
const extractedPreviewContent =
116-
typeof previewContentState === 'object' && previewContentState !== null
117-
? (previewContentState as { value?: unknown }).value
118-
: previewContentState
119-
103+
const extractedPreviewFormat = extractFieldValue(data.subBlockValues.format)
104+
const extractedPreviewContent = extractFieldValue(data.subBlockValues.content)
120105
return {
121106
format: typeof extractedPreviewFormat === 'string' ? extractedPreviewFormat : 'plain',
122107
content: typeof extractedPreviewContent === 'string' ? extractedPreviewContent : '',
123108
}
124109
}
125110

126-
const format =
127-
storedValues && typeof storedValues.format === 'string'
128-
? storedValues.format
129-
: typeof storedValues?.format === 'object' && storedValues?.format !== null
130-
? (storedValues.format as { value?: unknown }).value
131-
: undefined
132-
const content =
133-
storedValues && typeof storedValues.content === 'string'
134-
? storedValues.content
135-
: typeof storedValues?.content === 'object' && storedValues?.content !== null
136-
? (storedValues.content as { value?: unknown }).value
137-
: undefined
111+
const format = extractFieldValue(storedValues?.format)
112+
const content = extractFieldValue(storedValues?.content)
138113

139114
return {
140115
format: typeof format === 'string' ? format : 'plain',
141116
content: typeof content === 'string' ? content : '',
142117
}
143118
}, [data.isPreview, data.subBlockValues, storedValues])
144119

145-
const trimmedContent = noteValues.content?.trim() ?? ''
146-
const isEmpty = trimmedContent.length === 0
120+
const content = noteValues.content ?? ''
121+
const isEmpty = content.trim().length === 0
147122
const showMarkdown = noteValues.format === 'markdown' && !isEmpty
148123

149124
const userPermissions = useUserPermissionsContext()
@@ -215,14 +190,14 @@ export const NoteBlock = memo(function NoteBlock({ id, data }: NodeProps<NoteBlo
215190
</div>
216191
</div>
217192

218-
<div className='relative px-[12px] py-[10px]'>
193+
<div className='relative px-[12px] pt-[6px] pb-[8px]'>
219194
<div className='relative whitespace-pre-wrap break-words'>
220195
{isEmpty ? (
221-
<p className='text-sm italic text-[#868686]'>Add your note...</p>
196+
<p className='text-[#868686] text-sm italic'>Add a note...</p>
222197
) : showMarkdown ? (
223-
<MarkdownContent content={trimmedContent} />
198+
<NoteMarkdown content={content} />
224199
) : (
225-
<p className='text-sm leading-relaxed text-[#E5E5E5]'>{trimmedContent}</p>
200+
<p className='whitespace-pre-wrap text-[#E5E5E5] text-sm leading-relaxed'>{content}</p>
226201
)}
227202
</div>
228203
</div>
@@ -235,4 +210,3 @@ export const NoteBlock = memo(function NoteBlock({ id, data }: NodeProps<NoteBlo
235210
</div>
236211
)
237212
})
238-

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
22
import { useParams } from 'next/navigation'
3+
import ReactMarkdown from 'react-markdown'
4+
import remarkGfm from 'remark-gfm'
35
import { Handle, type NodeProps, Position, useUpdateNodeInternals } from 'reactflow'
46
import { Badge } from '@/components/emcn/components/badge/badge'
57
import { Tooltip } from '@/components/emcn/components/tooltip/tooltip'

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import { DiffControls } from '@/app/workspace/[workspaceId]/w/[workflowId]/compo
1818
import { Chat } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat'
1919
import { UserAvatarStack } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/user-avatar-stack'
2020
import { ErrorBoundary } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/error/index'
21-
import { Panel } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/panel-new'
2221
import { NoteBlock } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/note-block/note-block'
22+
import { Panel } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/panel-new'
2323
import { SubflowNodeComponent } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/subflow-node'
2424
import { Terminal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/terminal'
2525
import { TrainingControls } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/training-controls/training-controls'
@@ -1306,13 +1306,10 @@ const WorkflowContent = React.memo(() => {
13061306
typeof block.layout?.measuredHeight === 'number' ? block.layout.measuredHeight : undefined
13071307

13081308
const nodeType = block.type === 'note' ? 'noteBlock' : 'workflowBlock'
1309-
const dragHandle =
1310-
block.type === 'note' ? '.note-drag-handle' : '.workflow-drag-handle'
1309+
const dragHandle = block.type === 'note' ? '.note-drag-handle' : '.workflow-drag-handle'
13111310

13121311
const defaultWidth =
1313-
block.type === 'note'
1314-
? Math.max(measuredWidth ?? block.data?.width ?? 260, 200)
1315-
: 250
1312+
block.type === 'note' ? Math.max(measuredWidth ?? block.data?.width ?? 260, 200) : 250
13161313

13171314
const defaultHeight =
13181315
block.type === 'note'

apps/sim/blocks/blocks/note.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,3 @@ export const NoteBlock: BlockConfig = {
4444
},
4545
outputs: {},
4646
}
47-

apps/sim/lib/workflows/autolayout/containers.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import {
99
CONTAINER_PADDING_Y,
1010
DEFAULT_CONTAINER_HEIGHT,
1111
DEFAULT_CONTAINER_WIDTH,
12+
filterLayoutEligibleBlockIds,
1213
getBlocksByParent,
1314
prepareBlockMetrics,
14-
shouldSkipAutoLayout,
1515
} from './utils'
1616

1717
const logger = createLogger('AutoLayout:Containers')
@@ -36,14 +36,10 @@ export function layoutContainers(
3636

3737
logger.debug('Processing container', { parentId, childCount: childIds.length })
3838

39+
const layoutChildIds = filterLayoutEligibleBlockIds(childIds, blocks)
3940
const childBlocks: Record<string, BlockState> = {}
40-
const layoutChildIds: string[] = []
41-
for (const childId of childIds) {
42-
const childBlock = blocks[childId]
43-
if (!childBlock) continue
44-
if (shouldSkipAutoLayout(childBlock)) continue
45-
childBlocks[childId] = childBlock
46-
layoutChildIds.push(childId)
41+
for (const childId of layoutChildIds) {
42+
childBlocks[childId] = blocks[childId]
4743
}
4844

4945
const childEdges = edges.filter(

apps/sim/lib/workflows/autolayout/index.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { adjustForNewBlock as adjustForNewBlockInternal, compactHorizontally } f
55
import { assignLayers, groupByLayer } from './layering'
66
import { calculatePositions } from './positioning'
77
import type { AdjustmentOptions, Edge, LayoutOptions, LayoutResult, Loop, Parallel } from './types'
8-
import { getBlocksByParent, prepareBlockMetrics, shouldSkipAutoLayout } from './utils'
8+
import { filterLayoutEligibleBlockIds, getBlocksByParent, prepareBlockMetrics } from './utils'
99

1010
const logger = createLogger('AutoLayout')
1111

@@ -28,12 +28,7 @@ export function applyAutoLayout(
2828

2929
const { root: rootBlockIds } = getBlocksByParent(blocksCopy)
3030

31-
const layoutRootIds = rootBlockIds.filter((id) => {
32-
const block = blocksCopy[id]
33-
if (!block) return false
34-
if (shouldSkipAutoLayout(block)) return false
35-
return true
36-
})
31+
const layoutRootIds = filterLayoutEligibleBlockIds(rootBlockIds, blocksCopy)
3732

3833
const rootBlocks: Record<string, BlockState> = {}
3934
for (const id of layoutRootIds) {

apps/sim/lib/workflows/autolayout/targeted.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
CONTAINER_PADDING_Y,
1010
DEFAULT_CONTAINER_HEIGHT,
1111
DEFAULT_CONTAINER_WIDTH,
12+
filterLayoutEligibleBlockIds,
1213
getBlockMetrics,
1314
getBlocksByParent,
1415
isContainerType,
@@ -72,12 +73,7 @@ function layoutGroup(
7273

7374
const parentBlock = parentId ? blocks[parentId] : undefined
7475

75-
const layoutEligibleChildIds = childIds.filter((id) => {
76-
const block = blocks[id]
77-
if (!block) return false
78-
if (shouldSkipAutoLayout(block)) return false
79-
return true
80-
})
76+
const layoutEligibleChildIds = filterLayoutEligibleBlockIds(childIds, blocks)
8177

8278
if (layoutEligibleChildIds.length === 0) {
8379
if (parentBlock) {
@@ -86,19 +82,17 @@ function layoutGroup(
8682
return
8783
}
8884

89-
const requestedLayout = childIds.filter((id) => {
85+
const requestedLayout = layoutEligibleChildIds.filter((id) => {
9086
const block = blocks[id]
9187
if (!block) return false
9288
// Never reposition containers, only update their dimensions
9389
if (isContainerType(block.type)) return false
94-
if (shouldSkipAutoLayout(block)) return false
9590
return changedSet.has(id)
9691
})
97-
const missingPositions = childIds.filter((id) => {
92+
const missingPositions = layoutEligibleChildIds.filter((id) => {
9893
const block = blocks[id]
9994
if (!block) return false
10095
// Containers with missing positions should still get positioned
101-
if (shouldSkipAutoLayout(block)) return false
10296
return !hasPosition(block)
10397
})
10498
const needsLayoutSet = new Set([...requestedLayout, ...missingPositions])
@@ -116,7 +110,7 @@ function layoutGroup(
116110

117111
const oldPositions = new Map<string, { x: number; y: number }>()
118112

119-
for (const id of childIds) {
113+
for (const id of layoutEligibleChildIds) {
120114
const block = blocks[id]
121115
if (!block) continue
122116
oldPositions.set(id, { ...block.position })

apps/sim/lib/workflows/autolayout/utils.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,26 @@ function resolveNumeric(value: number | undefined, fallback: number): number {
1818
return typeof value === 'number' && Number.isFinite(value) ? value : fallback
1919
}
2020

21+
const AUTO_LAYOUT_EXCLUDED_TYPES = new Set(['note'])
22+
2123
export function isContainerType(blockType: string): boolean {
2224
return blockType === 'loop' || blockType === 'parallel'
2325
}
2426

25-
export function isNoteBlockType(blockType: string): boolean {
26-
return blockType === 'note'
27+
export function shouldSkipAutoLayout(block?: BlockState): boolean {
28+
if (!block) return true
29+
return AUTO_LAYOUT_EXCLUDED_TYPES.has(block.type)
2730
}
2831

29-
export function shouldSkipAutoLayout(block: BlockState): boolean {
30-
return isNoteBlockType(block.type)
32+
export function filterLayoutEligibleBlockIds(
33+
blockIds: string[],
34+
blocks: Record<string, BlockState>
35+
): string[] {
36+
return blockIds.filter((id) => {
37+
const block = blocks[id]
38+
if (!block) return false
39+
return !shouldSkipAutoLayout(block)
40+
})
3141
}
3242

3343
function getContainerMetrics(block: BlockState): BlockMetrics {

0 commit comments

Comments
 (0)