mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
wip
This commit is contained in:
@@ -1783,7 +1783,8 @@
|
||||
"textPlaceholder": "Empty Text",
|
||||
"workflowBuilderAlphaWarning": "The workflow builder is currently in alpha. There may be breaking changes before the stable release.",
|
||||
"minimum": "Minimum",
|
||||
"maximum": "Maximum"
|
||||
"maximum": "Maximum",
|
||||
"deploy": "Deploy"
|
||||
}
|
||||
},
|
||||
"controlLayers": {
|
||||
|
||||
@@ -28,7 +28,8 @@ export type AppFeature =
|
||||
| 'starterModels'
|
||||
| 'hfToken'
|
||||
| 'retryQueueItem'
|
||||
| 'cancelAndClearAll';
|
||||
| 'cancelAndClearAll'
|
||||
| 'deployWorkflow';
|
||||
/**
|
||||
* A disable-able Stable Diffusion feature
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { Box } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $isDeploying } from 'features/nodes/components/sidePanel/builder/deploy';
|
||||
import { DeployWorkflowPanelContent } from 'features/nodes/components/sidePanel/workflow/DeployWorkflowPanelContent';
|
||||
import { HorizontalResizeHandle } from 'features/ui/components/tabs/ResizeHandle';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
@@ -20,6 +23,8 @@ export const EditModeLeftPanelContent = memo(() => {
|
||||
panelGroupRef.current.setLayout([50, 50]);
|
||||
}, []);
|
||||
|
||||
const isDeploying = useStore($isDeploying);
|
||||
|
||||
return (
|
||||
<Box position="relative" w="full" h="full">
|
||||
<PanelGroup
|
||||
@@ -30,7 +35,8 @@ export const EditModeLeftPanelContent = memo(() => {
|
||||
style={panelGroupStyles}
|
||||
>
|
||||
<Panel id="workflow" collapsible minSize={25}>
|
||||
<WorkflowFieldsLinearViewPanel />
|
||||
{!isDeploying && <WorkflowFieldsLinearViewPanel />}
|
||||
{isDeploying && <DeployWorkflowPanelContent />}
|
||||
</Panel>
|
||||
<HorizontalResizeHandle onDoubleClick={handleDoubleClickHandle} />
|
||||
<Panel id="inspector" collapsible minSize={25}>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export const $isDeploying = atom(false);
|
||||
export const $outputNodeId = atom<string | null>(null);
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Button } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { $isDeploying } from 'features/nodes/components/sidePanel/builder/deploy';
|
||||
import { selectIsWorkflowSaved } from 'features/nodes/store/workflowSlice';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiLightningFill } from 'react-icons/pi';
|
||||
|
||||
export const DeployWorkflowButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const deployWorkflowIsEnabled = useFeatureStatus('deployWorkflow');
|
||||
const isWorkflowSaved = useAppSelector(selectIsWorkflowSaved);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
$isDeploying.set(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={onClick}
|
||||
leftIcon={<PiLightningFill />}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
isDisabled={!deployWorkflowIsEnabled || !isWorkflowSaved}
|
||||
>
|
||||
{t('workflows.builder.deploy')}
|
||||
</Button>
|
||||
);
|
||||
});
|
||||
|
||||
DeployWorkflowButton.displayName = 'DeployWorkflowButton';
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useInputFieldTemplateTitleOrThrow } from 'features/nodes/hooks/useInputFieldTemplateTitleOrThrow';
|
||||
import { useInputFieldUserTitleOrThrow } from 'features/nodes/hooks/useInputFieldUserTitleOrThrow';
|
||||
import { useNodeTemplateTitleOrThrow } from 'features/nodes/hooks/useNodeTemplateTitleOrThrow';
|
||||
import { useNodeUserTitleOrThrow } from 'features/nodes/hooks/useNodeUserTitleOrThrow';
|
||||
import { selectNodeFieldElementsDeduped } from 'features/nodes/store/workflowSlice';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const DeployWorkflowPanelContent = memo(() => {
|
||||
const nodeFieldElements = useAppSelector(selectNodeFieldElementsDeduped);
|
||||
return (
|
||||
<Flex flexDir="column">
|
||||
{nodeFieldElements.map((el) => {
|
||||
const { nodeId, fieldName } = el.data.fieldIdentifier;
|
||||
return <NodeFieldPreview key={`${nodeId}-${fieldName}`} nodeId={nodeId} fieldName={fieldName} />;
|
||||
})}
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
DeployWorkflowPanelContent.displayName = 'DeployWorkflowPanelContent';
|
||||
|
||||
const NodeFieldPreview = memo(({ nodeId, fieldName }: { nodeId: string; fieldName: string }) => {
|
||||
const nodeUserTitle = useNodeUserTitleOrThrow(nodeId);
|
||||
const nodeTemplateTitle = useNodeTemplateTitleOrThrow(nodeId);
|
||||
const fieldUserTitle = useInputFieldUserTitleOrThrow(nodeId, fieldName);
|
||||
const fieldTemplateTitle = useInputFieldTemplateTitleOrThrow(nodeId, fieldName);
|
||||
return (
|
||||
<Flex>
|
||||
<Text>{`${nodeUserTitle || nodeTemplateTitle} -> ${fieldUserTitle || fieldTemplateTitle}`}</Text>
|
||||
<Text>{`${nodeId} -> ${fieldName}`}</Text>
|
||||
<Text></Text>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
NodeFieldPreview.displayName = 'NodeFieldPreview';
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||
import { Spacer, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||
import { WorkflowBuilder } from 'features/nodes/components/sidePanel/builder/WorkflowBuilder';
|
||||
import { DeployWorkflowButton } from 'features/nodes/components/sidePanel/workflow/DeployWorkflowButton';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -8,12 +10,15 @@ import WorkflowJSONTab from './WorkflowJSONTab';
|
||||
|
||||
const WorkflowFieldsLinearViewPanel = () => {
|
||||
const { t } = useTranslation();
|
||||
const deployWorkflowIsEnabled = useFeatureStatus('deployWorkflow');
|
||||
return (
|
||||
<Tabs variant="enclosed" display="flex" w="full" h="full" flexDir="column">
|
||||
<TabList>
|
||||
<Tab>{t('workflows.builder.builder')}</Tab>
|
||||
<Tab>{t('common.details')}</Tab>
|
||||
<Tab>JSON</Tab>
|
||||
<Spacer />
|
||||
{deployWorkflowIsEnabled && <DeployWorkflowButton />}
|
||||
</TabList>
|
||||
|
||||
<TabPanels h="full" pt={2}>
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
isNodeFieldElement,
|
||||
isTextElement,
|
||||
} from 'features/nodes/types/workflow';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { isEqual, uniqBy } from 'lodash-es';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { selectNodesSlice } from './selectors';
|
||||
@@ -350,6 +350,9 @@ export const selectWorkflowMode = createWorkflowSelector((workflow) => workflow.
|
||||
export const selectWorkflowIsTouched = createWorkflowSelector((workflow) => workflow.isTouched);
|
||||
export const selectWorkflowDescription = createWorkflowSelector((workflow) => workflow.description);
|
||||
export const selectWorkflowForm = createWorkflowSelector((workflow) => workflow.form);
|
||||
export const selectIsWorkflowSaved = createSelector(selectWorkflowId, selectWorkflowIsTouched, (id, isTouched) => {
|
||||
return id !== undefined && !isTouched;
|
||||
});
|
||||
|
||||
export const selectCleanEditor = createSelector([selectNodesSlice, selectWorkflowSlice], (nodes, workflow) => {
|
||||
const noNodes = !nodes.nodes.length;
|
||||
@@ -375,6 +378,9 @@ export const selectFormInitialValues = createWorkflowSelector((workflow) => work
|
||||
export const selectNodeFieldElements = createWorkflowSelector((workflow) =>
|
||||
Object.values(workflow.form.elements).filter(isNodeFieldElement)
|
||||
);
|
||||
export const selectNodeFieldElementsDeduped = createSelector(selectNodeFieldElements, (nodeFieldElements) =>
|
||||
uniqBy(nodeFieldElements, (el) => `${el.data.fieldIdentifier.nodeId}-${el.data.fieldIdentifier.fieldName}`)
|
||||
);
|
||||
const buildSelectElement = (id: string) => createWorkflowSelector((workflow) => workflow.form?.elements[id]);
|
||||
export const useElement = (id: string): FormElement | undefined => {
|
||||
const selector = useMemo(() => buildSelectElement(id), [id]);
|
||||
|
||||
Reference in New Issue
Block a user