Updates for mobile
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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")) {
|
||||||
|
|||||||
@@ -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 = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
37
src/lib/notify.ts
Normal 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);
|
||||||
|
}
|
||||||
@@ -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 + ")"
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user