mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-17 02:48:02 -05:00
Compare commits
33 Commits
fix/socket
...
v0.5.62
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8bb0db660 | ||
|
|
5de7228dd9 | ||
|
|
75898c69ed | ||
|
|
b14672887b | ||
|
|
d024c1e489 | ||
|
|
af82820a28 | ||
|
|
4372841797 | ||
|
|
5e8c843241 | ||
|
|
7bf3d73ee6 | ||
|
|
7ffc11a738 | ||
|
|
be578e2ed7 | ||
|
|
f415e5edc4 | ||
|
|
13a6e6c3fa | ||
|
|
f5ab7f21ae | ||
|
|
bfb6fffe38 | ||
|
|
4fbec0a43f | ||
|
|
585f5e365b | ||
|
|
3792bdd252 | ||
|
|
eb5d1f3e5b | ||
|
|
54ab82c8dd | ||
|
|
f895bf469b | ||
|
|
dd3209af06 | ||
|
|
b6ba3b50a7 | ||
|
|
b304233062 | ||
|
|
57e4b49bd6 | ||
|
|
e12dd204ed | ||
|
|
3d9d9cbc54 | ||
|
|
0f4ec962ad | ||
|
|
4827866f9a | ||
|
|
3e697d9ed9 | ||
|
|
4431a1a484 | ||
|
|
4d1a9a3f22 | ||
|
|
eb07a080fb |
@@ -168,12 +168,17 @@ const NoteMarkdown = memo(function NoteMarkdown({ content }: { content: string }
|
||||
)
|
||||
})
|
||||
|
||||
export const NoteBlock = memo(function NoteBlock({ id, data }: NodeProps<NoteBlockNodeData>) {
|
||||
export const NoteBlock = memo(function NoteBlock({
|
||||
id,
|
||||
data,
|
||||
selected,
|
||||
}: NodeProps<NoteBlockNodeData>) {
|
||||
const { type, config, name } = data
|
||||
|
||||
const { activeWorkflowId, isEnabled, handleClick, hasRing, ringStyles } = useBlockVisual({
|
||||
blockId: id,
|
||||
data,
|
||||
isSelected: selected,
|
||||
})
|
||||
const storedValues = useSubBlockStore(
|
||||
useCallback(
|
||||
|
||||
@@ -66,7 +66,7 @@ export interface SubflowNodeData {
|
||||
* @param props - Node properties containing data and id
|
||||
* @returns Rendered subflow node component
|
||||
*/
|
||||
export const SubflowNodeComponent = memo(({ data, id }: NodeProps<SubflowNodeData>) => {
|
||||
export const SubflowNodeComponent = memo(({ data, id, selected }: NodeProps<SubflowNodeData>) => {
|
||||
const { getNodes } = useReactFlow()
|
||||
const blockRef = useRef<HTMLDivElement>(null)
|
||||
const userPermissions = useUserPermissionsContext()
|
||||
@@ -134,13 +134,15 @@ export const SubflowNodeComponent = memo(({ data, id }: NodeProps<SubflowNodeDat
|
||||
|
||||
/**
|
||||
* Determine the ring styling based on subflow state priority:
|
||||
* 1. Focused (selected in editor) or preview selected - blue ring
|
||||
* 1. Focused (selected in editor), selected (shift-click/box), or preview selected - blue ring
|
||||
* 2. Diff status (version comparison) - green/orange ring
|
||||
*/
|
||||
const hasRing = isFocused || isPreviewSelected || diffStatus === 'new' || diffStatus === 'edited'
|
||||
const isSelected = !isPreview && selected
|
||||
const hasRing =
|
||||
isFocused || isSelected || isPreviewSelected || diffStatus === 'new' || diffStatus === 'edited'
|
||||
const ringStyles = cn(
|
||||
hasRing && 'ring-[1.75px]',
|
||||
(isFocused || isPreviewSelected) && 'ring-[var(--brand-secondary)]',
|
||||
(isFocused || isSelected || isPreviewSelected) && 'ring-[var(--brand-secondary)]',
|
||||
diffStatus === 'new' && 'ring-[var(--brand-tertiary-2)]',
|
||||
diffStatus === 'edited' && 'ring-[var(--warning)]'
|
||||
)
|
||||
@@ -167,7 +169,7 @@ export const SubflowNodeComponent = memo(({ data, id }: NodeProps<SubflowNodeDat
|
||||
data-node-id={id}
|
||||
data-type='subflowNode'
|
||||
data-nesting-level={nestingLevel}
|
||||
data-subflow-selected={isFocused || isPreviewSelected}
|
||||
data-subflow-selected={isFocused || isSelected || isPreviewSelected}
|
||||
>
|
||||
{!isPreview && (
|
||||
<ActionBar blockId={id} blockType={data.kind} disabled={!userPermissions.canEdit} />
|
||||
|
||||
@@ -208,7 +208,6 @@ const tryParseJson = (value: unknown): unknown => {
|
||||
export const getDisplayValue = (value: unknown): string => {
|
||||
if (value == null || value === '') return '-'
|
||||
|
||||
// Try parsing JSON strings first
|
||||
const parsedValue = tryParseJson(value)
|
||||
|
||||
if (isMessagesArray(parsedValue)) {
|
||||
@@ -557,6 +556,7 @@ const SubBlockRow = ({
|
||||
export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
id,
|
||||
data,
|
||||
selected,
|
||||
}: NodeProps<WorkflowBlockProps>) {
|
||||
const { type, config, name, isPending } = data
|
||||
|
||||
@@ -574,7 +574,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
hasRing,
|
||||
ringStyles,
|
||||
runPathStatus,
|
||||
} = useBlockVisual({ blockId: id, data, isPending })
|
||||
} = useBlockVisual({ blockId: id, data, isPending, isSelected: selected })
|
||||
|
||||
const currentBlock = currentWorkflow.getBlockById(id)
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ interface UseBlockVisualProps {
|
||||
data: WorkflowBlockProps
|
||||
/** Whether the block is pending execution */
|
||||
isPending?: boolean
|
||||
/** Whether the block is selected (via shift-click or selection box) */
|
||||
isSelected?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,7 +30,12 @@ interface UseBlockVisualProps {
|
||||
* @param props - The hook properties
|
||||
* @returns Visual state, click handler, and ring styling for the block
|
||||
*/
|
||||
export function useBlockVisual({ blockId, data, isPending = false }: UseBlockVisualProps) {
|
||||
export function useBlockVisual({
|
||||
blockId,
|
||||
data,
|
||||
isPending = false,
|
||||
isSelected = false,
|
||||
}: UseBlockVisualProps) {
|
||||
const isPreview = data.isPreview ?? false
|
||||
const isPreviewSelected = data.isPreviewSelected ?? false
|
||||
|
||||
@@ -42,7 +49,6 @@ export function useBlockVisual({ blockId, data, isPending = false }: UseBlockVis
|
||||
isDeletedBlock,
|
||||
} = useBlockState(blockId, currentWorkflow, data)
|
||||
|
||||
// Check if the editor panel is open for this block
|
||||
const currentBlockId = usePanelEditorStore((state) => state.currentBlockId)
|
||||
const activeTab = usePanelStore((state) => state.activeTab)
|
||||
const isEditorOpen = !isPreview && currentBlockId === blockId && activeTab === 'editor'
|
||||
@@ -68,6 +74,7 @@ export function useBlockVisual({ blockId, data, isPending = false }: UseBlockVis
|
||||
diffStatus: isPreview ? undefined : diffStatus,
|
||||
runPathStatus,
|
||||
isPreviewSelection: isPreview && isPreviewSelected,
|
||||
isSelected: isPreview ? false : isSelected,
|
||||
}),
|
||||
[
|
||||
isExecuting,
|
||||
@@ -78,6 +85,7 @@ export function useBlockVisual({ blockId, data, isPending = false }: UseBlockVis
|
||||
runPathStatus,
|
||||
isPreview,
|
||||
isPreviewSelected,
|
||||
isSelected,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ export interface BlockRingOptions {
|
||||
diffStatus: BlockDiffStatus
|
||||
runPathStatus: BlockRunPathStatus
|
||||
isPreviewSelection?: boolean
|
||||
/** Whether the block is selected via shift-click or selection box (shows blue ring) */
|
||||
isSelected?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,11 +34,13 @@ export function getBlockRingStyles(options: BlockRingOptions): {
|
||||
diffStatus,
|
||||
runPathStatus,
|
||||
isPreviewSelection,
|
||||
isSelected,
|
||||
} = options
|
||||
|
||||
const hasRing =
|
||||
isExecuting ||
|
||||
isEditorOpen ||
|
||||
isSelected ||
|
||||
isPending ||
|
||||
diffStatus === 'new' ||
|
||||
diffStatus === 'edited' ||
|
||||
@@ -46,25 +50,37 @@ export function getBlockRingStyles(options: BlockRingOptions): {
|
||||
const ringClassName = cn(
|
||||
// Executing block: pulsing success ring with prominent thickness (highest priority)
|
||||
isExecuting && 'ring-[3.5px] ring-[var(--border-success)] animate-ring-pulse',
|
||||
// Editor open or preview selection: static blue ring
|
||||
// Editor open, selected, or preview selection: static blue ring
|
||||
!isExecuting &&
|
||||
(isEditorOpen || isPreviewSelection) &&
|
||||
(isEditorOpen || isSelected || isPreviewSelection) &&
|
||||
'ring-[1.75px] ring-[var(--brand-secondary)]',
|
||||
// Non-active states use standard ring utilities
|
||||
!isExecuting && !isEditorOpen && !isPreviewSelection && hasRing && 'ring-[1.75px]',
|
||||
!isExecuting &&
|
||||
!isEditorOpen &&
|
||||
!isSelected &&
|
||||
!isPreviewSelection &&
|
||||
hasRing &&
|
||||
'ring-[1.75px]',
|
||||
// Pending state: warning ring
|
||||
!isExecuting && !isEditorOpen && isPending && 'ring-[var(--warning)]',
|
||||
!isExecuting && !isEditorOpen && !isSelected && isPending && 'ring-[var(--warning)]',
|
||||
// Deleted state (highest priority after active/pending)
|
||||
!isExecuting && !isEditorOpen && !isPending && isDeletedBlock && 'ring-[var(--text-error)]',
|
||||
!isExecuting &&
|
||||
!isEditorOpen &&
|
||||
!isSelected &&
|
||||
!isPending &&
|
||||
isDeletedBlock &&
|
||||
'ring-[var(--text-error)]',
|
||||
// Diff states
|
||||
!isExecuting &&
|
||||
!isEditorOpen &&
|
||||
!isSelected &&
|
||||
!isPending &&
|
||||
!isDeletedBlock &&
|
||||
diffStatus === 'new' &&
|
||||
'ring-[var(--brand-tertiary-2)]',
|
||||
!isExecuting &&
|
||||
!isEditorOpen &&
|
||||
!isSelected &&
|
||||
!isPending &&
|
||||
!isDeletedBlock &&
|
||||
diffStatus === 'edited' &&
|
||||
@@ -72,6 +88,7 @@ export function getBlockRingStyles(options: BlockRingOptions): {
|
||||
// Run path states (lowest priority - only show if no other states active)
|
||||
!isExecuting &&
|
||||
!isEditorOpen &&
|
||||
!isSelected &&
|
||||
!isPending &&
|
||||
!isDeletedBlock &&
|
||||
!diffStatus &&
|
||||
@@ -79,6 +96,7 @@ export function getBlockRingStyles(options: BlockRingOptions): {
|
||||
'ring-[var(--border-success)]',
|
||||
!isExecuting &&
|
||||
!isEditorOpen &&
|
||||
!isSelected &&
|
||||
!isPending &&
|
||||
!isDeletedBlock &&
|
||||
!diffStatus &&
|
||||
|
||||
@@ -700,7 +700,23 @@ const WorkflowContent = React.memo(() => {
|
||||
triggerMode,
|
||||
})
|
||||
|
||||
collaborativeBatchAddBlocks([block], autoConnectEdge ? [autoConnectEdge] : [], {}, {}, {})
|
||||
const subBlockValues: Record<string, Record<string, unknown>> = {}
|
||||
if (block.subBlocks && Object.keys(block.subBlocks).length > 0) {
|
||||
subBlockValues[id] = {}
|
||||
for (const [subBlockId, subBlock] of Object.entries(block.subBlocks)) {
|
||||
if (subBlock.value !== null && subBlock.value !== undefined) {
|
||||
subBlockValues[id][subBlockId] = subBlock.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collaborativeBatchAddBlocks(
|
||||
[block],
|
||||
autoConnectEdge ? [autoConnectEdge] : [],
|
||||
{},
|
||||
{},
|
||||
subBlockValues
|
||||
)
|
||||
usePanelEditorStore.getState().setCurrentBlockId(id)
|
||||
},
|
||||
[collaborativeBatchAddBlocks, setSelectedEdges]
|
||||
|
||||
@@ -406,21 +406,13 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
|
||||
socketInstance.on('cursor-update', (data) => {
|
||||
setPresenceUsers((prev) => {
|
||||
const existingIndex = prev.findIndex((user) => user.socketId === data.socketId)
|
||||
if (existingIndex !== -1) {
|
||||
return prev.map((user) =>
|
||||
user.socketId === data.socketId ? { ...user, cursor: data.cursor } : user
|
||||
)
|
||||
if (existingIndex === -1) {
|
||||
logger.debug('Received cursor-update for unknown user', { socketId: data.socketId })
|
||||
return prev
|
||||
}
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
socketId: data.socketId,
|
||||
userId: data.userId,
|
||||
userName: data.userName,
|
||||
avatarUrl: data.avatarUrl,
|
||||
cursor: data.cursor,
|
||||
},
|
||||
]
|
||||
return prev.map((user) =>
|
||||
user.socketId === data.socketId ? { ...user, cursor: data.cursor } : user
|
||||
)
|
||||
})
|
||||
eventHandlers.current.cursorUpdate?.(data)
|
||||
})
|
||||
@@ -428,21 +420,15 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
|
||||
socketInstance.on('selection-update', (data) => {
|
||||
setPresenceUsers((prev) => {
|
||||
const existingIndex = prev.findIndex((user) => user.socketId === data.socketId)
|
||||
if (existingIndex !== -1) {
|
||||
return prev.map((user) =>
|
||||
user.socketId === data.socketId ? { ...user, selection: data.selection } : user
|
||||
)
|
||||
}
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
if (existingIndex === -1) {
|
||||
logger.debug('Received selection-update for unknown user', {
|
||||
socketId: data.socketId,
|
||||
userId: data.userId,
|
||||
userName: data.userName,
|
||||
avatarUrl: data.avatarUrl,
|
||||
selection: data.selection,
|
||||
},
|
||||
]
|
||||
})
|
||||
return prev
|
||||
}
|
||||
return prev.map((user) =>
|
||||
user.socketId === data.socketId ? { ...user, selection: data.selection } : user
|
||||
)
|
||||
})
|
||||
eventHandlers.current.selectionUpdate?.(data)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user