Updates for mobile

This commit is contained in:
space-nuko
2023-05-07 11:54:54 -05:00
parent 5018200266
commit 71c9617133
15 changed files with 162 additions and 54 deletions

View File

@@ -22,6 +22,7 @@
import ComfyUnlockUIButton from "./ComfyUnlockUIButton.svelte"; import ComfyUnlockUIButton from "./ComfyUnlockUIButton.svelte";
import ComfyGraphView from "./ComfyGraphView.svelte"; import ComfyGraphView from "./ComfyGraphView.svelte";
import { download } from "$lib/utils"; import { download } from "$lib/utils";
import notify from "$lib/notify";
export let app: ComfyApp = undefined; export let app: ComfyApp = undefined;
let imageViewer: ImageViewer; let imageViewer: ImageViewer;
@@ -141,7 +142,7 @@
return; return;
app.saveStateToLocalStorage(); app.saveStateToLocalStorage();
toast.push("Saved to local storage.") notify("Saved to local storage.")
// //
// const date = new Date(); // const date = new Date();
// const formattedDate = date.toISOString().replace(/:/g, '-').replace(/\.\d{3}/g, '').replace('T', '_').replace("Z", ""); // const formattedDate = date.toISOString().replace(/:/g, '-').replace(/\.\d{3}/g, '').replace('T', '_').replace("Z", "");
@@ -165,7 +166,7 @@
$: if ($uiState.uiUnlocked && !hasShownUIHelpToast) { $: if ($uiState.uiUnlocked && !hasShownUIHelpToast) {
hasShownUIHelpToast = true; hasShownUIHelpToast = true;
toast.push("Right-click to open context menu.") notify("Right-click to open context menu.")
} }
if (debugLayout) { if (debugLayout) {

View File

@@ -28,6 +28,7 @@ import { ComfyBackendNode } from "$lib/nodes/ComfyBackendNode";
import { get } from "svelte/store"; import { get } from "svelte/store";
import uiState from "$lib/stores/uiState"; import uiState from "$lib/stores/uiState";
import { promptToGraphVis, workflowToGraphVis } from "$lib/utils"; import { promptToGraphVis, workflowToGraphVis } from "$lib/utils";
import notify from "$lib/notify";
export const COMFYBOX_SERIAL_VERSION = 1; export const COMFYBOX_SERIAL_VERSION = 1;
@@ -601,11 +602,7 @@ export default class ComfyApp {
} catch (error) { } catch (error) {
// this.ui.dialog.show(error.response || error.toString()); // this.ui.dialog.show(error.response || error.toString());
const mes = error.response || error.toString() const mes = error.response || error.toString()
toast.push(`Error queuing prompt:\n${mes}`, { notify(`Error queuing prompt:\n${mes}`, null, "error")
theme: {
'--toastBackground': 'var(--color-red-500)',
}
})
console.error(promptToGraphVis(p)) console.error(promptToGraphVis(p))
console.error("Error queuing prompt", mes, num, p) console.error("Error queuing prompt", mes, num, p)
break; break;
@@ -643,7 +640,7 @@ export default class ComfyApp {
} }
else { else {
console.error("No metadata found in image file.", pngInfo) console.error("No metadata found in image file.", pngInfo)
toast.push("No metadata found in image file.") notify("No metadata found in image file.")
} }
} }
} else if (file.type === "application/json" || file.name.endsWith(".json")) { } else if (file.type === "application/json" || file.name.endsWith(".json")) {

View File

@@ -1,15 +1,7 @@
<script lang="ts"> <script lang="ts">
import queueState from "$lib/stores/queueState"; import queueState from "$lib/stores/queueState";
import ProgressBar from "./ProgressBar.svelte"; import ProgressBar from "./ProgressBar.svelte";
import { getNodeInfo } from "$lib/utils"
function getNodeInfo(nodeId: number): string {
let app = (window as any).app;
if (!app)
return String(nodeId);
const title = app.lGraph.getNodeById(nodeId)?.title || String(nodeId);
return title + " (" + nodeId + ")"
}
const entries = [ const entries = [
{ {

View File

@@ -23,15 +23,17 @@
<style> <style>
.progress { .progress {
width: 100%;
height: 100%;
text-align: center; text-align: center;
background-color: lightgrey; background-color: lightgrey;
padding: 3px; padding: 0px;
position: relative; position: relative;
} }
.bar { .bar {
height: 100%;
background-color: #B3D8A9; background-color: #B3D8A9;
height: 20px;
} }
.label { .label {

View File

@@ -6,6 +6,7 @@ import { toast } from '@zerodevx/svelte-toast'
import type { GalleryOutput } from "./ComfyWidgetNodes"; import type { GalleryOutput } from "./ComfyWidgetNodes";
import { get } from "svelte/store"; import { get } from "svelte/store";
import queueState from "$lib/stores/queueState"; import queueState from "$lib/stores/queueState";
import notify from "$lib/notify";
export interface ComfyQueueEventsProperties extends Record<any, any> { export interface ComfyQueueEventsProperties extends Record<any, any> {
} }
@@ -199,7 +200,7 @@ export class ComfyNotifyAction extends ComfyGraphNode {
override onAction(action: any, param: any) { override onAction(action: any, param: any) {
const message = this.getInputData(0); const message = this.getInputData(0);
if (message) { if (message) {
toast.push(message); notify(message);
} }
}; };
} }

View File

@@ -113,7 +113,7 @@ export class ComfyBackendNode extends ComfyGraphNode {
} }
} }
else { else {
console.error("[ComfyBackendNode] Missing input config in onConfigure()!", input, configs) console.debug("[ComfyBackendNode] Missing input config in onConfigure()", input, configs)
input.config = {} input.config = {}
} }
} }

View File

@@ -158,7 +158,7 @@ export default class ComfyImageCacheNode extends ComfyGraphNode {
else { else {
this.properties.filenames[newIndex] = { filename: null, status: "uploading" } this.properties.filenames[newIndex] = { filename: null, status: "uploading" }
this.onPropertyChanged("filenames", this.properties.filenames) this.onPropertyChanged("filenames", this.properties.filenames)
const url = "http://localhost:8188" // TODO make configurable const url = `http://${location.hostname}:8188` // TODO make configurable
const params = new URLSearchParams(data) const params = new URLSearchParams(data)
const promise = fetch(url + "/view?" + params) const promise = fetch(url + "/view?" + params)

View File

@@ -516,11 +516,11 @@ export class ComfyGalleryNode extends ComfyWidgetNode<GradioFileData[]> {
private convertItems(output: GalleryOutput): GradioFileData[] { private convertItems(output: GalleryOutput): GradioFileData[] {
return output.images.map(r => { return output.images.map(r => {
// TODO configure backend URL // TODO configure backend URL
const url = "http://localhost:8188/view?" const url = `http://${location.hostname}:8188` // TODO make configurable
const params = new URLSearchParams(r) const params = new URLSearchParams(r)
return { return {
name: null, name: null,
data: url + params data: url + "/view?" + params
} }
}); });
} }

37
src/lib/notify.ts Normal file
View File

@@ -0,0 +1,37 @@
import { toast } from "@zerodevx/svelte-toast";
import type { SvelteToastOptions } from "@zerodevx/svelte-toast/stores";
import { f7 } from "framework7-svelte"
let notification;
function notifyf7(text: string, title?: string) {
if (!notification) {
notification = f7.notification.create({
title: title,
titleRightText: 'now',
// subtitle: 'Notification with close on click',
text: text,
closeOnClick: true,
closeTimeout: 3000,
});
}
// Open it
notification.open();
}
function notifyToast(text: string, type?: string) {
const options: SvelteToastOptions = {}
if (type === "error") {
options.theme = {
'--toastBackground': 'var(--color-red-500)',
}
}
toast.push(text, options);
}
export default function notify(text: string, title?: string, type?: string) {
notifyf7(text, title);
notifyToast(text, title);
}

View File

@@ -99,3 +99,12 @@ export function promptToGraphVis(prompt: SerializedPrompt): string {
out += "}" out += "}"
return out return out
} }
export function getNodeInfo(nodeId: number): string {
let app = (window as any).app;
if (!app)
return String(nodeId);
const title = app.lGraph.getNodeById(nodeId)?.title || String(nodeId);
return title + " (" + nodeId + ")"
}

View File

@@ -11,6 +11,7 @@
let option: any let option: any
export let debug: boolean = false; export let debug: boolean = false;
let input: HTMLInputElement | null = null
$: widget && setNodeValue(widget); $: widget && setNodeValue(widget);
@@ -53,6 +54,11 @@
return links[0].data return links[0].data
} }
function onSelect() {
if (input)
input.blur();
}
let lastPropsChanged: number = 0; let lastPropsChanged: number = 0;
let werePropsChanged: boolean = false; let werePropsChanged: boolean = false;
@@ -76,8 +82,10 @@
disabled={widget.attrs.disabled || node.properties.values.length === 0} disabled={widget.attrs.disabled || node.properties.values.length === 0}
clearable={false} clearable={false}
showChevron={true} showChevron={true}
inputAttributes={{ autocomplete: 'off' }}
bind:input
on:change on:change
on:select on:select={onSelect}
on:filter on:filter
on:blur on:blur
/> />

View File

@@ -35,6 +35,7 @@
function onRelease(e: Event) { function onRelease(e: Event) {
if (nodeValue && option) { if (nodeValue && option) {
$nodeValue = option $nodeValue = option
navigator.vibrate(100)
} }
} }

View File

@@ -1,38 +1,101 @@
<script lang="ts"> <script lang="ts">
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp"; import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
import notify from "$lib/notify";
import uiState from "$lib/stores/uiState"; import uiState from "$lib/stores/uiState";
import queueState from "$lib/stores/queueState";
import { getNodeInfo } from "$lib/utils"
import { Link, Toolbar } from "framework7-svelte" import { Link, Toolbar } from "framework7-svelte"
import { f7 } from "framework7-svelte" import ProgressBar from "$lib/components/ProgressBar.svelte";
export let subworkflowID: number = -1; export let subworkflowID: number = -1;
let app: ComfyApp = undefined; let app: ComfyApp = undefined;
let fileInput: HTMLInputElement = undefined;
$: if (!app) $: if (!app)
app = $uiState.app app = $uiState.app
function queuePrompt() { function queuePrompt() {
app.queuePrompt(0, 1); app.queuePrompt(0, 1);
showNotification(); notify("Prompt was queued", "Queued");
} }
let notification; function doLoad(): void {
const showNotification = () => { if (!app?.lGraph || !fileInput)
if (!notification) { return;
notification = f7.notification.create({
title: 'Queued', fileInput.click();
titleRightText: 'now',
// subtitle: 'Notification with close on click',
text: 'Prompt was queued',
closeOnClick: true,
closeTimeout: 3000,
});
}
// Open it
notification.open();
} }
function loadWorkflow(): void {
app.handleFile(fileInput.files[0]);
fileInput.files = null;
}
</script> </script>
<div class="bottom">
{#if $queueState.runningNodeId || $queueState.progress}
<div class="node-name">
<span>Node: {getNodeInfo($queueState.runningNodeId)}</span>
</div>
<div class="progress-bar">
<ProgressBar value={$queueState.progress?.value} max={$queueState.progress?.max} />
</div>
{/if}
{#if typeof $queueState.queueRemaining === "number" && $queueState.queueRemaining > 0}
<div class="queue-remaining in-progress">
<div>
Queued prompts: {$queueState.queueRemaining}.
</div>
</div>
{/if}
</div>
<Toolbar bottom> <Toolbar bottom>
<Link on:click={queuePrompt}>Queue Prompt</Link> <Link on:click={queuePrompt}>Queue Prompt</Link>
<Link on:click={() => app.refreshComboInNodes()}>🔄</Link>
<Link on:click={doLoad}>Load</Link>
<input bind:this={fileInput} id="comfy-file-input" type="file" accept=".json" on:change={loadWorkflow} />
</Toolbar> </Toolbar>
<style lang="scss">
#comfy-file-input {
display: none;
}
.bottom {
display: flex;
flex-direction: row;
position: absolute;
text-align: center;
width: 100%;
height: 2rem;
bottom: calc(var(--f7-toolbar-height) + var(--f7-safe-area-bottom));
z-index: var(--layer-top);
background-color: grey;
.node-name {
flex-grow: 1;
background-color: var(--color-red-300);
padding: 0.2em;
display: flex;
justify-content: center;
align-items: center;
}
.progress-bar {
flex-grow: 10;
background-color: var(--color-red-300);
display: flex;
justify-content: center;
align-items: center;
}
.queue-remaining {
flex-grow: 1;
padding: 0.2em;
&.in-progress {
background-color: var(--secondary-300);
}
}
}
</style>

View File

@@ -7,7 +7,7 @@
import uiState from "$lib/stores/uiState" import uiState from "$lib/stores/uiState"
let app: ComfyApp | null = null; let app: ComfyApp | null = null;
let lCanvas: LGraphCanvas | null = null; let lCanvas: ComfyGraphCanvas | null = null;
let canvasEl: HTMLCanvasElement | null = null; let canvasEl: HTMLCanvasElement | null = null;
$: if (!app) $: if (!app)
@@ -21,13 +21,14 @@
lCanvas.draw(true, true); lCanvas.draw(true, true);
} }
$: if (app && canvasEl) { $: if (app != null && canvasEl != null) {
if (!lCanvas) { if (!lCanvas) {
lCanvas = new ComfyGraphCanvas(app, canvasEl); lCanvas = new ComfyGraphCanvas(app, canvasEl);
lCanvas.allow_interaction = false; lCanvas.allow_interaction = false;
LiteGraph.dialog_close_on_mouse_leave = false; LiteGraph.dialog_close_on_mouse_leave = false;
LiteGraph.search_hide_on_mouse_leave = false; LiteGraph.search_hide_on_mouse_leave = false;
LiteGraph.pointerevents_method = "pointer"; LiteGraph.pointerevents_method = "pointer";
app.lGraph.eventBus.on("afterExecute", () => lCanvas.draw(true))
} }
resizeCanvas(); resizeCanvas();
} }

View File

@@ -1,26 +1,22 @@
<script lang="ts"> <script lang="ts">
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp"; import layoutState, { type IDragItem } from "$lib/stores/layoutState";
import uiState from "$lib/stores/uiState";
import queueState from "$lib/stores/queueState";
import { Page, Navbar, Link, BlockTitle, Block, List, ListItem, Toolbar } from "framework7-svelte" import { Page, Navbar, Link, BlockTitle, Block, List, ListItem, Toolbar } from "framework7-svelte"
import WidgetContainer from "$lib/components/WidgetContainer.svelte";
export let subworkflowID: number = -1; export let subworkflowID: number = -1;
let app: ComfyApp = undefined;
$: if (!app)
app = $uiState.app
</script> </script>
<Page name="subworkflow"> <Page name="subworkflow">
<Navbar title="Workflow {subworkflowID}" backLink="Back" /> <Navbar title="Workflow {subworkflowID}" backLink="Back" />
<div>Workflow!</div> <WidgetContainer bind:dragItem={$layoutState.root} classes={["root-container", "mobile"]} />
</Page> </Page>
<style> <style lang="scss">
.is-executing { // TODO generalize this to all properties!
border: 5px dashed var(--color-green-600) !important; :global(.root-container.mobile > .block > .v-pane) {
flex-direction: column !important;
} }
</style> </style>