wwf
2025-05-20 938c3e5a587ce950a94964ea509b9e7f8834dfae
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import {
  memo,
  useCallback,
  useRef,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useClickAway } from 'ahooks'
import type { NodeProps } from 'reactflow'
import NodeResizer from '../nodes/_base/components/node-resizer'
import {
  useNodeDataUpdate,
  useNodesInteractions,
} from '../hooks'
import { useStore } from '../store'
import {
  NoteEditor,
  NoteEditorContextProvider,
  NoteEditorToolbar,
} from './note-editor'
import { THEME_MAP } from './constants'
import { useNote } from './hooks'
import type { NoteNodeType } from './types'
import cn from '@/utils/classnames'
 
const Icon = () => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
      <path fillRule="evenodd" clipRule="evenodd" d="M12 9.75V6H13.5V9.75C13.5 11.8211 11.8211 13.5 9.75 13.5H6V12H9.75C10.9926 12 12 10.9926 12 9.75Z" fill="black" fillOpacity="0.16" />
    </svg>
  )
}
 
const NoteNode = ({
  id,
  data,
}: NodeProps<NoteNodeType>) => {
  const { t } = useTranslation()
  const controlPromptEditorRerenderKey = useStore(s => s.controlPromptEditorRerenderKey)
  const ref = useRef<HTMLDivElement | null>(null)
  const theme = data.theme
  const {
    handleThemeChange,
    handleEditorChange,
    handleShowAuthorChange,
  } = useNote(id)
  const {
    handleNodesCopy,
    handleNodesDuplicate,
    handleNodeDelete,
  } = useNodesInteractions()
  const { handleNodeDataUpdateWithSyncDraft } = useNodeDataUpdate()
 
  const handleDeleteNode = useCallback(() => {
    handleNodeDelete(id)
  }, [id, handleNodeDelete])
 
  useClickAway(() => {
    handleNodeDataUpdateWithSyncDraft({ id, data: { selected: false } })
  }, ref)
 
  return (
    <div
      className={cn(
        'relative flex flex-col rounded-md border shadow-xs hover:shadow-md',
        THEME_MAP[theme].bg,
        data.selected ? THEME_MAP[theme].border : 'border-black/5',
      )}
      style={{
        width: data.width,
        height: data.height,
      }}
      ref={ref}
    >
      <NoteEditorContextProvider
        key={controlPromptEditorRerenderKey}
        value={data.text}
      >
        <>
          <NodeResizer
            nodeId={id}
            nodeData={data}
            icon={<Icon />}
            minWidth={240}
            minHeight={88}
          />
          <div
            className={cn(
              'h-2 shrink-0 rounded-t-md opacity-50',
              THEME_MAP[theme].title,
            )}></div>
          {
            data.selected && (
              <div className='absolute left-1/2 top-[-41px] -translate-x-1/2'>
                <NoteEditorToolbar
                  theme={theme}
                  onThemeChange={handleThemeChange}
                  onCopy={handleNodesCopy}
                  onDuplicate={handleNodesDuplicate}
                  onDelete={handleDeleteNode}
                  showAuthor={data.showAuthor}
                  onShowAuthorChange={handleShowAuthorChange}
                />
              </div>
            )
          }
          <div className='grow overflow-y-auto px-3 py-2.5'>
            <div className={cn(
              data.selected && 'nodrag nopan nowheel cursor-text',
            )}>
              <NoteEditor
                containerElement={ref.current}
                placeholder={t('workflow.nodes.note.editor.placeholder') || ''}
                onChange={handleEditorChange}
              />
            </div>
          </div>
          {
            data.showAuthor && (
              <div className='p-3 pt-0 text-xs text-text-tertiary'>
                {data.author}
              </div>
            )
          }
        </>
      </NoteEditorContextProvider>
    </div>
  )
}
 
export default memo(NoteNode)