Mobile overhaul

This commit is contained in:
space-nuko
2023-05-31 15:49:45 -05:00
parent 5474687041
commit 6f3275da00
7 changed files with 245 additions and 99 deletions

View File

@@ -9,14 +9,16 @@
import "framework7/css/bundle" import "framework7/css/bundle"
import "./scss/global.scss"; import "./scss/global.scss";
import MainToolbar from './mobile/MainToolbar.svelte'
import GenToolbar from './mobile/GenToolbar.svelte' import GenToolbar from './mobile/GenToolbar.svelte'
import HomePage from './mobile/routes/home.svelte'; import WorkflowsPage from './mobile/routes/workflows.svelte';
import AboutPage from './mobile/routes/about.svelte'; import AboutPage from './mobile/routes/about.svelte';
import LoginPage from './mobile/routes/login.svelte'; import LoginPage from './mobile/routes/login.svelte';
import GraphPage from './mobile/routes/graph.svelte'; import GraphPage from './mobile/routes/graph.svelte';
import WorkflowPage from './mobile/routes/workflow.svelte'; import WorkflowPage from './mobile/routes/workflow.svelte';
import type { Framework7Parameters, Modal } from "framework7/types"; import type { Framework7Parameters, Modal } from "framework7/types";
import interfaceState from "$lib/stores/interfaceState";
export let app: ComfyApp; export let app: ComfyApp;
@@ -82,7 +84,14 @@
routes: [ routes: [
{ {
path: '/', path: '/',
component: HomePage, component: WorkflowsPage,
options: {
props: { app }
}
},
{
path: '/workflows',
component: WorkflowsPage,
options: { options: {
props: { app } props: { app }
} }
@@ -138,14 +147,17 @@
</div> </div>
{:then} {:then}
<View <View
url="/" url="/workflows/"
main={true} main={true}
class="safe-areas" class="safe-areas"
masterDetailBreakpoint={768}, masterDetailBreakpoint={768},
browserHistory=true, browserHistory=true,
browserHistoryRoot="/mobile/" browserHistoryRoot="/mobile/"
> >
<GenToolbar {app} /> <MainToolbar {app} />
{#if $interfaceState.selectedWorkflowID && $interfaceState.showingWorkflow}
<GenToolbar {app} />
{/if}
</View> </View>
{:catch error} {:catch error}
<div class="comfy-loading-error"> <div class="comfy-loading-error">

View File

@@ -1,6 +1,7 @@
import { debounce } from '$lib/utils'; import { debounce } from '$lib/utils';
import { get, writable } from 'svelte/store'; import { get, writable } from 'svelte/store';
import type { Readable, Writable } from 'svelte/store'; import type { Readable, Writable } from 'svelte/store';
import type { WorkflowInstID } from './workflowState';
export type InterfaceState = { export type InterfaceState = {
// Show a large indicator of the currently editing number value for mobile // Show a large indicator of the currently editing number value for mobile
@@ -10,8 +11,11 @@ export type InterfaceState = {
showIndicator: boolean, showIndicator: boolean,
indicatorValue: any, indicatorValue: any,
graphTransitioning: boolean graphTransitioning: boolean,
isJumpingToNode: boolean isJumpingToNode: boolean,
selectedWorkflowID: WorkflowInstID | null
showingWorkflow: boolean
} }
type InterfaceStateOps = { type InterfaceStateOps = {
@@ -28,6 +32,9 @@ const store: Writable<InterfaceState> = writable(
graphTransitioning: false, graphTransitioning: false,
isJumpingToNode: false, isJumpingToNode: false,
selectedWorkflowID: null,
showingWorkflow: false
}) })
const debounceDrag = debounce(() => { store.update(s => { s.showIndicator = false; return s }) }, 1000) const debounceDrag = debounce(() => { store.update(s => { s.showIndicator = false; return s }) }, 1000)

View File

@@ -3,13 +3,14 @@
import queueState from "$lib/stores/queueState"; import queueState from "$lib/stores/queueState";
import workflowState, { ComfyBoxWorkflow } from "$lib/stores/workflowState"; import workflowState, { ComfyBoxWorkflow } from "$lib/stores/workflowState";
import { getNodeInfo } from "$lib/utils" import { getNodeInfo } from "$lib/utils"
import { Image, LayoutTextSidebarReverse } from "svelte-bootstrap-icons";
import { Link, Toolbar } from "framework7-svelte" import { Link, Toolbar } from "framework7-svelte"
import ProgressBar from "$lib/components/ProgressBar.svelte"; import ProgressBar from "$lib/components/ProgressBar.svelte";
import Progressbar from "$lib/components/f7/progressbar.svelte"; import Progressbar from "$lib/components/f7/progressbar.svelte";
import Indicator from "./Indicator.svelte"; import Indicator from "./Indicator.svelte";
import interfaceState from "$lib/stores/interfaceState"; import interfaceState from "$lib/stores/interfaceState";
import type { WritableLayoutStateStore } from "$lib/stores/layoutStates"; import type { WritableLayoutStateStore } from "$lib/stores/layoutStates";
export let subworkflowID: number = -1; export let subworkflowID: number = -1;
export let app: ComfyApp = undefined; export let app: ComfyApp = undefined;
@@ -74,31 +75,21 @@
progressText = "??.?%" progressText = "??.?%"
} }
let centerHref = "/workflows/"
$: if ($interfaceState.selectedWorkflowID) {
centerHref = `/workflows/${$interfaceState.selectedWorkflowID}/`
}
else {
centerHref = "/workflows/";
}
</script> </script>
<div class="bottom"> <Toolbar bottom color="red" style="bottom: calc(var(--f7-toolbar-height))">
<div class="bars">
{#if queued}
<div class="node-name">
<span>Node: {getNodeInfo($queueState.runningNodeID)} ({progressText})</span>
</div>
{/if}
</div>
<div class="wrapper">
{#if queued}
{#if progress}
<Progressbar color="blue" progress={progressPercent} />
{:else if running}
<Progressbar color="blue" infinite />
{/if}
{/if}
</div>
</div>
<Toolbar bottom>
{#if workflow != null && workflow.attrs.queuePromptButtonName != ""} {#if workflow != null && workflow.attrs.queuePromptButtonName != ""}
<Link on:click={queuePrompt}> <Link on:click={queuePrompt}>
{workflow.attrs.queuePromptButtonName} {workflow.attrs.queuePromptButtonName}
</Link> </Link>
{/if} {/if}
<Link on:click={refreshCombos}>🔄</Link> <Link on:click={refreshCombos}>🔄</Link>
<Link on:click={doSave}>Save</Link> <Link on:click={doSave}>Save</Link>
@@ -106,56 +97,9 @@
<Link on:click={doLoad}>Load</Link> <Link on:click={doLoad}>Load</Link>
<input bind:this={fileInput} id="comfy-file-input" type="file" accept=".json" on:change={loadWorkflow} /> <input bind:this={fileInput} id="comfy-file-input" type="file" accept=".json" on:change={loadWorkflow} />
</Toolbar> </Toolbar>
{#if $interfaceState.showIndicator}
<Indicator value={$interfaceState.indicatorValue} />
{/if}
<style lang="scss"> <style lang="scss">
#comfy-file-input { #comfy-file-input {
display: none; display: none;
} }
.bottom {
position: absolute;
text-align: center;
width: 100%;
font-size: 13pt;
bottom: calc(var(--f7-toolbar-height));
z-index: var(--layer-top);
}
.bars {
display: flex;
flex-direction: row;
.bars {
display: flex;
flex-direction: row;
}
.node-name {
flex-grow: 1;
background-color: var(--secondary-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> </style>

View File

@@ -0,0 +1,167 @@
<script lang="ts">
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
import queueState from "$lib/stores/queueState";
import workflowState, { ComfyBoxWorkflow } from "$lib/stores/workflowState";
import { getNodeInfo } from "$lib/utils"
import { Image, LayoutTextSidebarReverse } from "svelte-bootstrap-icons";
import { Link, Toolbar } from "framework7-svelte"
import ProgressBar from "$lib/components/ProgressBar.svelte";
import Progressbar from "$lib/components/f7/progressbar.svelte";
import Indicator from "./Indicator.svelte";
import interfaceState from "$lib/stores/interfaceState";
import type { WritableLayoutStateStore } from "$lib/stores/layoutStates";
export let subworkflowID: number = -1;
export let app: ComfyApp = undefined;
let layoutState: WritableLayoutStateStore = null;
let fileInput: HTMLInputElement = undefined;
let workflow: ComfyBoxWorkflow | null = null;
$: workflow = $workflowState.activeWorkflow;
function queuePrompt() {
navigator.vibrate(20)
app.runDefaultQueueAction()
}
async function refreshCombos() {
navigator.vibrate(20)
await app.refreshComboInNodes()
}
function doSave(): void {
if (!fileInput)
return;
navigator.vibrate(20)
app.querySave()
}
function doLoad(): void {
if (!fileInput)
return;
navigator.vibrate(20)
fileInput.value = null;
fileInput.click();
}
function loadWorkflow(): void {
app.handleFile(fileInput.files[0]);
}
function doSaveLocal(): void {
navigator.vibrate(20)
app.saveStateToLocalStorage();
}
let queued: false;
$: queued = Boolean($queueState.runningNodeID || $queueState.progress)
let running = false;
$: running = typeof $queueState.queueRemaining === "number" && $queueState.queueRemaining > 0;
let progress;
$: progress = $queueState.progress
let progressPercent = 0
let progressText = ""
$: if (progress) {
progressPercent = (progress.value / progress.max) * 100;
progressText = progressPercent.toFixed(1) + "%";
} else {
progressPercent = 0
progressText = "??.?%"
}
let centerHref = "/workflows/"
$: if ($interfaceState.selectedWorkflowID) {
centerHref = `/workflows/${$interfaceState.selectedWorkflowID}/`
}
else {
centerHref = "/workflows/";
}
let toolbarCount = 0;
$: toolbarCount = $interfaceState.showingWorkflow ? 2 : 1;
</script>
<div class="bottom" style:--toolbarCount={toolbarCount}>
<div class="bars">
{#if queued}
<div class="node-name">
<span>Node: {getNodeInfo($queueState.runningNodeID)} ({progressText})</span>
</div>
{/if}
</div>
<div class="wrapper">
{#if queued}
{#if progress}
<Progressbar color="blue" progress={progressPercent} />
{:else if running}
<Progressbar color="blue" infinite />
{/if}
{/if}
</div>
</div>
<Toolbar bottom>
<Link transition="f7-dive" href="/about/">Tab 1</Link>
<Link transition="f7-dive" href={centerHref} tabLinkActive><Image /></Link>
<Link transition="f7-dive" href="/login/"><LayoutTextSidebarReverse /></Link>
</Toolbar>
{#if $interfaceState.showIndicator}
<Indicator value={$interfaceState.indicatorValue} />
{/if}
<style lang="scss">
#comfy-file-input {
display: none;
}
.bottom {
--toolbarCount: 1;
position: absolute;
text-align: center;
width: 100%;
font-size: 13pt;
bottom: calc(var(--f7-toolbar-height) * var(--toolbarCount));
z-index: var(--layer-top);
}
.bars {
display: flex;
flex-direction: row;
.bars {
display: flex;
flex-direction: row;
}
.node-name {
flex-grow: 1;
background-color: var(--secondary-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

@@ -1,16 +0,0 @@
<script lang="ts">
import ComfyApp from "$lib/components/ComfyApp";
import { Page, Navbar, Link, BlockTitle, Block, List, ListItem } from "framework7-svelte"
export let app: ComfyApp;
</script>
<Page name="subworkflows">
<Navbar title="Workflows" backLink="Back" />
<List strong inset dividersIos class="components-list searchbar-found">
<ListItem link="/subworkflows/{1}/" title="Workflow 1">
<i class="icon icon-f7" slot="media" />
</ListItem>
</List>
</Page>

View File

@@ -1,10 +1,13 @@
<script lang="ts"> <script lang="ts">
import { Page, Navbar, Link, BlockTitle, Block, List, ListItem, Toolbar } from "framework7-svelte" import { Page, Navbar, Tabs, Tab, NavLeft, NavTitle, NavRight, Link } from "framework7-svelte"
import WidgetContainer from "$lib/components/WidgetContainer.svelte"; import WidgetContainer from "$lib/components/WidgetContainer.svelte";
import type ComfyApp from "$lib/components/ComfyApp"; import type ComfyApp from "$lib/components/ComfyApp";
import { writable, type Writable } from "svelte/store"; import { writable, type Writable } from "svelte/store";
import type { WritableLayoutStateStore } from "$lib/stores/layoutStates"; import type { IDragItem, WritableLayoutStateStore } from "$lib/stores/layoutStates";
import workflowState, { type ComfyBoxWorkflow, type WorkflowInstID } from "$lib/stores/workflowState"; import workflowState, { type ComfyBoxWorkflow, type WorkflowInstID } from "$lib/stores/workflowState";
import interfaceState from "$lib/stores/interfaceState";
import { onMount } from "svelte";
import GenToolbar from '../GenToolbar.svelte'
export let workflowID: WorkflowInstID; export let workflowID: WorkflowInstID;
export let app: ComfyApp export let app: ComfyApp
@@ -13,7 +16,21 @@
let root: IDragItem | null; let root: IDragItem | null;
let title = "" let title = ""
$: workflow = workflowState.getWorkflow(workflowID); function onPageBeforeIn() {
$interfaceState.selectedWorkflowID = workflowID;
$interfaceState.showingWorkflow = true;
}
function onPageBeforeOut() {
$interfaceState.showingWorkflow = false;
}
$: {
workflow = workflowState.getWorkflow(workflowID);
if (workflow) {
workflowState.setActiveWorkflow(app.lCanvas, workflow.id);
}
}
$: layoutState = workflow?.layout; $: layoutState = workflow?.layout;
$: title = workflow?.attrs?.title || `Workflow: ${workflowID}`; $: title = workflow?.attrs?.title || `Workflow: ${workflowID}`;
@@ -24,8 +41,17 @@
} }
</script> </script>
<Page name="workflow"> <Page name="workflow" style="--f7-page-toolbar-bottom-offset: calc(var(--f7-toolbar-height) * 2)"
<Navbar title="{title}" backLink="Back" /> on:pageBeforeIn={onPageBeforeIn}
on:pageBeforeOut={onPageBeforeOut}
>
<Navbar>
<NavLeft backLink="Back" backLinkUrl="/workflows/" backLinkForce={true}></NavLeft>
<NavTitle>{title}</NavTitle>
<NavRight>
<Link icon="icon-bars" panelOpen="right"></Link>
</NavRight>
</Navbar>
{#if workflow} {#if workflow}
{#if root} {#if root}

View File

@@ -1,6 +1,8 @@
<script lang="ts"> <script lang="ts">
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp"; import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
import workflowState, { ComfyBoxWorkflow, type WorkflowInstID } from "$lib/stores/workflowState"; import workflowState, { ComfyBoxWorkflow, type WorkflowInstID } from "$lib/stores/workflowState";
import { onMount } from "svelte";
import interfaceState from "$lib/stores/interfaceState";
import { f7 } from 'framework7-svelte'; import { f7 } from 'framework7-svelte';
import { XCircle } from 'svelte-bootstrap-icons'; import { XCircle } from 'svelte-bootstrap-icons';
@@ -19,15 +21,19 @@
e.stopImmediatePropagation(); e.stopImmediatePropagation();
f7.dialog.confirm("Are you sure you want to delete this workflow?", workflow.attrs.title || `Workflow: ${workflow.id}`, f7.dialog.confirm("Are you sure you want to delete this workflow?", workflow.attrs.title || `Workflow: ${workflow.id}`,
() => { app.closeWorkflow(workflow.id); })} () => { app.closeWorkflow(workflow.id); })}
function onPageBeforeIn() {
$interfaceState.selectedWorkflowID = null;
}
</script> </script>
<Page name="home"> <Page name="home" on:pageBeforeIn={onPageBeforeIn}>
<Navbar title="Home Page" /> <Navbar title="Home Page" />
{#if $workflowState.openedWorkflows} {#if $workflowState.openedWorkflows}
<List strong inset dividersIos class="components-list searchbar-found"> <List strong inset dividersIos class="components-list searchbar-found">
{#each $workflowState.openedWorkflows as workflow} {#each $workflowState.openedWorkflows as workflow}
<ListItem link="/workflows/{workflow.id}/" title={workflow.attrs.title || `Workflow: ${workflow.id}`}> <ListItem link="/workflows/{workflow.id}/" transition="f7-cover" title={workflow.attrs.title || `Workflow: ${workflow.id}`}>
<svelte:fragment slot="media"> <svelte:fragment slot="media">
<div on:pointerdown={(e) => onClickDelete(workflow, e)}> <div on:pointerdown={(e) => onClickDelete(workflow, e)}>
<XCircle width="1.5em" height="1.5em" /> <XCircle width="1.5em" height="1.5em" />