Skip to content

Commit 82754d9

Browse files
committed
feat: Add subspell socket config
1 parent 1352119 commit 82754d9

3 files changed

Lines changed: 172 additions & 39 deletions

File tree

packages/client/editor/src/components/PropertiesWindow/SingleElement.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// DOCUMENTED
2-
import { GridViewRounded } from '@mui/icons-material'
2+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3+
import { faCodeCommit } from '@fortawesome/free-solid-svg-icons'
34
import { Icon, IconBtn } from 'client/core'
45
import styles from './datacontrols.module.css'
56

@@ -23,16 +24,15 @@ type Props = {
2324
* @param {Props} props - The props needed for the SingleElement component.
2425
* @returns {React.JSX.Element} The rendered SingleElement component.
2526
*/
26-
const SingleElement = ({ name, delete: handleDelete }: Props): React.JSX.Element => {
27+
const SingleElement = ({
28+
name,
29+
delete: handleDelete,
30+
}: Props): React.JSX.Element => {
2731
return (
2832
<div className={`${styles.flexCenterBtn} ${styles.inputContainer}`}>
2933
<div className={styles.flexCenterBtn}>
30-
<span style={{ float: 'right' }}>
31-
<IconBtn
32-
Icon={<GridViewRounded color="inherit" />}
33-
style={{ cursor: 'auto' }}
34-
label={name}
35-
/>
34+
<span className="pl-2 pr-4" style={{ float: 'right' }}>
35+
<FontAwesomeIcon icon={faCodeCommit} />
3636
</span>
3737
<p style={{ display: 'inline' }}>{name}</p>
3838
</div>

packages/client/editor/src/components/PropertiesWindow/SocketConfig.tsx

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,18 @@ import { useReactFlow } from '@xyflow/react'
1616
import { setEdges } from 'client/state'
1717
import { InputSocketSpecJSON } from '@magickml/behave-graph'
1818

19+
export type AddedSocket = {
20+
name: string
21+
valueType: string
22+
description?: string // Added description field
23+
}
24+
1925
type Props = {
20-
addSocket: (socket: any) => void
26+
addSocket: (socket: AddedSocket) => void
2127
valueTypes: string[]
2228
definedValueType: string | null
2329
sockets: any[]
30+
includeDescription?: boolean
2431
}
2532
/**
2633
* AddNewSocket component provides a form input to add a new socket.
@@ -30,25 +37,26 @@ type Props = {
3037
* @returns {React.JSX.Element} Form input to add a new socket.
3138
*/
3239

33-
const AddNewSocket = ({
40+
export const AddNewSocket = ({
3441
addSocket,
3542
valueTypes,
3643
definedValueType,
3744
sockets,
45+
includeDescription = false, // New prop to include description field
3846
}: Props) => {
3947
const [value, setValue] = useState('')
48+
const [description, setDescription] = useState('') // State for description
4049
const [selectedValueType, setSelectedValueType] = useState(valueTypes[0])
4150
const { enqueueSnackbar } = useSnackbar()
4251

43-
/**
44-
* Update the input value when changed.
45-
*
46-
* @param {Event} e - The change event.
47-
*/
4852
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
4953
setValue(e.target.value)
5054
}
5155

56+
const onDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
57+
setDescription(e.target.value)
58+
}
59+
5260
useEffect(() => {
5361
if (definedValueType) {
5462
setSelectedValueType(definedValueType)
@@ -57,12 +65,8 @@ const AddNewSocket = ({
5765
}
5866
}, [definedValueType, valueTypes])
5967

60-
/**
61-
* Add a new socket on form submission.
62-
*
63-
* @param {Event} e - The submit event.
64-
*/
65-
const onAdd = () => {
68+
const onAdd = (e: React.FormEvent) => {
69+
e.preventDefault()
6670
if (!value) return
6771
const socketExists = sockets.some(socket => socket.name === value)
6872

@@ -73,37 +77,35 @@ const AddNewSocket = ({
7377
return
7478
}
7579

76-
addSocket({ name: value, valueType: definedValueType || selectedValueType })
80+
addSocket({
81+
name: value,
82+
valueType: definedValueType || selectedValueType,
83+
description: includeDescription ? description : undefined, // Pass description if included
84+
})
7785
setValue('')
86+
setDescription('') // Reset description
7887
setSelectedValueType(valueTypes[0])
7988
}
8089

8190
return (
82-
<form className="w-full mt-1">
83-
{/* Flexbox container for input field and add button */}
84-
<div className="flex h-20 mr-2">
85-
<div className="flex gap-2 items-center h-10 ">
86-
{/* Input field */}
91+
<form className="w-full mt-1" onSubmit={onAdd}>
92+
<div className="flex flex-col gap-1">
93+
<div className="flex gap-1">
8794
<Input
8895
value={value}
8996
type="text"
9097
onChange={onChange}
9198
required
9299
placeholder="Add new socket"
93-
onKeyDown={e => {
94-
if (e.key === 'Enter') {
95-
onAdd()
96-
}
97-
}}
98-
className="w-28 h-8 pl-2 input-placeholder bg-[var(--dark-3)] border-[var(--dark-3)]"
100+
className="w-full h-8 pl-2 input-placeholder bg-[var(--dark-3)] border-[var(--dark-3)] m-0"
99101
/>
100102
{!definedValueType && (
101103
<Select
102104
value={selectedValueType}
103-
onValueChange={e => setSelectedValueType(e)}
105+
onValueChange={value => setSelectedValueType(value)}
104106
>
105-
<SelectTrigger className="h-8 pl-2 text-xs pr-1 font-medium border-0 bg-[var(--dark-1)]">
106-
<SelectValue placeholder={'Select a type'}>
107+
<SelectTrigger className="h-8 pl-2 text-xs pr-1 font-medium border-0 bg-[var(--dark-1)] w-28">
108+
<SelectValue placeholder="Select a type">
107109
{selectedValueType.charAt(0).toUpperCase() +
108110
selectedValueType.slice(1)}
109111
</SelectValue>
@@ -117,15 +119,22 @@ const AddNewSocket = ({
117119
</SelectContent>
118120
</Select>
119121
)}
120-
{/* Add button */}
121122
<Button
122-
onClick={onAdd}
123-
// type="submit"
123+
type="submit"
124124
className="h-8 w-8 border border-[var(--dark-3)] bg-ds-neutral rounded-sm"
125125
>
126126
+
127127
</Button>
128128
</div>
129+
{includeDescription && (
130+
<Input
131+
value={description}
132+
type="text"
133+
onChange={onDescriptionChange}
134+
placeholder="Socket description"
135+
className="w-full h-8 pl-2 input-placeholder bg-[var(--dark-3)] border-[var(--dark-3)] m-0"
136+
/>
137+
)}
129138
</div>
130139
</form>
131140
)
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { GraphJSON, GraphSocketJSON } from '@magickml/behave-graph'
2+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3+
import { faPlus } from '@fortawesome/free-solid-svg-icons'
4+
import { useCallback, useEffect, useState } from 'react'
5+
import SingleElement from './SingleElement'
6+
import { AddNewSocket, AddedSocket } from './SocketConfig'
7+
import { Tab, usePubSub } from '@magickml/providers'
8+
import { Button } from '@magickml/client-ui'
9+
10+
const defaultSocketValues = [
11+
'string',
12+
'float',
13+
'integer',
14+
'boolean',
15+
'array',
16+
'object',
17+
]
18+
19+
export const SubspellSocketConfig = ({
20+
sockets,
21+
type,
22+
socketValues = defaultSocketValues,
23+
tab,
24+
graph,
25+
title,
26+
}: {
27+
sockets: GraphSocketJSON[]
28+
type: 'input' | 'output'
29+
socketValues?: string[]
30+
tab: Tab
31+
graph: GraphJSON
32+
title: string
33+
}) => {
34+
const { publish, events } = usePubSub()
35+
const [initialSockets, setInitialSockets] = useState<GraphSocketJSON[]>(
36+
sockets || []
37+
)
38+
const [showForm, setShowForm] = useState(false)
39+
const toggleForm = () => {
40+
setShowForm(!showForm)
41+
}
42+
43+
useEffect(() => {
44+
if (!sockets) return
45+
setInitialSockets(sockets)
46+
}, [sockets])
47+
48+
const addSocket = useCallback(
49+
(socket: AddedSocket) => {
50+
const newSocket: GraphSocketJSON = {
51+
key: socket.name,
52+
valueType: socket.valueType,
53+
description: socket.description,
54+
}
55+
56+
const socketType = type === 'input' ? 'graphInputs' : 'graphOutputs'
57+
58+
const sockets = graph[socketType] || []
59+
const newSockets = [...sockets, newSocket]
60+
61+
setInitialSockets(newSockets)
62+
63+
const newGraph = { ...graph, [socketType]: newSockets }
64+
65+
publish(events.$SAVE_SPELL_DIFF(tab.id), { graph: newGraph })
66+
setShowForm(false)
67+
},
68+
[sockets]
69+
)
70+
71+
const deleteSocket = (name: string) => {
72+
const socketType = type === 'input' ? 'graphInputs' : 'graphOutputs'
73+
74+
const sockets = graph[socketType] || []
75+
const newSockets = sockets.filter(
76+
(socket: GraphSocketJSON) => socket.key !== name
77+
)
78+
79+
const newGraph = { ...graph, [socketType]: newSockets }
80+
81+
setInitialSockets(newSockets)
82+
83+
publish(events.$SAVE_SPELL_DIFF(tab.id), { graph: newGraph })
84+
}
85+
86+
return (
87+
<div className="pb-2">
88+
<div className="w-full p-1 bg-[var(--background-color-light)] flex justify-between items-center">
89+
<h3>{title}</h3>
90+
<Button
91+
className="float-right"
92+
variant="outline"
93+
size="sm"
94+
onClick={() => {
95+
setShowForm(prev => !prev)
96+
}}
97+
>
98+
<FontAwesomeIcon icon={faPlus} />
99+
</Button>
100+
</div>
101+
{showForm && (
102+
<div className="pr-1 pl-1">
103+
<AddNewSocket
104+
sockets={initialSockets}
105+
addSocket={addSocket}
106+
valueTypes={socketValues}
107+
definedValueType={null}
108+
includeDescription={true}
109+
/>
110+
</div>
111+
)}
112+
{initialSockets.map((socket: GraphSocketJSON, i: number) => (
113+
<div className="pr-1 pl-1">
114+
<SingleElement
115+
name={socket.key}
116+
key={socket.key + i}
117+
delete={deleteSocket}
118+
type={socket.valueType}
119+
/>
120+
</div>
121+
))}
122+
</div>
123+
)
124+
}

0 commit comments

Comments
 (0)