mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
fix(notes): fix notes, tighten spacing, update deprecated zustand function, update use mention data to ignore block positon (#2002)
This commit is contained in:
@@ -35,33 +35,47 @@ const NoteMarkdown = memo(function NoteMarkdown({ content }: { content: string }
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
p: ({ children }) => <p className='mb-0 text-[#E5E5E5] text-sm'>{children}</p>,
|
||||
p: ({ children }) => (
|
||||
<p className='!mt-0 !-mb-4 text-[#E5E5E5] text-sm leading-tight'>{children}</p>
|
||||
),
|
||||
h1: ({ children }) => (
|
||||
<h1 className='mt-0 mb-[-2px] font-semibold text-[#E5E5E5] text-lg'>{children}</h1>
|
||||
<h1 className='!mt-0 !-mb-4 font-semibold text-[#E5E5E5] text-lg leading-tight'>
|
||||
{children}
|
||||
</h1>
|
||||
),
|
||||
h2: ({ children }) => (
|
||||
<h2 className='mt-0 mb-[-2px] font-semibold text-[#E5E5E5] text-base'>{children}</h2>
|
||||
<h2 className='!mt-0 !-mb-4 font-semibold text-[#E5E5E5] text-base leading-tight'>
|
||||
{children}
|
||||
</h2>
|
||||
),
|
||||
h3: ({ children }) => (
|
||||
<h3 className='mt-0 mb-[-2px] font-semibold text-[#E5E5E5] text-sm'>{children}</h3>
|
||||
<h3 className='!mt-0 !-mb-4 font-semibold text-[#E5E5E5] text-sm leading-tight'>
|
||||
{children}
|
||||
</h3>
|
||||
),
|
||||
h4: ({ children }) => (
|
||||
<h4 className='mt-0 mb-[-2px] font-semibold text-[#E5E5E5] text-xs'>{children}</h4>
|
||||
<h4 className='!mt-0 !-mb-4 font-semibold text-[#E5E5E5] text-xs leading-tight'>
|
||||
{children}
|
||||
</h4>
|
||||
),
|
||||
ul: ({ children }) => (
|
||||
<ul className='-mt-[2px] mb-0 list-disc pl-4 text-[#E5E5E5] text-sm'>{children}</ul>
|
||||
<ul className='!-mt-4 !-mb-4 [&_li>ul]:!mt-0 [&_li>ul]:!mb-0 [&_li>ol]:!mt-0 [&_li>ol]:!mb-0 list-disc pl-4 text-[#E5E5E5] text-sm leading-tight'>
|
||||
{children}
|
||||
</ul>
|
||||
),
|
||||
ol: ({ children }) => (
|
||||
<ol className='-mt-[2px] mb-0 list-decimal pl-4 text-[#E5E5E5] text-sm'>{children}</ol>
|
||||
<ol className='!-mt-4 !-mb-4 [&_li>ul]:!mt-0 [&_li>ul]:!mb-0 [&_li>ol]:!mt-0 [&_li>ol]:!mb-0 list-decimal pl-4 text-[#E5E5E5] text-sm leading-tight'>
|
||||
{children}
|
||||
</ol>
|
||||
),
|
||||
li: ({ children }) => <li className='mb-0'>{children}</li>,
|
||||
li: ({ children }) => <li className='!mb-0 leading-tight'>{children}</li>,
|
||||
code: ({ inline, children }: any) =>
|
||||
inline ? (
|
||||
<code className='rounded bg-[var(--divider)] px-1 py-0.5 text-[#F59E0B] text-xs'>
|
||||
<code className='break-words rounded bg-[var(--divider)] px-1 py-0.5 text-[#F59E0B] text-xs'>
|
||||
{children}
|
||||
</code>
|
||||
) : (
|
||||
<code className='block rounded bg-[#1A1A1A] p-2 text-[#E5E5E5] text-xs'>
|
||||
<code className='block whitespace-pre-wrap break-words rounded bg-[#1A1A1A] p-2 text-[#E5E5E5] text-xs'>
|
||||
{children}
|
||||
</code>
|
||||
),
|
||||
@@ -78,7 +92,7 @@ const NoteMarkdown = memo(function NoteMarkdown({ content }: { content: string }
|
||||
strong: ({ children }) => <strong className='font-semibold text-white'>{children}</strong>,
|
||||
em: ({ children }) => <em className='text-[#B8B8B8]'>{children}</em>,
|
||||
blockquote: ({ children }) => (
|
||||
<blockquote className='m-0 border-[#F59E0B] border-l-2 pl-3 text-[#B8B8B8] italic'>
|
||||
<blockquote className='!mt-0 !-mb-4 border-[#F59E0B] border-l-2 pl-3 text-[#B8B8B8] italic'>
|
||||
{children}
|
||||
</blockquote>
|
||||
),
|
||||
@@ -187,9 +201,7 @@ export const NoteBlock = memo(function NoteBlock({ id, data }: NodeProps<NoteBlo
|
||||
) : showMarkdown ? (
|
||||
<NoteMarkdown content={content} />
|
||||
) : (
|
||||
<p className='whitespace-pre-wrap text-[#E5E5E5] text-sm leading-relaxed'>
|
||||
{content}
|
||||
</p>
|
||||
<p className='whitespace-pre-wrap text-[#E5E5E5] text-sm leading-snug'>{content}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { shallow } from 'zustand/shallow'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
@@ -109,7 +110,11 @@ export function useMentionData(props: UseMentionDataProps) {
|
||||
const [workflowBlocks, setWorkflowBlocks] = useState<WorkflowBlockItem[]>([])
|
||||
const [isLoadingWorkflowBlocks, setIsLoadingWorkflowBlocks] = useState(false)
|
||||
|
||||
const workflowStoreBlocks = useWorkflowStore((state) => state.blocks)
|
||||
// Only subscribe to block keys to avoid re-rendering on position updates
|
||||
const blockKeys = useWorkflowStore(
|
||||
useCallback((state) => Object.keys(state.blocks), []),
|
||||
shallow
|
||||
)
|
||||
|
||||
// Use workflow registry as source of truth for workflows
|
||||
const registryWorkflows = useWorkflowRegistry((state) => state.workflows)
|
||||
@@ -139,15 +144,19 @@ export function useMentionData(props: UseMentionDataProps) {
|
||||
|
||||
/**
|
||||
* Syncs workflow blocks from store
|
||||
* Only re-runs when blocks are added/removed (not on position updates)
|
||||
*/
|
||||
useEffect(() => {
|
||||
const syncWorkflowBlocks = async () => {
|
||||
if (!workflowId || !workflowStoreBlocks || Object.keys(workflowStoreBlocks).length === 0) {
|
||||
if (!workflowId || blockKeys.length === 0) {
|
||||
setWorkflowBlocks([])
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch current blocks from store
|
||||
const workflowStoreBlocks = useWorkflowStore.getState().blocks
|
||||
|
||||
const { registry: blockRegistry } = await import('@/blocks/registry')
|
||||
const mapped = Object.values(workflowStoreBlocks).map((b: any) => {
|
||||
const reg = (blockRegistry as any)[b.type]
|
||||
@@ -169,7 +178,7 @@ export function useMentionData(props: UseMentionDataProps) {
|
||||
}
|
||||
|
||||
syncWorkflowBlocks()
|
||||
}, [workflowStoreBlocks, workflowId])
|
||||
}, [blockKeys, workflowId])
|
||||
|
||||
/**
|
||||
* Ensures past chats are loaded
|
||||
@@ -323,10 +332,10 @@ export function useMentionData(props: UseMentionDataProps) {
|
||||
if (!workflowId) return
|
||||
logger.debug('ensureWorkflowBlocksLoaded called', {
|
||||
workflowId,
|
||||
storeBlocksCount: Object.keys(workflowStoreBlocks || {}).length,
|
||||
storeBlocksCount: blockKeys.length,
|
||||
workflowBlocksCount: workflowBlocks.length,
|
||||
})
|
||||
}, [workflowId, workflowStoreBlocks, workflowBlocks.length])
|
||||
}, [workflowId, blockKeys.length, workflowBlocks.length])
|
||||
|
||||
return {
|
||||
// State
|
||||
|
||||
@@ -210,9 +210,14 @@ export function Editor() {
|
||||
/>
|
||||
) : (
|
||||
<h2
|
||||
className='min-w-0 flex-1 cursor-pointer truncate pr-[8px] font-medium text-[14px] text-[var(--white)] dark:text-[var(--white)]'
|
||||
className='min-w-0 flex-1 cursor-pointer select-none truncate pr-[8px] font-medium text-[14px] text-[var(--white)] dark:text-[var(--white)]'
|
||||
title={title}
|
||||
onDoubleClick={handleStartRename}
|
||||
onMouseDown={(e) => {
|
||||
if (e.detail === 2) {
|
||||
e.preventDefault()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
@@ -11,7 +11,7 @@ const DEFAULT_CONTAINER_HEIGHT = 300
|
||||
* Hook providing utilities for node position, hierarchy, and dimension calculations
|
||||
*/
|
||||
export function useNodeUtilities(blocks: Record<string, any>) {
|
||||
const { getNodes, project } = useReactFlow()
|
||||
const { getNodes } = useReactFlow()
|
||||
|
||||
/**
|
||||
* Check if a block is a container type (loop, parallel, or subflow)
|
||||
|
||||
@@ -110,7 +110,7 @@ const WorkflowContent = React.memo(() => {
|
||||
// Hooks
|
||||
const params = useParams()
|
||||
const router = useRouter()
|
||||
const { project, getNodes, fitView } = useReactFlow()
|
||||
const { screenToFlowPosition, getNodes, fitView } = useReactFlow()
|
||||
const { emitCursorUpdate } = useSocket()
|
||||
|
||||
// Get workspace ID from the params
|
||||
@@ -170,7 +170,7 @@ const WorkflowContent = React.memo(() => {
|
||||
// Get diff analysis for edge reconstruction
|
||||
const { diffAnalysis, isShowingDiff, isDiffReady } = useWorkflowDiffStore()
|
||||
|
||||
// Reconstruct deleted edges when viewing original workflow and filter trigger edges
|
||||
// Reconstruct deleted edges when viewing original workflow and filter out invalid edges
|
||||
const edgesForDisplay = useMemo(() => {
|
||||
let edgesToFilter = edges
|
||||
|
||||
@@ -237,7 +237,21 @@ const WorkflowContent = React.memo(() => {
|
||||
// Combine existing edges with reconstructed deleted edges
|
||||
edgesToFilter = [...edges, ...reconstructedEdges]
|
||||
}
|
||||
return edgesToFilter
|
||||
|
||||
// Filter out edges that connect to/from annotation-only blocks (note blocks)
|
||||
// These blocks don't have handles and shouldn't have connections
|
||||
return edgesToFilter.filter((edge) => {
|
||||
const sourceBlock = blocks[edge.source]
|
||||
const targetBlock = blocks[edge.target]
|
||||
|
||||
// Remove edge if either source or target is an annotation-only block
|
||||
if (!sourceBlock || !targetBlock) return false
|
||||
if (isAnnotationOnlyBlock(sourceBlock.type) || isAnnotationOnlyBlock(targetBlock.type)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}, [edges, isShowingDiff, isDiffReady, diffAnalysis, blocks])
|
||||
|
||||
// User permissions - get current user's specific permissions from context
|
||||
@@ -680,7 +694,11 @@ const WorkflowContent = React.memo(() => {
|
||||
// Auto-connect logic for blocks inside containers
|
||||
const isAutoConnectEnabled = useGeneralStore.getState().isAutoConnectEnabled
|
||||
let autoConnectEdge
|
||||
if (isAutoConnectEnabled && data.type !== 'starter') {
|
||||
if (
|
||||
isAutoConnectEnabled &&
|
||||
data.type !== 'starter' &&
|
||||
!isAnnotationOnlyBlock(data.type)
|
||||
) {
|
||||
if (existingChildBlocks.length > 0) {
|
||||
// Connect to the nearest existing child block within the container
|
||||
const closestBlock = existingChildBlocks
|
||||
@@ -694,7 +712,7 @@ const WorkflowContent = React.memo(() => {
|
||||
.sort((a, b) => a.distance - b.distance)[0]?.block
|
||||
|
||||
if (closestBlock) {
|
||||
// Don't create edges into trigger blocks
|
||||
// Don't create edges into trigger blocks or annotation blocks
|
||||
const targetBlockConfig = getBlock(data.type)
|
||||
const isTargetTrigger =
|
||||
data.enableTriggerMode === true || targetBlockConfig?.category === 'triggers'
|
||||
@@ -769,10 +787,14 @@ const WorkflowContent = React.memo(() => {
|
||||
// Regular auto-connect logic
|
||||
const isAutoConnectEnabled = useGeneralStore.getState().isAutoConnectEnabled
|
||||
let autoConnectEdge
|
||||
if (isAutoConnectEnabled && data.type !== 'starter') {
|
||||
if (
|
||||
isAutoConnectEnabled &&
|
||||
data.type !== 'starter' &&
|
||||
!isAnnotationOnlyBlock(data.type)
|
||||
) {
|
||||
const closestBlock = findClosestOutput(position)
|
||||
if (closestBlock) {
|
||||
// Don't create edges into trigger blocks
|
||||
// Don't create edges into trigger blocks or annotation blocks
|
||||
const targetBlockConfig = getBlock(data.type)
|
||||
const isTargetTrigger =
|
||||
data.enableTriggerMode === true || targetBlockConfig?.category === 'triggers'
|
||||
@@ -842,7 +864,7 @@ const WorkflowContent = React.memo(() => {
|
||||
const baseName = type === 'loop' ? 'Loop' : 'Parallel'
|
||||
const name = getUniqueBlockName(baseName, blocks)
|
||||
|
||||
const centerPosition = project({
|
||||
const centerPosition = screenToFlowPosition({
|
||||
x: window.innerWidth / 2,
|
||||
y: window.innerHeight / 2,
|
||||
})
|
||||
@@ -891,7 +913,7 @@ const WorkflowContent = React.memo(() => {
|
||||
}
|
||||
|
||||
// Calculate the center position of the viewport
|
||||
const centerPosition = project({
|
||||
const centerPosition = screenToFlowPosition({
|
||||
x: window.innerWidth / 2,
|
||||
y: window.innerHeight / 2,
|
||||
})
|
||||
@@ -906,11 +928,11 @@ const WorkflowContent = React.memo(() => {
|
||||
// Auto-connect logic
|
||||
const isAutoConnectEnabled = useGeneralStore.getState().isAutoConnectEnabled
|
||||
let autoConnectEdge
|
||||
if (isAutoConnectEnabled && type !== 'starter') {
|
||||
if (isAutoConnectEnabled && type !== 'starter' && !isAnnotationOnlyBlock(type)) {
|
||||
const closestBlock = findClosestOutput(centerPosition)
|
||||
logger.info('Closest block found:', closestBlock)
|
||||
if (closestBlock) {
|
||||
// Don't create edges into trigger blocks
|
||||
// Don't create edges into trigger blocks or annotation blocks
|
||||
const targetBlockConfig = blockConfig
|
||||
const isTargetTrigger = enableTriggerMode || targetBlockConfig?.category === 'triggers'
|
||||
|
||||
@@ -977,7 +999,7 @@ const WorkflowContent = React.memo(() => {
|
||||
)
|
||||
}
|
||||
}, [
|
||||
project,
|
||||
screenToFlowPosition,
|
||||
blocks,
|
||||
addBlock,
|
||||
addEdge,
|
||||
@@ -1014,7 +1036,7 @@ const WorkflowContent = React.memo(() => {
|
||||
}
|
||||
|
||||
const bounds = canvasElement.getBoundingClientRect()
|
||||
const position = project({
|
||||
const position = screenToFlowPosition({
|
||||
x: detail.clientX - bounds.left,
|
||||
y: detail.clientY - bounds.top,
|
||||
})
|
||||
@@ -1041,7 +1063,7 @@ const WorkflowContent = React.memo(() => {
|
||||
'toolbar-drop-on-empty-workflow-overlay',
|
||||
handleOverlayToolbarDrop as EventListener
|
||||
)
|
||||
}, [project, handleToolbarDrop])
|
||||
}, [screenToFlowPosition, handleToolbarDrop])
|
||||
|
||||
/**
|
||||
* Recenter canvas when diff appears
|
||||
@@ -1090,7 +1112,7 @@ const WorkflowContent = React.memo(() => {
|
||||
if (!data?.type) return
|
||||
|
||||
const reactFlowBounds = event.currentTarget.getBoundingClientRect()
|
||||
const position = project({
|
||||
const position = screenToFlowPosition({
|
||||
x: event.clientX - reactFlowBounds.left,
|
||||
y: event.clientY - reactFlowBounds.top,
|
||||
})
|
||||
@@ -1106,7 +1128,7 @@ const WorkflowContent = React.memo(() => {
|
||||
logger.error('Error dropping block on ReactFlow canvas:', { err })
|
||||
}
|
||||
},
|
||||
[project, handleToolbarDrop]
|
||||
[screenToFlowPosition, handleToolbarDrop]
|
||||
)
|
||||
|
||||
const handleCanvasPointerMove = useCallback(
|
||||
@@ -1114,14 +1136,14 @@ const WorkflowContent = React.memo(() => {
|
||||
const target = event.currentTarget as HTMLElement
|
||||
const bounds = target.getBoundingClientRect()
|
||||
|
||||
const position = project({
|
||||
const position = screenToFlowPosition({
|
||||
x: event.clientX - bounds.left,
|
||||
y: event.clientY - bounds.top,
|
||||
})
|
||||
|
||||
emitCursorUpdate(position)
|
||||
},
|
||||
[project, emitCursorUpdate]
|
||||
[screenToFlowPosition, emitCursorUpdate]
|
||||
)
|
||||
|
||||
const handleCanvasPointerLeave = useCallback(() => {
|
||||
@@ -1144,7 +1166,7 @@ const WorkflowContent = React.memo(() => {
|
||||
|
||||
try {
|
||||
const reactFlowBounds = event.currentTarget.getBoundingClientRect()
|
||||
const position = project({
|
||||
const position = screenToFlowPosition({
|
||||
x: event.clientX - reactFlowBounds.left,
|
||||
y: event.clientY - reactFlowBounds.top,
|
||||
})
|
||||
@@ -1188,7 +1210,7 @@ const WorkflowContent = React.memo(() => {
|
||||
logger.error('Error in onDragOver', { err })
|
||||
}
|
||||
},
|
||||
[project, isPointInLoopNode, getNodes]
|
||||
[screenToFlowPosition, isPointInLoopNode, getNodes]
|
||||
)
|
||||
|
||||
// Initialize workflow when it exists in registry and isn't active
|
||||
@@ -1584,8 +1606,8 @@ const WorkflowContent = React.memo(() => {
|
||||
// Store currently dragged node ID
|
||||
setDraggedNodeId(node.id)
|
||||
|
||||
// Emit collaborative position update during drag for smooth real-time movement
|
||||
collaborativeUpdateBlockPosition(node.id, node.position, false)
|
||||
// Note: We don't emit position updates during drag to avoid flooding socket events.
|
||||
// The final position is sent in onNodeDragStop for collaborative updates.
|
||||
|
||||
// Get the current parent ID of the node being dragged
|
||||
const currentParentId = blocks[node.id]?.data?.parentId || null
|
||||
@@ -1721,14 +1743,7 @@ const WorkflowContent = React.memo(() => {
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
getNodes,
|
||||
potentialParentId,
|
||||
blocks,
|
||||
getNodeAbsolutePosition,
|
||||
getNodeDepth,
|
||||
collaborativeUpdateBlockPosition,
|
||||
]
|
||||
[getNodes, potentialParentId, blocks, getNodeAbsolutePosition, getNodeDepth]
|
||||
)
|
||||
|
||||
// Add in a nodeDrag start event to set the dragStartParentId
|
||||
@@ -1855,7 +1870,8 @@ const WorkflowContent = React.memo(() => {
|
||||
|
||||
// Auto-connect when moving an existing block into a container
|
||||
const isAutoConnectEnabled = useGeneralStore.getState().isAutoConnectEnabled
|
||||
if (isAutoConnectEnabled) {
|
||||
// Don't auto-connect annotation blocks (like note blocks)
|
||||
if (isAutoConnectEnabled && !isAnnotationOnlyBlock(node.data?.type)) {
|
||||
// Existing children in the target container (excluding the moved node)
|
||||
const existingChildBlocks = Object.values(blocks).filter(
|
||||
(b) => b.data?.parentId === potentialParentId && b.id !== node.id
|
||||
|
||||
Reference in New Issue
Block a user