Add in safetensors metadata (trained words, base model file). For #223

This commit is contained in:
rgthree
2024-05-17 21:51:25 -04:00
parent 872d264df1
commit a8dceb0d7b
13 changed files with 1492 additions and 1337 deletions
+20 -5
View File
@@ -8,8 +8,7 @@ import asyncio
from datetime import datetime
from .utils_server import get_param
from .utils import get_dict_value, load_json_file, is_dict_value_falsy, save_json_file
from .utils_info import get_model_info, get_sha256_hash, set_model_info_partial
from .utils_info import delete_all_model_info, get_model_info, set_model_info_partial
from server import PromptServer
from aiohttp import web
@@ -104,18 +103,33 @@ async def api_get_loras(request):
@routes.get('/rgthree/api/loras/info')
async def api_get_loras_info(request):
""" Returns a list loras info; either all or a single if provided a 'file' param. """
api_response = await get_loras_info_response(request)
api_response = await get_loras_info_response(request, maybe_fetch_metadata=True)
return web.json_response(api_response)
@routes.get('/rgthree/api/loras/info/clear')
async def delete_lora_info(request):
"""Clears lora info from the filesystem for the provided file."""
api_response = {'status': 200}
lora_file = get_param(request, 'file')
if lora_file is None:
api_response['status'] = '404'
api_response['error'] = 'No Lora found at path'
else:
await delete_all_model_info(lora_file)
return web.json_response(api_response)
@routes.get('/rgthree/api/loras/info/refresh')
async def refresh_get_loras_info(request):
""" Refreshes lora info; either all or a single if provided a 'file' param. """
api_response = await get_loras_info_response(request, maybe_fetch_civitai=True)
api_response = await get_loras_info_response(request,
maybe_fetch_civitai=True,
maybe_fetch_metadata=True)
return web.json_response(api_response)
async def get_loras_info_response(request, maybe_fetch_civitai=False):
async def get_loras_info_response(request, maybe_fetch_civitai=False, maybe_fetch_metadata=False):
"""Gets lora info for all or a single lora"""
api_response = {'status': 200}
lora_file = get_param(request, 'file')
@@ -124,6 +138,7 @@ async def get_loras_info_response(request, maybe_fetch_civitai=False):
if lora_file is not None:
info_data = await get_model_info(lora_file,
maybe_fetch_civitai=maybe_fetch_civitai,
maybe_fetch_metadata=maybe_fetch_metadata,
abandon_if_no_file=light)
if info_data is None:
api_response['status'] = '404'
+295 -110
View File
@@ -8,24 +8,39 @@ import copy
from datetime import datetime
from .utils import get_dict_value, is_dict_value_falsy, load_json_file, save_json_file
from .utils_userdata import read_userdata_json, save_userdata_json
from .utils import get_dict_value, load_json_file, save_json_file
from .utils_userdata import read_userdata_json, save_userdata_json, delete_userdata_file
import folder_paths
from server import PromptServer
async def delete_all_model_info(file: str, model_type="loras"):
"""Delete the info json, and the civitai & metadata caches."""
file_path = get_folder_path(file, model_type)
if file_path is None:
return
try_info_path = f'{file_path}.rgthree-info.json'
if os.path.isfile(try_info_path):
os.remove(try_info_path)
file_hash = _get_sha256_hash(file_path)
json_file_path = f'civitai/{file_hash}.json'
delete_userdata_file(json_file_path)
json_file_path = f'metadata/{file_hash}.json'
delete_userdata_file(json_file_path)
async def get_model_info(file: str,
model_type="loras",
default=None,
maybe_fetch_civitai=False,
force_fetch_civitai=False,
maybe_fetch_metadata=False,
force_fetch_metadata=False,
abandon_if_no_file=False):
file_path = folder_paths.get_full_path(model_type, file)
if not os.path.exists(file_path):
file_path = os.path.abspath(file_path)
if not os.path.exists(file_path):
file_path = get_folder_path(file, model_type)
if file_path is None:
return default
info_data = {}
@@ -67,136 +82,226 @@ async def get_model_info(file: str,
info_data['raw'] = {}
should_save = True
if force_fetch_civitai is True or (maybe_fetch_civitai is True and
'civitai' not in info_data['raw']):
data_civitai = get_model_civitai_data(file,
model_type=model_type,
default={},
refresh=force_fetch_civitai)
should_save = _update_data(info_data) or should_save
if 'name' not in info_data:
info_data['name'] = get_dict_value(data_civitai, 'model.name', '')
should_fetch_civitai = force_fetch_civitai is True or (maybe_fetch_civitai is True and
'civitai' not in info_data['raw'])
should_fetch_metadata = force_fetch_metadata is True or (maybe_fetch_metadata is True and
'metadata' not in info_data['raw'])
if should_fetch_metadata:
data_meta = _get_model_metadata(file,
model_type=model_type,
default={},
refresh=force_fetch_metadata)
should_save = _merge_metadata(info_data, data_meta) or should_save
if should_fetch_civitai:
data_civitai = _get_model_civitai_data(file,
model_type=model_type,
default={},
refresh=force_fetch_civitai)
should_save = _merge_civitai_data(info_data, data_civitai) or should_save
if 'sha256' not in info_data:
file_hash = _get_sha256_hash(file_path)
if file_hash is not None:
info_data['sha256'] = file_hash
should_save = True
version_name = get_dict_value(data_civitai, 'name')
if version_name is not None:
info_data['name'] += f' - {version_name}'
if 'type' not in info_data:
info_data['type'] = get_dict_value(data_civitai, 'model.type')
should_save = True
if 'baseModel' not in info_data:
info_data['baseModel'] = get_dict_value(data_civitai, 'baseModel')
should_save = True
if 'triggerWords' not in info_data:
if 'triggerWords' in data_civitai:
info_data['triggerWords'] = get_dict_value(data_civitai, 'triggerWords')
should_save = True
if is_dict_value_falsy(data_civitai, 'triggerWords') and 'trainedWords' in data_civitai:
info_data['triggerWords'] = get_dict_value(data_civitai, 'trainedWords')
if not is_dict_value_falsy(data_civitai,
'triggerWords') and ',' in info_data['triggerWords'][0]:
trigger_words = re.sub(r"^,", "", info_data['triggerWords'][0])
trigger_words = re.sub(r",$", "", trigger_words)
trigger_words = re.sub(r"\s*,\s*", ",", trigger_words)
info_data['triggerWords'] = trigger_words.split(',')
should_save = True
if 'sha256' not in info_data:
info_data['sha256'] = data_civitai['_sha256']
should_save = True
if 'modelId' in data_civitai:
info_data['links'] = info_data['links'] if 'links' in info_data else []
civitai_link = f'https://civitai.com/models/{get_dict_value(data_civitai, "modelId")}'
if get_dict_value(data_civitai, "id"):
civitai_link += f'?modelVersionId={get_dict_value(data_civitai, "id")}'
info_data['links'].append(civitai_link)
info_data['links'].append(data_civitai['_civitai_api'])
should_save = True
# Take images from civitai
if 'images' in data_civitai:
info_data_image_urls = list(
map(lambda i: i['url'] if 'url' in i else None, info_data['images']))
for img in data_civitai['images']:
img_url = get_dict_value(img, 'url')
if img_url is not None and img_url not in info_data_image_urls:
img_id = os.path.splitext(os.path.basename(img_url))[0] if img_url is not None else None
img_data = {
'url': img_url,
'civitaiUrl': f'https://civitai.com/images/{img_id}' if img_id is not None else None,
'width': get_dict_value(img, 'width'),
'height': get_dict_value(img, 'height'),
'type': get_dict_value(img, 'type'),
'nsfwLevel': get_dict_value(img, 'nsfwLevel'),
'seed': get_dict_value(img, 'meta.seed'),
'positive': get_dict_value(img, 'meta.prompt'),
'negative': get_dict_value(img, 'meta.negativePrompt'),
'steps': get_dict_value(img, 'meta.steps'),
'sampler': get_dict_value(img, 'meta.sampler'),
'cfg': get_dict_value(img, 'meta.cfgScale'),
'model': get_dict_value(img, 'meta.Model'),
'resources': get_dict_value(img, 'meta.resources'),
}
info_data['images'].append(img_data)
should_save = True
# The raw data
if 'civitai' not in info_data['raw']:
info_data['raw']['civitai'] = data_civitai
should_save = True
else:
if 'sha256' not in info_data:
file_hash = get_sha256_hash(file_path)
if file_hash is not None:
info_data['sha256'] = file_hash
should_save = True
# If we've fetched civitai, then the UI is likely waiting to see if the refreshed data is coming
# in. Not needed yet, only for Lora Chooser.
# await PromptServer.instance.send("rgthree-lora-info", {"data": info_data})
if should_save:
if 'trainedWords' in info_data:
# Sort by count; if it doesn't exist, then assume it's a top item from civitai or elsewhere.
info_data['trainedWords'] = sorted(info_data['trainedWords'],
key=lambda w: w['count'] if 'count' in w else 99999,
reverse=True)
save_model_info(file, info_data, model_type=model_type)
return info_data
def get_sha256_hash(path: str):
"""Returns the hash for the file."""
file_hash = None
sha256_hash = hashlib.sha256()
with open(path, "rb") as f:
# Read and update hash string value in blocks of 4K
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
file_hash = sha256_hash.hexdigest()
return file_hash
def _update_data(info_data: dict) -> bool:
"""Ports old data to new data if necessary."""
should_save = False
# If we have "triggerWords" then move them over to "trainedWords"
if 'triggerWords' in info_data and len(info_data['triggerWords']) > 0:
civitai_words = ','.join((get_dict_value(info_data, 'raw.civitai.triggerWords', default=[]) +
get_dict_value(info_data, 'raw.civitai.trainedWords', default=[])))
if 'trainedWords' not in info_data:
info_data['trainedWords'] = []
for trigger_word in info_data['triggerWords']:
word_data = next((data for data in info_data['trainedWords'] if data['word'] == trigger_word),
None)
if word_data is None:
word_data = {'word': trigger_word}
info_data['trainedWords'].append(word_data)
if trigger_word in civitai_words:
word_data['civitai'] = True
else:
word_data['user'] = True
del info_data['triggerWords']
should_save = True
return should_save
def get_model_civitai_data(file: str, model_type="loras", default=None, refresh=False):
def _merge_metadata(info_data: dict, data_meta: dict) -> bool:
"""Returns true if data was saved."""
should_save = False
base_model_file = get_dict_value(data_meta, 'ss_sd_model_name', None)
if base_model_file:
info_data['baseModelFile'] = base_model_file
# Loop over metadata tags
trained_words = {}
if 'ss_tag_frequency' in data_meta and isinstance(data_meta['ss_tag_frequency'], dict):
for bucket_value in data_meta['ss_tag_frequency'].values():
if isinstance(bucket_value, dict):
for tag, count in bucket_value.items():
if tag not in trained_words:
trained_words[tag] = {'word': tag, 'count': 0, 'metadata': True}
trained_words[tag]['count'] = trained_words[tag]['count'] + count
if 'trainedWords' not in info_data:
info_data['trainedWords'] = list(trained_words.values())
should_save = True
else:
# We can't merge, because the list may have other data, like it's part of civitaidata.
merged_dict = {}
for existing_word_data in info_data['trainedWords']:
merged_dict[existing_word_data['word']] = existing_word_data
for new_key, new_word_data in trained_words.items():
if new_key not in merged_dict:
merged_dict[new_key] = {}
merged_dict[new_key] = {**merged_dict[new_key], **new_word_data}
info_data['trainedWords'] = list(merged_dict.values())
should_save = True
# trained_words = list(trained_words.values())
# info_data['meta_trained_words'] = trained_words
info_data['raw']['metadata'] = data_meta
should_save = True
if 'sha256' not in info_data and '_sha256' in data_meta:
info_data['sha256'] = data_meta['_sha256']
should_save = True
return should_save
def _merge_civitai_data(info_data: dict, data_civitai: dict) -> bool:
"""Returns true if data was saved."""
should_save = False
if 'name' not in info_data:
info_data['name'] = get_dict_value(data_civitai, 'model.name', '')
should_save = True
version_name = get_dict_value(data_civitai, 'name')
if version_name is not None:
info_data['name'] += f' - {version_name}'
if 'type' not in info_data:
info_data['type'] = get_dict_value(data_civitai, 'model.type')
should_save = True
if 'baseModel' not in info_data:
info_data['baseModel'] = get_dict_value(data_civitai, 'baseModel')
should_save = True
# We always want to merge triggerword.
civitai_trigger = get_dict_value(data_civitai, 'triggerWords', default=[])
civitai_trained = get_dict_value(data_civitai, 'trainedWords', default=[])
civitai_words = ','.join(civitai_trigger + civitai_trained)
if civitai_words:
civitai_words = re.sub(r"\s*,\s*", ",", civitai_words)
civitai_words = re.sub(r",+", ",", civitai_words)
civitai_words = re.sub(r"^,", "", civitai_words)
civitai_words = re.sub(r",$", "", civitai_words)
if civitai_words:
civitai_words = civitai_words.split(',')
if 'trainedWords' not in info_data:
info_data['trainedWords'] = []
for trigger_word in civitai_words:
word_data = next(
(data for data in info_data['trainedWords'] if data['word'] == trigger_word), None)
if word_data is None:
word_data = {'word': trigger_word}
info_data['trainedWords'].append(word_data)
word_data['civitai'] = True
if 'sha256' not in info_data:
info_data['sha256'] = data_civitai['_sha256']
should_save = True
if 'modelId' in data_civitai:
info_data['links'] = info_data['links'] if 'links' in info_data else []
civitai_link = f'https://civitai.com/models/{get_dict_value(data_civitai, "modelId")}'
if get_dict_value(data_civitai, "id"):
civitai_link += f'?modelVersionId={get_dict_value(data_civitai, "id")}'
info_data['links'].append(civitai_link)
info_data['links'].append(data_civitai['_civitai_api'])
should_save = True
# Take images from civitai
if 'images' in data_civitai:
info_data_image_urls = list(map(lambda i: i['url']
if 'url' in i else None, info_data['images']))
for img in data_civitai['images']:
img_url = get_dict_value(img, 'url')
if img_url is not None and img_url not in info_data_image_urls:
img_id = os.path.splitext(os.path.basename(img_url))[0] if img_url is not None else None
img_data = {
'url': img_url,
'civitaiUrl': f'https://civitai.com/images/{img_id}' if img_id is not None else None,
'width': get_dict_value(img, 'width'),
'height': get_dict_value(img, 'height'),
'type': get_dict_value(img, 'type'),
'nsfwLevel': get_dict_value(img, 'nsfwLevel'),
'seed': get_dict_value(img, 'meta.seed'),
'positive': get_dict_value(img, 'meta.prompt'),
'negative': get_dict_value(img, 'meta.negativePrompt'),
'steps': get_dict_value(img, 'meta.steps'),
'sampler': get_dict_value(img, 'meta.sampler'),
'cfg': get_dict_value(img, 'meta.cfgScale'),
'model': get_dict_value(img, 'meta.Model'),
'resources': get_dict_value(img, 'meta.resources'),
}
info_data['images'].append(img_data)
should_save = True
# The raw data
if 'civitai' not in info_data['raw']:
info_data['raw']['civitai'] = data_civitai
should_save = True
return should_save
def _get_model_civitai_data(file: str, model_type="loras", default=None, refresh=False):
"""Gets the civitai data, either cached from the user directory, or from civitai api."""
file_path = folder_paths.get_full_path(model_type, file)
if not os.path.exists(file_path):
file_path = os.path.abspath(file_path)
file_hash = get_sha256_hash(file_path)
file_hash = _get_sha256_hash(get_folder_path(file, model_type))
if file_hash is None:
return None
json_file_path = f'civitai/{file_hash}.json'
api_url = f'https://civitai.com/api/v1/model-versions/by-hash/{file_hash}'
file_data = read_userdata_json(f'info/{file_hash}_civitai.json')
if file_data is None or refresh == True:
file_data = read_userdata_json(json_file_path)
if file_data is None or refresh is True:
try:
response = requests.get(api_url, timeout=5000)
data = response.json()
save_userdata_json(f'civitai/{file_hash}.json', {
save_userdata_json(json_file_path, {
'url': api_url,
'timestamp': datetime.now().timestamp(),
'response': data
})
file_data = read_userdata_json(f'civitai/{file_hash}.json')
file_data = read_userdata_json(json_file_path)
except requests.exceptions.RequestException as e: # This is the correct syntax
print(e)
response = file_data['response'] if file_data is not None and 'response' in file_data else None
@@ -206,6 +311,86 @@ def get_model_civitai_data(file: str, model_type="loras", default=None, refresh=
return response if response is not None else default
def _get_model_metadata(file: str, model_type="loras", default=None, refresh=False):
"""Gets the metadata from the file itself."""
file_path = get_folder_path(file, model_type)
file_hash = _get_sha256_hash(file_path)
if file_hash is None:
return default
json_file_path = f'metadata/{file_hash}.json'
file_data = read_userdata_json(json_file_path)
if file_data is None or refresh is True:
data = _read_file_metadata_from_header(file_path)
if data is not None:
file_data = {'url': file, 'timestamp': datetime.now().timestamp(), 'response': data}
save_userdata_json(json_file_path, file_data)
response = file_data['response'] if file_data is not None and 'response' in file_data else None
if response is not None:
response['_sha256'] = file_hash
return response if response is not None else default
def _read_file_metadata_from_header(file_path: str) -> dict:
"""Reads the file's header and returns a JSON dict metdata if available."""
data = None
try:
if file_path.endswith('.safetensors'):
with open(file_path, "rb") as file:
# https://github.com/huggingface/safetensors#format
# 8 bytes: N, an unsigned little-endian 64-bit integer, containing the size of the header
header_size = int.from_bytes(file.read(8), "little", signed=False)
if header_size <= 0:
raise BufferError("Invalid header size")
header = file.read(header_size)
if header is None:
raise BufferError("Invalid header")
header_json = json.loads(header)
data = header_json["__metadata__"] if "__metadata__" in header_json else None
if data is not None:
for key, value in data.items():
if isinstance(value, str) and value.startswith('{') and value.endswith('}'):
try:
value_as_json = json.loads(value)
data[key] = value_as_json
except Exception:
print(f'metdata for field {key} did not parse as json')
except requests.exceptions.RequestException as e:
print(e)
data = None
return data
def get_folder_path(file: str, model_type="loras"):
"""Gets the file path ensuring it exists."""
file_path = folder_paths.get_full_path(model_type, file)
if not os.path.exists(file_path):
file_path = os.path.abspath(file_path)
if not os.path.exists(file_path):
return None
return file_path
def _get_sha256_hash(file_path: str):
"""Returns the hash for the file."""
if not file_path or not os.path.exists(file_path):
return None
file_hash = None
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
# Read and update hash string value in blocks of 4K
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
file_hash = sha256_hash.hexdigest()
return file_hash
async def set_model_info_partial(file: str, info_data_partial, model_type="loras"):
"""Sets partial data into the existing model info data."""
info_data = await get_model_info(file, model_type=model_type, default={})
+7
View File
@@ -22,6 +22,13 @@ def save_userdata_file(rel_path: str, content: str):
file.write(content)
def delete_userdata_file(rel_path: str):
"""Deletes a file from the userdata directory."""
file_path = clean_path(rel_path)
if os.path.isfile(file_path):
os.remove(file_path)
def read_userdata_json(rel_path: str):
"""Reads a json file from the userdata directory."""
file_path = clean_path(rel_path)
+130 -58
View File
@@ -5,18 +5,30 @@ import {
appendChildren,
getClosestOrSelf,
queryOne,
setAttributes,
query,
} from "rgthree/common/utils_dom.js";
import {
logoCivitai,
link,
pencilColored,
diskColored,
dotdotdot,
} from "rgthree/common/media/svgs.js";
import { rgthreeApi } from "rgthree/common/rgthree_api.js";
import { logoCivitai, link, pencilColored, diskColored } from "rgthree/common/media/svgs.js";
import { RgthreeModelInfo } from "typings/rgthree.js";
import { SERVICE as MODEL_INFO_SERVICE } from "rgthree/common/model_info_service.js";
import { rgthree } from "./rgthree.js";
import { generateId } from "rgthree/common/shared_utils.js";
function injectCss(): Promise<void> {
const href = "rgthree/common/css/dialog_model_info.css";
if (queryOne(`link[href^="${href}"]`)) {
return Promise.resolve();
}
return new Promise((resolve) => {
const link = $el<HTMLLinkElement>('link[rel="stylesheet"][type="text/css"]');
const timeout = setTimeout(resolve, 1000);
link.addEventListener("load", (e) => {
clearInterval(timeout);
resolve();
});
link.href = href;
document.head.appendChild(link);
});
}
/**
* A dialog that displays information about a model/lora/etc.
@@ -39,7 +51,9 @@ export class RgthreeInfoDialog extends RgthreeDialog {
}
private async init(file: string) {
const cssPromise = injectCss();
this.modelInfo = await MODEL_INFO_SERVICE.getLora(file, false, false);
await cssPromise;
this.setContent(this.getInfoContent());
this.setTitle(this.modelInfo?.["name"] || this.modelInfo?.["file"] || "Unknown");
this.attachEvents();
@@ -54,53 +68,86 @@ export class RgthreeInfoDialog extends RgthreeDialog {
private attachEvents() {
this.contentElement.addEventListener("click", async (e: MouseEvent) => {
const info = this.modelInfo;
if (!info?.file) {
return;
}
const target = getClosestOrSelf(e.target as HTMLElement, "[data-action]");
const action = target?.getAttribute("data-action");
if (action === "fetch-civitai") {
this.modelInfo = await MODEL_INFO_SERVICE.refreshLora(info.file);
this.setContent(this.getInfoContent());
this.setTitle(this.modelInfo?.["name"] || this.modelInfo?.["file"] || "Unknown");
} else if (action === "edit-row") {
const tr = target!.closest("tr")!;
const td = queryOne("td:nth-child(2)", tr)!;
const input = td.querySelector("input,textarea");
if (!input) {
const fieldName = tr.dataset["fieldName"] as string;
tr.classList.add("-rgthree-editing");
const isTextarea = fieldName === "userNote";
const input = $el(`${isTextarea ? "textarea" : 'input[type="text"]'}`, {
value: td.textContent,
});
input.addEventListener("keydown", (e) => {
if (!isTextarea && e.key === "Enter") {
const modified = saveEditableRow(info!, tr, true);
this.modifiedModelData = this.modifiedModelData || modified;
e.stopPropagation();
e.preventDefault();
} else if (e.key === "Escape") {
const modified = saveEditableRow(info!, tr, false);
this.modifiedModelData = this.modifiedModelData || modified;
e.stopPropagation();
e.preventDefault();
}
});
appendChildren(empty(td), [input]);
input.focus();
} else if (target!.nodeName.toLowerCase() === "button") {
const modified = saveEditableRow(info!, tr, true);
this.modifiedModelData = this.modifiedModelData || modified;
}
e.preventDefault();
e.stopPropagation();
if (!target || !action) {
return;
}
await this.handleEventAction(action, target, e);
});
}
private async handleEventAction(action: string, target: HTMLElement, e?: Event) {
const info = this.modelInfo!;
if (!info?.file) {
return;
}
if (action === "fetch-civitai") {
this.modelInfo = await MODEL_INFO_SERVICE.refreshLora(info.file);
this.setContent(this.getInfoContent());
this.setTitle(this.modelInfo?.["name"] || this.modelInfo?.["file"] || "Unknown");
} else if (action === "copy-trained-words") {
const selected = query(".-rgthree-is-selected", target.closest("tr")!);
const text = selected.map((el) => el.getAttribute("data-word")).join(", ");
await navigator.clipboard.writeText(text);
rgthree.showMessage({
id: "copy-trained-words-" + generateId(4),
type: "success",
message: `Successfully copied ${selected.length} key word${
selected.length === 1 ? "" : "s"
}.`,
timeout: 4000,
});
} else if (action === "toggle-trained-word") {
target?.classList.toggle("-rgthree-is-selected");
const tr = target.closest("tr");
if (tr) {
const span = queryOne("td:first-child > *", tr)!;
let small = queryOne("small", span);
if (!small) {
small = $el("small", { parent: span });
}
const num = query(".-rgthree-is-selected", tr).length;
small.innerHTML = num
? `${num} selected | <span role="button" data-action="copy-trained-words">Copy</span>`
: "";
// this.handleEventAction('copy-trained-words', target, e);
}
} else if (action === "edit-row") {
const tr = target!.closest("tr")!;
const td = queryOne("td:nth-child(2)", tr)!;
const input = td.querySelector("input,textarea");
if (!input) {
const fieldName = tr.dataset["fieldName"] as string;
tr.classList.add("-rgthree-editing");
const isTextarea = fieldName === "userNote";
const input = $el(`${isTextarea ? "textarea" : 'input[type="text"]'}`, {
value: td.textContent,
});
input.addEventListener("keydown", (e) => {
if (!isTextarea && e.key === "Enter") {
const modified = saveEditableRow(info!, tr, true);
this.modifiedModelData = this.modifiedModelData || modified;
e.stopPropagation();
e.preventDefault();
} else if (e.key === "Escape") {
const modified = saveEditableRow(info!, tr, false);
this.modifiedModelData = this.modifiedModelData || modified;
e.stopPropagation();
e.preventDefault();
}
});
appendChildren(empty(td), [input]);
input.focus();
} else if (target!.nodeName.toLowerCase() === "button") {
const modified = saveEditableRow(info!, tr, true);
this.modifiedModelData = this.modifiedModelData || modified;
}
e?.preventDefault();
e?.stopPropagation();
}
}
private getInfoContent() {
const info = this.modelInfo || {};
const civitaiLink = info.links?.find((i) => i.includes("civitai.com/models"));
@@ -145,14 +192,21 @@ export class RgthreeInfoDialog extends RgthreeDialog {
)
: ""
}
${infoTableRow("Base Model", info.baseModel || "")}
${infoTableRow("Name", info.name || "", "The name for display.", "name")}
${infoTableRow(
"Trigger Words",
info.triggerWords?.join(", ") ?? "",
"Easily keep track of the trigger words to reference here.",
"triggerWords",
"Base Model",
(info.baseModel || " ") + (info.baseModelFile ? `(${info.baseModelFile})` : ""),
)}
${infoTableRow(
"Name",
info.name || info.raw?.metadata?.ss_output_name || "",
"The name for display.",
"name",
)}
${infoTableRow(
"Trained Words",
getTrainedWordsMarkup(info.trainedWords) ?? "",
"Trained words from the metadata and/or civitai. Click to select for copy.",
)}
${infoTableRow(
"Strength Min",
@@ -241,7 +295,9 @@ function infoTableRow(
editableFieldName ? `data-field-name="${editableFieldName}"` : ""
}>
<td><span>${name} ${help ? `<span class="-help" title="${help}"></span>` : ""}<span></td>
<td ${editableFieldName ? "" : 'colspan="2"'}><span>${value}</span></td>
<td ${editableFieldName ? "" : 'colspan="2"'}>${
String(value).startsWith("<") ? value : `<span>${value}<span>`
}</td>
${
editableFieldName
? `<td style="width: 24px;"><button class="rgthree-button-reset rgthree-button-edit" data-action="edit-row">${pencilColored}${diskColored}</button></td>`
@@ -250,6 +306,22 @@ function infoTableRow(
</tr>`;
}
function getTrainedWordsMarkup(words: RgthreeModelInfo["trainedWords"]) {
let markup = `<ul class="rgthree-info-trained-words-list">`;
for (const wordData of words || []) {
markup += `<li title="${wordData.word}" data-word="${
wordData.word
}" class="rgthree-info-trained-words-list-item" data-action="toggle-trained-word">
<span>${wordData.word}</span>
${wordData.civitai ? logoCivitai : ""}
${wordData.count != null ? `<small>${wordData.count}</small>` : ""}
</li>`;
}
markup += `</ul>`;
console.log(markup);
return markup;
}
/**
* Saves / cancels an editable row. Returns a boolean if the data was modified.
*/
+11
View File
@@ -23,6 +23,7 @@ import { RgthreeProgressBar } from "rgthree/common/progress_bar.js";
import { RgthreeConfigDialog } from "./config.js";
import { iconGear, iconReplace, iconStarFilled, logoRgthree } from "rgthree/common/media/svgs.js";
import type { Bookmark } from "./bookmark";
import { query } from "rgthree/common/utils_dom.js";
declare const LiteGraph: typeof TLiteGraph;
declare const LGraphCanvas: typeof TLGraphCanvas;
@@ -789,6 +790,16 @@ class Rgthree extends EventTarget {
container.classList.add("rgthree-top-messages-container");
document.body.appendChild(container);
}
// If we have a dialog open then we want to append the message to the dialog so they show over
// the modal.
const dialogs = query<HTMLDialogElement>('dialog[open]');
if (dialogs.length) {
let dialog = dialogs[dialogs.length - 1]!;
dialog.appendChild(container);
dialog.addEventListener('close', (e) => {
document.body.appendChild(container!);
});
}
// Hide if we exist.
await this.hideMessage(data.id);
-402
View File
@@ -127,405 +127,3 @@ body.rgthree-dialog-open > *:not(.rgthree-dialog):not(.rgthree-top-messages-cont
filter: blur(5px);
}
.rgthree-info-dialog {
min-width: 600px;
.rgthree-info-tags {
list-style: none;
padding: 0;
margin: 0;
display: flex;
> li {
display: inline-flex;
margin: 0;
vertical-align: top;
+ li {
margin-left: 6px;
}
&:not(.-link) + li.-link {
margin-left: auto;
}
> * {
border-radius: 4px;
line-height: 1;
color: rgba(255,255,255,0.85);
background: rgb(69, 92, 85);;
font-size: 14px;
font-weight: bold;
text-decoration: none;
display: flex;
height: 1.6em;
padding-left: .5em;
padding-right: .5em;
padding-bottom: .1em;
align-content: center;
justify-content: center;
align-items: center;
box-shadow: inset 0px 0px 0 1px rgba(0, 0, 0, 0.5);
> svg {
width: 16px;
height: 16px;
&:first-child {
margin-right: .3em;
display: none;
}
&:last-child {
margin-left: .5em;
fill: currentColor;
}
}
&[href] {
box-shadow: inset 0px 1px 0px rgba(255,255,255,0.25), inset 0px -1px 0px rgba(0,0,0,0.66);
}
&:empty {
display: none;
}
}
&.-civitai > * {
color: #ddd;
background: #1b65aa;
transition: all 0.15s ease-in-out;
&:hover {
color: #fff;
border-color: #1971c2;
background: #1971c2;
}
}
&.-type > * {
background: rgb(73, 54, 94);
color: rgb(228, 209, 248);
}
}
}
.rgthree-info-table {
border-collapse: collapse;
margin: 16px 0px;
width: 100%;
font-size: 12px;
tr.editable button {
display: flex;
width: 28px;
height: 28px;
align-items: center;
justify-content: center;
svg + svg {display: none;}
}
tr.editable.-rgthree-editing button {
svg {display: none;}
svg + svg {display: inline-block;}
}
td {
position: relative;
border: 1px solid rgba(255,255,255,0.25);
padding: 0;
&:first-child {
background: rgba(255,255,255,0.075);
width: 10px; // Small, so it doesn't adjust.
> *:first-child {
white-space: nowrap;
padding-right: 32px;
}
}
a, a:hover, a:visited {
color: inherit;
}
ul {
margin: .5em 0;
padding: 0 0 0 1em;
}
svg {
width: 1.3333em;
height: 1.3333em;
vertical-align: -0.285em;
&.logo-civitai {
margin-right: 0.3333em;
}
}
> *:first-child {
display: block;
padding: 6px 10px;
}
> input, > textarea{
padding: 5px 10px;
border: 0;
box-shadow: inset 1px 1px 5px 0px rgba(0,0,0,0.5);
font: inherit;
appearance: none;
background: #fff;
color: #121212;
resize: vertical;
&:only-child {
width: 100%;
}
}
}
tr[data-field-name="userNote"] td > span:first-child {
white-space: pre;
}
tr.rgthree-info-table-break-row td {
border: 0;
background: transparent;
padding: 12px 4px 4px;
font-size: 1.2em;
> small {
font-style: italic;
opacity: 0.66;
}
&:empty {
padding: 4px;
}
}
td .-help {
border: 1px solid currentColor;
position: absolute;
right: 5px;
top: 50%;
margin-top: -7px;
line-height: 1;
font-size: 11px;
width: 12px;
height: 12px;
border-radius: 8px;
display: flex;
align-content: center;
justify-content: center;
cursor: help;
&::before {
content: '?';
}
}
}
.rgthree-info-images {
list-style:none;
padding:0;
margin:0;
scroll-snap-type: x mandatory;
display:flex;
flex-direction:row;
overflow: auto;
> li {
scroll-snap-align: start;
max-width: 90%;
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
overflow: hidden;
padding: 8px;
.rgthree-info-table {
width: calc(100% - 16px);
}
}
}
.rgthree-info-civitai-link {
margin: 8px;
color: #eee;
a, a:hover, a:visited {
color: inherit;
text-decoration: none;
}
> svg {
width: 16px;
height: 16px;
margin-right: 8px;
}
}
}
.rgthree-lora-chooser-dialog {
max-width: 100%;
.rgthree-dialog-container-title {
display: flex;
flex-direction: column;
}
.rgthree-dialog-container-title h2 {
display: flex;
width: 100%;
}
.rgthree-lora-chooser-search {
margin-left: auto;
border-radius: 50px;
width: 50%;
max-width: 170px;
padding: 2px 8px;
}
.rgthree-lora-chooser-header {
display: flex;
flex-direction: row;
}
.rgthree-lora-filters-container {
svg {width: 16px; height: 16px;}
}
.rgthree-dialog-container-content {
width: 80vw;
height: 80vh;
}
.rgthree-button-reset {
width: 32px;
height: 32px;
> svg {width: 100%; height: 100%;}
}
ul.rgthree-lora-chooser-list {
list-style: none;
margin: 0;
padding: 0;
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: start;
justify-content: space-around;
> li {
position: relative;
flex: 0 0 auto;
width: 170px;
max-width: 100%;
margin: 8px 8px 16px;
label {
position: absolute;
display: block;
inset: 0;
z-index: 3;
cursor: pointer;
}
input[type="checkbox"] {
position: absolute;
right: 8px;
top: 8px;
margin: 0;
z-index: 2;
appearance: none;
background-color: #fff;
width: 48px;
height: 48px;
border-radius: 4px;
border: 1px solid rgba(120,120,120,1);
opacity: 0;
transition: opacity 0.15s ease-in-out;
&:checked {
opacity: 1;
background: #0060df;
&::before {
content: "";
display: block;
width: 100%;
height: 100%;
box-shadow: inset 100px 100px #fff;
clip-path: polygon(40.13% 68.39%, 23.05% 51.31%, 17.83% 48.26%, 12.61% 49.57%, 9.57% 53.04%, 8% 60%, 34.13% 85.87%, 39.82% 89.57%, 45.88% 86.73%, 90.66% 32.39%, 88.92% 26.1%, 83.03% 22.17%, 76.94% 22.62%)
}
}
}
figure {
position: relative;
display: block;
margin: 0 0 8px;
padding: 0;
border: 1px solid rgba(120, 120, 120, .8);
background: rgba(120, 120, 120, .5);
width: 100%;
padding-top: 120%;
transition: box-shadow 0.15s ease-in-out;
opacity: 0.75;
&::after {
content: '';
display: block;
position: absolute;
inset: 0;
}
&:empty {
&::before {
content: 'No image.';
color: rgba(200, 200, 200, .8);
position: absolute;
display: block;
inset: 0;
font-size: 1.2em;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
}
> img, > video {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
object-fit: cover;
}
}
div {
word-wrap: break-word;
font-size: 0.8rem;
opacity: 0.75;
}
&:hover figure::after{
box-shadow: 0px 2px 6px rgba(0,0,0,0.75);
}
:checked ~ figure::after {
box-shadow: 0 0 5px #fff, 0px 0px 15px rgba(49, 131, 255, 0.88), inset 0 0 3px #fff, inset 0px 0px 5px rgba(49, 131, 255, 0.88)
}
&:hover *,
&:hover input[type="checkbox"],
:checked ~ * {
opacity: 1
}
}
}
}
+475
View File
@@ -0,0 +1,475 @@
.rgthree-info-dialog {
width: 90vw;
max-width: 960px;
.rgthree-info-tags {
list-style: none;
padding: 0;
margin: 0;
display: flex;
> li {
display: inline-flex;
margin: 0;
vertical-align: top;
+ li {
margin-left: 6px;
}
&:not(.-link) + li.-link {
margin-left: auto;
}
> * {
border-radius: 4px;
line-height: 1;
color: rgba(255,255,255,0.85);
background: rgb(69, 92, 85);;
font-size: 14px;
font-weight: bold;
text-decoration: none;
display: flex;
height: 1.6em;
padding-left: .5em;
padding-right: .5em;
padding-bottom: .1em;
align-content: center;
justify-content: center;
align-items: center;
box-shadow: inset 0px 0px 0 1px rgba(0, 0, 0, 0.5);
> svg {
width: 16px;
height: 16px;
&:first-child {
margin-right: .3em;
display: none;
}
&:last-child {
margin-left: .5em;
fill: currentColor;
}
}
&[href] {
box-shadow: inset 0px 1px 0px rgba(255,255,255,0.25), inset 0px -1px 0px rgba(0,0,0,0.66);
}
&:empty {
display: none;
}
}
&.-civitai > * {
color: #ddd;
background: #1b65aa;
transition: all 0.15s ease-in-out;
&:hover {
color: #fff;
border-color: #1971c2;
background: #1971c2;
}
}
&.-type > * {
background: rgb(73, 54, 94);
color: rgb(228, 209, 248);
}
}
}
.rgthree-info-table {
border-collapse: collapse;
margin: 16px 0px;
width: 100%;
font-size: 12px;
tr.editable button {
display: flex;
width: 28px;
height: 28px;
align-items: center;
justify-content: center;
svg + svg {display: none;}
}
tr.editable.-rgthree-editing button {
svg {display: none;}
svg + svg {display: inline-block;}
}
td {
position: relative;
border: 1px solid rgba(255,255,255,0.25);
padding: 0;
vertical-align: top;
&:first-child {
background: rgba(255,255,255,0.075);
width: 10px; // Small, so it doesn't adjust.
> *:first-child {
white-space: nowrap;
padding-right: 32px;
}
small {
display: block;
margin-top: 2px;
opacity: 0.75;
> [data-action] {
text-decoration: underline;
cursor: pointer;
&:hover {
text-decoration: none;
}
}
}
}
a, a:hover, a:visited {
color: inherit;
}
svg {
width: 1.3333em;
height: 1.3333em;
vertical-align: -0.285em;
&.logo-civitai {
margin-right: 0.3333em;
}
}
> *:first-child {
display: block;
padding: 6px 10px;
}
> input, > textarea{
padding: 5px 10px;
border: 0;
box-shadow: inset 1px 1px 5px 0px rgba(0,0,0,0.5);
font: inherit;
appearance: none;
background: #fff;
color: #121212;
resize: vertical;
&:only-child {
width: 100%;
}
}
}
tr[data-field-name="userNote"] td > span:first-child {
white-space: pre;
}
tr.rgthree-info-table-break-row td {
border: 0;
background: transparent;
padding: 12px 4px 4px;
font-size: 1.2em;
> small {
font-style: italic;
opacity: 0.66;
}
&:empty {
padding: 4px;
}
}
td .-help {
border: 1px solid currentColor;
position: absolute;
right: 5px;
top: 6px;
line-height: 1;
font-size: 11px;
width: 12px;
height: 12px;
border-radius: 8px;
display: flex;
align-content: center;
justify-content: center;
cursor: help;
&::before {
content: '?';
}
}
td > ul.rgthree-info-trained-words-list {
list-style: none;
padding: 2px 8px;
margin: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
max-height: 15vh;
overflow: auto;
> li {
display: inline-flex;
margin: 2px;
vertical-align: top;
border-radius: 4px;
line-height: 1;
color: rgba(255,255,255,0.85);
background: rgb(73, 91, 106);
font-size: 1.2em;
font-weight: 600;
text-decoration: none;
display: flex;
height: 1.6em;
align-content: center;
justify-content: center;
align-items: center;
box-shadow: inset 0px 0px 0 1px rgba(0, 0, 0, 0.5);
cursor: pointer;
white-space: nowrap;
max-width: 183px;
&:hover {
background: rgb(68, 109, 142);
}
> svg {
width: auto;
height: 1.2em;
}
> span {
padding-left: .5em;
padding-right: .5em;
padding-bottom: .1em;
text-overflow: ellipsis;
overflow: hidden;
}
> small {
align-self: stretch;
display: flex;
align-items: center;
justify-content: center;
padding: 0 0.5em;
background: rgba(0,0,0,0.2);
}
&.-rgthree-is-selected {
background: rgb(42, 126, 193);
}
}
}
}
.rgthree-info-images {
list-style:none;
padding:0;
margin:0;
scroll-snap-type: x mandatory;
display:flex;
flex-direction:row;
overflow: auto;
> li {
scroll-snap-align: start;
max-width: 90%;
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
overflow: hidden;
padding: 8px;
.rgthree-info-table {
width: calc(100% - 16px);
}
}
}
.rgthree-info-civitai-link {
margin: 8px;
color: #eee;
a, a:hover, a:visited {
color: inherit;
text-decoration: none;
}
> svg {
width: 16px;
height: 16px;
margin-right: 8px;
}
}
}
.rgthree-lora-chooser-dialog {
max-width: 100%;
.rgthree-dialog-container-title {
display: flex;
flex-direction: column;
}
.rgthree-dialog-container-title h2 {
display: flex;
width: 100%;
}
.rgthree-lora-chooser-search {
margin-left: auto;
border-radius: 50px;
width: 50%;
max-width: 170px;
padding: 2px 8px;
}
.rgthree-lora-chooser-header {
display: flex;
flex-direction: row;
}
.rgthree-lora-filters-container {
svg {width: 16px; height: 16px;}
}
.rgthree-dialog-container-content {
width: 80vw;
height: 80vh;
}
.rgthree-button-reset {
width: 32px;
height: 32px;
> svg {width: 100%; height: 100%;}
}
ul.rgthree-lora-chooser-list {
list-style: none;
margin: 0;
padding: 0;
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: start;
justify-content: space-around;
> li {
position: relative;
flex: 0 0 auto;
width: 170px;
max-width: 100%;
margin: 8px 8px 16px;
label {
position: absolute;
display: block;
inset: 0;
z-index: 3;
cursor: pointer;
}
input[type="checkbox"] {
position: absolute;
right: 8px;
top: 8px;
margin: 0;
z-index: 2;
appearance: none;
background-color: #fff;
width: 48px;
height: 48px;
border-radius: 4px;
border: 1px solid rgba(120,120,120,1);
opacity: 0;
transition: opacity 0.15s ease-in-out;
&:checked {
opacity: 1;
background: #0060df;
&::before {
content: "";
display: block;
width: 100%;
height: 100%;
box-shadow: inset 100px 100px #fff;
clip-path: polygon(40.13% 68.39%, 23.05% 51.31%, 17.83% 48.26%, 12.61% 49.57%, 9.57% 53.04%, 8% 60%, 34.13% 85.87%, 39.82% 89.57%, 45.88% 86.73%, 90.66% 32.39%, 88.92% 26.1%, 83.03% 22.17%, 76.94% 22.62%)
}
}
}
figure {
position: relative;
display: block;
margin: 0 0 8px;
padding: 0;
border: 1px solid rgba(120, 120, 120, .8);
background: rgba(120, 120, 120, .5);
width: 100%;
padding-top: 120%;
transition: box-shadow 0.15s ease-in-out;
opacity: 0.75;
&::after {
content: '';
display: block;
position: absolute;
inset: 0;
}
&:empty {
&::before {
content: 'No image.';
color: rgba(200, 200, 200, .8);
position: absolute;
display: block;
inset: 0;
font-size: 1.2em;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
}
> img, > video {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
object-fit: cover;
}
}
div {
word-wrap: break-word;
font-size: 0.8rem;
opacity: 0.75;
}
&:hover figure::after{
box-shadow: 0px 2px 6px rgba(0,0,0,0.75);
}
:checked ~ figure::after {
box-shadow: 0 0 5px #fff, 0px 0px 15px rgba(49, 131, 255, 0.88), inset 0 0 3px #fff, inset 0px 0px 5px rgba(49, 131, 255, 0.88)
}
&:hover *,
&:hover input[type="checkbox"],
:checked ~ * {
opacity: 1
}
}
}
}
+7
View File
@@ -25,10 +25,17 @@ export type RgthreeModelInfo = {
name?: string;
type?: string;
baseModel?: string;
baseModelFile?: string;
links?: string[];
strengthMin?: number;
strengthMax?: number;
triggerWords?: string[];
trainedWords?: {
word: string;
count?: number;
civitai?: boolean
user?: boolean
}[];
description?: string;
sha256?: string;
path?: string;
+121 -56
View File
@@ -1,7 +1,25 @@
import { RgthreeDialog } from "../../rgthree/common/dialog.js";
import { createElement as $el, empty, appendChildren, getClosestOrSelf, queryOne, } from "../../rgthree/common/utils_dom.js";
import { logoCivitai, link, pencilColored, diskColored, } from "../../rgthree/common/media/svgs.js";
import { createElement as $el, empty, appendChildren, getClosestOrSelf, queryOne, query, } from "../../rgthree/common/utils_dom.js";
import { logoCivitai, link, pencilColored, diskColored } from "../../rgthree/common/media/svgs.js";
import { SERVICE as MODEL_INFO_SERVICE } from "../../rgthree/common/model_info_service.js";
import { rgthree } from "./rgthree.js";
import { generateId } from "../../rgthree/common/shared_utils.js";
function injectCss() {
const href = "rgthree/common/css/dialog_model_info.css";
if (queryOne(`link[href^="${href}"]`)) {
return Promise.resolve();
}
return new Promise((resolve) => {
const link = $el('link[rel="stylesheet"][type="text/css"]');
const timeout = setTimeout(resolve, 1000);
link.addEventListener("load", (e) => {
clearInterval(timeout);
resolve();
});
link.href = href;
document.head.appendChild(link);
});
}
export class RgthreeInfoDialog extends RgthreeDialog {
constructor(file) {
const dialogOptions = {
@@ -19,7 +37,9 @@ export class RgthreeInfoDialog extends RgthreeDialog {
}
async init(file) {
var _a, _b;
const cssPromise = injectCss();
this.modelInfo = await MODEL_INFO_SERVICE.getLora(file, false, false);
await cssPromise;
this.setContent(this.getInfoContent());
this.setTitle(((_a = this.modelInfo) === null || _a === void 0 ? void 0 : _a["name"]) || ((_b = this.modelInfo) === null || _b === void 0 ? void 0 : _b["file"]) || "Unknown");
this.attachEvents();
@@ -32,57 +52,89 @@ export class RgthreeInfoDialog extends RgthreeDialog {
}
attachEvents() {
this.contentElement.addEventListener("click", async (e) => {
var _a, _b;
const info = this.modelInfo;
if (!(info === null || info === void 0 ? void 0 : info.file)) {
return;
}
const target = getClosestOrSelf(e.target, "[data-action]");
const action = target === null || target === void 0 ? void 0 : target.getAttribute("data-action");
if (action === "fetch-civitai") {
this.modelInfo = await MODEL_INFO_SERVICE.refreshLora(info.file);
this.setContent(this.getInfoContent());
this.setTitle(((_a = this.modelInfo) === null || _a === void 0 ? void 0 : _a["name"]) || ((_b = this.modelInfo) === null || _b === void 0 ? void 0 : _b["file"]) || "Unknown");
}
else if (action === "edit-row") {
const tr = target.closest("tr");
const td = queryOne("td:nth-child(2)", tr);
const input = td.querySelector("input,textarea");
if (!input) {
const fieldName = tr.dataset["fieldName"];
tr.classList.add("-rgthree-editing");
const isTextarea = fieldName === "userNote";
const input = $el(`${isTextarea ? "textarea" : 'input[type="text"]'}`, {
value: td.textContent,
});
input.addEventListener("keydown", (e) => {
if (!isTextarea && e.key === "Enter") {
const modified = saveEditableRow(info, tr, true);
this.modifiedModelData = this.modifiedModelData || modified;
e.stopPropagation();
e.preventDefault();
}
else if (e.key === "Escape") {
const modified = saveEditableRow(info, tr, false);
this.modifiedModelData = this.modifiedModelData || modified;
e.stopPropagation();
e.preventDefault();
}
});
appendChildren(empty(td), [input]);
input.focus();
}
else if (target.nodeName.toLowerCase() === "button") {
const modified = saveEditableRow(info, tr, true);
this.modifiedModelData = this.modifiedModelData || modified;
}
e.preventDefault();
e.stopPropagation();
if (!target || !action) {
return;
}
await this.handleEventAction(action, target, e);
});
}
async handleEventAction(action, target, e) {
var _a, _b;
const info = this.modelInfo;
if (!(info === null || info === void 0 ? void 0 : info.file)) {
return;
}
if (action === "fetch-civitai") {
this.modelInfo = await MODEL_INFO_SERVICE.refreshLora(info.file);
this.setContent(this.getInfoContent());
this.setTitle(((_a = this.modelInfo) === null || _a === void 0 ? void 0 : _a["name"]) || ((_b = this.modelInfo) === null || _b === void 0 ? void 0 : _b["file"]) || "Unknown");
}
else if (action === "copy-trained-words") {
const selected = query(".-rgthree-is-selected", target.closest("tr"));
const text = selected.map((el) => el.getAttribute("data-word")).join(", ");
await navigator.clipboard.writeText(text);
rgthree.showMessage({
id: "copy-trained-words-" + generateId(4),
type: "success",
message: `Successfully copied ${selected.length} key word${selected.length === 1 ? "" : "s"}.`,
timeout: 4000,
});
}
else if (action === "toggle-trained-word") {
target === null || target === void 0 ? void 0 : target.classList.toggle("-rgthree-is-selected");
const tr = target.closest("tr");
if (tr) {
const span = queryOne("td:first-child > *", tr);
let small = queryOne("small", span);
if (!small) {
small = $el("small", { parent: span });
}
const num = query(".-rgthree-is-selected", tr).length;
small.innerHTML = num
? `${num} selected | <span role="button" data-action="copy-trained-words">Copy</span>`
: "";
}
}
else if (action === "edit-row") {
const tr = target.closest("tr");
const td = queryOne("td:nth-child(2)", tr);
const input = td.querySelector("input,textarea");
if (!input) {
const fieldName = tr.dataset["fieldName"];
tr.classList.add("-rgthree-editing");
const isTextarea = fieldName === "userNote";
const input = $el(`${isTextarea ? "textarea" : 'input[type="text"]'}`, {
value: td.textContent,
});
input.addEventListener("keydown", (e) => {
if (!isTextarea && e.key === "Enter") {
const modified = saveEditableRow(info, tr, true);
this.modifiedModelData = this.modifiedModelData || modified;
e.stopPropagation();
e.preventDefault();
}
else if (e.key === "Escape") {
const modified = saveEditableRow(info, tr, false);
this.modifiedModelData = this.modifiedModelData || modified;
e.stopPropagation();
e.preventDefault();
}
});
appendChildren(empty(td), [input]);
input.focus();
}
else if (target.nodeName.toLowerCase() === "button") {
const modified = saveEditableRow(info, tr, true);
this.modifiedModelData = this.modifiedModelData || modified;
}
e === null || e === void 0 ? void 0 : e.preventDefault();
e === null || e === void 0 ? void 0 : e.stopPropagation();
}
}
getInfoContent() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
const info = this.modelInfo || {};
const civitaiLink = (_a = info.links) === null || _a === void 0 ? void 0 : _a.find((i) => i.includes("civitai.com/models"));
const html = `
@@ -109,18 +161,18 @@ export class RgthreeInfoDialog extends RgthreeDialog {
: !((_h = info.raw) === null || _h === void 0 ? void 0 : _h.civitai)
? infoTableRow("Civitai", `<button data-action="fetch-civitai">Fetch info from civitai</button>`)
: ""}
${infoTableRow("Base Model", info.baseModel || "")}
${infoTableRow("Base Model", (info.baseModel || " ") + (info.baseModelFile ? `(${info.baseModelFile})` : ""))}
${infoTableRow("Name", info.name || ((_k = (_j = info.raw) === null || _j === void 0 ? void 0 : _j.metadata) === null || _k === void 0 ? void 0 : _k.ss_output_name) || "", "The name for display.", "name")}
${infoTableRow("Name", info.name || "", "The name for display.", "name")}
${infoTableRow("Trigger Words", (_k = (_j = info.triggerWords) === null || _j === void 0 ? void 0 : _j.join(", ")) !== null && _k !== void 0 ? _k : "", "Easily keep track of the trigger words to reference here.", "triggerWords")}
${infoTableRow("Strength Min", (_l = info.strengthMin) !== null && _l !== void 0 ? _l : "", "The recommended minimum strength, In the Power Lora Loader node, strength will signal when it is below this threshold.", "strengthMin")}
${infoTableRow("Strength Max", (_m = info.strengthMax) !== null && _m !== void 0 ? _m : "", "The recommended maximum strength. In the Power Lora Loader node, strength will signal when it is above this threshold.", "strengthMax")}
${infoTableRow("Trained Words", (_l = getTrainedWordsMarkup(info.trainedWords)) !== null && _l !== void 0 ? _l : "", "Trained words from the metadata and/or civitai. Click to select for copy.")}
${infoTableRow("Strength Min", (_m = info.strengthMin) !== null && _m !== void 0 ? _m : "", "The recommended minimum strength, In the Power Lora Loader node, strength will signal when it is below this threshold.", "strengthMin")}
${infoTableRow("Strength Max", (_o = info.strengthMax) !== null && _o !== void 0 ? _o : "", "The recommended maximum strength. In the Power Lora Loader node, strength will signal when it is above this threshold.", "strengthMax")}
${""}
${infoTableRow("Additional Notes", (_o = info.userNote) !== null && _o !== void 0 ? _o : "", "Additional notes you'd like to keep and reference in the info dialog.", "userNote")}
${infoTableRow("Additional Notes", (_p = info.userNote) !== null && _p !== void 0 ? _p : "", "Additional notes you'd like to keep and reference in the info dialog.", "userNote")}
</table>
<ul class="rgthree-info-images">${(_q = (_p = info.images) === null || _p === void 0 ? void 0 : _p.map((img) => {
<ul class="rgthree-info-images">${(_r = (_q = info.images) === null || _q === void 0 ? void 0 : _q.map((img) => {
var _a;
return `
<li>
@@ -146,7 +198,7 @@ export class RgthreeInfoDialog extends RgthreeDialog {
: ""}
</table-->
</li>`;
}).join("")) !== null && _q !== void 0 ? _q : ""}</ul>
}).join("")) !== null && _r !== void 0 ? _r : ""}</ul>
`;
const div = $el("div", { html });
return div;
@@ -156,12 +208,25 @@ function infoTableRow(name, value, help = "", editableFieldName = "") {
return `
<tr class="${editableFieldName ? "editable" : ""}" ${editableFieldName ? `data-field-name="${editableFieldName}"` : ""}>
<td><span>${name} ${help ? `<span class="-help" title="${help}"></span>` : ""}<span></td>
<td ${editableFieldName ? "" : 'colspan="2"'}><span>${value}</span></td>
<td ${editableFieldName ? "" : 'colspan="2"'}>${String(value).startsWith("<") ? value : `<span>${value}<span>`}</td>
${editableFieldName
? `<td style="width: 24px;"><button class="rgthree-button-reset rgthree-button-edit" data-action="edit-row">${pencilColored}${diskColored}</button></td>`
: ""}
</tr>`;
}
function getTrainedWordsMarkup(words) {
let markup = `<ul class="rgthree-info-trained-words-list">`;
for (const wordData of words || []) {
markup += `<li title="${wordData.word}" data-word="${wordData.word}" class="rgthree-info-trained-words-list-item" data-action="toggle-trained-word">
<span>${wordData.word}</span>
${wordData.civitai ? logoCivitai : ""}
${wordData.count != null ? `<small>${wordData.count}</small>` : ""}
</li>`;
}
markup += `</ul>`;
console.log(markup);
return markup;
}
function saveEditableRow(info, tr, saving = true) {
var _a;
const fieldName = tr.dataset["fieldName"];
-353
View File
@@ -336,359 +336,6 @@ body.rgthree-dialog-open > *:not(.rgthree-dialog):not(.rgthree-top-messages-cont
filter: blur(5px);
}
.rgthree-info-dialog {
min-width: 600px;
}
.rgthree-info-dialog .rgthree-info-tags {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.rgthree-info-dialog .rgthree-info-tags > li {
display: inline-flex;
margin: 0;
vertical-align: top;
}
.rgthree-info-dialog .rgthree-info-tags > li + li {
margin-left: 6px;
}
.rgthree-info-dialog .rgthree-info-tags > li:not(.-link) + li.-link {
margin-left: auto;
}
.rgthree-info-dialog .rgthree-info-tags > li > * {
border-radius: 4px;
line-height: 1;
color: rgba(255, 255, 255, 0.85);
background: rgb(69, 92, 85);
font-size: 14px;
font-weight: bold;
text-decoration: none;
display: flex;
height: 1.6em;
padding-left: 0.5em;
padding-right: 0.5em;
padding-bottom: 0.1em;
align-content: center;
justify-content: center;
align-items: center;
box-shadow: inset 0px 0px 0 1px rgba(0, 0, 0, 0.5);
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg {
width: 16px;
height: 16px;
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg:first-child {
margin-right: 0.3em;
display: none;
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg:last-child {
margin-left: 0.5em;
fill: currentColor;
}
.rgthree-info-dialog .rgthree-info-tags > li > *[href] {
box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.25), inset 0px -1px 0px rgba(0, 0, 0, 0.66);
}
.rgthree-info-dialog .rgthree-info-tags > li > *:empty {
display: none;
}
.rgthree-info-dialog .rgthree-info-tags > li.-civitai > * {
color: #ddd;
background: #1b65aa;
transition: all 0.15s ease-in-out;
}
.rgthree-info-dialog .rgthree-info-tags > li.-civitai > *:hover {
color: #fff;
border-color: #1971c2;
background: #1971c2;
}
.rgthree-info-dialog .rgthree-info-tags > li.-type > * {
background: rgb(73, 54, 94);
color: rgb(228, 209, 248);
}
.rgthree-info-dialog .rgthree-info-table {
border-collapse: collapse;
margin: 16px 0px;
width: 100%;
font-size: 12px;
}
.rgthree-info-dialog .rgthree-info-table tr.editable button {
display: flex;
width: 28px;
height: 28px;
align-items: center;
justify-content: center;
}
.rgthree-info-dialog .rgthree-info-table tr.editable button svg + svg {
display: none;
}
.rgthree-info-dialog .rgthree-info-table tr.editable.-rgthree-editing button svg {
display: none;
}
.rgthree-info-dialog .rgthree-info-table tr.editable.-rgthree-editing button svg + svg {
display: inline-block;
}
.rgthree-info-dialog .rgthree-info-table td {
position: relative;
border: 1px solid rgba(255, 255, 255, 0.25);
padding: 0;
}
.rgthree-info-dialog .rgthree-info-table td:first-child {
background: rgba(255, 255, 255, 0.075);
width: 10px;
}
.rgthree-info-dialog .rgthree-info-table td:first-child > *:first-child {
white-space: nowrap;
padding-right: 32px;
}
.rgthree-info-dialog .rgthree-info-table td a, .rgthree-info-dialog .rgthree-info-table td a:hover, .rgthree-info-dialog .rgthree-info-table td a:visited {
color: inherit;
}
.rgthree-info-dialog .rgthree-info-table td ul {
margin: 0.5em 0;
padding: 0 0 0 1em;
}
.rgthree-info-dialog .rgthree-info-table td svg {
width: 1.3333em;
height: 1.3333em;
vertical-align: -0.285em;
}
.rgthree-info-dialog .rgthree-info-table td svg.logo-civitai {
margin-right: 0.3333em;
}
.rgthree-info-dialog .rgthree-info-table td > *:first-child {
display: block;
padding: 6px 10px;
}
.rgthree-info-dialog .rgthree-info-table td > input, .rgthree-info-dialog .rgthree-info-table td > textarea {
padding: 5px 10px;
border: 0;
box-shadow: inset 1px 1px 5px 0px rgba(0, 0, 0, 0.5);
font: inherit;
appearance: none;
background: #fff;
color: #121212;
resize: vertical;
}
.rgthree-info-dialog .rgthree-info-table td > input:only-child, .rgthree-info-dialog .rgthree-info-table td > textarea:only-child {
width: 100%;
}
.rgthree-info-dialog .rgthree-info-table tr[data-field-name=userNote] td > span:first-child {
white-space: pre;
}
.rgthree-info-dialog .rgthree-info-table tr.rgthree-info-table-break-row td {
border: 0;
background: transparent;
padding: 12px 4px 4px;
font-size: 1.2em;
}
.rgthree-info-dialog .rgthree-info-table tr.rgthree-info-table-break-row td > small {
font-style: italic;
opacity: 0.66;
}
.rgthree-info-dialog .rgthree-info-table tr.rgthree-info-table-break-row td:empty {
padding: 4px;
}
.rgthree-info-dialog .rgthree-info-table td .-help {
border: 1px solid currentColor;
position: absolute;
right: 5px;
top: 50%;
margin-top: -7px;
line-height: 1;
font-size: 11px;
width: 12px;
height: 12px;
border-radius: 8px;
display: flex;
align-content: center;
justify-content: center;
cursor: help;
}
.rgthree-info-dialog .rgthree-info-table td .-help::before {
content: "?";
}
.rgthree-info-dialog .rgthree-info-images {
list-style: none;
padding: 0;
margin: 0;
scroll-snap-type: x mandatory;
display: flex;
flex-direction: row;
overflow: auto;
}
.rgthree-info-dialog .rgthree-info-images > li {
scroll-snap-align: start;
max-width: 90%;
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
overflow: hidden;
padding: 8px;
}
.rgthree-info-dialog .rgthree-info-images > li .rgthree-info-table {
width: calc(100% - 16px);
}
.rgthree-info-dialog .rgthree-info-civitai-link {
margin: 8px;
color: #eee;
}
.rgthree-info-dialog .rgthree-info-civitai-link a, .rgthree-info-dialog .rgthree-info-civitai-link a:hover, .rgthree-info-dialog .rgthree-info-civitai-link a:visited {
color: inherit;
text-decoration: none;
}
.rgthree-info-dialog .rgthree-info-civitai-link > svg {
width: 16px;
height: 16px;
margin-right: 8px;
}
.rgthree-lora-chooser-dialog {
max-width: 100%;
}
.rgthree-lora-chooser-dialog .rgthree-dialog-container-title {
display: flex;
flex-direction: column;
}
.rgthree-lora-chooser-dialog .rgthree-dialog-container-title h2 {
display: flex;
width: 100%;
}
.rgthree-lora-chooser-dialog .rgthree-lora-chooser-search {
margin-left: auto;
border-radius: 50px;
width: 50%;
max-width: 170px;
padding: 2px 8px;
}
.rgthree-lora-chooser-dialog .rgthree-lora-chooser-header {
display: flex;
flex-direction: row;
}
.rgthree-lora-chooser-dialog .rgthree-lora-filters-container svg {
width: 16px;
height: 16px;
}
.rgthree-lora-chooser-dialog .rgthree-dialog-container-content {
width: 80vw;
height: 80vh;
}
.rgthree-lora-chooser-dialog .rgthree-button-reset {
width: 32px;
height: 32px;
}
.rgthree-lora-chooser-dialog .rgthree-button-reset > svg {
width: 100%;
height: 100%;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list {
list-style: none;
margin: 0;
padding: 0;
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: start;
justify-content: space-around;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li {
position: relative;
flex: 0 0 auto;
width: 170px;
max-width: 100%;
margin: 8px 8px 16px;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li label {
position: absolute;
display: block;
inset: 0;
z-index: 3;
cursor: pointer;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li input[type=checkbox] {
position: absolute;
right: 8px;
top: 8px;
margin: 0;
z-index: 2;
appearance: none;
background-color: #fff;
width: 48px;
height: 48px;
border-radius: 4px;
border: 1px solid rgb(120, 120, 120);
opacity: 0;
transition: opacity 0.15s ease-in-out;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li input[type=checkbox]:checked {
opacity: 1;
background: #0060df;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li input[type=checkbox]:checked::before {
content: "";
display: block;
width: 100%;
height: 100%;
box-shadow: inset 100px 100px #fff;
clip-path: polygon(40.13% 68.39%, 23.05% 51.31%, 17.83% 48.26%, 12.61% 49.57%, 9.57% 53.04%, 8% 60%, 34.13% 85.87%, 39.82% 89.57%, 45.88% 86.73%, 90.66% 32.39%, 88.92% 26.1%, 83.03% 22.17%, 76.94% 22.62%);
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure {
position: relative;
display: block;
margin: 0 0 8px;
padding: 0;
border: 1px solid rgba(120, 120, 120, 0.8);
background: rgba(120, 120, 120, 0.5);
width: 100%;
padding-top: 120%;
transition: box-shadow 0.15s ease-in-out;
opacity: 0.75;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure::after {
content: "";
display: block;
position: absolute;
inset: 0;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure:empty::before {
content: "No image.";
color: rgba(200, 200, 200, 0.8);
position: absolute;
display: block;
inset: 0;
font-size: 1.2em;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure > img, .rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure > video {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
object-fit: cover;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li div {
word-wrap: break-word;
font-size: 0.8rem;
opacity: 0.75;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li:hover figure::after {
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.75);
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li :checked ~ figure::after {
box-shadow: 0 0 5px #fff, 0px 0px 15px rgba(49, 131, 255, 0.88), inset 0 0 3px #fff, inset 0px 0px 5px rgba(49, 131, 255, 0.88);
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li:hover *, .rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li:hover input[type=checkbox],
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li :checked ~ * {
opacity: 1;
}
.rgthree-dialog.-settings {
width: 100%;
}
+9
View File
@@ -8,6 +8,7 @@ import { NodeTypesString } from "./constants.js";
import { RgthreeProgressBar } from "../../rgthree/common/progress_bar.js";
import { RgthreeConfigDialog } from "./config.js";
import { iconGear, iconReplace, iconStarFilled, logoRgthree } from "../../rgthree/common/media/svgs.js";
import { query } from "../../rgthree/common/utils_dom.js";
export var LogLevel;
(function (LogLevel) {
LogLevel[LogLevel["IMPORTANT"] = 1] = "IMPORTANT";
@@ -539,6 +540,14 @@ class Rgthree extends EventTarget {
container.classList.add("rgthree-top-messages-container");
document.body.appendChild(container);
}
const dialogs = query('dialog[open]');
if (dialogs.length) {
let dialog = dialogs[dialogs.length - 1];
dialog.appendChild(container);
dialog.addEventListener('close', (e) => {
document.body.appendChild(container);
});
}
await this.hideMessage(data.id);
const messageContainer = document.createElement("div");
messageContainer.setAttribute("type", data.type || "info");
-353
View File
@@ -122,356 +122,3 @@
body.rgthree-dialog-open > *:not(.rgthree-dialog):not(.rgthree-top-messages-container) {
filter: blur(5px);
}
.rgthree-info-dialog {
min-width: 600px;
}
.rgthree-info-dialog .rgthree-info-tags {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.rgthree-info-dialog .rgthree-info-tags > li {
display: inline-flex;
margin: 0;
vertical-align: top;
}
.rgthree-info-dialog .rgthree-info-tags > li + li {
margin-left: 6px;
}
.rgthree-info-dialog .rgthree-info-tags > li:not(.-link) + li.-link {
margin-left: auto;
}
.rgthree-info-dialog .rgthree-info-tags > li > * {
border-radius: 4px;
line-height: 1;
color: rgba(255, 255, 255, 0.85);
background: rgb(69, 92, 85);
font-size: 14px;
font-weight: bold;
text-decoration: none;
display: flex;
height: 1.6em;
padding-left: 0.5em;
padding-right: 0.5em;
padding-bottom: 0.1em;
align-content: center;
justify-content: center;
align-items: center;
box-shadow: inset 0px 0px 0 1px rgba(0, 0, 0, 0.5);
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg {
width: 16px;
height: 16px;
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg:first-child {
margin-right: 0.3em;
display: none;
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg:last-child {
margin-left: 0.5em;
fill: currentColor;
}
.rgthree-info-dialog .rgthree-info-tags > li > *[href] {
box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.25), inset 0px -1px 0px rgba(0, 0, 0, 0.66);
}
.rgthree-info-dialog .rgthree-info-tags > li > *:empty {
display: none;
}
.rgthree-info-dialog .rgthree-info-tags > li.-civitai > * {
color: #ddd;
background: #1b65aa;
transition: all 0.15s ease-in-out;
}
.rgthree-info-dialog .rgthree-info-tags > li.-civitai > *:hover {
color: #fff;
border-color: #1971c2;
background: #1971c2;
}
.rgthree-info-dialog .rgthree-info-tags > li.-type > * {
background: rgb(73, 54, 94);
color: rgb(228, 209, 248);
}
.rgthree-info-dialog .rgthree-info-table {
border-collapse: collapse;
margin: 16px 0px;
width: 100%;
font-size: 12px;
}
.rgthree-info-dialog .rgthree-info-table tr.editable button {
display: flex;
width: 28px;
height: 28px;
align-items: center;
justify-content: center;
}
.rgthree-info-dialog .rgthree-info-table tr.editable button svg + svg {
display: none;
}
.rgthree-info-dialog .rgthree-info-table tr.editable.-rgthree-editing button svg {
display: none;
}
.rgthree-info-dialog .rgthree-info-table tr.editable.-rgthree-editing button svg + svg {
display: inline-block;
}
.rgthree-info-dialog .rgthree-info-table td {
position: relative;
border: 1px solid rgba(255, 255, 255, 0.25);
padding: 0;
}
.rgthree-info-dialog .rgthree-info-table td:first-child {
background: rgba(255, 255, 255, 0.075);
width: 10px;
}
.rgthree-info-dialog .rgthree-info-table td:first-child > *:first-child {
white-space: nowrap;
padding-right: 32px;
}
.rgthree-info-dialog .rgthree-info-table td a, .rgthree-info-dialog .rgthree-info-table td a:hover, .rgthree-info-dialog .rgthree-info-table td a:visited {
color: inherit;
}
.rgthree-info-dialog .rgthree-info-table td ul {
margin: 0.5em 0;
padding: 0 0 0 1em;
}
.rgthree-info-dialog .rgthree-info-table td svg {
width: 1.3333em;
height: 1.3333em;
vertical-align: -0.285em;
}
.rgthree-info-dialog .rgthree-info-table td svg.logo-civitai {
margin-right: 0.3333em;
}
.rgthree-info-dialog .rgthree-info-table td > *:first-child {
display: block;
padding: 6px 10px;
}
.rgthree-info-dialog .rgthree-info-table td > input, .rgthree-info-dialog .rgthree-info-table td > textarea {
padding: 5px 10px;
border: 0;
box-shadow: inset 1px 1px 5px 0px rgba(0, 0, 0, 0.5);
font: inherit;
appearance: none;
background: #fff;
color: #121212;
resize: vertical;
}
.rgthree-info-dialog .rgthree-info-table td > input:only-child, .rgthree-info-dialog .rgthree-info-table td > textarea:only-child {
width: 100%;
}
.rgthree-info-dialog .rgthree-info-table tr[data-field-name=userNote] td > span:first-child {
white-space: pre;
}
.rgthree-info-dialog .rgthree-info-table tr.rgthree-info-table-break-row td {
border: 0;
background: transparent;
padding: 12px 4px 4px;
font-size: 1.2em;
}
.rgthree-info-dialog .rgthree-info-table tr.rgthree-info-table-break-row td > small {
font-style: italic;
opacity: 0.66;
}
.rgthree-info-dialog .rgthree-info-table tr.rgthree-info-table-break-row td:empty {
padding: 4px;
}
.rgthree-info-dialog .rgthree-info-table td .-help {
border: 1px solid currentColor;
position: absolute;
right: 5px;
top: 50%;
margin-top: -7px;
line-height: 1;
font-size: 11px;
width: 12px;
height: 12px;
border-radius: 8px;
display: flex;
align-content: center;
justify-content: center;
cursor: help;
}
.rgthree-info-dialog .rgthree-info-table td .-help::before {
content: "?";
}
.rgthree-info-dialog .rgthree-info-images {
list-style: none;
padding: 0;
margin: 0;
scroll-snap-type: x mandatory;
display: flex;
flex-direction: row;
overflow: auto;
}
.rgthree-info-dialog .rgthree-info-images > li {
scroll-snap-align: start;
max-width: 90%;
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
overflow: hidden;
padding: 8px;
}
.rgthree-info-dialog .rgthree-info-images > li .rgthree-info-table {
width: calc(100% - 16px);
}
.rgthree-info-dialog .rgthree-info-civitai-link {
margin: 8px;
color: #eee;
}
.rgthree-info-dialog .rgthree-info-civitai-link a, .rgthree-info-dialog .rgthree-info-civitai-link a:hover, .rgthree-info-dialog .rgthree-info-civitai-link a:visited {
color: inherit;
text-decoration: none;
}
.rgthree-info-dialog .rgthree-info-civitai-link > svg {
width: 16px;
height: 16px;
margin-right: 8px;
}
.rgthree-lora-chooser-dialog {
max-width: 100%;
}
.rgthree-lora-chooser-dialog .rgthree-dialog-container-title {
display: flex;
flex-direction: column;
}
.rgthree-lora-chooser-dialog .rgthree-dialog-container-title h2 {
display: flex;
width: 100%;
}
.rgthree-lora-chooser-dialog .rgthree-lora-chooser-search {
margin-left: auto;
border-radius: 50px;
width: 50%;
max-width: 170px;
padding: 2px 8px;
}
.rgthree-lora-chooser-dialog .rgthree-lora-chooser-header {
display: flex;
flex-direction: row;
}
.rgthree-lora-chooser-dialog .rgthree-lora-filters-container svg {
width: 16px;
height: 16px;
}
.rgthree-lora-chooser-dialog .rgthree-dialog-container-content {
width: 80vw;
height: 80vh;
}
.rgthree-lora-chooser-dialog .rgthree-button-reset {
width: 32px;
height: 32px;
}
.rgthree-lora-chooser-dialog .rgthree-button-reset > svg {
width: 100%;
height: 100%;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list {
list-style: none;
margin: 0;
padding: 0;
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: start;
justify-content: space-around;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li {
position: relative;
flex: 0 0 auto;
width: 170px;
max-width: 100%;
margin: 8px 8px 16px;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li label {
position: absolute;
display: block;
inset: 0;
z-index: 3;
cursor: pointer;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li input[type=checkbox] {
position: absolute;
right: 8px;
top: 8px;
margin: 0;
z-index: 2;
appearance: none;
background-color: #fff;
width: 48px;
height: 48px;
border-radius: 4px;
border: 1px solid rgb(120, 120, 120);
opacity: 0;
transition: opacity 0.15s ease-in-out;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li input[type=checkbox]:checked {
opacity: 1;
background: #0060df;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li input[type=checkbox]:checked::before {
content: "";
display: block;
width: 100%;
height: 100%;
box-shadow: inset 100px 100px #fff;
clip-path: polygon(40.13% 68.39%, 23.05% 51.31%, 17.83% 48.26%, 12.61% 49.57%, 9.57% 53.04%, 8% 60%, 34.13% 85.87%, 39.82% 89.57%, 45.88% 86.73%, 90.66% 32.39%, 88.92% 26.1%, 83.03% 22.17%, 76.94% 22.62%);
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure {
position: relative;
display: block;
margin: 0 0 8px;
padding: 0;
border: 1px solid rgba(120, 120, 120, 0.8);
background: rgba(120, 120, 120, 0.5);
width: 100%;
padding-top: 120%;
transition: box-shadow 0.15s ease-in-out;
opacity: 0.75;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure::after {
content: "";
display: block;
position: absolute;
inset: 0;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure:empty::before {
content: "No image.";
color: rgba(200, 200, 200, 0.8);
position: absolute;
display: block;
inset: 0;
font-size: 1.2em;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure > img, .rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure > video {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
object-fit: cover;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li div {
word-wrap: break-word;
font-size: 0.8rem;
opacity: 0.75;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li:hover figure::after {
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.75);
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li :checked ~ figure::after {
box-shadow: 0 0 5px #fff, 0px 0px 15px rgba(49, 131, 255, 0.88), inset 0 0 3px #fff, inset 0px 0px 5px rgba(49, 131, 255, 0.88);
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li:hover *, .rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li:hover input[type=checkbox],
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li :checked ~ * {
opacity: 1;
}
+417
View File
@@ -0,0 +1,417 @@
.rgthree-info-dialog {
width: 90vw;
max-width: 960px;
}
.rgthree-info-dialog .rgthree-info-tags {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.rgthree-info-dialog .rgthree-info-tags > li {
display: inline-flex;
margin: 0;
vertical-align: top;
}
.rgthree-info-dialog .rgthree-info-tags > li + li {
margin-left: 6px;
}
.rgthree-info-dialog .rgthree-info-tags > li:not(.-link) + li.-link {
margin-left: auto;
}
.rgthree-info-dialog .rgthree-info-tags > li > * {
border-radius: 4px;
line-height: 1;
color: rgba(255, 255, 255, 0.85);
background: rgb(69, 92, 85);
font-size: 14px;
font-weight: bold;
text-decoration: none;
display: flex;
height: 1.6em;
padding-left: 0.5em;
padding-right: 0.5em;
padding-bottom: 0.1em;
align-content: center;
justify-content: center;
align-items: center;
box-shadow: inset 0px 0px 0 1px rgba(0, 0, 0, 0.5);
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg {
width: 16px;
height: 16px;
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg:first-child {
margin-right: 0.3em;
display: none;
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg:last-child {
margin-left: 0.5em;
fill: currentColor;
}
.rgthree-info-dialog .rgthree-info-tags > li > *[href] {
box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.25), inset 0px -1px 0px rgba(0, 0, 0, 0.66);
}
.rgthree-info-dialog .rgthree-info-tags > li > *:empty {
display: none;
}
.rgthree-info-dialog .rgthree-info-tags > li.-civitai > * {
color: #ddd;
background: #1b65aa;
transition: all 0.15s ease-in-out;
}
.rgthree-info-dialog .rgthree-info-tags > li.-civitai > *:hover {
color: #fff;
border-color: #1971c2;
background: #1971c2;
}
.rgthree-info-dialog .rgthree-info-tags > li.-type > * {
background: rgb(73, 54, 94);
color: rgb(228, 209, 248);
}
.rgthree-info-dialog .rgthree-info-table {
border-collapse: collapse;
margin: 16px 0px;
width: 100%;
font-size: 12px;
}
.rgthree-info-dialog .rgthree-info-table tr.editable button {
display: flex;
width: 28px;
height: 28px;
align-items: center;
justify-content: center;
}
.rgthree-info-dialog .rgthree-info-table tr.editable button svg + svg {
display: none;
}
.rgthree-info-dialog .rgthree-info-table tr.editable.-rgthree-editing button svg {
display: none;
}
.rgthree-info-dialog .rgthree-info-table tr.editable.-rgthree-editing button svg + svg {
display: inline-block;
}
.rgthree-info-dialog .rgthree-info-table td {
position: relative;
border: 1px solid rgba(255, 255, 255, 0.25);
padding: 0;
vertical-align: top;
}
.rgthree-info-dialog .rgthree-info-table td:first-child {
background: rgba(255, 255, 255, 0.075);
width: 10px;
}
.rgthree-info-dialog .rgthree-info-table td:first-child > *:first-child {
white-space: nowrap;
padding-right: 32px;
}
.rgthree-info-dialog .rgthree-info-table td:first-child small {
display: block;
margin-top: 2px;
opacity: 0.75;
}
.rgthree-info-dialog .rgthree-info-table td:first-child small > [data-action] {
text-decoration: underline;
cursor: pointer;
}
.rgthree-info-dialog .rgthree-info-table td:first-child small > [data-action]:hover {
text-decoration: none;
}
.rgthree-info-dialog .rgthree-info-table td a, .rgthree-info-dialog .rgthree-info-table td a:hover, .rgthree-info-dialog .rgthree-info-table td a:visited {
color: inherit;
}
.rgthree-info-dialog .rgthree-info-table td svg {
width: 1.3333em;
height: 1.3333em;
vertical-align: -0.285em;
}
.rgthree-info-dialog .rgthree-info-table td svg.logo-civitai {
margin-right: 0.3333em;
}
.rgthree-info-dialog .rgthree-info-table td > *:first-child {
display: block;
padding: 6px 10px;
}
.rgthree-info-dialog .rgthree-info-table td > input, .rgthree-info-dialog .rgthree-info-table td > textarea {
padding: 5px 10px;
border: 0;
box-shadow: inset 1px 1px 5px 0px rgba(0, 0, 0, 0.5);
font: inherit;
appearance: none;
background: #fff;
color: #121212;
resize: vertical;
}
.rgthree-info-dialog .rgthree-info-table td > input:only-child, .rgthree-info-dialog .rgthree-info-table td > textarea:only-child {
width: 100%;
}
.rgthree-info-dialog .rgthree-info-table tr[data-field-name=userNote] td > span:first-child {
white-space: pre;
}
.rgthree-info-dialog .rgthree-info-table tr.rgthree-info-table-break-row td {
border: 0;
background: transparent;
padding: 12px 4px 4px;
font-size: 1.2em;
}
.rgthree-info-dialog .rgthree-info-table tr.rgthree-info-table-break-row td > small {
font-style: italic;
opacity: 0.66;
}
.rgthree-info-dialog .rgthree-info-table tr.rgthree-info-table-break-row td:empty {
padding: 4px;
}
.rgthree-info-dialog .rgthree-info-table td .-help {
border: 1px solid currentColor;
position: absolute;
right: 5px;
top: 6px;
line-height: 1;
font-size: 11px;
width: 12px;
height: 12px;
border-radius: 8px;
display: flex;
align-content: center;
justify-content: center;
cursor: help;
}
.rgthree-info-dialog .rgthree-info-table td .-help::before {
content: "?";
}
.rgthree-info-dialog .rgthree-info-table td > ul.rgthree-info-trained-words-list {
list-style: none;
padding: 2px 8px;
margin: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
max-height: 15vh;
overflow: auto;
}
.rgthree-info-dialog .rgthree-info-table td > ul.rgthree-info-trained-words-list > li {
display: inline-flex;
margin: 2px;
vertical-align: top;
border-radius: 4px;
line-height: 1;
color: rgba(255, 255, 255, 0.85);
background: rgb(73, 91, 106);
font-size: 1.2em;
font-weight: 600;
text-decoration: none;
display: flex;
height: 1.6em;
align-content: center;
justify-content: center;
align-items: center;
box-shadow: inset 0px 0px 0 1px rgba(0, 0, 0, 0.5);
cursor: pointer;
white-space: nowrap;
max-width: 183px;
}
.rgthree-info-dialog .rgthree-info-table td > ul.rgthree-info-trained-words-list > li:hover {
background: rgb(68, 109, 142);
}
.rgthree-info-dialog .rgthree-info-table td > ul.rgthree-info-trained-words-list > li > svg {
width: auto;
height: 1.2em;
}
.rgthree-info-dialog .rgthree-info-table td > ul.rgthree-info-trained-words-list > li > span {
padding-left: 0.5em;
padding-right: 0.5em;
padding-bottom: 0.1em;
text-overflow: ellipsis;
overflow: hidden;
}
.rgthree-info-dialog .rgthree-info-table td > ul.rgthree-info-trained-words-list > li > small {
align-self: stretch;
display: flex;
align-items: center;
justify-content: center;
padding: 0 0.5em;
background: rgba(0, 0, 0, 0.2);
}
.rgthree-info-dialog .rgthree-info-table td > ul.rgthree-info-trained-words-list > li.-rgthree-is-selected {
background: rgb(42, 126, 193);
}
.rgthree-info-dialog .rgthree-info-images {
list-style: none;
padding: 0;
margin: 0;
scroll-snap-type: x mandatory;
display: flex;
flex-direction: row;
overflow: auto;
}
.rgthree-info-dialog .rgthree-info-images > li {
scroll-snap-align: start;
max-width: 90%;
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
overflow: hidden;
padding: 8px;
}
.rgthree-info-dialog .rgthree-info-images > li .rgthree-info-table {
width: calc(100% - 16px);
}
.rgthree-info-dialog .rgthree-info-civitai-link {
margin: 8px;
color: #eee;
}
.rgthree-info-dialog .rgthree-info-civitai-link a, .rgthree-info-dialog .rgthree-info-civitai-link a:hover, .rgthree-info-dialog .rgthree-info-civitai-link a:visited {
color: inherit;
text-decoration: none;
}
.rgthree-info-dialog .rgthree-info-civitai-link > svg {
width: 16px;
height: 16px;
margin-right: 8px;
}
.rgthree-lora-chooser-dialog {
max-width: 100%;
}
.rgthree-lora-chooser-dialog .rgthree-dialog-container-title {
display: flex;
flex-direction: column;
}
.rgthree-lora-chooser-dialog .rgthree-dialog-container-title h2 {
display: flex;
width: 100%;
}
.rgthree-lora-chooser-dialog .rgthree-lora-chooser-search {
margin-left: auto;
border-radius: 50px;
width: 50%;
max-width: 170px;
padding: 2px 8px;
}
.rgthree-lora-chooser-dialog .rgthree-lora-chooser-header {
display: flex;
flex-direction: row;
}
.rgthree-lora-chooser-dialog .rgthree-lora-filters-container svg {
width: 16px;
height: 16px;
}
.rgthree-lora-chooser-dialog .rgthree-dialog-container-content {
width: 80vw;
height: 80vh;
}
.rgthree-lora-chooser-dialog .rgthree-button-reset {
width: 32px;
height: 32px;
}
.rgthree-lora-chooser-dialog .rgthree-button-reset > svg {
width: 100%;
height: 100%;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list {
list-style: none;
margin: 0;
padding: 0;
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: start;
justify-content: space-around;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li {
position: relative;
flex: 0 0 auto;
width: 170px;
max-width: 100%;
margin: 8px 8px 16px;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li label {
position: absolute;
display: block;
inset: 0;
z-index: 3;
cursor: pointer;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li input[type=checkbox] {
position: absolute;
right: 8px;
top: 8px;
margin: 0;
z-index: 2;
appearance: none;
background-color: #fff;
width: 48px;
height: 48px;
border-radius: 4px;
border: 1px solid rgb(120, 120, 120);
opacity: 0;
transition: opacity 0.15s ease-in-out;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li input[type=checkbox]:checked {
opacity: 1;
background: #0060df;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li input[type=checkbox]:checked::before {
content: "";
display: block;
width: 100%;
height: 100%;
box-shadow: inset 100px 100px #fff;
clip-path: polygon(40.13% 68.39%, 23.05% 51.31%, 17.83% 48.26%, 12.61% 49.57%, 9.57% 53.04%, 8% 60%, 34.13% 85.87%, 39.82% 89.57%, 45.88% 86.73%, 90.66% 32.39%, 88.92% 26.1%, 83.03% 22.17%, 76.94% 22.62%);
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure {
position: relative;
display: block;
margin: 0 0 8px;
padding: 0;
border: 1px solid rgba(120, 120, 120, 0.8);
background: rgba(120, 120, 120, 0.5);
width: 100%;
padding-top: 120%;
transition: box-shadow 0.15s ease-in-out;
opacity: 0.75;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure::after {
content: "";
display: block;
position: absolute;
inset: 0;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure:empty::before {
content: "No image.";
color: rgba(200, 200, 200, 0.8);
position: absolute;
display: block;
inset: 0;
font-size: 1.2em;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure > img, .rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li figure > video {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
object-fit: cover;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li div {
word-wrap: break-word;
font-size: 0.8rem;
opacity: 0.75;
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li:hover figure::after {
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.75);
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li :checked ~ figure::after {
box-shadow: 0 0 5px #fff, 0px 0px 15px rgba(49, 131, 255, 0.88), inset 0 0 3px #fff, inset 0px 0px 5px rgba(49, 131, 255, 0.88);
}
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li:hover *, .rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li:hover input[type=checkbox],
.rgthree-lora-chooser-dialog ul.rgthree-lora-chooser-list > li :checked ~ * {
opacity: 1;
}