[mobile] Show graph

This commit is contained in:
space-nuko
2023-04-28 15:14:43 -07:00
parent f15990f9de
commit eec4fcaf2e
14 changed files with 236 additions and 89 deletions

View File

@@ -1,14 +1,28 @@
<script lang="ts">
import { App, View, Page, Navbar, Toolbar, Link } from 'framework7-svelte';
import "framework7/css/bundle"
import { onMount } from "svelte";
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 { onMount } from 'svelte';
import { f7, f7ready } from 'framework7-svelte';
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 pages components
import HomePage from './home.svelte';
import AboutPage from './about.svelte';
import LoginPage from './login.svelte';
import { f7, f7ready } from 'framework7-svelte';
import "framework7/css/bundle"
import HomePage from './mobile/routes/home.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) {
if(f7.view.current.router.currentRoute.path == '/'){
@@ -21,56 +35,61 @@
}
}
onMount(() => {
onMount(async () => {
window.addEventListener("backbutton", onBackKeyDown, false);
window.addEventListener("popstate", onBackKeyDown, false);
})
});
/*
/*
Now we need to map components to routes.
We need to pass them along with the F7 app parameters to <App> component
*/
const f7params = {
routes: [
{
path: '/',
component: HomePage,
},
{
path: '/about/',
component: AboutPage,
},
{
path: '/login/',
component: LoginPage,
},
],
popup: {
closeOnEscape: true,
},
sheet: {
closeOnEscape: true,
},
popover: {
closeOnEscape: true,
},
actions: {
closeOnEscape: true,
},
}
let f7params = {
routes: [
{
path: '/',
component: HomePage,
},
{
path: '/about/',
component: AboutPage,
},
{
path: '/login/',
component: LoginPage,
},
{
path: '/graph/',
component: GraphPage,
},
],
popup: {
closeOnEscape: true,
},
sheet: {
closeOnEscape: true,
},
popover: {
closeOnEscape: true,
},
actions: {
closeOnEscape: true,
},
}
</script>
<!-- Main Framework7 App component where we pass Framework7 params -->
<App theme="ios" name="ComfyBox" {...f7params}>
<App theme="auto" name="ComfyBox" {...f7params}>
<View
url="/"
main={true}
class="safe-areas"
masterDetailBreakpoint={768},
browserHistory=true,
browserHistoryRoot="/mobile/"
/>
<!-- Your main view, should have "main" prop -->
<View
url="/"
main={true}
class="safe-areas"
masterDetailBreakpoint={768},
browserHistory=true,
browserHistoryRoot="/mobile/"
/>
<div class="canvas-wrapper pane-wrapper" style="display: none">
<canvas id="graph-canvas" />
</div>
</App>

View File

@@ -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>

View File

@@ -9,7 +9,6 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
constructor(
app: ComfyApp,
canvas: HTMLCanvasElement | string,
graph?: LGraph,
options: {
skip_render?: boolean;
skip_events?: boolean;
@@ -17,7 +16,7 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
viewport?: Vector4;
} = {}
) {
super(canvas, graph, options);
super(canvas, app.lGraph, options);
this.app = app;
}

View File

@@ -88,11 +88,11 @@ export default class GraphSync {
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) {
console.log("DELSTORES", this.stores[nodeId])
console.debug("DELSTORES", this.stores[nodeId])
for (const ss of this.stores[nodeId]) {
ss.unsubscribe();
}

View File

@@ -49,8 +49,6 @@ export default class ComfyAPI extends EventTarget {
private getBackendUrl(): string {
const hostname = this.hostname || location.hostname;
const port = this.port || location.port;
console.log(hostname)
console.log(port)
return `${window.location.protocol}//${hostname}:${port}`
}

View File

@@ -120,11 +120,11 @@
});
await app.setup();
refreshView();
(window as any).app = app;
(window as any).appPane = uiPane;
refreshView();
imageViewer = new ImageViewer(app.rootEl);
let wrappers = containerElem.querySelectorAll<HTMLDivElement>(".pane-wrapper")

View File

@@ -88,7 +88,7 @@ export default class ComfyApp {
this.rootEl = document.getElementById("main") as HTMLDivElement;
this.canvasEl = document.getElementById("graph-canvas") as HTMLCanvasElement;
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.graphSync = new GraphSync(this);
@@ -151,37 +151,37 @@ export default class ComfyApp {
}
private graphOnConfigure() {
console.log("Configured");
console.debug("Configured");
this.eventBus.emit("configured", this.lGraph);
}
private graphOnBeforeChange(graph: LGraph, info: any) {
console.log("BeforeChange", info);
console.debug("BeforeChange", info);
this.eventBus.emit("beforeChange", graph, info);
}
private graphOnAfterChange(graph: LGraph, info: any) {
console.log("AfterChange", info);
console.debug("AfterChange", info);
this.eventBus.emit("afterChange", graph, info);
}
private graphOnNodeAdded(node: LGraphNode) {
console.log("Added", node);
console.debug("Added", node);
this.eventBus.emit("nodeAdded", node);
}
private graphOnNodeRemoved(node: LGraphNode) {
console.log("Removed", node);
console.debug("Removed", node);
this.eventBus.emit("nodeRemoved", node);
}
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);
}
private canvasOnClear() {
console.log("CanvasClear");
console.debug("CanvasClear");
this.eventBus.emit("cleared");
}
@@ -306,11 +306,13 @@ export default class ComfyApp {
}
private showDropZone() {
this.dropZone.style.display = "block";
if (this.dropZone)
this.dropZone.style.display = "block";
}
private hideDropZone() {
this.dropZone.style.display = "none";
if (this.dropZone)
this.dropZone.style.display = "none";
}
private allowDrag(event: DragEvent) {
@@ -334,10 +336,15 @@ export default class ComfyApp {
private addDropHandler() {
this.dropZone = document.getElementById("dropzone");
window.addEventListener('dragenter', this.allowDrag.bind(this));
this.dropZone.addEventListener('dragover', this.allowDrag.bind(this));
this.dropZone.addEventListener('dragleave', this.hideDropZone.bind(this));
this.dropZone.addEventListener('drop', this.handleDrop.bind(this));
if (this.dropZone) {
window.addEventListener('dragenter', this.allowDrag.bind(this));
this.dropZone.addEventListener('dragover', this.allowDrag.bind(this));
this.dropZone.addEventListener('dragleave', this.hideDropZone.bind(this));
this.dropZone.addEventListener('drop', this.handleDrop.bind(this));
}
else {
console.warn("No dropzone detected (probably on mobile).")
}
}
/**

View File

@@ -7,7 +7,7 @@ export type QueueItem = {
}
type QueueStateOps = {
statusUpdated: (status: ComfyAPIQueueStatus) => void,
statusUpdated: (status: ComfyAPIQueueStatus | null) => void,
executingUpdated: (runningNodeId: string | null) => void,
progressUpdated: (progress: Progress | null) => void
}
@@ -21,9 +21,10 @@ type WritableQueueStateStore = Writable<QueueState> & QueueStateOps;
const store: Writable<QueueState> = writable({ queueRemaining: null, runningNodeId: null, progress: null })
function statusUpdated(status: ComfyAPIQueueStatus) {
function statusUpdated(status: ComfyAPIQueueStatus | null) {
store.update((s) => {
s.queueRemaining = status.exec_info.queue_remaining;
if (status !== null)
s.queueRemaining = status.exec_info.queue_remaining;
return s
})
}

View File

@@ -62,7 +62,7 @@ function nodeAdded(node: LGraphNode) {
}
}
console.log("NODEADDED", state)
console.debug("NODEADDED", state)
store.set(state);
}

View 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>

View 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>