Some cleanup and new menu class.

This commit is contained in:
rgthree
2024-05-17 23:21:42 -04:00
parent 2fb14983b1
commit 5f86b99bc3
18 changed files with 572 additions and 97 deletions
+59 -17
View File
@@ -6,12 +6,15 @@ import {
getClosestOrSelf,
queryOne,
query,
setAttributes,
} from "rgthree/common/utils_dom.js";
import { logoCivitai, link, pencilColored, diskColored } from "rgthree/common/media/svgs.js";
import { logoCivitai, link, pencilColored, diskColored, dotdotdot } 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 { MenuButton } from "rgthree/common/menu.js";
import { generateId } from "rgthree/common/shared_utils.js";
import { rgthreeApi } from "rgthree/common/rgthree_api.js";
function injectCss(): Promise<void> {
const href = "rgthree/common/css/dialog_model_info.css";
@@ -152,20 +155,20 @@ export class RgthreeInfoDialog extends RgthreeDialog {
const info = this.modelInfo || {};
const civitaiLink = info.links?.find((i) => i.includes("civitai.com/models"));
const html = `
<ul class="rgthree-info-tags">
<li title="Type" class="-type -type-${(info.type || "").toLowerCase()}"><span>${
<ul class="rgthree-info-area">
<li title="Type" class="rgthree-info-tag -type -type-${(info.type || "").toLowerCase()}"><span>${
info.type || ""
}</span></li>
<li title="Base Model" class="-basemodel -basemodel-${(
<li title="Base Model" class="rgthree-info-tag -basemodel -basemodel-${(
info.baseModel || ""
).toLowerCase()}"><span>${info.baseModel || ""}</span></li>
<li stub="menu"></li>
${
!civitaiLink
? ""
: `
<li title="Visit on Civitai" class="-link -civitai"><a href="${civitaiLink}" target="_blank">${logoCivitai} Civitai ${link}</a></li>
`
<li class="rgthree-info-menu" stub="menu"></li>
${''
// !civitaiLink
// ? ""
// : `
// <li title="Visit on Civitai" class="-link -civitai"><a href="${civitaiLink}" target="_blank">Civitai ${link}</a></li>
// `
}
</ul>
@@ -188,14 +191,11 @@ export class RgthreeInfoDialog extends RgthreeDialog {
: !info.raw?.civitai
? infoTableRow(
"Civitai",
`<button data-action="fetch-civitai">Fetch info from civitai</button>`,
`<button class="rgthree-button" data-action="fetch-civitai">Fetch info from civitai</button>`,
)
: ""
}
${infoTableRow(
"Base Model",
(info.baseModel || " ") + (info.baseModelFile ? `(${info.baseModelFile})` : ""),
)}
${infoTableRow(
"Name",
info.name || info.raw?.metadata?.ss_output_name || "",
@@ -203,11 +203,22 @@ export class RgthreeInfoDialog extends RgthreeDialog {
"name",
)}
${infoTableRow(
${!info.baseModelFile && !info.baseModelFile ? '' : infoTableRow(
"Base Model",
(info.baseModel || "") + (info.baseModelFile ? ` (${info.baseModelFile})` : ""),
)}
${!info.trainedWords?.length ? '' : infoTableRow(
"Trained Words",
getTrainedWordsMarkup(info.trainedWords) ?? "",
"Trained words from the metadata and/or civitai. Click to select for copy.",
)}
${!info.raw?.metadata.ss_clip_skip || info.raw?.metadata.ss_clip_skip == 'None' ? '' : infoTableRow(
"Clip Skip",
info.raw?.metadata.ss_clip_skip
)}
${infoTableRow(
"Strength Min",
info.strengthMin ?? "",
@@ -277,6 +288,37 @@ export class RgthreeInfoDialog extends RgthreeDialog {
const div = $el("div", { html });
if (rgthree.isDevMode()) {
setAttributes(queryOne('[stub="menu"]', div)!, {
children: [
new MenuButton({
icon: dotdotdot,
options: [
{ label: "More Actions", type: "title" },
{
label: "Open API JSON",
callback: async (e: PointerEvent) => {
if (this.modelInfo?.file) {
window.open(`rgthree/api/loras/info?file=${encodeURIComponent(this.modelInfo.file)}`);
}
},
},
{
label: "Clear all local info",
callback: async (e: PointerEvent) => {
if (this.modelInfo?.file) {
this.modelInfo = await MODEL_INFO_SERVICE.clearLoraFetchedData(this.modelInfo.file);
this.setContent(this.getInfoContent());
this.setTitle(this.modelInfo?.["name"] || this.modelInfo?.["file"] || "Unknown");
}
},
},
],
}),
],
});
}
return div;
}
}
+1
View File
@@ -115,6 +115,7 @@
@import '../common/css/buttons.scss';
@import '../common/css/dialog.scss';
@import '../common/css/menu.scss';
.rgthree-dialog.-settings {
width: 100%;
+3
View File
@@ -990,6 +990,9 @@ class Rgthree extends EventTarget {
}
isDevMode() {
if (window.location.href.includes("#rgthree-dev=false")) {
return false;
}
return GLOBAL_LOG_LEVEL >= LogLevel.DEBUG || window.location.href.includes("#rgthree-dev");
}
+4 -1
View File
@@ -25,9 +25,12 @@ export async function showLoraChooser(
event: PointerEvent,
callback: ContextMenuEventListener,
parentMenu?: ContextMenu,
loras?: string[],
) {
const canvas = app.canvas as TLGraphCanvas;
const loras = ["None", ...(await rgthreeApi.getLoras())];
if (!loras) {
loras = ["None", ...(await rgthreeApi.getLoras())];
}
new LiteGraph.ContextMenu(loras, {
event: event,
parentMenu,
+35 -17
View File
@@ -4,7 +4,7 @@
width: 90vw;
max-width: 960px;
.rgthree-info-tags {
.rgthree-info-area {
list-style: none;
padding: 0;
margin: 0;
@@ -22,7 +22,8 @@
margin-left: auto;
}
> * {
&.rgthree-info-tag > * {
min-height: 24px;
border-radius: 4px;
line-height: 1;
color: rgba(255,255,255,0.85);
@@ -44,13 +45,8 @@
width: 16px;
height: 16px;
&:first-child {
margin-right: .3em;
display: none;
}
&:last-child {
margin-left: .5em;
fill: currentColor;
}
}
@@ -64,20 +60,35 @@
}
}
&.-civitai > * {
color: #ddd;
background: #1b65aa;
transition: all 0.15s ease-in-out;
&:hover {
color: #fff;
border-color: #1971c2;
background: #1971c2;
}
}
// &.-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-menu {
margin-left: auto;
:not(#fakeid) & .rgthree-button {
margin: 0;
min-height: 24px;
padding: 0 12px;
}
svg {
width: 16px;
height: 16px;
}
}
}
}
@@ -148,6 +159,7 @@
display: block;
padding: 6px 10px;
}
> input, > textarea{
padding: 5px 10px;
border: 0;
@@ -162,6 +174,12 @@
width: 100%;
}
}
:not(#fakeid) & .rgthree-button[data-action="fetch-civitai"] {
font-size: inherit;
padding: 6px 16px;
margin: 2px;
}
}
tr[data-field-name="userNote"] td > span:first-child {
+43
View File
@@ -0,0 +1,43 @@
.rgthree-menu {
list-style: none;
padding: 0;
margin: 0;
position: fixed;
z-index: 999999;
pointer-events: none;
opacity: 0;
transition: opacity 0.08s ease-in-out;
color: #dde;
background-color: #111;
font-size: 12px;
box-shadow: 0 0 10px black !important;
> li {
position: relative;
padding: 4px 6px;
z-index: 9999;
white-space: nowrap;
&[role="button"] {
background-color: var(--comfy-menu-bg) !important;
color: var(--input-text);
cursor: pointer;
&:hover {
filter: brightness(155%);
}
}
}
&[state^="measuring"] {
display: block;
opacity: 0;
}
&[state="open"] {
display: block;
opacity: 1;
pointer-events: all;
}
}
+130
View File
@@ -0,0 +1,130 @@
import { generateId, wait } from "./shared_utils.js";
import { createElement as $el, getClosestOrSelf, setAttributes} from "./utils_dom.js";
/**
* A menu. Mimics the comfy menu.
*/
class Menu {
private element: HTMLMenuElement = $el('menu.rgthree-menu');
private callbacks: Map<string, (e: PointerEvent) => Promise<boolean|void>> = new Map();
private handleWindowPointerDownBound = this.handleWindowPointerDown.bind(this);
constructor(options: MenuOption[]) {
this.setOptions(options);
this.element.addEventListener('pointerup', async (e) => {
const target = getClosestOrSelf(e.target as HTMLElement, "[data-callback],menu");
if (e.which !== 1) {
return;
}
const callback = target?.dataset?.['callback'];
if (callback) {
const halt = await this.callbacks.get(callback)?.(e);
if (halt !== false) {
this.close();
}
}
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
});
}
setOptions(options: MenuOption[]) {
for (const option of options) {
if (option.type === 'title') {
this.element.appendChild($el(`li`, {
html: option.label
}));
} else {
const id = generateId(8);
this.callbacks.set(id, async (e: PointerEvent) => { return option?.callback?.(e); });
this.element.appendChild($el(`li[role="button"][data-callback="${id}"]`, {
html: option.label
}));
}
}
}
toElement() {
return this.element;
}
async open(e: PointerEvent) {
const parent = (e.target as HTMLElement).closest('div,dialog,body') as HTMLElement
parent.appendChild(this.element);
setAttributes(this.element, {
style: {
left: `${e.clientX + 16}px`,
top: `${e.clientY - 16}px`,
}
});
this.element.setAttribute('state', 'measuring-open');
await wait(16);
const rect = this.element.getBoundingClientRect();
if (rect.right > window.innerWidth) {
this.element.style.left = `${e.clientX - rect.width - 16}px`;
await wait(16);
}
this.element.setAttribute('state', 'open');
setTimeout(() => {
window.addEventListener('pointerdown', this.handleWindowPointerDownBound);
});
}
handleWindowPointerDown(e:PointerEvent) {
if (!this.element.contains(e.target as HTMLElement)) {
this.close();
}
}
async close() {
window.removeEventListener('pointerdown', this.handleWindowPointerDownBound);
this.element.setAttribute('state', 'measuring-closed');
await wait(16);
this.element.setAttribute('state', 'closed');
this.element.remove();
}
isOpen() {
return (this.element.getAttribute('state') || '').includes('open');
}
}
type MenuOption = {
label: string;
type?: 'title'|'item'|'separator';
callback?: (e: PointerEvent) => void;
}
type MenuButtonOptions = {
icon: string;
options: MenuOption[];
}
export class MenuButton {
private options: MenuButtonOptions;
private menu: Menu;
private element: HTMLButtonElement = $el('button.rgthree-button[data-action="open-menu"]')
constructor(options: MenuButtonOptions) {
this.options = options;
this.element.innerHTML = options.icon;
this.menu = new Menu(options.options);
this.element.addEventListener('pointerdown', (e) => {
if (!this.menu.isOpen()) {
this.menu.open(e);
}
});
}
toElement() {
return this.element;
}
}
+7 -1
View File
@@ -19,7 +19,7 @@ class ModelInfoService {
if (!refresh) {
info = await rgthreeApi.getLoraInfo(file, light);
} else {
info = await rgthreeApi.refreshLoraInfo(file);
info = await rgthreeApi.refreshLorasInfo(file);
}
if (!light) {
this.loraToInfo.set(file, info);
@@ -31,6 +31,12 @@ class ModelInfoService {
return this.fetchLora(file, true);
}
async clearLoraFetchedData(file: string) {
await rgthreeApi.clearLorasInfo(file);
this.loraToInfo.delete(file);
return null;
}
async saveLoraPartial(file: string, data: Partial<RgthreeModelInfo>) {
let info = await rgthreeApi.saveLoraInfo(file, data);
this.loraToInfo.set(file, info);
+13 -8
View File
@@ -59,12 +59,6 @@ class RgthreeApi {
);
}
async refreshLoraInfo(lora: string): Promise<RgthreeModelInfo | null> {
return await this.fetchApiJsonOrNull<RgthreeModelInfo>(
`/loras/info/refresh?file=${encodeURIComponent(lora)}`,
);
}
async saveLoraInfo(
lora: string,
data: Partial<RgthreeModelInfo>,
@@ -81,9 +75,20 @@ class RgthreeApi {
return await this.fetchApiJsonOrNull<RgthreeModelInfo[]>(`/loras/info`);
}
async refreshLorasInfo(): Promise<RgthreeModelInfo[] | null> {
return await this.fetchApiJsonOrNull<RgthreeModelInfo[]>(`/loras/info/refresh`);
async refreshLorasInfo(file: string): Promise<RgthreeModelInfo | null>;
async refreshLorasInfo(): Promise<RgthreeModelInfo[] | null>;
async refreshLorasInfo(file?: string) {
const path = `/loras/info/refresh` + (file ? `?file=${encodeURIComponent(file)}` : '');
const infos = await this.fetchApiJsonOrNull<RgthreeModelInfo[]|RgthreeModelInfo>(path);
return infos;
}
async clearLorasInfo(file?: string): Promise<void> {
const path = `/loras/info/clear` + (file ? `?file=${encodeURIComponent(file)}` : '');
await this.fetchApiJsonOrNull<RgthreeModelInfo[]>(path);
return;
}
}
export const rgthreeApi = new RgthreeApi();
+54 -20
View File
@@ -1,8 +1,9 @@
import { RgthreeDialog } from "../../rgthree/common/dialog.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 { createElement as $el, empty, appendChildren, getClosestOrSelf, queryOne, query, setAttributes, } from "../../rgthree/common/utils_dom.js";
import { logoCivitai, pencilColored, diskColored, dotdotdot } 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 { MenuButton } from "../../rgthree/common/menu.js";
import { generateId } from "../../rgthree/common/shared_utils.js";
function injectCss() {
const href = "rgthree/common/css/dialog_model_info.css";
@@ -134,19 +135,15 @@ export class RgthreeInfoDialog extends RgthreeDialog {
}
}
getInfoContent() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
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 = `
<ul class="rgthree-info-tags">
<li title="Type" class="-type -type-${(info.type || "").toLowerCase()}"><span>${info.type || ""}</span></li>
<li title="Base Model" class="-basemodel -basemodel-${(info.baseModel || "").toLowerCase()}"><span>${info.baseModel || ""}</span></li>
<li stub="menu"></li>
${!civitaiLink
? ""
: `
<li title="Visit on Civitai" class="-link -civitai"><a href="${civitaiLink}" target="_blank">${logoCivitai} Civitai ${link}</a></li>
`}
<ul class="rgthree-info-area">
<li title="Type" class="rgthree-info-tag -type -type-${(info.type || "").toLowerCase()}"><span>${info.type || ""}</span></li>
<li title="Base Model" class="rgthree-info-tag -basemodel -basemodel-${(info.baseModel || "").toLowerCase()}"><span>${info.baseModel || ""}</span></li>
<li class="rgthree-info-menu" stub="menu"></li>
${''}
</ul>
<table class="rgthree-info-table">
@@ -159,20 +156,25 @@ export class RgthreeInfoDialog extends RgthreeDialog {
: ((_e = (_d = info.raw) === null || _d === void 0 ? void 0 : _d.civitai) === null || _e === void 0 ? void 0 : _e.error)
? infoTableRow("Civitai", (_g = (_f = info.raw) === null || _f === void 0 ? void 0 : _f.civitai) === null || _g === void 0 ? void 0 : _g.error)
: !((_h = info.raw) === null || _h === void 0 ? void 0 : _h.civitai)
? infoTableRow("Civitai", `<button data-action="fetch-civitai">Fetch info from civitai</button>`)
? infoTableRow("Civitai", `<button class="rgthree-button" data-action="fetch-civitai">Fetch info from civitai</button>`)
: ""}
${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("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")}
${!info.baseModelFile && !info.baseModelFile ? '' : infoTableRow("Base Model", (info.baseModel || "") + (info.baseModelFile ? ` (${info.baseModelFile})` : ""))}
${!((_l = info.trainedWords) === null || _l === void 0 ? void 0 : _l.length) ? '' : infoTableRow("Trained Words", (_m = getTrainedWordsMarkup(info.trainedWords)) !== null && _m !== void 0 ? _m : "", "Trained words from the metadata and/or civitai. Click to select for copy.")}
${!((_o = info.raw) === null || _o === void 0 ? void 0 : _o.metadata.ss_clip_skip) || ((_p = info.raw) === null || _p === void 0 ? void 0 : _p.metadata.ss_clip_skip) == 'None' ? '' : infoTableRow("Clip Skip", (_q = info.raw) === null || _q === void 0 ? void 0 : _q.metadata.ss_clip_skip)}
${infoTableRow("Strength Min", (_r = info.strengthMin) !== null && _r !== void 0 ? _r : "", "The recommended minimum strength, In the Power Lora Loader node, strength will signal when it is below this threshold.", "strengthMin")}
${infoTableRow("Strength Max", (_s = info.strengthMax) !== null && _s !== void 0 ? _s : "", "The recommended maximum strength. In the Power Lora Loader node, strength will signal when it is above this threshold.", "strengthMax")}
${""}
${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")}
${infoTableRow("Additional Notes", (_t = info.userNote) !== null && _t !== void 0 ? _t : "", "Additional notes you'd like to keep and reference in the info dialog.", "userNote")}
</table>
<ul class="rgthree-info-images">${(_r = (_q = info.images) === null || _q === void 0 ? void 0 : _q.map((img) => {
<ul class="rgthree-info-images">${(_v = (_u = info.images) === null || _u === void 0 ? void 0 : _u.map((img) => {
var _a;
return `
<li>
@@ -198,9 +200,41 @@ export class RgthreeInfoDialog extends RgthreeDialog {
: ""}
</table-->
</li>`;
}).join("")) !== null && _r !== void 0 ? _r : ""}</ul>
}).join("")) !== null && _v !== void 0 ? _v : ""}</ul>
`;
const div = $el("div", { html });
if (rgthree.isDevMode()) {
setAttributes(queryOne('[stub="menu"]', div), {
children: [
new MenuButton({
icon: dotdotdot,
options: [
{ label: "More Actions", type: "title" },
{
label: "Open API JSON",
callback: async (e) => {
var _a;
if ((_a = this.modelInfo) === null || _a === void 0 ? void 0 : _a.file) {
window.open(`rgthree/api/loras/info?file=${encodeURIComponent(this.modelInfo.file)}`);
}
},
},
{
label: "Clear all local info",
callback: async (e) => {
var _a, _b, _c;
if ((_a = this.modelInfo) === null || _a === void 0 ? void 0 : _a.file) {
this.modelInfo = await MODEL_INFO_SERVICE.clearLoraFetchedData(this.modelInfo.file);
this.setContent(this.getInfoContent());
this.setTitle(((_b = this.modelInfo) === null || _b === void 0 ? void 0 : _b["name"]) || ((_c = this.modelInfo) === null || _c === void 0 ? void 0 : _c["file"]) || "Unknown");
}
},
},
],
}),
],
});
}
return div;
}
}
+38
View File
@@ -336,6 +336,44 @@ body.rgthree-dialog-open > *:not(.rgthree-dialog):not(.rgthree-top-messages-cont
filter: blur(5px);
}
.rgthree-menu {
list-style: none;
padding: 0;
margin: 0;
position: fixed;
z-index: 999999;
pointer-events: none;
opacity: 0;
transition: opacity 0.08s ease-in-out;
color: #dde;
background-color: #111;
font-size: 12px;
box-shadow: 0 0 10px black !important;
}
.rgthree-menu > li {
position: relative;
padding: 4px 6px;
z-index: 9999;
white-space: nowrap;
}
.rgthree-menu > li[role=button] {
background-color: var(--comfy-menu-bg) !important;
color: var(--input-text);
cursor: pointer;
}
.rgthree-menu > li[role=button]:hover {
filter: brightness(155%);
}
.rgthree-menu[state^=measuring] {
display: block;
opacity: 0;
}
.rgthree-menu[state=open] {
display: block;
opacity: 1;
pointer-events: all;
}
.rgthree-dialog.-settings {
width: 100%;
}
+3
View File
@@ -675,6 +675,9 @@ class Rgthree extends EventTarget {
return this.logger.newSession(name);
}
isDevMode() {
if (window.location.href.includes("#rgthree-dev=false")) {
return false;
}
return GLOBAL_LOG_LEVEL >= LogLevel.DEBUG || window.location.href.includes("#rgthree-dev");
}
monitorBadLinks() {
+4 -2
View File
@@ -3,10 +3,12 @@ import { rgthreeApi } from "../../rgthree/common/rgthree_api.js";
const PASS_THROUGH = function (item) {
return item;
};
export async function showLoraChooser(event, callback, parentMenu) {
export async function showLoraChooser(event, callback, parentMenu, loras) {
var _a, _b;
const canvas = app.canvas;
const loras = ["None", ...(await rgthreeApi.getLoras())];
if (!loras) {
loras = ["None", ...(await rgthreeApi.getLoras())];
}
new LiteGraph.ContextMenu(loras, {
event: event,
parentMenu,
+28 -25
View File
@@ -2,24 +2,25 @@
width: 90vw;
max-width: 960px;
}
.rgthree-info-dialog .rgthree-info-tags {
.rgthree-info-dialog .rgthree-info-area {
list-style: none;
padding: 0;
margin: 0;
display: flex;
}
.rgthree-info-dialog .rgthree-info-tags > li {
.rgthree-info-dialog .rgthree-info-area > li {
display: inline-flex;
margin: 0;
vertical-align: top;
}
.rgthree-info-dialog .rgthree-info-tags > li + li {
.rgthree-info-dialog .rgthree-info-area > li + li {
margin-left: 6px;
}
.rgthree-info-dialog .rgthree-info-tags > li:not(.-link) + li.-link {
.rgthree-info-dialog .rgthree-info-area > li:not(.-link) + li.-link {
margin-left: auto;
}
.rgthree-info-dialog .rgthree-info-tags > li > * {
.rgthree-info-dialog .rgthree-info-area > li.rgthree-info-tag > * {
min-height: 24px;
border-radius: 4px;
line-height: 1;
color: rgba(255, 255, 255, 0.85);
@@ -37,38 +38,35 @@
align-items: center;
box-shadow: inset 0px 0px 0 1px rgba(0, 0, 0, 0.5);
}
.rgthree-info-dialog .rgthree-info-tags > li > * > svg {
.rgthree-info-dialog .rgthree-info-area > li.rgthree-info-tag > * > 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 {
.rgthree-info-dialog .rgthree-info-area > li.rgthree-info-tag > * > svg:last-child {
margin-left: 0.5em;
fill: currentColor;
}
.rgthree-info-dialog .rgthree-info-tags > li > *[href] {
.rgthree-info-dialog .rgthree-info-area > li.rgthree-info-tag > *[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 {
.rgthree-info-dialog .rgthree-info-area > li.rgthree-info-tag > *: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 > * {
.rgthree-info-dialog .rgthree-info-area > li.-type > * {
background: rgb(73, 54, 94);
color: rgb(228, 209, 248);
}
.rgthree-info-dialog .rgthree-info-area > li.rgthree-info-menu {
margin-left: auto;
}
:not(#fakeid) .rgthree-info-dialog .rgthree-info-area > li.rgthree-info-menu .rgthree-button {
margin: 0;
min-height: 24px;
padding: 0 12px;
}
.rgthree-info-dialog .rgthree-info-area > li.rgthree-info-menu svg {
width: 16px;
height: 16px;
}
.rgthree-info-dialog .rgthree-info-table {
border-collapse: collapse;
margin: 16px 0px;
@@ -145,6 +143,11 @@
.rgthree-info-dialog .rgthree-info-table td > input:only-child, .rgthree-info-dialog .rgthree-info-table td > textarea:only-child {
width: 100%;
}
:not(#fakeid) .rgthree-info-dialog .rgthree-info-table td .rgthree-button[data-action=fetch-civitai] {
font-size: inherit;
padding: 6px 16px;
margin: 2px;
}
.rgthree-info-dialog .rgthree-info-table tr[data-field-name=userNote] td > span:first-child {
white-space: pre;
}
+37
View File
@@ -0,0 +1,37 @@
.rgthree-menu {
list-style: none;
padding: 0;
margin: 0;
position: fixed;
z-index: 999999;
pointer-events: none;
opacity: 0;
transition: opacity 0.08s ease-in-out;
color: #dde;
background-color: #111;
font-size: 12px;
box-shadow: 0 0 10px black !important;
}
.rgthree-menu > li {
position: relative;
padding: 4px 6px;
z-index: 9999;
white-space: nowrap;
}
.rgthree-menu > li[role=button] {
background-color: var(--comfy-menu-bg) !important;
color: var(--input-text);
cursor: pointer;
}
.rgthree-menu > li[role=button]:hover {
filter: brightness(155%);
}
.rgthree-menu[state^=measuring] {
display: block;
opacity: 0;
}
.rgthree-menu[state=open] {
display: block;
opacity: 1;
pointer-events: all;
}
+98
View File
@@ -0,0 +1,98 @@
import { generateId, wait } from "./shared_utils.js";
import { createElement as $el, getClosestOrSelf, setAttributes } from "./utils_dom.js";
class Menu {
constructor(options) {
this.element = $el('menu.rgthree-menu');
this.callbacks = new Map();
this.handleWindowPointerDownBound = this.handleWindowPointerDown.bind(this);
this.setOptions(options);
this.element.addEventListener('pointerup', async (e) => {
var _a, _b;
const target = getClosestOrSelf(e.target, "[data-callback],menu");
if (e.which !== 1) {
return;
}
const callback = (_a = target === null || target === void 0 ? void 0 : target.dataset) === null || _a === void 0 ? void 0 : _a['callback'];
if (callback) {
const halt = await ((_b = this.callbacks.get(callback)) === null || _b === void 0 ? void 0 : _b(e));
if (halt !== false) {
this.close();
}
}
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
});
}
setOptions(options) {
for (const option of options) {
if (option.type === 'title') {
this.element.appendChild($el(`li`, {
html: option.label
}));
}
else {
const id = generateId(8);
this.callbacks.set(id, async (e) => { var _a; return (_a = option === null || option === void 0 ? void 0 : option.callback) === null || _a === void 0 ? void 0 : _a.call(option, e); });
this.element.appendChild($el(`li[role="button"][data-callback="${id}"]`, {
html: option.label
}));
}
}
}
toElement() {
return this.element;
}
async open(e) {
const parent = e.target.closest('div,dialog,body');
parent.appendChild(this.element);
setAttributes(this.element, {
style: {
left: `${e.clientX + 16}px`,
top: `${e.clientY - 16}px`,
}
});
this.element.setAttribute('state', 'measuring-open');
await wait(16);
const rect = this.element.getBoundingClientRect();
if (rect.right > window.innerWidth) {
this.element.style.left = `${e.clientX - rect.width - 16}px`;
await wait(16);
}
this.element.setAttribute('state', 'open');
setTimeout(() => {
window.addEventListener('pointerdown', this.handleWindowPointerDownBound);
});
}
handleWindowPointerDown(e) {
if (!this.element.contains(e.target)) {
this.close();
}
}
async close() {
window.removeEventListener('pointerdown', this.handleWindowPointerDownBound);
this.element.setAttribute('state', 'measuring-closed');
await wait(16);
this.element.setAttribute('state', 'closed');
this.element.remove();
}
isOpen() {
return (this.element.getAttribute('state') || '').includes('open');
}
}
export class MenuButton {
constructor(options) {
this.element = $el('button.rgthree-button[data-action="open-menu"]');
this.options = options;
this.element.innerHTML = options.icon;
this.menu = new Menu(options.options);
this.element.addEventListener('pointerdown', (e) => {
if (!this.menu.isOpen()) {
this.menu.open(e);
}
});
}
toElement() {
return this.element;
}
}
+6 -1
View File
@@ -15,7 +15,7 @@ class ModelInfoService {
info = await rgthreeApi.getLoraInfo(file, light);
}
else {
info = await rgthreeApi.refreshLoraInfo(file);
info = await rgthreeApi.refreshLorasInfo(file);
}
if (!light) {
this.loraToInfo.set(file, info);
@@ -25,6 +25,11 @@ class ModelInfoService {
async refreshLora(file) {
return this.fetchLora(file, true);
}
async clearLoraFetchedData(file) {
await rgthreeApi.clearLorasInfo(file);
this.loraToInfo.delete(file);
return null;
}
async saveLoraPartial(file, data) {
let info = await rgthreeApi.saveLoraInfo(file, data);
this.loraToInfo.set(file, info);
+9 -5
View File
@@ -38,9 +38,6 @@ class RgthreeApi {
async getLoraInfo(lora, light = true) {
return await this.fetchApiJsonOrNull(`/loras/info?file=${encodeURIComponent(lora)}&light=${light ? 1 : 0}`, { cache: "no-store" });
}
async refreshLoraInfo(lora) {
return await this.fetchApiJsonOrNull(`/loras/info/refresh?file=${encodeURIComponent(lora)}`);
}
async saveLoraInfo(lora, data) {
const body = new FormData();
body.append("json", JSON.stringify(data));
@@ -49,8 +46,15 @@ class RgthreeApi {
async getLorasInfo() {
return await this.fetchApiJsonOrNull(`/loras/info`);
}
async refreshLorasInfo() {
return await this.fetchApiJsonOrNull(`/loras/info/refresh`);
async refreshLorasInfo(file) {
const path = `/loras/info/refresh` + (file ? `?file=${encodeURIComponent(file)}` : '');
const infos = await this.fetchApiJsonOrNull(path);
return infos;
}
async clearLorasInfo(file) {
const path = `/loras/info/clear` + (file ? `?file=${encodeURIComponent(file)}` : '');
await this.fetchApiJsonOrNull(path);
return;
}
}
export const rgthreeApi = new RgthreeApi();