Merge branch 'p284' into deploy/dev

This commit is contained in:
hjlarry
2025-09-22 18:03:09 +08:00
88 changed files with 8365 additions and 2587 deletions

View File

@ -75,6 +75,10 @@ import { DataSourceClassification } from '@/app/components/workflow/nodes/data-s
import { useModalContext } from '@/context/modal-context'
import DataSourceBeforeRunForm from '@/app/components/workflow/nodes/data-source/before-run-form'
import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud'
import { useCollaboration } from '@/app/components/workflow/collaboration/hooks/use-collaboration'
import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager'
import { useAppContext } from '@/context/app-context'
import { UserAvatarList } from '@/app/components/base/user-avatar-list'
const getCustomRunForm = (params: CustomRunFormProps): React.JSX.Element => {
const nodeType = params.payload.type
@ -97,11 +101,51 @@ const BasePanel: FC<BasePanelProps> = ({
children,
}) => {
const { t } = useTranslation()
const appId = useStore(s => s.appId)
const { userProfile } = useAppContext()
const { isConnected, nodePanelPresence } = useCollaboration(appId as string)
const { showMessageLogModal } = useAppStore(useShallow(state => ({
showMessageLogModal: state.showMessageLogModal,
})))
const isSingleRunning = data._singleRunningStatus === NodeRunningStatus.Running
const currentUserPresence = useMemo(() => {
const userId = userProfile?.id || ''
const username = userProfile?.name || userProfile?.email || 'User'
const avatar = userProfile?.avatar_url || userProfile?.avatar || null
return {
userId,
username,
avatar,
}
}, [userProfile?.avatar, userProfile?.avatar_url, userProfile?.email, userProfile?.id, userProfile?.name])
useEffect(() => {
if (!isConnected || !currentUserPresence.userId)
return
collaborationManager.emitNodePanelPresence(id, true, currentUserPresence)
return () => {
collaborationManager.emitNodePanelPresence(id, false, currentUserPresence)
}
}, [id, isConnected, currentUserPresence])
const viewingUsers = useMemo(() => {
const presence = nodePanelPresence?.[id]
if (!presence)
return []
return Object.values(presence)
.filter(viewer => viewer.userId && viewer.userId !== currentUserPresence.userId)
.map(viewer => ({
id: viewer.userId,
name: viewer.username,
avatar_url: viewer.avatar || null,
}))
}, [currentUserPresence.userId, id, nodePanelPresence])
const showSingleRunPanel = useStore(s => s.showSingleRunPanel)
const workflowCanvasWidth = useStore(s => s.workflowCanvasWidth)
const nodePanelWidth = useStore(s => s.nodePanelWidth)
@ -393,6 +437,15 @@ const BasePanel: FC<BasePanelProps> = ({
value={data.title || ''}
onBlur={handleTitleBlur}
/>
{viewingUsers.length > 0 && (
<div className='ml-3 shrink-0'>
<UserAvatarList
users={viewingUsers}
maxVisible={3}
size={24}
/>
</div>
)}
<div className='flex shrink-0 items-center text-text-tertiary'>
{
isSupportSingleRun && !nodesReadOnly && (

View File

@ -47,6 +47,10 @@ import BlockIcon from '@/app/components/workflow/block-icon'
import Tooltip from '@/app/components/base/tooltip'
import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud'
import { ToolTypeEnum } from '../../block-selector/types'
import { UserAvatarList } from '@/app/components/base/user-avatar-list'
import { useAppContext } from '@/context/app-context'
import { useStore } from '@/app/components/workflow/store'
import { useCollaboration } from '@/app/components/workflow/collaboration/hooks/use-collaboration'
type BaseNodeProps = {
children: ReactElement
@ -65,6 +69,35 @@ const BaseNode: FC<BaseNodeProps> = ({
const { handleNodeIterationChildSizeChange } = useNodeIterationInteractions()
const { handleNodeLoopChildSizeChange } = useNodeLoopInteractions()
const toolIcon = useToolIcon(data)
const { userProfile } = useAppContext()
const appId = useStore(s => s.appId)
const { nodePanelPresence } = useCollaboration(appId as string)
const currentUserPresence = useMemo(() => {
const userId = userProfile?.id || ''
const username = userProfile?.name || userProfile?.email || 'User'
const avatar = userProfile?.avatar_url || userProfile?.avatar || null
return {
userId,
username,
avatar,
}
}, [userProfile?.avatar, userProfile?.avatar_url, userProfile?.email, userProfile?.id, userProfile?.name])
const viewingUsers = useMemo(() => {
const presence = nodePanelPresence?.[id]
if (!presence)
return []
return Object.values(presence)
.filter(viewer => viewer.userId && viewer.userId !== currentUserPresence.userId)
.map(viewer => ({
id: viewer.userId,
name: viewer.username,
avatar_url: viewer.avatar || null,
}))
}, [currentUserPresence.userId, id, nodePanelPresence])
useEffect(() => {
if (nodeRef.current && data.selected && data.isInIteration) {
@ -237,7 +270,7 @@ const BaseNode: FC<BaseNodeProps> = ({
/>
<div
title={data.title}
className='system-sm-semibold-uppercase mr-1 flex grow items-center truncate text-text-primary'
className='system-sm-semibold-uppercase mr-1 flex grow items-center justify-between truncate text-text-primary'
>
<div>
{data.title}
@ -258,6 +291,15 @@ const BaseNode: FC<BaseNodeProps> = ({
</Tooltip>
)
}
{viewingUsers.length > 0 && (
<div className='ml-3 shrink-0'>
<UserAvatarList
users={viewingUsers}
maxVisible={3}
size={24}
/>
</div>
)}
</div>
{
data._iterationLength && data._iterationIndex && data._runningStatus === NodeRunningStatus.Running && (