[mobile] Show graph
This commit is contained in:
@@ -1,14 +1,28 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { App, View, Page, Navbar, Toolbar, Link } from 'framework7-svelte';
|
import { onMount } from "svelte";
|
||||||
import "framework7/css/bundle"
|
import { get } from "svelte/store";
|
||||||
|
import { Button } from "@gradio/button";
|
||||||
|
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
||||||
|
import { Checkbox } from "@gradio/form"
|
||||||
|
import widgetState from "$lib/stores/widgetState";
|
||||||
|
import nodeState from "$lib/stores/nodeState";
|
||||||
|
import uiState from "$lib/stores/uiState";
|
||||||
|
import { ImageViewer } from "$lib/ImageViewer";
|
||||||
|
import { download } from "$lib/utils"
|
||||||
|
|
||||||
|
import { LGraph, LGraphNode } from "@litegraph-ts/core";
|
||||||
|
import type { ComfyAPIStatus } from "$lib/api";
|
||||||
|
import queueState from "$lib/stores/queueState";
|
||||||
|
import { App, View, Toolbar, Page, Navbar, Link, BlockTitle, Block, List, ListItem } from "framework7-svelte"
|
||||||
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { f7, f7ready } from 'framework7-svelte';
|
import { f7, f7ready } from 'framework7-svelte';
|
||||||
|
|
||||||
// Import pages components
|
import "framework7/css/bundle"
|
||||||
import HomePage from './home.svelte';
|
|
||||||
import AboutPage from './about.svelte';
|
import HomePage from './mobile/routes/home.svelte';
|
||||||
import LoginPage from './login.svelte';
|
import AboutPage from './mobile/routes/about.svelte';
|
||||||
|
import LoginPage from './mobile/routes/login.svelte';
|
||||||
|
import GraphPage from './mobile/routes/graph.svelte';
|
||||||
|
|
||||||
function onBackKeyDown(e) {
|
function onBackKeyDown(e) {
|
||||||
if(f7.view.current.router.currentRoute.path == '/'){
|
if(f7.view.current.router.currentRoute.path == '/'){
|
||||||
@@ -21,17 +35,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(async () => {
|
||||||
window.addEventListener("backbutton", onBackKeyDown, false);
|
window.addEventListener("backbutton", onBackKeyDown, false);
|
||||||
window.addEventListener("popstate", onBackKeyDown, false);
|
window.addEventListener("popstate", onBackKeyDown, false);
|
||||||
})
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Now we need to map components to routes.
|
Now we need to map components to routes.
|
||||||
We need to pass them along with the F7 app parameters to <App> component
|
We need to pass them along with the F7 app parameters to <App> component
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const f7params = {
|
let f7params = {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
@@ -45,6 +59,10 @@
|
|||||||
path: '/login/',
|
path: '/login/',
|
||||||
component: LoginPage,
|
component: LoginPage,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/graph/',
|
||||||
|
component: GraphPage,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
popup: {
|
popup: {
|
||||||
closeOnEscape: true,
|
closeOnEscape: true,
|
||||||
@@ -61,10 +79,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Main Framework7 App component where we pass Framework7 params -->
|
<App theme="auto" name="ComfyBox" {...f7params}>
|
||||||
<App theme="ios" name="ComfyBox" {...f7params}>
|
|
||||||
|
|
||||||
<!-- Your main view, should have "main" prop -->
|
|
||||||
<View
|
<View
|
||||||
url="/"
|
url="/"
|
||||||
main={true}
|
main={true}
|
||||||
@@ -73,4 +88,8 @@
|
|||||||
browserHistory=true,
|
browserHistory=true,
|
||||||
browserHistoryRoot="/mobile/"
|
browserHistoryRoot="/mobile/"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="canvas-wrapper pane-wrapper" style="display: none">
|
||||||
|
<canvas id="graph-canvas" />
|
||||||
|
</div>
|
||||||
</App>
|
</App>
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
<!-- home.svelte -->
|
|
||||||
<Page name="home">
|
|
||||||
<Navbar title="Home Page" />
|
|
||||||
...
|
|
||||||
<Link href="/about/">About Page</Link>
|
|
||||||
<Link href="/login/">Login Page</Link>
|
|
||||||
</Page>
|
|
||||||
<script>
|
|
||||||
import { Page, Navbar, Link } from 'framework7-svelte';
|
|
||||||
</script>
|
|
||||||
@@ -9,7 +9,6 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
|||||||
constructor(
|
constructor(
|
||||||
app: ComfyApp,
|
app: ComfyApp,
|
||||||
canvas: HTMLCanvasElement | string,
|
canvas: HTMLCanvasElement | string,
|
||||||
graph?: LGraph,
|
|
||||||
options: {
|
options: {
|
||||||
skip_render?: boolean;
|
skip_render?: boolean;
|
||||||
skip_events?: boolean;
|
skip_events?: boolean;
|
||||||
@@ -17,7 +16,7 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
|
|||||||
viewport?: Vector4;
|
viewport?: Vector4;
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
super(canvas, graph, options);
|
super(canvas, app.lGraph, options);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,11 +88,11 @@ export default class GraphSync {
|
|||||||
this.stores[nodeId].push({ store: wuis.value, unsubscribe: unsub });
|
this.stores[nodeId].push({ store: wuis.value, unsubscribe: unsub });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("NEWSTORES", this.stores[nodeId])
|
console.debug("NEWSTORES", this.stores[nodeId])
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeStores(nodeId: string) {
|
private removeStores(nodeId: string) {
|
||||||
console.log("DELSTORES", this.stores[nodeId])
|
console.debug("DELSTORES", this.stores[nodeId])
|
||||||
for (const ss of this.stores[nodeId]) {
|
for (const ss of this.stores[nodeId]) {
|
||||||
ss.unsubscribe();
|
ss.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,8 +49,6 @@ export default class ComfyAPI extends EventTarget {
|
|||||||
private getBackendUrl(): string {
|
private getBackendUrl(): string {
|
||||||
const hostname = this.hostname || location.hostname;
|
const hostname = this.hostname || location.hostname;
|
||||||
const port = this.port || location.port;
|
const port = this.port || location.port;
|
||||||
console.log(hostname)
|
|
||||||
console.log(port)
|
|
||||||
return `${window.location.protocol}//${hostname}:${port}`
|
return `${window.location.protocol}//${hostname}:${port}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,11 +120,11 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
await app.setup();
|
await app.setup();
|
||||||
refreshView();
|
|
||||||
|
|
||||||
(window as any).app = app;
|
(window as any).app = app;
|
||||||
(window as any).appPane = uiPane;
|
(window as any).appPane = uiPane;
|
||||||
|
|
||||||
|
refreshView();
|
||||||
|
|
||||||
imageViewer = new ImageViewer(app.rootEl);
|
imageViewer = new ImageViewer(app.rootEl);
|
||||||
|
|
||||||
let wrappers = containerElem.querySelectorAll<HTMLDivElement>(".pane-wrapper")
|
let wrappers = containerElem.querySelectorAll<HTMLDivElement>(".pane-wrapper")
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export default class ComfyApp {
|
|||||||
this.rootEl = document.getElementById("main") as HTMLDivElement;
|
this.rootEl = document.getElementById("main") as HTMLDivElement;
|
||||||
this.canvasEl = document.getElementById("graph-canvas") as HTMLCanvasElement;
|
this.canvasEl = document.getElementById("graph-canvas") as HTMLCanvasElement;
|
||||||
this.lGraph = new LGraph();
|
this.lGraph = new LGraph();
|
||||||
this.lCanvas = new ComfyGraphCanvas(this, this.canvasEl, this.lGraph);
|
this.lCanvas = new ComfyGraphCanvas(this, this.canvasEl);
|
||||||
this.canvasCtx = this.canvasEl.getContext("2d");
|
this.canvasCtx = this.canvasEl.getContext("2d");
|
||||||
this.graphSync = new GraphSync(this);
|
this.graphSync = new GraphSync(this);
|
||||||
|
|
||||||
@@ -151,37 +151,37 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private graphOnConfigure() {
|
private graphOnConfigure() {
|
||||||
console.log("Configured");
|
console.debug("Configured");
|
||||||
this.eventBus.emit("configured", this.lGraph);
|
this.eventBus.emit("configured", this.lGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
private graphOnBeforeChange(graph: LGraph, info: any) {
|
private graphOnBeforeChange(graph: LGraph, info: any) {
|
||||||
console.log("BeforeChange", info);
|
console.debug("BeforeChange", info);
|
||||||
this.eventBus.emit("beforeChange", graph, info);
|
this.eventBus.emit("beforeChange", graph, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
private graphOnAfterChange(graph: LGraph, info: any) {
|
private graphOnAfterChange(graph: LGraph, info: any) {
|
||||||
console.log("AfterChange", info);
|
console.debug("AfterChange", info);
|
||||||
this.eventBus.emit("afterChange", graph, info);
|
this.eventBus.emit("afterChange", graph, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
private graphOnNodeAdded(node: LGraphNode) {
|
private graphOnNodeAdded(node: LGraphNode) {
|
||||||
console.log("Added", node);
|
console.debug("Added", node);
|
||||||
this.eventBus.emit("nodeAdded", node);
|
this.eventBus.emit("nodeAdded", node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private graphOnNodeRemoved(node: LGraphNode) {
|
private graphOnNodeRemoved(node: LGraphNode) {
|
||||||
console.log("Removed", node);
|
console.debug("Removed", node);
|
||||||
this.eventBus.emit("nodeRemoved", node);
|
this.eventBus.emit("nodeRemoved", node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private graphOnNodeConnectionChange(kind: LConnectionKind, node: LGraphNode, slot: INodeSlot, targetNode: LGraphNode, targetSlot: INodeSlot) {
|
private graphOnNodeConnectionChange(kind: LConnectionKind, node: LGraphNode, slot: INodeSlot, targetNode: LGraphNode, targetSlot: INodeSlot) {
|
||||||
console.log("ConnectionChange", node);
|
console.debug("ConnectionChange", node);
|
||||||
this.eventBus.emit("nodeConnectionChanged", kind, node, slot, targetNode, targetSlot);
|
this.eventBus.emit("nodeConnectionChanged", kind, node, slot, targetNode, targetSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private canvasOnClear() {
|
private canvasOnClear() {
|
||||||
console.log("CanvasClear");
|
console.debug("CanvasClear");
|
||||||
this.eventBus.emit("cleared");
|
this.eventBus.emit("cleared");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,10 +306,12 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private showDropZone() {
|
private showDropZone() {
|
||||||
|
if (this.dropZone)
|
||||||
this.dropZone.style.display = "block";
|
this.dropZone.style.display = "block";
|
||||||
}
|
}
|
||||||
|
|
||||||
private hideDropZone() {
|
private hideDropZone() {
|
||||||
|
if (this.dropZone)
|
||||||
this.dropZone.style.display = "none";
|
this.dropZone.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,11 +336,16 @@ export default class ComfyApp {
|
|||||||
private addDropHandler() {
|
private addDropHandler() {
|
||||||
this.dropZone = document.getElementById("dropzone");
|
this.dropZone = document.getElementById("dropzone");
|
||||||
|
|
||||||
|
if (this.dropZone) {
|
||||||
window.addEventListener('dragenter', this.allowDrag.bind(this));
|
window.addEventListener('dragenter', this.allowDrag.bind(this));
|
||||||
this.dropZone.addEventListener('dragover', this.allowDrag.bind(this));
|
this.dropZone.addEventListener('dragover', this.allowDrag.bind(this));
|
||||||
this.dropZone.addEventListener('dragleave', this.hideDropZone.bind(this));
|
this.dropZone.addEventListener('dragleave', this.hideDropZone.bind(this));
|
||||||
this.dropZone.addEventListener('drop', this.handleDrop.bind(this));
|
this.dropZone.addEventListener('drop', this.handleDrop.bind(this));
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
console.warn("No dropzone detected (probably on mobile).")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a handler on paste that extracts and loads workflows from pasted JSON data
|
* Adds a handler on paste that extracts and loads workflows from pasted JSON data
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export type QueueItem = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type QueueStateOps = {
|
type QueueStateOps = {
|
||||||
statusUpdated: (status: ComfyAPIQueueStatus) => void,
|
statusUpdated: (status: ComfyAPIQueueStatus | null) => void,
|
||||||
executingUpdated: (runningNodeId: string | null) => void,
|
executingUpdated: (runningNodeId: string | null) => void,
|
||||||
progressUpdated: (progress: Progress | null) => void
|
progressUpdated: (progress: Progress | null) => void
|
||||||
}
|
}
|
||||||
@@ -21,8 +21,9 @@ type WritableQueueStateStore = Writable<QueueState> & QueueStateOps;
|
|||||||
|
|
||||||
const store: Writable<QueueState> = writable({ queueRemaining: null, runningNodeId: null, progress: null })
|
const store: Writable<QueueState> = writable({ queueRemaining: null, runningNodeId: null, progress: null })
|
||||||
|
|
||||||
function statusUpdated(status: ComfyAPIQueueStatus) {
|
function statusUpdated(status: ComfyAPIQueueStatus | null) {
|
||||||
store.update((s) => {
|
store.update((s) => {
|
||||||
|
if (status !== null)
|
||||||
s.queueRemaining = status.exec_info.queue_remaining;
|
s.queueRemaining = status.exec_info.queue_remaining;
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ function nodeAdded(node: LGraphNode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("NODEADDED", state)
|
console.debug("NODEADDED", state)
|
||||||
|
|
||||||
store.set(state);
|
store.set(state);
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/mobile/routes/graph.svelte
Normal file
43
src/mobile/routes/graph.svelte
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Page, Navbar, Block, Tab, BlockHeader, Segmented, Button, Icon } from 'framework7-svelte';
|
||||||
|
import { LGraphCanvas } from "@litegraph-ts/core";
|
||||||
|
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
||||||
|
import ComfyGraphCanvas from "$lib/ComfyGraphCanvas";
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
export let app: ComfyApp = undefined;
|
||||||
|
let lCanvas: LGraphCanvas | null = null;
|
||||||
|
let canvasEl: HTMLCanvasElement | null = null;
|
||||||
|
|
||||||
|
function resizeCanvas() {
|
||||||
|
canvasEl.width = canvasEl.parentElement.offsetWidth;
|
||||||
|
canvasEl.height = canvasEl.parentElement.offsetHeight;
|
||||||
|
canvasEl.style.width = ""
|
||||||
|
canvasEl.style.height = ""
|
||||||
|
lCanvas.draw(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (app && canvasEl) {
|
||||||
|
if (!lCanvas) {
|
||||||
|
lCanvas = new ComfyGraphCanvas(app, canvasEl);
|
||||||
|
lCanvas.allow_interaction = false;
|
||||||
|
}
|
||||||
|
resizeCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Page>
|
||||||
|
<Navbar title="Node Graph" backLink="Back" />
|
||||||
|
<div class="canvas-wrapper pane-wrapper">
|
||||||
|
<canvas bind:this={canvasEl} id="extra-canvas" />
|
||||||
|
</div>
|
||||||
|
</Page>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.canvas-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
90
src/mobile/routes/home.svelte
Normal file
90
src/mobile/routes/home.svelte
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { get } from "svelte/store";
|
||||||
|
import { Pane, Splitpanes } from 'svelte-splitpanes';
|
||||||
|
import { Button } from "@gradio/button";
|
||||||
|
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
||||||
|
import { Checkbox } from "@gradio/form"
|
||||||
|
import widgetState from "$lib/stores/widgetState";
|
||||||
|
import nodeState from "$lib/stores/nodeState";
|
||||||
|
import uiState from "$lib/stores/uiState";
|
||||||
|
import { ImageViewer } from "$lib/ImageViewer";
|
||||||
|
import { download } from "$lib/utils"
|
||||||
|
|
||||||
|
import { LGraph, LGraphNode } from "@litegraph-ts/core";
|
||||||
|
import type { ComfyAPIStatus } from "$lib/api";
|
||||||
|
import queueState from "$lib/stores/queueState";
|
||||||
|
import { Page, Navbar, Link, BlockTitle, Block, List, ListItem } from "framework7-svelte"
|
||||||
|
|
||||||
|
let app: ComfyApp = undefined;
|
||||||
|
|
||||||
|
let serializedPaneOrder = {};
|
||||||
|
|
||||||
|
function serializeAppState(): SerializedAppState {
|
||||||
|
const graph = app.lGraph;
|
||||||
|
|
||||||
|
const serializedGraph = graph.serialize()
|
||||||
|
|
||||||
|
return {
|
||||||
|
createdBy: "ComfyBox",
|
||||||
|
version: 1,
|
||||||
|
workflow: serializedGraph,
|
||||||
|
panes: serializedPaneOrder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doAutosave(graph: LGraph): void {
|
||||||
|
const savedWorkflow = serializeAppState();
|
||||||
|
localStorage.setItem("workflow", JSON.stringify(savedWorkflow))
|
||||||
|
}
|
||||||
|
|
||||||
|
function doRestore(workflow: SerializedAppState) {
|
||||||
|
serializedPaneOrder = workflow.panes;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
app = new ComfyApp();
|
||||||
|
|
||||||
|
// TODO dedup
|
||||||
|
app.eventBus.on("nodeAdded", nodeState.nodeAdded);
|
||||||
|
app.eventBus.on("nodeRemoved", nodeState.nodeRemoved);
|
||||||
|
app.eventBus.on("configured", nodeState.configureFinished);
|
||||||
|
app.eventBus.on("cleared", nodeState.clear);
|
||||||
|
|
||||||
|
app.eventBus.on("nodeAdded", widgetState.nodeAdded);
|
||||||
|
app.eventBus.on("nodeRemoved", widgetState.nodeRemoved);
|
||||||
|
app.eventBus.on("configured", widgetState.configureFinished);
|
||||||
|
app.eventBus.on("cleared", widgetState.clear);
|
||||||
|
app.eventBus.on("autosave", doAutosave);
|
||||||
|
app.eventBus.on("restored", doRestore);
|
||||||
|
|
||||||
|
app.api.addEventListener("status", (ev: CustomEvent) => {
|
||||||
|
queueState.statusUpdated(ev.detail as ComfyAPIStatus);
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.setup();
|
||||||
|
(window as any).app = app;
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Page name="home">
|
||||||
|
<Navbar title="Home Page" />
|
||||||
|
|
||||||
|
<BlockTitle>Yo</BlockTitle>
|
||||||
|
<Block>
|
||||||
|
<div>{app} Nodes</div>
|
||||||
|
</Block>
|
||||||
|
|
||||||
|
<List strong inset dividersIos class="components-list searchbar-found">
|
||||||
|
<ListItem link="/accordion/" title="Accordion">
|
||||||
|
<i class="icon icon-f7" slot="media" />
|
||||||
|
</ListItem>
|
||||||
|
<ListItem link="/action-sheet/" title="Action Sheet">
|
||||||
|
<i class="icon icon-f7" slot="media" />
|
||||||
|
</ListItem>
|
||||||
|
<ListItem link="/graph/" title="Show Node Graph" routeProps={{ app: app }}>
|
||||||
|
<i class="icon icon-f7" slot="media" />
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
</Page>
|
||||||
@@ -28,9 +28,9 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
|
|
||||||
hmr: {
|
// hmr: {
|
||||||
clientPort: 443,
|
// clientPort: 443,
|
||||||
},
|
// },
|
||||||
// fs: {
|
// fs: {
|
||||||
// allow: [
|
// allow: [
|
||||||
// "src",
|
// "src",
|
||||||
|
|||||||
Reference in New Issue
Block a user