+ '--node-bg-color': (isInProgress || !!nodeError) ? nodeBgColor : Color(nodeBgColor).alpha(.92).hexa(),
+ } as React.CSSProperties}
+ onMouseEnter={onMouseEnter}
+ onMouseUp={onMouseUp}
+ onMouseLeave={onMouseLeave}
+ onMouseMove={onMouseMove}
+ >
- {!collapsed &&
- {getWidgetIcon(widget)}
- {title}
- {isPositive && {"("}Positive{")"}}
- {isNegative && {"("}Negative{")"}}
-
+ {getWidgetIcon(widget)}
+ {title}
+ {isPositive && {"("}Positive{")"}}
+ {isNegative && {"("}Negative{")"}}
+
- {isInProgress?
+ {isInProgress ?
{inputs.map((input, index) => (
-
+
))}
@@ -116,22 +127,22 @@ export const NodeComponent = memo(({
<>
{params.map(({ property, input }) => (
-
+
))}
-
>
)
}
- {!collapsed &&
}
+ {!collapsed && }
+ {connectingIndicator}
) : (
<>
-
-
+
+
>
)}
diff --git a/apps/electron-frontend/src/components/workflow-editor/workflow-editor.style.module.scss b/apps/electron-frontend/src/components/workflow-editor/workflow-editor.style.module.scss
index aaa6f98e..c14c706f 100755
--- a/apps/electron-frontend/src/components/workflow-editor/workflow-editor.style.module.scss
+++ b/apps/electron-frontend/src/components/workflow-editor/workflow-editor.style.module.scss
@@ -16,10 +16,27 @@
right: 0;
bottom: 0;
}
+ .reactflow-editor.loading {
+ .react-flow__nodes, .react-flow__viewport {
+ visibility: hidden;
+ opacity: 0;
+ pointer-events: none;
+ }
+ }
+
.react-flow__panel {
.ant-space-item {
display: flex;
align-items: center;
+ .action.action-Run {
+ background-color: var(--primaryColor);
+
+ }
+ .title {
+ color:#C5C5D359;
+ padding-left: 8px;
+ }
+
}
}
@@ -191,17 +208,46 @@
pointer-events: none;
min-width: 5px;
min-height: 5px;
- width: 6px;
- height: 6px;
- background: #92939B;
- border-radius: 100%;
+ width: 30px;
+ height: 30px;
+ background: transparent;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ transition: var(--transition);
+ .handle-visible {
+ width: 10px;
+ height: 10px;
+ border-radius: 100%;
+ pointer-events: none;
+ }
}
-
+
+ .react-flow__handle-left {
+ top: -3px;
+ left: -2px;
+ transform: translate(0, -50%);
+ }
+
+ .react-flow__handle-right {
+ right: -2px;
+ top: -3px;
+ transform: translate(0, -50%);
+ }
+
+ .node-slot-left .connectionindicator {
+ transform-origin: 40% 50%;
+ }
+
+ .node-slot-right .connectionindicator {
+ transform-origin: 60% 50%;
+ }
+
.react-flow__handle.connectionindicator {
pointer-events: all;
cursor: crosshair;
}
-
+
.react-flow__handle-bottom {
top: auto;
left: 50%;
@@ -215,18 +261,6 @@
transform: translate(-50%, 0);
}
- .react-flow__handle-left {
- top: 36%;
- left: 3px;
- transform: translate(0, -50%);
- }
-
- .react-flow__handle-right {
- right: 4px;
- top: 36%;
- transform: translate(0, -50%);
- }
-
.react-flow__edgeupdater {
cursor: move;
pointer-events: all;
@@ -352,23 +386,25 @@
}
.react-flow__controls {
- // box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.08);
- background-color: var(--backgroundColorL1);
- border-radius: 4px;
+ box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.08);
+ background: rgba(41, 42, 53, 0.65);
+ backdrop-filter: blur(5px);
+ border-radius: 12px;
overflow: hidden;
+ padding: 8px;
}
.react-flow__controls-button {
border: none;
transition: var(--transition);
- background:var(--backgroundColorL1);
- border-bottom: 1px solid var(--borderColor);
+ background: none;
+ // border-bottom: 1px solid var(--borderColor);
box-sizing: content-box;
display: flex;
justify-content: center;
align-items: center;
width: 16px;
- height: 16px;
+ height: 18px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
diff --git a/apps/electron-package/package-lock.json b/apps/electron-package/package-lock.json
index 1ef51068..4b68de9e 100644
--- a/apps/electron-package/package-lock.json
+++ b/apps/electron-package/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "comflowy",
- "version": "0.2.1-alpha",
+ "version": "0.2.2-alpha",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "comflowy",
- "version": "0.2.1-alpha",
+ "version": "0.2.2-alpha",
"dependencies": {
"node-pty": "^1.0.0",
"regedit": "^5.1.2"
diff --git a/apps/electron-package/package.json b/apps/electron-package/package.json
index 1c19a5e0..e3104c10 100644
--- a/apps/electron-package/package.json
+++ b/apps/electron-package/package.json
@@ -1,6 +1,6 @@
{
"name": "comflowy",
- "version": "0.2.1-alpha",
+ "version": "0.2.2-alpha",
"private": true,
"main": "layers/main/dist/index.js",
"scripts": {
diff --git a/apps/node/src/app.ts b/apps/node/src/app.ts
index 0d48483c..1c5303ec 100644
--- a/apps/node/src/app.ts
+++ b/apps/node/src/app.ts
@@ -94,7 +94,7 @@ export async function startAppServer(params: {
ComflowyConsole.serve(app, server, wss);
channelService.serve(app, server, wss);
server.listen(port, () => {
- logger.info(`Server is running at http://localhost:${port}`);
+ console.log(`Server is running at http://localhost:${port}`);
});
setTimeout(() => {
diff --git a/apps/node/src/modules/comfyui/bootstrap.ts b/apps/node/src/modules/comfyui/bootstrap.ts
index d92a9bcc..6795efff 100644
--- a/apps/node/src/modules/comfyui/bootstrap.ts
+++ b/apps/node/src/modules/comfyui/bootstrap.ts
@@ -26,9 +26,12 @@ export async function checkBasicRequirements() {
isTorchInstalled = false,
isComfyUIInstalled = false,
isComfyUIStarted = false,
+ isCustomComfyEnv = false,
isGitInstalled = false;
if (isSetupedConfig) {
isComfyUIInstalled = await checkIfInstalledComfyUI();
+ const appConfig = appConfigManager.getSetupConfig();
+ isCustomComfyEnv = appConfig?.isCustomComfyEnv ?? false;
/**
* if user delete ComfyUI, user should re-setup the config
*/
@@ -48,6 +51,7 @@ export async function checkBasicRequirements() {
return {
isCondaInstalled,
isPythonInstalled,
+ isCustomComfyEnv,
isGitInstalled,
isTorchInstalled,
isComfyUIInstalled,
diff --git a/apps/node/src/modules/utils/conda.ts b/apps/node/src/modules/utils/conda.ts
index cd521e05..ddb2bccb 100644
--- a/apps/node/src/modules/utils/conda.ts
+++ b/apps/node/src/modules/utils/conda.ts
@@ -1,7 +1,7 @@
import { execSync } from "child_process";
import { OS_HOME_DIRECTORY, SHELL_ENV_PATH, getSystemPath, isWindows } from "./env";
import path from "path";
-import { CONDA_ENV_NAME } from "../config-manager";
+import { CONDA_ENV_NAME, CONFIG_KEYS, appConfigManager } from "../config-manager";
import logger from "./logger";
export const DEFAULT_CONDA_PATH = isWindows ? 'C:\\tools\\Miniconda3' : `${OS_HOME_DIRECTORY}/miniconda3`;
@@ -34,10 +34,18 @@ class Conda {
* Init conda env
*/
updateCondaInfo () {
+ // 如果有配置本地自定义的 python 环境,那么就简单设置 PYTHON 和 PIP 就好
this.info = getCondaInfo();
this.env = getCondaEnv(CONDA_ENV_NAME, this.info!);
console.log("env", this.info, this.env);
logger.info("update conda info" + JSON.stringify(this.info) + JSON.stringify(this.env));
+
+ const {pythonPath} = appConfigManager.getSetupConfig();
+ if (pythonPath && pythonPath !== "") {
+ logger.info("use custom env python: ", pythonPath)
+ this.env!.PYTHON_PATH = pythonPath;
+ this.env!.PIP_PATH = `${pythonPath} -m pip`;
+ }
}
getCondaPaths(): CondaInfo & CondaEnvInfo {
diff --git a/apps/node/src/modules/utils/logger.ts b/apps/node/src/modules/utils/logger.ts
index 290f28db..77b13e81 100644
--- a/apps/node/src/modules/utils/logger.ts
+++ b/apps/node/src/modules/utils/logger.ts
@@ -33,9 +33,9 @@ const logger = winston.createLogger({
});
if (process.env.NODE_ENV !== 'production') {
- // logger.add(new winston.transports.Console({
- // format: winston.format.simple(),
- // }));
+ logger.add(new winston.transports.Console({
+ format: winston.format.simple(),
+ }));
}
export default logger;
\ No newline at end of file
diff --git a/apps/node/src/modules/utils/run-command.ts b/apps/node/src/modules/utils/run-command.ts
index a07f55a1..1e289208 100644
--- a/apps/node/src/modules/utils/run-command.ts
+++ b/apps/node/src/modules/utils/run-command.ts
@@ -14,7 +14,7 @@ export async function runCommand(
}> {
const { systemProxy, systemProxyString } = await getSystemProxy();
if (systemProxy) {
- logger.info("run command with proxy:" + systemProxyString);
+ logger.info("run command with proxy:" + command + " " + systemProxyString);
} else {
logger.info("run command without proxy")
}
diff --git a/apps/node/src/modules/utils/verify-python.ts b/apps/node/src/modules/utils/verify-python.ts
new file mode 100644
index 00000000..b100992f
--- /dev/null
+++ b/apps/node/src/modules/utils/verify-python.ts
@@ -0,0 +1,19 @@
+import { exec } from 'child_process';
+import { promisify } from 'util';
+
+const execAsync = promisify(exec);
+
+export async function verifyPythonPath(pythonPath: string): Promise
{
+ try {
+ const { stdout, stderr } = await execAsync(`${pythonPath} --version`);
+ if (stderr) {
+ throw new Error(stderr)
+ }
+ console.log(`Python found: ${stdout}`);
+ return true;
+ } catch (error: any) {
+ throw new Error(`Python not found at ${pythonPath}`);
+ }
+}
+
+// verifyPythonPath("/Users/chenxuejia/comflowy/tmp/python");
\ No newline at end of file
diff --git a/apps/node/src/routes/api/bootstrap.ts b/apps/node/src/routes/api/bootstrap.ts
index 591bcaf0..0f8884af 100644
--- a/apps/node/src/routes/api/bootstrap.ts
+++ b/apps/node/src/routes/api/bootstrap.ts
@@ -13,7 +13,8 @@ import { verifyIsTorchInstalled } from 'src/modules/comfyui/verify-torch';
import { runCommand } from '../../modules/utils/run-command';
import { systemProxyString } from '../../modules/utils/env';
import { conda } from '../../modules/utils/conda';
-import { modelManager } from 'src/modules/model-manager/model-manager';
+import { modelManager } from '../../modules/model-manager/model-manager';
+import { verifyPythonPath } from '../../modules/utils/verify-python';
/**
* fetch all extensions
@@ -22,6 +23,7 @@ import { modelManager } from 'src/modules/model-manager/model-manager';
*/
export async function ApiEnvCheck(req: Request, res: Response) {
try {
+ logger.info("start env check")
const requirements = await checkBasicRequirements()
res.send({
success: true,
@@ -231,12 +233,23 @@ export async function ApiSetupConfig(req: Request, res: Response) {
}
}
+ if (data.pythonPath) {
+ console.log("python path", data.pythonPath)
+ await verifyPythonPath(data.pythonPath);
+ }
+
const setupString = JSON.stringify({
comfyUIDir: comfyUIPath,
+ pythonPath: data.pythonPath || undefined,
+ isCustomComfyEnv: !!data.pythonPath,
stableDiffusionDir: stableDiffusionPath
});
appConfigManager.set(CONFIG_KEYS.appSetupConfig, setupString);
+
+ // if (data.pythonPath) {
+ // conda.updateCondaInfo();
+ // }
res.send({
success: true,
@@ -386,4 +399,4 @@ export async function ApiUpdateComfyUIAndRestart(req: Request, res: Response) {
error: err.message
});
}
-}
\ No newline at end of file
+}
diff --git a/packages/common/comfyui-bridge/prompt.ts b/packages/common/comfyui-bridge/prompt.ts
index 9c9edc25..cf29ea97 100644
--- a/packages/common/comfyui-bridge/prompt.ts
+++ b/packages/common/comfyui-bridge/prompt.ts
@@ -294,7 +294,9 @@ export function createPrompt(workflowSource: PersistedWorkflowDocument, widgets:
}
if (source.value.widget === NODE_PRIMITIVE) {
- value = findPrimitiveNodeValue(source);
+ const targetNode = workflow.nodes[edge.target!];
+ const targetHandle = edge.targetHandle!;
+ value = findPrimitiveNodeValue(source, targetNode, targetHandle);
}
return value;
@@ -309,12 +311,21 @@ export function createPrompt(workflowSource: PersistedWorkflowDocument, widgets:
}
/**
- * primitive value is the first field value
- * @param node
- */
- function findPrimitiveNodeValue(node: PersistedWorkflowNode): any {
- const fieldKeys = Object.keys(node.value.fields).filter(it => it !== "undefined");
- const value = node.value.fields[fieldKeys[0]];
+ * primitive value is the first field value
+ * @param node
+ */
+ function findPrimitiveNodeValue(node: PersistedWorkflowNode, targetNode: PersistedWorkflowNode, targetHandle: string): any {
+ const fieldKey = targetHandle.toLocaleLowerCase();
+ const targetWidget = widgets[targetNode.value.widget];
+
+ if (!targetWidget || !targetWidget.input) {
+ throw new Error("Invalid target widget or widget input");
+ }
+
+ const targetField = (targetWidget.input.required || {})[fieldKey] || (targetWidget.input.optional || {})[fieldKey];
+ const defaultValue = Array.isArray(targetField) && (targetField[1] as any)?.default !== undefined ? (targetField[1] as any)?.default : undefined;
+ const value = node.value.fields[fieldKey] ?? defaultValue;
+
return value;
}
}
diff --git a/packages/common/store/app-state/app-state-create-workflow-execution-hooks.ts b/packages/common/store/app-state/app-state-create-workflow-execution-hooks.ts
index ff56b2de..962726cf 100644
--- a/packages/common/store/app-state/app-state-create-workflow-execution-hooks.ts
+++ b/packages/common/store/app-state/app-state-create-workflow-execution-hooks.ts
@@ -39,6 +39,20 @@ export default function createHook(set: AppStateSetter, get: AppStateGetter): Pa
let newSeed = Widget.getControlledSeedValue(control_after_generated, oldSeed);
onNodeFieldChange(node.id, seedFieldName, newSeed);
}
+
+ // 如果是 PrimitNode 且有 control_after_generated 字段,则更新 control_after_generated 字段
+ if (Widget.isPrimitive(widget?.name)) {
+ const control_after_generated = sdnode.fields.control_after_generated;
+ if (control_after_generated) {
+ const edge = state.edges.find(edge => edge.source === node.id);
+ if (edge && edge.targetHandle) {
+ const seedFieldName = edge.targetHandle.toLowerCase()
+ const oldSeed = sdnode.fields[seedFieldName];
+ let newSeed = Widget.getControlledSeedValue(control_after_generated, oldSeed);
+ onNodeFieldChange(node.id, seedFieldName, newSeed);
+ }
+ }
+ }
});
console.log("res", res);
diff --git a/packages/common/store/dashboard-state.ts b/packages/common/store/dashboard-state.ts
index 4c35d39d..dc0415ec 100644
--- a/packages/common/store/dashboard-state.ts
+++ b/packages/common/store/dashboard-state.ts
@@ -1,6 +1,7 @@
import { getComflowyAppConfig, getComfyUIEnvRequirements } from "../comfyui-bridge/bridge";
import { create } from "zustand";
import { AppConfigs, BootstrapError, ComfyUIRunFPMode, ComfyUIRunVAEMode, EnvRequirements } from "../types";
+import { isCustomModelField } from "../types/model.types";
type DashboardState = {
loading: boolean;
@@ -42,7 +43,6 @@ type DashboardAction = {
const useDashboardState = create((set, get) => ({
docs: [],
- appConfigs: {},
bootstraped: false,
loading: true,
bootstrapMessages: [],
@@ -101,40 +101,43 @@ function checkEnvRequirements(env: EnvRequirements): BootstrapTask[] {
const tasks: BootstrapTask[] = [];
tasks.push({
type: BootStrapTaskType.setupConfig,
- title: "Setup",
+ title: "Setup",
description: "Considering network issue, you can setup a http_proxy url",
finished: env.isSetupedConfig,
});
- tasks.push({
- type: BootStrapTaskType.installConda,
- title: "Install Conda",
- description: "We use conda to manage python enviroment for comfyUI",
- finished: env.isCondaInstalled
- });
- tasks.push({
- type: BootStrapTaskType.installPython,
- title: "Create Conda venv",
- description: "ComfyUI need a safe and proper python enviroment to run. We will use conda to create virtual env called comflowy to manage your python & pip packages",
- finished: env.isPythonInstalled
- });
- tasks.push({
- type: BootStrapTaskType.installGit,
- title: "Install Git",
- description: "ComfyUI need a safe and proper git enviroment to run. We will use git to manage your git repository",
- finished: env.isGitInstalled
- });
- tasks.push({
- type: BootStrapTaskType.installTorch,
- title: "Install Torch",
- description: "ComfyUI need a safe and proper torch enviroment to run. We will use torch to manage your torch model",
- finished: env.isTorchInstalled
- });
- tasks.push({
- type: BootStrapTaskType.installComfyUI,
- title: "Install ComfyUI",
- description: "ComfyUI need a safe and proper comfyUI enviroment to run. We will use comfyUI to manage your comfyUI model",
- finished: env.isComfyUIInstalled
- });
+ if (!env.isCustomComfyEnv) {
+ tasks.push({
+ type: BootStrapTaskType.installConda,
+ title: "Install Conda",
+ description: "We use conda to manage python enviroment for comfyUI",
+ finished: env.isCondaInstalled
+ });
+ tasks.push({
+ type: BootStrapTaskType.installPython,
+ title: "Create Conda venv",
+ description: "ComfyUI need a safe and proper python enviroment to run. We will use conda to create virtual env called comflowy to manage your python & pip packages",
+ finished: env.isPythonInstalled
+ });
+ tasks.push({
+ type: BootStrapTaskType.installGit,
+ title: "Install Git",
+ description: "ComfyUI need a safe and proper git enviroment to run. We will use git to manage your git repository",
+ finished: env.isGitInstalled
+ });
+ tasks.push({
+ type: BootStrapTaskType.installTorch,
+ title: "Install Torch",
+ description: "ComfyUI need a safe and proper torch enviroment to run. We will use torch to manage your torch model",
+ finished: env.isTorchInstalled
+ });
+ tasks.push({
+ type: BootStrapTaskType.installComfyUI,
+ title: "Install ComfyUI",
+ description: "ComfyUI need a safe and proper comfyUI enviroment to run. We will use comfyUI to manage your comfyUI model",
+ finished: env.isComfyUIInstalled
+ });
+ }
+
tasks.push({
type: BootStrapTaskType.installBasicModel,
title: "Install Basic Model",
diff --git a/packages/common/types/comflowy-bootstrap.types.ts b/packages/common/types/comflowy-bootstrap.types.ts
index 49b2e29c..b6572bfc 100644
--- a/packages/common/types/comflowy-bootstrap.types.ts
+++ b/packages/common/types/comflowy-bootstrap.types.ts
@@ -9,17 +9,20 @@ export type EnvRequirements = {
isBasicModelInstalled: boolean;
isBasicExtensionInstalled: boolean;
isComfyUIStarted: boolean;
+ isCustomComfyEnv: boolean;
isSetupedConfig: boolean;
comfyUIVersion: string;
}
export type AppConfigs = {
- appSetupConfig?: {
+ appSetupConfig: {
+ pythonPath: string;
+ isCustomComfyEnv: boolean,
comfyUIDir: string,
stableDiffusionDir?: string
civitaiToken?: string
},
- runConfig?: ComfyUIRunConfig
+ runConfig: ComfyUIRunConfig
};
export type BootstrapError = {
diff --git a/packages/common/workflow-editor/node-rendering.tsx b/packages/common/workflow-editor/node-rendering.tsx
index d16f95bd..7757d8e3 100644
--- a/packages/common/workflow-editor/node-rendering.tsx
+++ b/packages/common/workflow-editor/node-rendering.tsx
@@ -188,10 +188,20 @@ export function getPrimitiveNodeRenderingInfo(node: SDNode, widget: Widget): Wor
property: input[0],
input: input[1]
})
+
+ // if it has a seed, add seed control_after_generated param
+ if (input[0] === "seed" || input[0] === "noise_seed") {
+ refParams.push({
+ property: "control_after_generated",
+ input: [ContrlAfterGeneratedValuesOptions, {
+ default: ""
+ }]
+ });
+ }
}
}
- const input = refParams[0]?.input;
+ const input = refParams[0]?.input as Input;
let typeName = "*";
let name = "Connect to widget input";
if (input) {
@@ -199,7 +209,6 @@ export function getPrimitiveNodeRenderingInfo(node: SDNode, widget: Widget): Wor
name = typeName;
}
-
return {
title: `${title}${enabled ? " (Disabled)" : ""}`,
widget,
@@ -210,14 +219,13 @@ export function getPrimitiveNodeRenderingInfo(node: SDNode, widget: Widget): Wor
links: [],
slot_index: 0
}],
- params: refParams,
+ params: refParams as any,
nodeBgColor,
nodeColor,
enabled
}
}
-
export function useSubflowNodeRenderingInfo(node: NodeProps<{
value: SDNode;
}>): SubflowNodeRenderingInfo | undefined {