refactor cursor and add hide comment

This commit is contained in:
hjlarry 2025-09-23 22:13:02 +08:00
parent 05a67f4716
commit 9e8ac5c96b
7 changed files with 90 additions and 22 deletions

View File

@ -1,11 +0,0 @@
import { create } from 'zustand'
type UserCursorsState = {
showUserCursors: boolean
toggleUserCursors: () => void
}
export const useUserCursorsState = create<UserCursorsState>(set => ({
showUserCursors: true,
toggleUserCursors: () => set(state => ({ showUserCursors: !state.showUserCursors })),
}))

View File

@ -12,8 +12,6 @@ import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
import { WorkflowWithInnerContext } from '@/app/components/workflow'
import type { WorkflowProps } from '@/app/components/workflow'
import WorkflowChildren from './workflow-children'
import UserCursors from '@/app/components/workflow/collaboration/components/user-cursors'
import { useUserCursorsState } from './user-cursors-state'
import {
useAvailableNodesMetaData,
@ -49,7 +47,6 @@ const WorkflowMain = ({
const store = useStoreApi()
const { startCursorTracking, stopCursorTracking, onlineUsers, cursors, isConnected } = useCollaboration(appId || '', store)
const [myUserId, setMyUserId] = useState<string | null>(null)
const { showUserCursors } = useUserCursorsState()
useEffect(() => {
if (isConnected)
@ -299,10 +296,12 @@ const WorkflowMain = ({
viewport={viewport}
onWorkflowDataUpdate={handleWorkflowDataUpdate}
hooksStore={hooksStore as any}
cursors={filteredCursors}
myUserId={myUserId}
onlineUsers={onlineUsers}
>
<WorkflowChildren />
</WorkflowWithInnerContext>
<UserCursors cursors={showUserCursors ? filteredCursors : {}} myUserId={myUserId} onlineUsers={onlineUsers} />
</div>
)
}

View File

@ -76,6 +76,7 @@ import LimitTips from './limit-tips'
import { setupScrollToNodeListener } from './utils/node-navigation'
import { CommentCursor, CommentIcon, CommentInput, CommentThread } from './comment'
import { useWorkflowComment } from './hooks/use-workflow-comment'
import UserCursors from './collaboration/components/user-cursors'
import {
useStore,
useWorkflowStore,
@ -119,6 +120,9 @@ export type WorkflowProps = {
viewport?: Viewport
children?: React.ReactNode
onWorkflowDataUpdate?: (v: any) => void
cursors?: Record<string, any>
myUserId?: string | null
onlineUsers?: any[]
}
export const Workflow: FC<WorkflowProps> = memo(({
nodes: originalNodes,
@ -126,6 +130,9 @@ export const Workflow: FC<WorkflowProps> = memo(({
viewport,
children,
onWorkflowDataUpdate,
cursors,
myUserId,
onlineUsers,
}) => {
const workflowContainerRef = useRef<HTMLDivElement>(null)
const workflowStore = useWorkflowStore()
@ -193,6 +200,8 @@ export const Workflow: FC<WorkflowProps> = memo(({
handleCommentReplyUpdate,
handleCommentReplyDelete,
} = useWorkflowComment()
const showUserComments = useStore(s => s.showUserComments)
const showUserCursors = useStore(s => s.showUserCursors)
const mousePosition = useStore(s => s.mousePosition)
eventEmitter?.useSubscription((v: any) => {
@ -463,13 +472,13 @@ export const Workflow: FC<WorkflowProps> = memo(({
)
}
return (
return (showUserComments || controlMode === ControlMode.Comment) ? (
<CommentIcon
key={comment.id}
comment={comment}
onClick={() => handleCommentIconClick(comment)}
/>
)
) : null
})}
{children}
<ReactFlow
@ -523,6 +532,13 @@ export const Workflow: FC<WorkflowProps> = memo(({
className="bg-workflow-canvas-workflow-bg"
color='var(--color-workflow-canvas-workflow-dot-color)'
/>
{showUserCursors && cursors && (
<UserCursors
cursors={cursors}
myUserId={myUserId || null}
onlineUsers={onlineUsers || []}
/>
)}
</ReactFlow>
</div>
)
@ -530,14 +546,25 @@ export const Workflow: FC<WorkflowProps> = memo(({
type WorkflowWithInnerContextProps = WorkflowProps & {
hooksStore?: Partial<HooksStoreShape>
cursors?: Record<string, any>
myUserId?: string | null
onlineUsers?: any[]
}
export const WorkflowWithInnerContext = memo(({
hooksStore,
cursors,
myUserId,
onlineUsers,
...restProps
}: WorkflowWithInnerContextProps) => {
return (
<HooksStoreContextProvider {...hooksStore}>
<Workflow {...restProps} />
<Workflow
{...restProps}
cursors={cursors}
myUserId={myUserId}
onlineUsers={onlineUsers}
/>
</HooksStoreContextProvider>
)
})

View File

@ -5,7 +5,7 @@ import ZoomInOut from './zoom-in-out'
import VariableTrigger from '../variable-inspect/trigger'
import VariableInspectPanel from '../variable-inspect'
import { useStore } from '../store'
import { useUserCursorsState } from '@/app/components/workflow-app/components/user-cursors-state'
import { ControlMode } from '../types'
export type OperatorProps = {
handleUndo: () => void
@ -15,12 +15,25 @@ export type OperatorProps = {
const Operator = ({ handleUndo, handleRedo }: OperatorProps) => {
const bottomPanelRef = useRef<HTMLDivElement>(null)
const [showMiniMap, setShowMiniMap] = useState(true)
const { showUserCursors, toggleUserCursors } = useUserCursorsState()
const showUserCursors = useStore(s => s.showUserCursors)
const setShowUserCursors = useStore(s => s.setShowUserCursors)
const showUserComments = useStore(s => s.showUserComments)
const setShowUserComments = useStore(s => s.setShowUserComments)
const controlMode = useStore(s => s.controlMode)
const isCommentMode = controlMode === ControlMode.Comment
const handleToggleMiniMap = useCallback(() => {
setShowMiniMap(prev => !prev)
}, [])
const handleToggleUserCursors = useCallback(() => {
setShowUserCursors(!showUserCursors)
}, [showUserCursors, setShowUserCursors])
const handleToggleUserComments = useCallback(() => {
setShowUserComments(!showUserComments)
}, [showUserComments, setShowUserComments])
const workflowCanvasWidth = useStore(s => s.workflowCanvasWidth)
const rightPanelWidth = useStore(s => s.rightPanelWidth)
const setBottomPanelWidth = useStore(s => s.setBottomPanelWidth)
@ -82,7 +95,10 @@ const Operator = ({ handleUndo, handleRedo }: OperatorProps) => {
showMiniMap={showMiniMap}
onToggleMiniMap={handleToggleMiniMap}
showUserCursors={showUserCursors}
onToggleUserCursors={toggleUserCursors}
onToggleUserCursors={handleToggleUserCursors}
showUserComments={showUserComments}
onToggleUserComments={handleToggleUserComments}
isCommentMode={isCommentMode}
/>
</div>
</div>

View File

@ -39,6 +39,7 @@ enum ZoomType {
zoomTo75 = 'zoomTo75',
zoomTo100 = 'zoomTo100',
zoomTo200 = 'zoomTo200',
toggleUserComments = 'toggleUserComments',
toggleUserCursors = 'toggleUserCursors',
toggleMiniMap = 'toggleMiniMap',
}
@ -48,6 +49,9 @@ type ZoomInOutProps = {
onToggleMiniMap?: () => void
showUserCursors?: boolean
onToggleUserCursors?: () => void
showUserComments?: boolean
onToggleUserComments?: () => void
isCommentMode?: boolean
}
const ZoomInOut: FC<ZoomInOutProps> = ({
@ -55,6 +59,9 @@ const ZoomInOut: FC<ZoomInOutProps> = ({
onToggleMiniMap,
showUserCursors = true,
onToggleUserCursors,
showUserComments = true,
onToggleUserComments,
isCommentMode = false,
}) => {
const { t } = useTranslation()
const {
@ -99,6 +106,10 @@ const ZoomInOut: FC<ZoomInOutProps> = ({
},
],
[
{
key: ZoomType.toggleUserComments,
text: t('workflow.operator.showUserComments'),
},
{
key: ZoomType.toggleUserCursors,
text: t('workflow.operator.showUserCursors'),
@ -132,6 +143,13 @@ const ZoomInOut: FC<ZoomInOutProps> = ({
if (type === ZoomType.zoomTo200)
zoomTo(2)
if (type === ZoomType.toggleUserComments) {
if (!isCommentMode)
onToggleUserComments?.()
return
}
if (type === ZoomType.toggleUserCursors) {
onToggleUserCursors?.()
return
@ -225,10 +243,20 @@ const ZoomInOut: FC<ZoomInOutProps> = ({
options.map(option => (
<div
key={option.key}
className='system-md-regular flex h-8 cursor-pointer items-center justify-between space-x-1 rounded-lg px-2 py-1.5 text-text-secondary hover:bg-state-base-hover'
className={`system-md-regular flex h-8 cursor-pointer items-center justify-between space-x-1 rounded-lg px-2 py-1.5 text-text-secondary hover:bg-state-base-hover ${
option.key === ZoomType.toggleUserComments && isCommentMode
? 'cursor-not-allowed opacity-50'
: ''
}`}
onClick={() => handleZoom(option.key)}
>
<div className='flex items-center space-x-2'>
{option.key === ZoomType.toggleUserComments && showUserComments && (
<RiCheckLine className='h-4 w-4 text-text-primary' />
)}
{option.key === ZoomType.toggleUserComments && !showUserComments && (
<div className='h-4 w-4' />
)}
{option.key === ZoomType.toggleUserCursors && showUserCursors && (
<RiCheckLine className='h-4 w-4 text-text-primary' />
)}

View File

@ -12,6 +12,10 @@ export type PanelSliceShape = {
setShowDebugAndPreviewPanel: (showDebugAndPreviewPanel: boolean) => void
showCommentsPanel: boolean
setShowCommentsPanel: (showCommentsPanel: boolean) => void
showUserComments: boolean
setShowUserComments: (showUserComments: boolean) => void
showUserCursors: boolean
setShowUserCursors: (showUserCursors: boolean) => void
panelMenu?: {
top: number
left: number
@ -42,6 +46,10 @@ export const createPanelSlice: StateCreator<PanelSliceShape> = set => ({
setShowDebugAndPreviewPanel: showDebugAndPreviewPanel => set(() => ({ showDebugAndPreviewPanel })),
showCommentsPanel: false,
setShowCommentsPanel: showCommentsPanel => set(() => ({ showCommentsPanel })),
showUserComments: true,
setShowUserComments: showUserComments => set(() => ({ showUserComments })),
showUserCursors: true,
setShowUserCursors: showUserCursors => set(() => ({ showUserCursors })),
panelMenu: undefined,
setPanelMenu: panelMenu => set(() => ({ panelMenu })),
selectionMenu: undefined,

View File

@ -326,6 +326,7 @@ const translation = {
zoomTo50: 'Zoom to 50%',
zoomTo100: 'Zoom to 100%',
zoomToFit: 'Zoom to Fit',
showUserComments: 'Comments',
showUserCursors: 'Collaborator cursors',
showMiniMap: 'Minimap',
alignNodes: 'Align Nodes',