26 Commits
Author SHA1 Message Date
Weixuan Fu acaa2e4c35 add some logging for workflow restore 2024-08-04 17:50:33 +08:00
Weixuan Fu 99126eea03 add base ./ to deal with comfyui on subpaths 2024-08-04 17:44:03 +08:00
Weixuan Fu fb5e3ae89e wait for document.body to load and comfyapp to load before loading workspace manager to avoid plugin not showup 2024-08-04 17:28:31 +08:00
Weixuan Fu 2f4d3c26a9 fix encrypt utils not found, unable to build 2024-08-04 16:56:46 +08:00
Weixuan Fu 8456ea44bf fix new settings not working in windows 2024-07-02 21:40:16 +08:00
Weixuan Fu 7daf20238b build dist - fix user settings, replace userSettings with settings.json with readable format 2024-07-01 23:29:27 +08:00
Weixuan Fu 2697daa7b8 Merge branch 'dev' into beta 2024-07-01 23:28:36 +08:00
Weixuan Fu c2780caad4 build dist 2024-06-30 02:09:49 +08:00
Weixuan Fu fb9f355c6a Merge branch 'beta' 2024-06-30 02:09:29 +08:00
Weixuan Fu e751e0543a Merge branch 'dev' into betat p 2024-06-30 02:09:10 +08:00
Weixuan Fu f62e1dc1a5 Merge branch 'beta' 2024-06-29 15:08:04 +08:00
Weixuan Fu cb623b6e5f build dist 2024-06-29 15:07:45 +08:00
Weixuan Fu b1052d32b2 Merge branch 'dev' into beta 2024-06-29 15:07:03 +08:00
Weixuan Fu 3b23b8aa91 Merge pull request #429 from 11cafe/beta
Beta
2024-06-29 14:12:29 +08:00
Weixuan Fu c385393313 build dis - copy share link, disable video thumbnail, disable save disk db 2024-06-29 14:07:46 +08:00
Weixuan Fu db9245b2ef Merge branch 'dev' into beta 2024-06-29 14:06:27 +08:00
Weixuan Fu 87a1d96c2f Merge pull request #424 from 11cafe/beta
Beta
2024-06-26 16:53:17 +08:00
Weixuan Fu 8e1c6a47cb build for v history 2024-06-26 16:52:39 +08:00
Weixuan Fu f68a5e994a Merge branch 'dev' into beta 2024-06-26 16:52:14 +08:00
Weixuan Fu 3da2fcfa72 Merge branch 'beta' 2024-06-26 12:56:09 +08:00
Weixuan Fu f3c8e8c98d build dist 2024-06-26 12:55:12 +08:00
Weixuan Fu 3f02557170 Merge branch 'dev' into beta 2024-06-26 12:54:55 +08:00
Weixuan Fu d9c2015850 Merge pull request #421 from 11cafe/beta
Beta
2024-06-26 11:04:57 +08:00
Weixuan Fu cf5ce2c3df build dist 2024-06-26 11:02:15 +08:00
Weixuan Fu 9cf00a6de0 Update README.md 2024-06-25 22:40:20 +08:00
Weixuan Fu b36e2a472f Update README.md 2024-06-25 22:37:58 +08:00
11 changed files with 93 additions and 104 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ pnpm-debug.log*
lerna-debug.log*
ui/node_modules
ui/src/utils/encryptUtils.ts
# ui/src/utils/encryptUtils.ts
db
dist-ssr
*.local
+6 -7
View File
@@ -2,13 +2,15 @@
A ComfyUI workflow and model manager extension to **organize and manage all your workflows, models and generated images in one place**. Seamlessly switch between workflows, track version history and image generation history, 1 click install models from Civit ai, browse/update your installed models
If you have questions or suggestions, please [join our Discord](https://discord.gg/HHvfEurv2Z)! I'm building a new platform that lets you quickly make a runnable web app from your comfyui workflow. www.nodecafe.co if you are interested to make your comfyui workflow into an app or colaborate with me, please DM me!
If you have questions or suggestions, please [join our Discord](https://discord.gg/HHvfEurv2Z)!
[6-21] tags are deprecated, please use folders to organize your workflows.
✨We have a new platform to turn your comfyui workflow to runnable web app: www.nodecafe.co please check it out! 🥳
[6-25] ⚠️ Please update workspace manager to latest version if it's not showing up for you after updating ComfyUI
[6-21] ‼️please upgrade to V2 and enable twoway sync if you haven't already. I will stop supporting V1 soon and will forcefully turn on twoway sync later. (twoway sync means that your workflow are twoway synced with your disk files /my_workflows directly)
[6-21] ‼️‼️Auto-save mode will be removed comopoletely from next version. Because there are reports that autosave mode sometimes overwrite workflows mistakenly. Therefore, please **manual save** your workflows always.
[6-21] ‼️ Auto-save mode will be removed comopoletely from next version. Because there are reports that autosave mode sometimes overwrite workflows mistakenly. Therefore, please **manual save** your workflows always.
[New!] We provide 👭**team workspace** now! If you need to **share workflows, backup workflow versions for enterprise and teams**, please contact me about our team/enterprise product at weixuanfu01@gmail.com or join discord and DM me @briefpeach
@@ -51,7 +53,7 @@ Recommanded settings for V2 (Do not use these settings if you are on V1):
- 🔁Switch between different workflows easily. 1click open workflow in multiple browser tabs.
- 🤏**Drag and drop** to insert subworkflows into current flow.
- 🗂️Organize workflows with folders
- 🗂️Organize workflows with folders (tags are deprecated, please use folders to organize)
- 📂Save and sync all your workflows in a local folder (by default under `/ComfyUI/my_workflows` customize in Settings)
- **Save workflow by pressing the 💾 button, or ⌨Shift+S shortcut** (customize in Settings).
- Every time you save, it will create a new record in **Version History**. You can easily revert to any version.
@@ -78,9 +80,6 @@ Recommend using Chrome browser for the best experience. Safari doesn't work well
[中文版教程](https://www.uisdc.com/workspace-manager)       [Roadmap](https://github.com/orgs/11cafe/projects/1/views/1)
## Updates
**🦩v2.1 New gallery! Inspect image metadata**
<img width="400" alt="Screenshot 2024-03-23 at 12 58 38 AM" src="https://github.com/11cafe/comfyui-workspace-manager/assets/18367033/5bac9793-7491-4131-b850-037094d03823">
**🦄v2.0 Two-way sync with local folder, models manager support external models path like Automatic1111, cloud sync workflows**
+3 -2
View File
@@ -15,11 +15,12 @@ def get_settings_endpoint(request):
return web.json_response(text=json.dumps(settings), content_type='application/json')
def get_settings():
if os.path.exists(f'{db_dir_path}/settings.json'):
new_settings_path = os.path.join(db_dir_path, 'settings.json')
if os.path.exists(new_settings_path):
# to deprecate and remove legacy settings file
# if os.path.exists(f'{db_dir_path}/useSettings.json'):
# os.remove(f'{db_dir_path}/useSettings.json')
with open(f'{db_dir_path}/settings.json', 'r') as file:
with open(new_settings_path, 'r') as file:
return json.load(file)
data = read_table('userSettings')
if (data):
+1 -1
View File
@@ -131,7 +131,7 @@ export default function App() {
try {
await loadDBs();
} catch (error) {
console.error("error loading db", error);
console.error("🦄Workspace Manager - Error loading db", error);
}
setLoadingDB(false);
-52
View File
@@ -1,52 +0,0 @@
import React from "react";
import { Flex, Button, Text, Spinner } from "@chakra-ui/react";
type PaginatorProps = {
pageNumber: number;
loading: boolean;
noMoreData: boolean;
onPageChange: (newPage: number) => void;
};
const Paginator: React.FC<PaginatorProps> = ({
pageNumber,
loading,
noMoreData,
onPageChange,
}) => {
const handlePrevPage = () => {
if (pageNumber > 1) {
onPageChange(pageNumber - 1);
}
};
const handleNextPage = () => {
if (!noMoreData) {
onPageChange(pageNumber + 1);
}
};
return (
<Flex justifyContent="center" alignItems="center" mt={2}>
<Button
onClick={handlePrevPage}
isDisabled={pageNumber === 1 || loading}
mr={2}
>
Previous
</Button>
<Text>{pageNumber}</Text>
<Button
onClick={handleNextPage}
isDisabled={noMoreData || loading}
ml={2}
>
Next
</Button>
{noMoreData && !loading && <Text ml={4}>No more data!</Text>}
{loading && <Spinner ml={2} size={"xs"} color="gray" />}
</Flex>
);
};
export default Paginator;
+10 -19
View File
@@ -14,6 +14,7 @@ import {
TabPanels,
Tab,
TabPanel,
useToast,
HStack,
Tag,
} from "@chakra-ui/react";
@@ -29,10 +30,10 @@ import { WorkspaceContext } from "../WorkspaceContext";
import { Changelog, WorkflowVersion } from "../types/dbTypes";
import DeleteConfirm from "./DeleteConfirm";
import { app } from "../utils/comfyapp";
import Paginator from "./Paginator";
export function VersionHistoryDrawer({ onClose }: { onClose: () => void }) {
const { isDirty, curVersion, session } = useContext(WorkspaceContext);
const { isDirty, loadWorkflowID, curVersion, session } =
useContext(WorkspaceContext);
const [active, setActive] = useState(0); // 0: version、1: changelog
const [selectedVersion, setSelectedVersion] = useState<string | null>(null);
const [changelogs, setChangelogs] = useState<Changelog[]>([]);
@@ -42,7 +43,6 @@ export function VersionHistoryDrawer({ onClose }: { onClose: () => void }) {
const curFlow = workflowsTable?.curWorkflow;
const cloudHost = userSettingsTable?.settings?.cloudHost;
const [loading, setLoading] = useState(false);
const [pageNumber, setPageNumber] = useState(1);
const loadChangeLogs = async (flowId: string) => {
const workflow = await workflowsTable?.get(flowId);
const changelogs = await changelogsTable?.listByWorkflowID(flowId);
@@ -54,13 +54,15 @@ export function VersionHistoryDrawer({ onClose }: { onClose: () => void }) {
selectedChangelogID && setSelectedVersion(selectedChangelogID);
};
const loadVersions = async (pageNum: number) => {
const loadVersions = async () => {
let vers =
(await workflowVersionsTable?.listByWorkflowID(curFlow!.id)) ?? [];
if (session?.shareKey && curFlow?.cloudID) {
setLoading(true);
await fetch(
`${userSettingsTable?.settings?.cloudHost}/api/listWorkflowVersionsByWorkflowID?workflowID=${curFlow?.cloudID}&pageNumber=${pageNum}`,
userSettingsTable?.settings?.cloudHost! +
"/api/listWorkflowVersionsByWorkflowID?workflowID=" +
curFlow?.cloudID,
{
headers: {
authorization: `Bearer ${session?.shareKey}`,
@@ -88,22 +90,16 @@ export function VersionHistoryDrawer({ onClose }: { onClose: () => void }) {
setVersions(vers);
};
const onVersionsPageChange = (num: number) => {
setPageNumber(num);
loadVersions(num);
};
const onDelete = (id: string) => {
workflowVersionsTable?.delete(id).then(() => {
loadVersions(pageNumber);
loadVersions();
});
};
useEffect(() => {
switch (active) {
case 0:
setPageNumber(1);
loadVersions(1);
loadVersions();
break;
case 1:
loadChangeLogs(curFlow!.id);
@@ -162,6 +158,7 @@ export function VersionHistoryDrawer({ onClose }: { onClose: () => void }) {
</Text>
)}
<Stack divider={<StackDivider />} spacing={2}>
{loading && <Text>Loading...</Text>}
{versions?.map((version) => {
return (
<Flex
@@ -226,12 +223,6 @@ export function VersionHistoryDrawer({ onClose }: { onClose: () => void }) {
</Flex>
);
})}
<Paginator
onPageChange={onVersionsPageChange}
pageNumber={pageNumber}
loading={loading}
noMoreData={versions.length === 0}
/>
</Stack>
</TabPanel>
<TabPanel>
+38 -16
View File
@@ -4,9 +4,8 @@ import { ColorModeScript, type StyleFunctionProps } from "@chakra-ui/react";
import { ChakraProvider, extendTheme } from "@chakra-ui/react";
import { AlertDialogProvider } from "./components/AlertDialogProvider.tsx";
import CSSReset from "./MyCSSReset.tsx";
import { waitForApp } from "./utils/comfyapp.ts";
const topbar = document.createElement("div");
document.body.append(topbar);
const App = React.lazy(() =>
import("./App.tsx").then(({ default: App }) => ({
default: App,
@@ -51,20 +50,6 @@ const theme = extendTheme({
},
});
ReactDOM.createRoot(topbar).render(
<React.StrictMode>
<ChakraProvider resetCSS={false} disableGlobalStyle={true} theme={theme}>
<CSSReset scope=".workspace_manager" />
<ColorModeScript initialColorMode="dark" />
<AlertDialogProvider>
<Suspense>
<App />
</Suspense>
</AlertDialogProvider>
</ChakraProvider>
</React.StrictMode>,
);
// hacky fix for chakra add extra className (chakra-ui-light) into body
// and resulted in user unable to copy new nodes into clipboard, because of this check: e.target.className === "litegraph"
// (https://github.com/comfyanonymous/ComfyUI/blob/57926635e8d84ae9eea4a0416cc75e363f5ede45/web/scripts/app.js#L861)
@@ -96,3 +81,40 @@ const callback = function (
};
const observer = new MutationObserver(callback);
observer.observe(targetNode, observerConfig);
function waitForDocumentBody() {
return new Promise((resolve) => {
if (document.body) {
return resolve(document.body);
}
document.addEventListener("DOMContentLoaded", () => {
resolve(document.body);
});
});
}
// wait for document.body to load so that the top menu is loaded and my react component has a place to mount
waitForDocumentBody()
.then(() => waitForApp())
.then(() => {
const topbar = document.createElement("div");
document.body.append(topbar);
ReactDOM.createRoot(topbar).render(
<React.StrictMode>
<ChakraProvider
resetCSS={false}
disableGlobalStyle={true}
theme={theme}
>
<CSSReset scope=".workspace_manager" />
<ColorModeScript initialColorMode="dark" />
<AlertDialogProvider>
<Suspense>
<App />
</Suspense>
</AlertDialogProvider>
</ChakraProvider>
</React.StrictMode>,
);
});
@@ -67,8 +67,13 @@ export default function AppIsDirtyEventListener() {
return;
}
const id = app.graph.extra?.[COMFYSPACE_TRACKING_FIELD_NAME]?.id;
console.log("🦄 Restoring workflow... id=", id);
if (id) {
const flow = await workflowsTable?.get(id);
console.log(
"🦄 Restored workflow",
flow?.parentFolderID + "/" + flow?.name,
);
flow && setCurFlowIDAndName(flow);
if (
+10 -6
View File
@@ -4,10 +4,14 @@ const api_base = location.pathname.split("/").slice(0, -1).join("/");
export let api: any | null = null;
import(api_base + "/scripts/api.js").then((apiJs) => {
api = apiJs?.api;
});
export async function waitForApp() {
await Promise.all([
import(api_base + "/scripts/api.js").then((apiJs) => {
api = apiJs?.api;
}),
import(api_base + "/scripts/app.js").then((appJs) => {
app = appJs?.app;
});
import(api_base + "/scripts/app.js").then((appJs) => {
app = appJs?.app;
}),
]);
}
+18
View File
@@ -0,0 +1,18 @@
// utils/encryptUtils
const defaultSalt = import.meta.env.VITE_SHARE_KEY_ENCODE_KEY;
export function encodeKey(text: string, salt: string = defaultSalt): string {
const randomLength = Math.floor(Math.random() * 13) + 1; // Random length between 1 and 13
const randomPadding = Math.random()
.toString(36)
.substring(2, 2 + randomLength);
const input = randomPadding + salt + text;
return btoa(encodeURIComponent(input));
}
export function decodeKey(encoded: string, salt: string = defaultSalt): string {
const decoded = decodeURIComponent(atob(encoded));
const saltIndex = decoded.indexOf(salt);
if (saltIndex === -1) throw new Error("Invalid encoded string or salt");
return decoded.slice(saltIndex + salt.length);
}
+1
View File
@@ -22,6 +22,7 @@ const rewriteImportPlugin = ({ isDev }) => {
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
base: "./",
envDir: ".",
build: {
watch: {