Basic settings screen
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
import notify from "$lib/notify";
|
||||
import ComfyBoxWorkflowsView from "./ComfyBoxWorkflowsView.svelte";
|
||||
import GlobalModal from "./GlobalModal.svelte";
|
||||
import ComfySettingsView from "./ComfySettingsView.svelte";
|
||||
|
||||
export let app: ComfyApp = undefined;
|
||||
let hasShownUIHelpToast: boolean = false;
|
||||
@@ -63,6 +64,7 @@
|
||||
<ComfyBoxWorkflowsView {app} {uiTheme} />
|
||||
</SidebarItem>
|
||||
<SidebarItem id="settings" name="Settings" icon={Gear}>
|
||||
<ComfySettingsView {app} />
|
||||
</SidebarItem>
|
||||
</Sidebar>
|
||||
</div>
|
||||
|
||||
@@ -261,17 +261,18 @@ export default class ComfyApp {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO
|
||||
*/
|
||||
async loadConfig() {
|
||||
try {
|
||||
const config = await fetch(`/config.json`, { cache: "no-store" });
|
||||
const newConfig = await config.json() as ConfigState;
|
||||
configState.set({ ...get(configState), ...newConfig });
|
||||
console.log("Loading config.json...")
|
||||
const config = localStorage.getItem("config")
|
||||
if (config == null)
|
||||
configState.loadDefault();
|
||||
else
|
||||
configState.load(JSON.parse(config));
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to load config`, error)
|
||||
console.error(`Failed to load config, falling back to defaults`, error)
|
||||
configState.loadDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
&.selected {
|
||||
color: var(--body-text-color);
|
||||
background-color: var(--panel-background-fill);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,6 +559,7 @@
|
||||
|
||||
.target-name {
|
||||
background: var(--input-background-fill);
|
||||
border-color: var(--input-border-color);
|
||||
white-space: nowrap;
|
||||
|
||||
.title {
|
||||
|
||||
230
src/lib/components/ComfySettingsView.svelte
Normal file
230
src/lib/components/ComfySettingsView.svelte
Normal file
@@ -0,0 +1,230 @@
|
||||
<script lang="ts">
|
||||
import { CONFIG_CATEGORIES, CONFIG_DEFS_BY_CATEGORY, type ConfigDefAny } from "$lib/stores/configDefs";
|
||||
import { capitalize } from "$lib/utils";
|
||||
import { Checkbox } from "@gradio/form";
|
||||
import configState from "$lib/stores/configState";
|
||||
import type ComfyApp from "./ComfyApp";
|
||||
import NumberInput from "./NumberInput.svelte";
|
||||
import Textbox from "@gradio/form/src/Textbox.svelte";
|
||||
import { Button } from "@gradio/button";
|
||||
import { SvelteToast } from "@zerodevx/svelte-toast";
|
||||
import notify from "$lib/notify";
|
||||
|
||||
export let app: ComfyApp
|
||||
|
||||
let selectedCategory = CONFIG_CATEGORIES[0];
|
||||
let changes = {}
|
||||
|
||||
const toastOptions = {
|
||||
intro: { duration: 200 },
|
||||
theme: {
|
||||
'--toastBarHeight': 0
|
||||
}
|
||||
}
|
||||
|
||||
function selectCategory(category: string) {
|
||||
selectedCategory = category;
|
||||
}
|
||||
|
||||
function setOption(def: ConfigDefAny, value: any) {
|
||||
if (!configState.validateConfigOption(def, value)) {
|
||||
console.warn(`[configState] Invalid value for option ${def.name} (${v}), setting to default (${def.defaultValue})`);
|
||||
value = def.defaultValue
|
||||
}
|
||||
changes[def.name] = value;
|
||||
}
|
||||
|
||||
function doSave() {
|
||||
$configState = { ...$configState, ...changes };
|
||||
changes = {};
|
||||
const json = JSON.stringify($configState);
|
||||
localStorage.setItem("config", json);
|
||||
notify("Config applied!", { type: "success" })
|
||||
}
|
||||
|
||||
function doReset() {
|
||||
if (!confirm("Are you sure you want to reset the config to the defaults?"))
|
||||
return;
|
||||
|
||||
configState.loadDefault();
|
||||
notify("Config reset!")
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="comfy-settings">
|
||||
<div class="comfy-settings-categories">
|
||||
{#each CONFIG_CATEGORIES as category}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="comfy-settings-category" class:selected={selectedCategory === category} on:click={() => selectCategory(category)}>
|
||||
{capitalize(category)}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="comfy-settings-main">
|
||||
{#if selectedCategory}
|
||||
{@const categoryDefs = CONFIG_DEFS_BY_CATEGORY[selectedCategory]}
|
||||
{#key $configState}
|
||||
<div class="comfy-settings-entries">
|
||||
{#each categoryDefs as def}
|
||||
{@const value = $configState[def.name]}
|
||||
<div class="comfy-settings-entry">
|
||||
<div class="name">{def.name}</div>
|
||||
{#if def.type === "boolean"}
|
||||
<span class="ctrl checkbox">
|
||||
<Checkbox label={def.description} {value} on:change={(e) => setOption(def, e.detail)} />
|
||||
</span>
|
||||
{:else if def.type === "number"}
|
||||
<div class="description">{def.description}</div>
|
||||
<span class="ctrl number">
|
||||
<NumberInput label="" min={def.options.min} max={def.options.max} step={def.options.step} {value} on:release={(e) => setOption(def, e.detail)} />
|
||||
</span>
|
||||
{:else if def.type === "string"}
|
||||
<div class="description">{def.description}</div>
|
||||
<span class="ctrl textbox">
|
||||
<Textbox label="" lines={1} max_lines={1} {value} on:change={(e) => setOption(def, e.detail)} />
|
||||
</span>
|
||||
{:else if def.type === "string[]"}
|
||||
<div class="description">{def.description}</div>
|
||||
<span class="ctrl string-array">
|
||||
{value.join(",")}
|
||||
</span>
|
||||
{:else}
|
||||
(Unknown config type {def.type})
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/key}
|
||||
{:else}
|
||||
Please select a category.
|
||||
{/if}
|
||||
<div class="comfy-settings-bottom-bar">
|
||||
<div>
|
||||
<div class="left">
|
||||
<Button variant="secondary" on:click={doReset}>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
<div class="right">
|
||||
<Button variant="primary" on:click={doSave}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SvelteToast options={toastOptions} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
$bottom-bar-height: 5rem;
|
||||
|
||||
.comfy-settings {
|
||||
color: var(--body-text-color);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.comfy-settings-categories {
|
||||
width: 20rem;
|
||||
height: 100%;
|
||||
color: var(--neutral-500);
|
||||
background: var(--neutral-800);
|
||||
border-left: 2px solid var(--comfy-splitpanes-background-fill);
|
||||
}
|
||||
|
||||
.comfy-settings-category {
|
||||
padding: 2rem 3rem;
|
||||
font-size: 14pt;
|
||||
|
||||
border-bottom: 1px solid grey;
|
||||
cursor: pointer;
|
||||
|
||||
&.selected {
|
||||
color: var(--body-text-color);
|
||||
background: var(--neutral-700);
|
||||
}
|
||||
}
|
||||
|
||||
.comfy-settings-main {
|
||||
width: 100%;
|
||||
height: calc(100% - $bottom-bar-height);
|
||||
}
|
||||
|
||||
.comfy-settings-entries {
|
||||
padding: 3rem 3rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.comfy-settings-entry {
|
||||
padding: 1rem 3rem;
|
||||
|
||||
.name {
|
||||
font-weight: bold;
|
||||
font-size: 13pt;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 11pt;
|
||||
color: var(--neutral-400);
|
||||
}
|
||||
|
||||
.ctrl {
|
||||
margin-top: 0.5rem;
|
||||
min-width: 5rem;
|
||||
display: block;
|
||||
|
||||
&:not(.checkbox) {
|
||||
width: 20rem;
|
||||
}
|
||||
|
||||
&.textbox {
|
||||
:global(span) {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.checkbox {
|
||||
display: inline-flex !important;
|
||||
padding: 0 0.75rem;
|
||||
|
||||
:global(label) {
|
||||
color: var(--neutral-400);
|
||||
font-size: 11pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.comfy-settings-bottom-bar {
|
||||
background: var(--neutral-900);
|
||||
width: 100%;
|
||||
border-top: 2px solid var(--neutral-800);
|
||||
gap: var(--layout-gap);
|
||||
overflow-x: hidden;
|
||||
height: $bottom-bar-height;
|
||||
justify-content: center;
|
||||
padding: 0 2rem;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: var(--layout-gap);
|
||||
margin: auto;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.left {
|
||||
left: 0;
|
||||
}
|
||||
.right {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -41,7 +41,7 @@
|
||||
#lightboxModal{
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
z-index: var(--layer-top);
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
export let value: number = 0;
|
||||
export let min: number = -1024
|
||||
export let max: number = 1024
|
||||
export let min: number | null = null
|
||||
export let max: number | null = null
|
||||
export let step: number = 1;
|
||||
export let label: string = "";
|
||||
export let disabled: boolean = false;
|
||||
@@ -41,9 +41,11 @@
|
||||
|
||||
<div class="wrap">
|
||||
<div class="head">
|
||||
<label>
|
||||
<BlockTitle>{label}</BlockTitle>
|
||||
</label>
|
||||
{#if label}
|
||||
<label>
|
||||
<BlockTitle>{label}</BlockTitle>
|
||||
</label>
|
||||
{/if}
|
||||
<input
|
||||
data-testid="number-input"
|
||||
type="number"
|
||||
@@ -83,11 +85,11 @@
|
||||
border: var(--input-border-width) solid var(--input-border-color);
|
||||
border-radius: var(--input-radius);
|
||||
background: var(--input-background-fill);
|
||||
padding: var(--size-2) var(--size-2);
|
||||
padding: var(--input-padding);
|
||||
color: var(--body-text-color);
|
||||
font-size: var(--input-text-size);
|
||||
line-height: var(--line-sm);
|
||||
text-align: center;
|
||||
// text-align: center;
|
||||
}
|
||||
input:disabled {
|
||||
-webkit-text-fill-color: var(--body-text-color);
|
||||
@@ -95,9 +97,16 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
input[type="number"]:focus {
|
||||
box-shadow: var(--input-shadow-focus);
|
||||
border-color: var(--input-border-color-focus);
|
||||
input[type="number"] {
|
||||
&:focus {
|
||||
box-shadow: var(--input-shadow-focus);
|
||||
border-color: var(--input-border-color-focus);
|
||||
}
|
||||
|
||||
&::-webkit-inner-spin-button,
|
||||
&::-webkit-outer-spin-button {
|
||||
opacity: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
@@ -107,4 +116,5 @@
|
||||
input[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user