LocalStorage used bar
This commit is contained in:
@@ -85,10 +85,8 @@ export default class DanbooruTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async load(force: boolean = false) {
|
async load(force: boolean = false) {
|
||||||
console.log("Parsing danbooru tags CSV...")
|
|
||||||
|
|
||||||
if (this.tags.length > 0 && !force) {
|
if (this.tags.length > 0 && !force) {
|
||||||
console.info("Danbooru tags already parsed")
|
console.warn("Danbooru tags already parsed")
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -297,7 +297,9 @@ export default class ComfyApp {
|
|||||||
if (templateAndSvg == null) {
|
if (templateAndSvg == null) {
|
||||||
errors.push("Invalid SVG template format")
|
errors.push("Invalid SVG template format")
|
||||||
}
|
}
|
||||||
templates.push(templateAndSvg)
|
else {
|
||||||
|
templates.push(templateAndSvg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { embedTemplateInSvg, type SerializedComfyBoxTemplate } from "$lib/ComfyBoxTemplate";
|
import { embedTemplateInSvg, type SerializedComfyBoxTemplate } from "$lib/ComfyBoxTemplate";
|
||||||
import templateState, { type TemplateState, type WritableTemplateStateStore } from "$lib/stores/templateState";
|
import templateState, { type TemplateState, type WritableTemplateStateStore } from "$lib/stores/templateState";
|
||||||
import { BoxSeam } from "svelte-bootstrap-icons";
|
import { BoxSeam, Hdd } from "svelte-bootstrap-icons";
|
||||||
import uiState from "$lib/stores/uiState";
|
import uiState from "$lib/stores/uiState";
|
||||||
import { download, truncateString } from "$lib/utils";
|
import { download, getLocalStorageUsedMB, MAX_LOCAL_STORAGE_MB, truncateString } from "$lib/utils";
|
||||||
import type ComfyApp from "./ComfyApp";
|
import type ComfyApp from "./ComfyApp";
|
||||||
import { flip } from 'svelte/animate';
|
import { flip } from 'svelte/animate';
|
||||||
import {fade} from 'svelte/transition';
|
import {fade} from 'svelte/transition';
|
||||||
@@ -27,6 +27,20 @@
|
|||||||
source?: string
|
source?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let storageUsedPercent = 0;
|
||||||
|
let storageUsedMB = 0;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if ($templateState) {
|
||||||
|
storageUsedMB = getLocalStorageUsedMB()
|
||||||
|
storageUsedPercent = (storageUsedMB / MAX_LOCAL_STORAGE_MB) * 100;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
storageUsedPercent = 0;
|
||||||
|
storageUsedMB = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let _sorted: TemplateLayout[] = []
|
let _sorted: TemplateLayout[] = []
|
||||||
|
|
||||||
$: $templateState && rebuildTemplates(templateState.getAllTemplates());
|
$: $templateState && rebuildTemplates(templateState.getAllTemplates());
|
||||||
@@ -149,8 +163,7 @@
|
|||||||
{
|
{
|
||||||
name: "Close",
|
name: "Close",
|
||||||
variant: "secondary",
|
variant: "secondary",
|
||||||
onClick: () => {
|
onClick: () => {}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -161,30 +174,42 @@
|
|||||||
<div class="template-entries">
|
<div class="template-entries">
|
||||||
{#if _sorted.length > 0}
|
{#if _sorted.length > 0}
|
||||||
{@const draggable = $uiState.uiUnlocked}
|
{@const draggable = $uiState.uiUnlocked}
|
||||||
<div class="template-category-group">
|
<div class="template-list">
|
||||||
<div class="template-category-header">
|
<div class="template-category-group">
|
||||||
General
|
<div class="template-category-header">
|
||||||
|
General
|
||||||
|
</div>
|
||||||
|
<div class="template-entries-wrapper"
|
||||||
|
use:dndzone={{
|
||||||
|
type: "layout",
|
||||||
|
items: _sorted,
|
||||||
|
flipDurationMs,
|
||||||
|
dragDisabled: !draggable,
|
||||||
|
dropFromOthersDisabled: true
|
||||||
|
}}
|
||||||
|
on:consider={handleDndConsider}
|
||||||
|
on:finalize={handleDndFinalize}>
|
||||||
|
{#each _sorted.filter(i => i.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<div class="template-entry" class:built-in={item.template.isBuiltIn} class:draggable on:click={() => handleClick(item)}>
|
||||||
|
<div class="template-name">{item.template.metadata.title}</div>
|
||||||
|
<div class="template-desc">{item.template.metadata.description}</div>
|
||||||
|
</div>
|
||||||
|
{#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
|
||||||
|
<div in:fade={{duration:200, easing: cubicIn}} class='template-drag-item-shadow'/>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="template-entries-wrapper"
|
</div>
|
||||||
use:dndzone={{
|
<div class="storage-used">
|
||||||
type: "layout",
|
<div class="storage-used-icon">
|
||||||
items: _sorted,
|
<Hdd width="100%" height="2rem" />
|
||||||
flipDurationMs,
|
</div>
|
||||||
dragDisabled: !draggable,
|
<div class="storage-used-bar-wrapper">
|
||||||
dropFromOthersDisabled: true
|
<div class="storage-used-bar" style="width: {storageUsedPercent}%;">
|
||||||
}}
|
<span class="storage-used-label">{storageUsedMB.toFixed(2)} / {MAX_LOCAL_STORAGE_MB} MB</span>
|
||||||
on:consider={handleDndConsider}
|
</div>
|
||||||
on:finalize={handleDndFinalize}>
|
|
||||||
{#each _sorted.filter(i => i.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
||||||
<div class="template-entry" class:built-in={item.template.isBuiltIn} class:draggable on:click={() => handleClick(item)}>
|
|
||||||
<div class="template-name">{item.template.metadata.title}</div>
|
|
||||||
<div class="template-desc">{item.template.metadata.description}</div>
|
|
||||||
</div>
|
|
||||||
{#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
|
|
||||||
<div in:fade={{duration:200, easing: cubicIn}} class='template-drag-item-shadow'/>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -207,9 +232,13 @@
|
|||||||
|
|
||||||
.template-entries {
|
.template-entries {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-list {
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.template-category-header {
|
.template-category-header {
|
||||||
@@ -234,6 +263,16 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.built-in {
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
var(--neutral-900),
|
||||||
|
var(--neutral-900) 5px,
|
||||||
|
var(--neutral-800) 5px,
|
||||||
|
var(--neutral-800) 10px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
font-size: 13pt;
|
font-size: 13pt;
|
||||||
.template-desc {
|
.template-desc {
|
||||||
opacity: 65%;
|
opacity: 65%;
|
||||||
@@ -288,4 +327,45 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.storage-used {
|
||||||
|
height: 2.5rem;
|
||||||
|
margin: 5px;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
padding: 0.1rem 0.5rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
|
||||||
|
.storage-used-icon {
|
||||||
|
color: var(--neutral-500);
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-used-bar-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--panel-background-fill);
|
||||||
|
border: 1px solid var(--neutral-700);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.storage-used-bar {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--neutral-700);
|
||||||
|
|
||||||
|
.storage-used-label {
|
||||||
|
width: 100%;
|
||||||
|
color: var(--neutral-200);
|
||||||
|
font-size: 12pt;
|
||||||
|
position: absolute;
|
||||||
|
margin: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -101,9 +101,12 @@
|
|||||||
<BlockTitle>Metadata</BlockTitle>
|
<BlockTitle>Metadata</BlockTitle>
|
||||||
<div>
|
<div>
|
||||||
<Textbox label="Name" disabled={!editable} bind:value={$state.name} lines={1} max_lines={1} />
|
<Textbox label="Name" disabled={!editable} bind:value={$state.name} lines={1} max_lines={1} />
|
||||||
<Textbox label="Created At" disabled={true} bind:value={createdAt} lines={1} max_lines={1} />
|
|
||||||
<Textbox label="Author" disabled={!editable} bind:value={$state.author} lines={1} max_lines={1} />
|
<Textbox label="Author" disabled={!editable} bind:value={$state.author} lines={1} max_lines={1} />
|
||||||
<Textbox label="Description" disabled={!editable} bind:value={$state.description} lines={5} max_lines={5} />
|
<Textbox label="Description" disabled={!editable} bind:value={$state.description} lines={5} max_lines={5} />
|
||||||
|
<Row>
|
||||||
|
<Textbox label="Created At" disabled={true} bind:value={createdAt} lines={1} max_lines={1} />
|
||||||
|
<Textbox label="Size" disabled={true} value="{(templateAndSvg.svg.length/1024).toFixed(2)} KB" lines={1} max_lines={1} />
|
||||||
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
</Block>
|
</Block>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ export function download(filename: string, text: string, type: string = "text/pl
|
|||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocalStorageUsed(): number {
|
export const MAX_LOCAL_STORAGE_MB = 5;
|
||||||
|
|
||||||
|
export function getLocalStorageUsedMB(): number {
|
||||||
var total = 0;
|
var total = 0;
|
||||||
for (const x in localStorage) {
|
for (const x in localStorage) {
|
||||||
// Value is multiplied by 2 due to data being stored in `utf-16` format, which requires twice the space.
|
// Value is multiplied by 2 due to data being stored in `utf-16` format, which requires twice the space.
|
||||||
|
|||||||
Reference in New Issue
Block a user