mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-06-09 12:39:23 -03:00
fix(ui): keep autocomplete text widget at max-height on node resize in Vue mode
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
:spellcheck="spellcheck ?? false"
|
||||
:class="['text-input', { 'vue-dom-mode': isVueDomMode, 'lm-wheel-scrollable': isVueDomMode }]"
|
||||
:style="maxHeight && isVueDomMode ? { maxHeight: maxHeight + 'px' } : undefined"
|
||||
data-capture-wheel="true"
|
||||
@input="onInput"
|
||||
@wheel="onWheel"
|
||||
/>
|
||||
|
||||
@@ -546,6 +546,27 @@ function normalizeAutocompleteWidgetValues(node: any, info: any) {
|
||||
}
|
||||
}
|
||||
|
||||
function applyAutocompleteTextLayoutFix(
|
||||
widget: any,
|
||||
container: HTMLElement | undefined,
|
||||
isVueMode: boolean
|
||||
): void {
|
||||
if (isVueMode) {
|
||||
;(widget as any).computeLayoutSize = undefined
|
||||
widget.computeSize = (width?: number) =>
|
||||
[width ?? 200, AUTOCOMPLETE_TEXT_WIDGET_MAX_HEIGHT - 4]
|
||||
if (container) {
|
||||
container.style.minHeight = `${AUTOCOMPLETE_TEXT_WIDGET_MAX_HEIGHT}px`
|
||||
}
|
||||
} else {
|
||||
delete (widget as any).computeLayoutSize
|
||||
delete (widget as any).computeSize
|
||||
if (container) {
|
||||
container.style.minHeight = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for Vue DOM mode setting changes and dispatch custom event
|
||||
const initVueDomModeListener = () => {
|
||||
if (app.ui?.settings?.addEventListener) {
|
||||
@@ -554,7 +575,47 @@ const initVueDomModeListener = () => {
|
||||
// before we read it (the event may fire before internal state updates)
|
||||
requestAnimationFrame(() => {
|
||||
const isVueDomMode = app.ui?.settings?.getSettingValue?.('Comfy.VueNodes.Enabled') ?? false
|
||||
// Dispatch custom event for Vue components to listen to
|
||||
|
||||
if (app.graph?.nodes) {
|
||||
for (const node of app.graph.nodes) {
|
||||
const textWidget = node.widgets?.find(
|
||||
(w: any) => w.type === 'AUTOCOMPLETE_TEXT_LORAS'
|
||||
)
|
||||
if (!textWidget) continue
|
||||
const container = (textWidget as any).element as HTMLElement | undefined
|
||||
applyAutocompleteTextLayoutFix(textWidget, container, isVueDomMode)
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
for (const nodeEl of document.querySelectorAll('[data-node-id]')) {
|
||||
const grid = nodeEl.querySelector('[data-testid="node-widgets"]') as HTMLElement | null
|
||||
if (!grid) continue
|
||||
const nodeId = nodeEl.getAttribute('data-node-id')
|
||||
const node = app.graph?.getNodeById(nodeId as any)
|
||||
if (!node) continue
|
||||
const rows: string[] = []
|
||||
let needsFix = false
|
||||
for (const w of node.widgets ?? []) {
|
||||
if (w.type === 'LORA_MANAGER_AUTOCOMPLETE_METADATA') {
|
||||
rows.push('min-content')
|
||||
} else if (w.name === 'loras') {
|
||||
rows.push('auto')
|
||||
} else if (w.name === 'text' && w.type === 'AUTOCOMPLETE_TEXT_LORAS') {
|
||||
rows.push(isVueDomMode ? 'min-content' : 'auto')
|
||||
needsFix = true
|
||||
} else {
|
||||
rows.push('auto')
|
||||
}
|
||||
}
|
||||
if (needsFix) {
|
||||
grid.style.gridTemplateRows = rows.join(' ')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.canvas?.setDirty(true, true)
|
||||
|
||||
document.dispatchEvent(new CustomEvent('lora-manager:vue-mode-change', {
|
||||
detail: { isVueDomMode }
|
||||
}))
|
||||
@@ -678,6 +739,15 @@ function createAutocompleteTextWidgetFactory(
|
||||
|
||||
if (maxHeight) {
|
||||
container.style.maxHeight = `${maxHeight}px`
|
||||
container.style.minHeight = `${maxHeight}px`
|
||||
}
|
||||
|
||||
if (modelType === 'loras') {
|
||||
applyAutocompleteTextLayoutFix(
|
||||
widget,
|
||||
container,
|
||||
typeof LiteGraph !== 'undefined' && LiteGraph.vueNodesMode
|
||||
)
|
||||
}
|
||||
|
||||
widget.onRemove = createVueWidgetCleanup(vueApp, () => {
|
||||
|
||||
@@ -2118,14 +2118,14 @@ to { transform: rotate(360deg);
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.autocomplete-text-widget[data-v-1c610e5d] {
|
||||
.autocomplete-text-widget[data-v-8555b560] {
|
||||
background: transparent;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.input-wrapper[data-v-1c610e5d] {
|
||||
.input-wrapper[data-v-8555b560] {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@@ -2133,7 +2133,7 @@ to { transform: rotate(360deg);
|
||||
}
|
||||
|
||||
/* Canvas mode styles (default) - matches built-in comfy-multiline-input */
|
||||
.text-input[data-v-1c610e5d] {
|
||||
.text-input[data-v-8555b560] {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
background-color: var(--comfy-input-bg, #222);
|
||||
@@ -2150,7 +2150,7 @@ to { transform: rotate(360deg);
|
||||
}
|
||||
|
||||
/* Vue DOM mode styles - matches built-in p-textarea in Vue DOM mode */
|
||||
.text-input.vue-dom-mode[data-v-1c610e5d] {
|
||||
.text-input.vue-dom-mode[data-v-8555b560] {
|
||||
background-color: var(--color-charcoal-400, #313235);
|
||||
color: #fff;
|
||||
padding: 8px 12px 30px 12px; /* Reserve bottom space for clear button */
|
||||
@@ -2159,12 +2159,12 @@ to { transform: rotate(360deg);
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
}
|
||||
.text-input[data-v-1c610e5d]:focus {
|
||||
.text-input[data-v-8555b560]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Clear button styles */
|
||||
.clear-button[data-v-1c610e5d] {
|
||||
.clear-button[data-v-8555b560] {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
bottom: 6px; /* Changed from top to bottom */
|
||||
@@ -2187,31 +2187,31 @@ to { transform: rotate(360deg);
|
||||
}
|
||||
|
||||
/* Show clear button when hovering over input wrapper */
|
||||
.input-wrapper:hover .clear-button[data-v-1c610e5d] {
|
||||
.input-wrapper:hover .clear-button[data-v-8555b560] {
|
||||
opacity: 0.7;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.clear-button[data-v-1c610e5d]:hover {
|
||||
.clear-button[data-v-8555b560]:hover {
|
||||
opacity: 1;
|
||||
background: rgba(255, 100, 100, 0.8);
|
||||
}
|
||||
.clear-button svg[data-v-1c610e5d] {
|
||||
.clear-button svg[data-v-8555b560] {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
/* Vue DOM mode adjustments for clear button */
|
||||
.text-input.vue-dom-mode ~ .clear-button[data-v-1c610e5d] {
|
||||
.text-input.vue-dom-mode ~ .clear-button[data-v-8555b560] {
|
||||
right: 8px;
|
||||
bottom: 10px; /* Changed from top to bottom, adjusted for Vue DOM padding */
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: rgba(107, 114, 128, 0.6);
|
||||
}
|
||||
.text-input.vue-dom-mode ~ .clear-button[data-v-1c610e5d]:hover {
|
||||
.text-input.vue-dom-mode ~ .clear-button[data-v-8555b560]:hover {
|
||||
background: oklch(62% 0.18 25);
|
||||
}
|
||||
.text-input.vue-dom-mode ~ .clear-button svg[data-v-1c610e5d] {
|
||||
.text-input.vue-dom-mode ~ .clear-button svg[data-v-8555b560] {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}`));
|
||||
@@ -14916,6 +14916,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
||||
spellcheck: __props.spellcheck ?? false,
|
||||
class: normalizeClass(["text-input", { "vue-dom-mode": isVueDomMode.value, "lm-wheel-scrollable": isVueDomMode.value }]),
|
||||
style: normalizeStyle(__props.maxHeight && isVueDomMode.value ? { maxHeight: __props.maxHeight + "px" } : void 0),
|
||||
"data-capture-wheel": "true",
|
||||
onInput,
|
||||
onWheel
|
||||
}, null, 46, _hoisted_3),
|
||||
@@ -14951,7 +14952,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
||||
};
|
||||
}
|
||||
});
|
||||
const AutocompleteTextWidget = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-1c610e5d"]]);
|
||||
const AutocompleteTextWidget = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-8555b560"]]);
|
||||
function createVueWidgetCleanup(vueApp, onCleanup) {
|
||||
let didUnmount = false;
|
||||
return () => {
|
||||
@@ -15715,13 +15716,66 @@ function normalizeAutocompleteWidgetValues(node, info) {
|
||||
info.widgets_values = repairedValues;
|
||||
}
|
||||
}
|
||||
function applyAutocompleteTextLayoutFix(widget, container, isVueMode) {
|
||||
if (isVueMode) {
|
||||
widget.computeLayoutSize = void 0;
|
||||
widget.computeSize = (width) => [width ?? 200, AUTOCOMPLETE_TEXT_WIDGET_MAX_HEIGHT - 4];
|
||||
if (container) {
|
||||
container.style.minHeight = `${AUTOCOMPLETE_TEXT_WIDGET_MAX_HEIGHT}px`;
|
||||
}
|
||||
} else {
|
||||
delete widget.computeLayoutSize;
|
||||
delete widget.computeSize;
|
||||
if (container) {
|
||||
container.style.minHeight = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
const initVueDomModeListener = () => {
|
||||
var _a2, _b;
|
||||
if ((_b = (_a2 = app$1.ui) == null ? void 0 : _a2.settings) == null ? void 0 : _b.addEventListener) {
|
||||
app$1.ui.settings.addEventListener("Comfy.VueNodes.Enabled.change", () => {
|
||||
requestAnimationFrame(() => {
|
||||
var _a3, _b2, _c;
|
||||
var _a3, _b2, _c, _d, _e2, _f;
|
||||
const isVueDomMode = ((_c = (_b2 = (_a3 = app$1.ui) == null ? void 0 : _a3.settings) == null ? void 0 : _b2.getSettingValue) == null ? void 0 : _c.call(_b2, "Comfy.VueNodes.Enabled")) ?? false;
|
||||
if ((_d = app$1.graph) == null ? void 0 : _d.nodes) {
|
||||
for (const node of app$1.graph.nodes) {
|
||||
const textWidget = (_e2 = node.widgets) == null ? void 0 : _e2.find(
|
||||
(w2) => w2.type === "AUTOCOMPLETE_TEXT_LORAS"
|
||||
);
|
||||
if (!textWidget) continue;
|
||||
const container = textWidget.element;
|
||||
applyAutocompleteTextLayoutFix(textWidget, container, isVueDomMode);
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
var _a4;
|
||||
for (const nodeEl of document.querySelectorAll("[data-node-id]")) {
|
||||
const grid = nodeEl.querySelector('[data-testid="node-widgets"]');
|
||||
if (!grid) continue;
|
||||
const nodeId = nodeEl.getAttribute("data-node-id");
|
||||
const node = (_a4 = app$1.graph) == null ? void 0 : _a4.getNodeById(nodeId);
|
||||
if (!node) continue;
|
||||
const rows = [];
|
||||
let needsFix = false;
|
||||
for (const w2 of node.widgets ?? []) {
|
||||
if (w2.type === "LORA_MANAGER_AUTOCOMPLETE_METADATA") {
|
||||
rows.push("min-content");
|
||||
} else if (w2.name === "loras") {
|
||||
rows.push("auto");
|
||||
} else if (w2.name === "text" && w2.type === "AUTOCOMPLETE_TEXT_LORAS") {
|
||||
rows.push(isVueDomMode ? "min-content" : "auto");
|
||||
needsFix = true;
|
||||
} else {
|
||||
rows.push("auto");
|
||||
}
|
||||
}
|
||||
if (needsFix) {
|
||||
grid.style.gridTemplateRows = rows.join(" ");
|
||||
}
|
||||
}
|
||||
});
|
||||
(_f = app$1.canvas) == null ? void 0 : _f.setDirty(true, true);
|
||||
document.dispatchEvent(new CustomEvent("lora-manager:vue-mode-change", {
|
||||
detail: { isVueDomMode }
|
||||
}));
|
||||
@@ -15820,6 +15874,14 @@ function createAutocompleteTextWidgetFactory(node, widgetName, modelType, inputO
|
||||
vueApps.set(appKey, vueApp);
|
||||
if (maxHeight) {
|
||||
container.style.maxHeight = `${maxHeight}px`;
|
||||
container.style.minHeight = `${maxHeight}px`;
|
||||
}
|
||||
if (modelType === "loras") {
|
||||
applyAutocompleteTextLayoutFix(
|
||||
widget,
|
||||
container,
|
||||
typeof LiteGraph !== "undefined" && LiteGraph.vueNodesMode
|
||||
);
|
||||
}
|
||||
widget.onRemove = createVueWidgetCleanup(vueApp, () => {
|
||||
vueApps.delete(appKey);
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user