Application sidebar

This commit is contained in:
space-nuko
2023-05-20 14:58:35 -05:00
parent d1ca5877f1
commit c253e0956f
3 changed files with 412 additions and 93 deletions

View File

@@ -1,4 +1,5 @@
<script lang="ts">
import { ListIcon as List, ImageIcon as Image, SettingsIcon as Settings } from "svelte-feather-icons";
import { onMount } from "svelte";
import { get, writable, type Writable } from "svelte/store";
import { Pane, Splitpanes } from 'svelte-splitpanes';
@@ -17,6 +18,8 @@
import LightboxModal from "./LightboxModal.svelte";
import ComfyQueue from "./ComfyQueue.svelte";
import ComfyProperties from "./ComfyProperties.svelte";
import Sidebar from "./Sidebar.svelte";
import SidebarItem from "./SidebarItem.svelte";
import queueState from "$lib/stores/queueState";
import ComfyUnlockUIButton from "./ComfyUnlockUIButton.svelte";
import ComfyGraphView from "./ComfyGraphView.svelte";
@@ -26,6 +29,7 @@
import ComfyBoxStdPrompt from "$lib/ComfyBoxStdPrompt";
import A1111PromptDisplay from "./A1111PromptDisplay.svelte";
import type { A1111ParsedInfotext } from "$lib/parseA1111";
import { TabItem, Tabs } from "@gradio/tabs";
export let app: ComfyApp = undefined;
let alreadySetup: Writable<boolean> = writable(false);
@@ -200,6 +204,8 @@
let showModal: boolean = false;
$: showModal = $a1111Prompt != null
let selectedTab
</script>
<svelte:head>
@@ -222,86 +228,114 @@
<div id="main" class:dark={uiTheme === "gradio-dark"}>
<div id="container" bind:this={containerElem}>
<Splitpanes theme="comfy" on:resize={refreshView}>
<Pane bind:size={propsSidebarSize}>
<div class="sidebar-wrapper pane-wrapper">
<ComfyProperties bind:this={props} />
<Sidebar selected="generate">
<SidebarItem id="generate" name="Generate" icon={Image}>
<div id="comfy-content">
<Splitpanes theme="comfy" on:resize={refreshView}>
<Pane bind:size={propsSidebarSize}>
<div class="sidebar-wrapper pane-wrapper">
<ComfyProperties bind:this={props} />
</div>
</Pane>
<Pane>
<Splitpanes theme="comfy" on:resize={refreshView} horizontal="{true}">
<Pane>
<ComfyUIPane {app} />
</Pane>
<Pane bind:size={graphSize}>
<ComfyGraphView {app} transitioning={graphTransitioning} />
</Pane>
</Splitpanes>
</Pane>
<Pane bind:size={queueSidebarSize}>
<div class="sidebar-wrapper pane-wrapper">
<ComfyQueue {app} />
</div>
</Pane>
</Splitpanes>
<div id="workflow-tabs">
<div class="workflow-tab selected">
txt2img
<!-- <Image /> -->
</div>
<div class="workflow-tab">
img2img
<!-- <Image /> -->
</div>
<div class="workflow-tab">
asdflkj
<!-- <Image /> -->
</div>
<div class="workflow-tab">
asdkajw
<!-- <Image /> -->
</div>
</div>
<div id="bottombar">
<div class="bottombar-content">
<div class="left">
{#if $layoutState.attrs.queuePromptButtonName != ""}
<Button variant="primary" disabled={!$alreadySetup} on:click={queuePrompt}>
{$layoutState.attrs.queuePromptButtonName}
</Button>
{/if}
<Button variant="secondary" disabled={!$alreadySetup} on:click={toggleGraph}>
Toggle Graph
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={toggleProps}>
Toggle Props
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={toggleQueue}>
Toggle Queue
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doSave}>
Save
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doSaveLocal}>
Save Local
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doLoad}>
Load
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doClear}>
Clear
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doLoadDefault}>
Load Default
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doRefreshCombos}>
🔄
</Button>
<!-- <Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/>
<Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/> -->
<span style="display: inline-flex !important">
<Checkbox label="Auto-Add UI" bind:value={$uiState.autoAddUI}/>
</span>
<span class="label" for="ui-edit-mode">
<BlockTitle>UI Edit mode</BlockTitle>
<select id="ui-edit-mode" name="ui-edit-mode" bind:value={$uiState.uiEditMode}>
<option value="widgets">Widgets</option>
</select>
</span>
<span class="label" for="ui-theme">
<BlockTitle>Theme</BlockTitle>
<select id="ui-theme" name="ui-theme" bind:value={uiTheme}>
<option value="gradio-dark">Gradio Dark</option>
<option value="gradio-light">Gradio Light</option>
<option value="anapnoe">Anapnoe</option>
</select>
</span>
</div>
<div class="right">
<ComfyUnlockUIButton bind:toggled={$uiState.uiUnlocked} />
</div>
</div>
</div>
</div>
</Pane>
<Pane>
<Splitpanes theme="comfy" on:resize={refreshView} horizontal="{true}">
<Pane>
<ComfyUIPane {app} />
</Pane>
<Pane bind:size={graphSize}>
<ComfyGraphView {app} transitioning={graphTransitioning} />
</Pane>
</Splitpanes>
</Pane>
<Pane bind:size={queueSidebarSize}>
<div class="sidebar-wrapper pane-wrapper">
<ComfyQueue {app} />
</div>
</Pane>
</Splitpanes>
</div>
<div id="bottombar">
<div class="left">
{#if $layoutState.attrs.queuePromptButtonName != ""}
<Button variant="primary" disabled={!$alreadySetup} on:click={queuePrompt}>
{$layoutState.attrs.queuePromptButtonName}
</Button>
{/if}
<Button variant="secondary" disabled={!$alreadySetup} on:click={toggleGraph}>
Toggle Graph
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={toggleProps}>
Toggle Props
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={toggleQueue}>
Toggle Queue
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doSave}>
Save
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doSaveLocal}>
Save Local
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doLoad}>
Load
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doClear}>
Clear
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doLoadDefault}>
Load Default
</Button>
<Button variant="secondary" disabled={!$alreadySetup} on:click={doRefreshCombos}>
🔄
</Button>
<!-- <Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/>
<Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/> -->
<span style="display: inline-flex !important">
<Checkbox label="Auto-Add UI" bind:value={$uiState.autoAddUI}/>
</span>
<span class="label" for="ui-edit-mode">
<BlockTitle>UI Edit mode</BlockTitle>
<select id="ui-edit-mode" name="ui-edit-mode" bind:value={$uiState.uiEditMode}>
<option value="widgets">Widgets</option>
</select>
</span>
<span class="label" for="ui-theme">
<BlockTitle>Theme</BlockTitle>
<select id="ui-theme" name="ui-theme" bind:value={uiTheme}>
<option value="gradio-dark">Gradio Dark</option>
<option value="gradio-light">Gradio Light</option>
<option value="anapnoe">Anapnoe</option>
</select>
</span>
</div>
<div class="right">
<ComfyUnlockUIButton bind:toggled={$uiState.uiUnlocked} />
</div>
</SidebarItem>
<SidebarItem id="settings" name="Settings" icon={Settings}>
</SidebarItem>
</Sidebar>
</div>
<LightboxModal />
<input bind:this={fileInput} id="comfy-file-input" type="file" accept=".json" on:change={loadWorkflow} />
@@ -309,37 +343,123 @@
<SvelteToast options={toastOptions} />
<style lang="scss">
$bottom-bar-height: 70px;
$top-bar-height: 3.5rem;
$workflow-tabs-height: 2.5rem;
$bottom-bar-height: 5rem;
#container {
height: calc(100vh - $bottom-bar-height);
height: 100vh;
max-width: 100vw;
display: grid;
display: relative;
width: 100%;
}
#comfy-content {
grid-area: content;
height: 100vh;
height: calc(100vh - $bottom-bar-height - $workflow-tabs-height);
}
#bottombar {
padding-top: 0.5em;
#workflow-tabs {
display: flex;
width: 100%;
gap: var(--layout-gap);
padding-left: 1em;
padding-right: 1em;
margin-top: auto;
overflow-x: auto;
}
/*
#topbar {
background: var(--neutral-900);
height: 100%;
flex-direction: column;
float: left;
position: sticky;
top: 0px;
padding: 0;
width: 4rem;
.topbar-button {
background: var(--neutral-800);
color: var(--neutral-500);
padding: 0.5rem;
border-right: 3px solid transparent;
display: flex;
flex-direction: column;
justify-content: center;
&:last-child {
border-right: 1px solid var(--neutral-600);
}
&:hover {
background: var(--neutral-700);
color: var(--neutral-300);
}
&.selected {
background: var(--neutral-700);
color: var(--neutral-300);
border-right-color: var(--primary-500);
}
}
} */
#workflow-tabs {
background: var(--neutral-800);
.workflow-tab {
background: var(--neutral-800);
color: var(--neutral-500);
padding: 0.5rem 1rem;
border-top: 3px solid transparent;
border-left: 1px solid var(--neutral-600);
display: flex;
flex-direction: column;
justify-content: center;
&:last-child {
border-right: 1px solid var(--neutral-600);
}
&:hover {
background: var(--neutral-700);
color: var(--neutral-300);
}
&.selected {
background: var(--neutral-700);
color: var(--neutral-300);
border-top-color: var(--primary-500);
}
}
}
#bottombar {
background: var(--neutral-900);
border-left: 2px solid var(--neutral-700);
display: flex;
width: 100%;
gap: var(--layout-gap);
overflow-x: hidden;
flex-wrap: nowrap;
height: $bottom-bar-height;
> .left {
flex-shrink: 0;
}
> .bottombar-content {
display: flex;
flex-wrap: nowrap;
width: 100%;
overflow-x: auto;
margin: auto 0;
padding-left: 1rem;
> .right {
margin-left: auto
> .left {
flex-shrink: 0;
}
> .right {
margin-left: auto;
margin-right: 1rem;
padding-left: 1rem;
}
}
}

View File

@@ -0,0 +1,154 @@
<script context="module">
export const TABS = {};
</script>
<script lang="ts">
import { setContext, createEventDispatcher } from "svelte";
import { writable } from "svelte/store";
import type { SelectData } from "@gradio/utils";
import { SvelteComponentDev } from "svelte/internal";
interface Tab {
name: string;
id: object;
icon: typeof SvelteComponentDev | null;
}
export let visible: boolean = true;
export let elem_id: string = "id";
export let elem_classes: Array<string> = [];
export let selected: number | string | object;
let tabs: Array<Tab> = [];
const selected_tab = writable<false | object | number | string>(false);
const selected_tab_index = writable<number>(0);
const dispatch = createEventDispatcher<{
change: undefined;
select: SelectData;
}>();
setContext(TABS, {
register_tab: (tab: Tab) => {
tabs.push({ name: tab.name, id: tab.id, icon: tab.icon });
selected_tab.update((current) => current ?? tab.id);
tabs = tabs;
return tabs.length - 1;
},
unregister_tab: (tab: Tab) => {
const i = tabs.findIndex((t) => t.id === tab.id);
tabs.splice(i, 1);
selected_tab.update((current) =>
current === tab.id ? tabs[i]?.id || tabs[tabs.length - 1]?.id : current
);
},
selected_tab,
selected_tab_index
});
function change_tab(id: object | string | number) {
selected = id;
$selected_tab = id;
$selected_tab_index = tabs.findIndex((t) => t.id === id);
dispatch("change");
}
$: selected !== null && change_tab(selected);
</script>
<div class="sidebar {elem_classes.join(' ')}" class:hide={!visible} id={elem_id}>
<div class="sidebar-nav scroll-hide">
{#each tabs as t, i (t.id)}
{#if t.id === $selected_tab}
<button class="selected">
{#if t.icon !== null}
<svelte:component this={t.icon} size="100%" strokeWidth={1.5} />
{:else}
{t.name}
{/if}
</button>
{:else}
<button
on:click={() => {
change_tab(t.id);
dispatch("select", { value: t.name, index: i });
}}
>
{#if t.icon !== null}
<svelte:component this={t.icon} size="100%" strokeWidth={1.5} />
{:else}
{t.name}
{/if}
</button>
{/if}
{/each}
<div class="sidebar-rest"/>
</div>
<slot />
</div>
<style lang="scss">
.sidebar {
background: var(--neutral-900);
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
float: left;
top: 0px;
padding: 0;
}
.hide {
display: none;
}
.sidebar-nav, .sidebar-rest {
display: flex;
position: relative;
flex-wrap: wrap;
white-space: nowrap;
width: 4rem;
height: 100%;
background: var(--neutral-800);
> button {
width: 4rem;
height: 4rem;
padding: 0.5rem;
color: var(--neutral-600);
border-right: 3px solid transparent;
display: flex;
flex-direction: column;
justify-content: center;
&:hover {
background: var(--neutral-700);
color: var(--neutral-500);
}
&.selected {
background: var(--neutral-700);
color: var(--neutral-300);
border-right-color: var(--primary-500);
}
}
}
.sidebar-rest {
height: 100%;
}
.bar {
display: block;
position: absolute;
bottom: -2px;
left: 0;
z-index: 999;
background: var(--background-fill-primary);
width: 100%;
height: 2px;
content: "";
}
</style>

View File

@@ -0,0 +1,45 @@
<script lang="ts">
import { getContext, onMount, createEventDispatcher, tick } from "svelte";
import { TABS } from "./Sidebar.svelte";
import Column from "$lib/components/gradio/app/Column.svelte"
import type { SelectData } from "@gradio/utils";
import { SvelteComponentDev } from "svelte/internal";
export let elem_id: string = "";
export let elem_classes: Array<string> = [];
export let name: string;
export let id: string | number | object = {};
export let icon: typeof SvelteComponentDev | null = null;
const dispatch = createEventDispatcher<{ select: SelectData }>();
const { register_tab, unregister_tab, selected_tab, selected_tab_index } =
getContext(TABS) as any;
let tab_index = register_tab({ name, id, icon });
onMount(() => {
return () => unregister_tab({ name, id, icon });
});
$: $selected_tab_index === tab_index &&
tick().then(() => dispatch("select", { value: name, index: tab_index }));
</script>
<div
id={elem_id}
class="tabitem {elem_classes.join(' ')}"
style:display={$selected_tab === id ? "block" : "none"}
>
<Column>
<slot />
</Column>
</div>
<style>
div {
display: flex;
position: relative;
width: calc(100% - 4rem);
}
</style>