diff --git a/src/renderer/components/Handle.tsx b/src/renderer/components/Handle.tsx index 8cfe836a7..eb3d73ed1 100644 --- a/src/renderer/components/Handle.tsx +++ b/src/renderer/components/Handle.tsx @@ -2,7 +2,9 @@ import { Box, Tooltip, chakra } from '@chakra-ui/react'; import React, { memo } from 'react'; import ReactMarkdown from 'react-markdown'; import { Connection, Position, Handle as RFHandle } from 'reactflow'; +import { useContext } from 'use-context-selector'; import { Validity } from '../../common/Validity'; +import { FakeNodeContext } from '../contexts/FakeExampleContext'; import { noContextMenu } from '../hooks/useContextMenu'; export type HandleType = 'input' | 'output'; @@ -12,7 +14,6 @@ interface HandleElementProps { isValidConnection: (connection: Readonly) => boolean; validity: Validity; id: string; - useFakeHandles: boolean; } // Had to do this garbage to prevent chakra from clashing the position prop @@ -23,9 +24,10 @@ const HandleElement = memo( validity, type, id, - useFakeHandles, ...props }: React.PropsWithChildren) => { + const { isFake } = useContext(FakeNodeContext); + return ( - {useFakeHandles ? ( + {isFake ? ( ) => boolean; handleColors: readonly string[]; connectedColor: string | undefined; - useFakeHandles: boolean; } const getBackground = (colors: readonly string[]): string => { @@ -102,15 +103,7 @@ const getBackground = (colors: readonly string[]): string => { }; export const Handle = memo( - ({ - id, - type, - validity, - isValidConnection, - handleColors, - connectedColor, - useFakeHandles, - }: HandleProps) => { + ({ id, type, validity, isValidConnection, handleColors, connectedColor }: HandleProps) => { const isConnected = !!connectedColor; const connectedBg = 'var(--connection-color)'; @@ -152,7 +145,6 @@ export const Handle = memo( position: 'relative', }} type={type} - useFakeHandles={useFakeHandles} validity={validity} onContextMenu={noContextMenu} /> diff --git a/src/renderer/components/NodeDocumentation/NodeExample.tsx b/src/renderer/components/NodeDocumentation/NodeExample.tsx index 703911197..26d3e77c2 100644 --- a/src/renderer/components/NodeDocumentation/NodeExample.tsx +++ b/src/renderer/components/NodeDocumentation/NodeExample.tsx @@ -16,6 +16,7 @@ import { DisabledStatus } from '../../../common/nodes/disabled'; import { TypeState } from '../../../common/nodes/TypeState'; import { EMPTY_ARRAY, EMPTY_MAP, EMPTY_OBJECT, EMPTY_SET } from '../../../common/util'; import { BackendContext } from '../../contexts/BackendContext'; +import { FakeNodeProvider } from '../../contexts/FakeExampleContext'; import { TypeInfo, testInputConditionTypeInfo } from '../../helpers/nodeState'; import { NodeBody } from '../node/NodeBody'; import { NodeFooter } from '../node/NodeFooter/NodeFooter'; @@ -105,59 +106,60 @@ export const NodeExample = memo(({ accentColor, selectedSchema }: NodeExamplePro key={selectedSchema.schemaId} pointerEvents="none" > -
- +
- - + + + testInputConditionTypeInfo(condition, inputData, typeInfo), + }} + /> + + - testInputConditionTypeInfo(condition, inputData, typeInfo), - useFakeHandles: true, - }} + id={nodeId} + validity={{ isValid: true }} /> - - -
+
+ ); }); diff --git a/src/renderer/components/inputs/DirectoryInput.tsx b/src/renderer/components/inputs/DirectoryInput.tsx index 59d29559f..3eb6e566e 100644 --- a/src/renderer/components/inputs/DirectoryInput.tsx +++ b/src/renderer/components/inputs/DirectoryInput.tsx @@ -18,7 +18,6 @@ import { ipcRenderer } from '../../../common/safeIpc'; import { useContextMenu } from '../../hooks/useContextMenu'; import { useInputRefactor } from '../../hooks/useInputRefactor'; import { useLastDirectory } from '../../hooks/useLastDirectory'; -import { CopyOverrideIdSection } from './elements/CopyOverrideIdSection'; import { MaybeLabel } from './InputContainer'; import { InputProps } from './props'; @@ -102,10 +101,6 @@ export const DirectoryInput = memo( {t('inputs.directory.copyFullDirectoryPath', 'Copy Full Directory Path')} {refactor} - )); diff --git a/src/renderer/components/inputs/FileInput.tsx b/src/renderer/components/inputs/FileInput.tsx index 48d6cfaa2..73c7be6f9 100644 --- a/src/renderer/components/inputs/FileInput.tsx +++ b/src/renderer/components/inputs/FileInput.tsx @@ -20,8 +20,8 @@ import { ipcRenderer } from '../../../common/safeIpc'; import { AlertBoxContext } from '../../contexts/AlertBoxContext'; import { getSingleFileWithExtension } from '../../helpers/dataTransfer'; import { useContextMenu } from '../../hooks/useContextMenu'; +import { useInputRefactor } from '../../hooks/useInputRefactor'; import { useLastDirectory } from '../../hooks/useLastDirectory'; -import { CopyOverrideIdSection } from './elements/CopyOverrideIdSection'; import { WithLabel } from './InputContainer'; import { InputProps } from './props'; @@ -102,6 +102,8 @@ export const FileInput = memo( } }; + const refactor = useInputRefactor(nodeId, input, filePath, isConnected); + const menu = useContextMenu(() => ( {t('inputs.file.copyFullFilePath', 'Copy Full File Path')} - + {refactor} )); diff --git a/src/renderer/components/inputs/InputContainer.tsx b/src/renderer/components/inputs/InputContainer.tsx index dee55f0b2..918723bed 100644 --- a/src/renderer/components/inputs/InputContainer.tsx +++ b/src/renderer/components/inputs/InputContainer.tsx @@ -19,17 +19,10 @@ export interface HandleWrapperProps { id: string; inputId: InputId; connectableType: Type; - useFakeHandles: boolean; } export const HandleWrapper = memo( - ({ - children, - id, - inputId, - connectableType, - useFakeHandles, - }: React.PropsWithChildren) => { + ({ children, id, inputId, connectableType }: React.PropsWithChildren) => { const { isValidConnection, edgeChanges, useConnectingFrom, typeState } = useContext(GlobalVolatileContext); const { getEdges, getNode } = useReactFlow(); @@ -113,7 +106,6 @@ export const HandleWrapper = memo( id={targetHandle} isValidConnection={isValidConnectionForRf} type="input" - useFakeHandles={useFakeHandles} validity={validity} /> diff --git a/src/renderer/components/inputs/NumberInput.tsx b/src/renderer/components/inputs/NumberInput.tsx index 1ad792dac..b86aacd6f 100644 --- a/src/renderer/components/inputs/NumberInput.tsx +++ b/src/renderer/components/inputs/NumberInput.tsx @@ -8,7 +8,6 @@ import { areApproximatelyEqual } from '../../../common/util'; import { useContextMenu } from '../../hooks/useContextMenu'; import { useInputRefactor } from '../../hooks/useInputRefactor'; import { AdvancedNumberInput } from './elements/AdvanceNumberInput'; -import { CopyOverrideIdSection } from './elements/CopyOverrideIdSection'; import { MaybeLabel } from './InputContainer'; import { InputProps } from './props'; @@ -80,10 +79,6 @@ export const NumberInput = memo( {t('inputs.number.paste', 'Paste')} {refactor} - )); diff --git a/src/renderer/components/inputs/SchemaInput.tsx b/src/renderer/components/inputs/SchemaInput.tsx index d0b6ad440..070e759bd 100644 --- a/src/renderer/components/inputs/SchemaInput.tsx +++ b/src/renderer/components/inputs/SchemaInput.tsx @@ -53,7 +53,6 @@ export const SchemaInput = memo(({ input, nodeState, afterInput }: SingleInputPr isLocked, connectedInputs, type, - useFakeHandles, } = nodeState; const functionDefinition = useContextSelector(BackendContext, (c) => @@ -119,7 +118,6 @@ export const SchemaInput = memo(({ input, nodeState, afterInput }: SingleInputPr connectableType={connectableType} id={nodeId} inputId={inputId} - useFakeHandles={useFakeHandles} > {inputElement} diff --git a/src/renderer/components/inputs/SliderInput.tsx b/src/renderer/components/inputs/SliderInput.tsx index 8492f8716..ea8e4b710 100644 --- a/src/renderer/components/inputs/SliderInput.tsx +++ b/src/renderer/components/inputs/SliderInput.tsx @@ -11,7 +11,6 @@ import { BackendContext } from '../../contexts/BackendContext'; import { useContextMenu } from '../../hooks/useContextMenu'; import { useInputRefactor } from '../../hooks/useInputRefactor'; import { AdvancedNumberInput } from './elements/AdvanceNumberInput'; -import { CopyOverrideIdSection } from './elements/CopyOverrideIdSection'; import { LINEAR_SCALE, LogScale, @@ -169,10 +168,6 @@ export const SliderInput = memo( {t('inputs.number.paste', 'Paste')} {refactor} - )); diff --git a/src/renderer/components/inputs/TextInput.tsx b/src/renderer/components/inputs/TextInput.tsx index c5abcf152..ada19fdbc 100644 --- a/src/renderer/components/inputs/TextInput.tsx +++ b/src/renderer/components/inputs/TextInput.tsx @@ -12,7 +12,6 @@ import { typeToString } from '../../helpers/naviHelpers'; import { useContextMenu } from '../../hooks/useContextMenu'; import { useInputRefactor } from '../../hooks/useInputRefactor'; import { DragHandleSVG } from '../CustomIcons'; -import { CopyOverrideIdSection } from './elements/CopyOverrideIdSection'; import { MaybeLabel } from './InputContainer'; import { InputProps } from './props'; @@ -113,10 +112,6 @@ export const TextInput = memo( {t('inputs.text.paste', 'Paste')} {refactor} - )); diff --git a/src/renderer/components/inputs/elements/CopyOverrideIdSection.tsx b/src/renderer/components/inputs/elements/CopyOverrideIdSection.tsx deleted file mode 100644 index 008dd3bd3..000000000 --- a/src/renderer/components/inputs/elements/CopyOverrideIdSection.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { MenuDivider, MenuItem } from '@chakra-ui/react'; -import { clipboard } from 'electron'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { MdContentCopy } from 'react-icons/md'; -import { InputId } from '../../../../common/common-types'; -import { createInputOverrideId } from '../../../../common/input-override'; - -interface CopyOverrideIdSectionProps { - nodeId: string | undefined; - inputId: InputId | undefined; -} - -export const CopyOverrideIdSection = memo(({ nodeId, inputId }: CopyOverrideIdSectionProps) => { - const { t } = useTranslation(); - - if (nodeId === undefined || inputId === undefined) { - return null; - } - - return ( - <> - - } - onClick={() => { - clipboard.writeText(createInputOverrideId(nodeId, inputId)); - }} - > - {t('inputs.copyInputOverrideId', 'Copy Input Override Id')} - - - ); -}); diff --git a/src/renderer/components/node/NodeOutputs.tsx b/src/renderer/components/node/NodeOutputs.tsx index 9e3775c25..3ef3a1718 100644 --- a/src/renderer/components/node/NodeOutputs.tsx +++ b/src/renderer/components/node/NodeOutputs.tsx @@ -48,7 +48,7 @@ interface NodeOutputProps { } export const NodeOutputs = memo(({ nodeState, animated }: NodeOutputProps) => { - const { id, schema, schemaId, useFakeHandles } = nodeState; + const { id, schema, schemaId } = nodeState; const { functionDefinitions } = useContext(BackendContext); const { setManualOutputType } = useContext(GlobalContext); @@ -100,7 +100,6 @@ export const NodeOutputs = memo(({ nodeState, animated }: NodeOutputProps) => { key={`${id}-${output.id}`} output={output} type={type} - useFakeHandles={useFakeHandles} > { const { selectNode, createNode, createEdge } = useContext(GlobalContext); const { getNodes, getEdges } = useReactFlow(); + const { isFake } = useContext(FakeNodeContext); return ( { + if (isFake) return; + const byId = new Map(getNodes().map((n) => [n.id, n])); const sourceHandle = stringifySourceHandle({ nodeId: id, outputId: output.id }); diff --git a/src/renderer/components/outputs/OutputContainer.tsx b/src/renderer/components/outputs/OutputContainer.tsx index 6789b01b2..e2748561d 100644 --- a/src/renderer/components/outputs/OutputContainer.tsx +++ b/src/renderer/components/outputs/OutputContainer.tsx @@ -18,7 +18,6 @@ interface OutputContainerProps { type: Type | undefined; generic: boolean; isConnected: boolean; - useFakeHandles: boolean; } export const OutputContainer = memo( @@ -30,7 +29,6 @@ export const OutputContainer = memo( type, generic, isConnected, - useFakeHandles, }: React.PropsWithChildren) => { const { isValidConnection, useConnectingFrom } = useContext(GlobalVolatileContext); const [connectingFrom] = useConnectingFrom; @@ -79,7 +77,6 @@ export const OutputContainer = memo( id={sourceHandle} isValidConnection={isValidConnectionForRf} type="output" - useFakeHandles={useFakeHandles} validity={validity} /> diff --git a/src/renderer/contexts/FakeExampleContext.tsx b/src/renderer/contexts/FakeExampleContext.tsx new file mode 100644 index 000000000..07058b005 --- /dev/null +++ b/src/renderer/contexts/FakeExampleContext.tsx @@ -0,0 +1,19 @@ +import React, { memo } from 'react'; +import { createContext } from 'use-context-selector'; +import { useMemoObject } from '../hooks/useMemo'; + +interface FakeNodeContextState { + isFake: boolean; +} + +export const FakeNodeContext = createContext>({ + isFake: false, +}); + +export const FakeNodeProvider = memo( + ({ children, isFake }: React.PropsWithChildren) => { + const value = useMemoObject({ isFake }); + + return {children}; + } +); diff --git a/src/renderer/helpers/nodeState.ts b/src/renderer/helpers/nodeState.ts index 7ff8f24ee..c2eb27e8d 100644 --- a/src/renderer/helpers/nodeState.ts +++ b/src/renderer/helpers/nodeState.ts @@ -71,7 +71,6 @@ export interface NodeState { readonly connectedOutputs: ReadonlySet; readonly type: TypeInfo; readonly testCondition: (condition: Condition) => boolean; - readonly useFakeHandles: boolean; } export const useNodeStateFromData = (data: NodeData): NodeState => { @@ -117,6 +116,5 @@ export const useNodeStateFromData = (data: NodeData): NodeState => { connectedOutputs, type, testCondition, - useFakeHandles: false, }); }; diff --git a/src/renderer/hooks/useInputRefactor.tsx b/src/renderer/hooks/useInputRefactor.tsx index 33a9ffe5b..95215c982 100644 --- a/src/renderer/hooks/useInputRefactor.tsx +++ b/src/renderer/hooks/useInputRefactor.tsx @@ -1,6 +1,8 @@ import { MenuDivider, MenuItem } from '@chakra-ui/react'; +import { clipboard } from 'electron'; import { useTranslation } from 'react-i18next'; import { CgArrowsExpandUpLeft } from 'react-icons/cg'; +import { MdContentCopy } from 'react-icons/md'; import { useReactFlow } from 'reactflow'; import { useContext } from 'use-context-selector'; import { @@ -14,8 +16,10 @@ import { PartialBy, SchemaId, } from '../../common/common-types'; +import { createInputOverrideId } from '../../common/input-override'; import { createUniqueId } from '../../common/util'; import { BackendContext } from '../contexts/BackendContext'; +import { FakeNodeContext } from '../contexts/FakeExampleContext'; import { GlobalContext } from '../contexts/GlobalNodeState'; const valueNodeMap = { @@ -35,15 +39,17 @@ export const useInputRefactor = ( const { createNode, createEdge } = useContext(GlobalContext); const { schemata } = useContext(BackendContext); const { getNode } = useReactFlow(); + const { isFake } = useContext(FakeNodeContext); const inputId = input.id; - if (nodeId === undefined || inputId === undefined) { + if (isFake || nodeId === undefined || inputId === undefined) { return null; } - const refactoringOptions: JSX.Element[] = []; + const refactoringOptions: (JSX.Element | 'divider')[] = []; const specificInput = input as Input; + // extract value into node if ( specificInput.hasHandle && ((specificInput.kind === 'text' && !specificInput.multiline) || @@ -92,11 +98,41 @@ export const useInputRefactor = ( ); } - if (refactoringOptions.length === 0) return null; + // copy override id + if ( + specificInput.kind === 'directory' || + specificInput.kind === 'file' || + specificInput.kind === 'text' || + specificInput.kind === 'number' || + specificInput.kind === 'slider' + ) { + refactoringOptions.push( + 'divider', + } + key="copy override" + onClick={() => { + clipboard.writeText(createInputOverrideId(nodeId, inputId)); + }} + > + {t('inputs.copyInputOverrideId', 'Copy Input Override Id')} + + ); + } + + // handle dividers + const finalOptions = refactoringOptions.filter( + (o, i) => o !== 'divider' || (i > 0 && refactoringOptions[i - 1] !== 'divider') + ); + + if (finalOptions.length === 0) return null; return ( <> - {refactoringOptions} + {finalOptions.map((o, i) => + // eslint-disable-next-line react/no-array-index-key + o === 'divider' ? : o + )} ); };