Compare commits

...
4 Commits
Author SHA1 Message Date
semantic-release-bot 02a046321f chore(release): 2.3.1 [skip ci] 2026-06-18 01:06:25 +00:00
Dumitru Picalau 6a5a8d318e Merge pull request #3 from DumiFlex/fix/instance-modal-scroll
fix(extension): scroll instance edit modals with pinned header + footer
2026-06-18 04:05:45 +03:00
DumiFlex a19e5f4500 Merge remote-tracking branch 'origin/main' into fix/instance-modal-scroll
# Conflicts:
#	scripts/check-bundle-size.mjs
2026-06-18 03:44:54 +03:00
DumiFlex c43bff0488 fix(extension): scroll instance edit modals with pinned header + footer
Every per-instance edit modal (wildcard, fixed-values, derivation, combine, constraint, bundle) now scrolls its body when content overflows, with the header pinned to the top and a sticky dock pinned to the bottom (runtime + footer, or target-reach + footer for constraints, which have no runtime) so Save is always reachable. The long inner lists — options, values, rules, exceptions — each scroll independently with sticky column headers. Constraint's target-reach moves to last. Compact + right-padded the exceptions delete button. Bumps the extension bundle budget 430->431 KB for the added modal CSS.
2026-06-18 03:23:24 +03:00
15 changed files with 119 additions and 32 deletions
+14
View File
@@ -1,3 +1,17 @@
## 🎉 Wildcard Pipeline 2.3.1
📖 [Docs (wiki)](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/wiki) · 💬 [Discord](https://discord.gg/BFYR9WQdVR) · 📦 [Install via ComfyUI Manager](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/wiki/Quick-Start) · 🐛 [Issues](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/issues)
<details>
<summary><b>📋 Full changelog</b> — click to expand the per-commit list</summary>
* Merge pull request #3 from DumiFlex/fix/instance-modal-scroll ([6a5a8d3](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/commit/6a5a8d3)), closes [#3](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/issues/3)
* Merge remote-tracking branch 'origin/main' into fix/instance-modal-scroll ([a19e5f4](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/commit/a19e5f4))
* fix(extension): scroll instance edit modals with pinned header + footer ([c43bff0](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/commit/c43bff0))
</details>
## 🎉 Wildcard Pipeline 2.3.0
📖 [Docs (wiki)](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/wiki) · 💬 [Discord](https://discord.gg/BFYR9WQdVR) · 📦 [Install via ComfyUI Manager](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/wiki/Quick-Start) · 🐛 [Issues](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/issues)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "comfyui-wildcard-pipeline",
"version": "2.3.0",
"version": "2.3.1",
"private": true,
"description": "Modular procedural prompt generation for ComfyUI",
"author": "DumiFlex",
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "comfyui-wildcard-pipeline"
version = "2.3.0"
version = "2.3.1"
description = "Modular procedural prompt generation for ComfyUI — wildcards, constraints, derivations, and context pipelines."
readme = "README.md"
requires-python = ">=3.10"
+8 -7
View File
@@ -157,13 +157,14 @@ const ENTRY_LIMIT = 30 * 1024; // 30 KB
// parity test) now; the upcoming reach/constraint-matrix UI phases need the
// total budget headroom in the lazy ContextWidget chunk. Approved alongside
// the SP3 constraint feature.
// Bumped 430 -> 431 KB on 2026-06-18 for the editor robustness pass:
// RichTextInput's scoped renderEpoch rebuild (nested-ref picker crash fix)
// + useEditorDraft flush-on-unmount/unload + ErrorBoundary copy, plus the
// clipboard-shield wired into _shared.ts + main.ts (Ctrl-A/C/V/X guard on
// editable widget inputs). ~0.3 KB net gzip across the extension entry +
// shared chunks. Landed on the bulk-editor branch (the bulk UI itself is
// manager-only and adds nothing to js/).
// Bumped 430 -> 431 KB on 2026-06-18 — two same-day landings share the
// ceiling (both CSS/glue; the manager-only bulk UI adds nothing to js/):
// - editor robustness: RichTextInput renderEpoch crash fix + useEditorDraft
// flush-on-unmount/unload + ErrorBoundary copy + clipboard-shield wired
// into _shared.ts / main.ts (Ctrl-A/C/V/X guard on editable inputs).
// - instance-modal scroll/sticky: sticky header + runtime/footer dock +
// own-scrollbar inner lists (options/values/rules/exceptions) + compact
// exceptions trash, across all 6 edit modals.
const TOTAL_LIMIT = 431 * 1024; // 431 KB
function gzipSize(path) {
@@ -20,6 +20,12 @@
.wp-wcm__head { --modal-accent: var(--wp-kind-wildcard); }
.wp-cbm__head, .wp-cnm__head, .wp-dvm__head, .wp-fvm__head, .wp-wcm__head {
/* Pinned to the top of the scrolling modal body so the title + close
* stay visible while the content scrolls. Opaque (gradient bottoms out
* at --wp-bg2) so scrolled content doesn't bleed through. */
position: sticky;
top: 0;
z-index: 2;
display: flex;
align-items: center;
gap: 10px;
@@ -102,6 +102,7 @@ const headerName = computed(() => props.bundle.name || props.libraryName || "Bun
:library-color="libraryColor"
@update="(patch) => emit('update', patch)"
/>
<div class="wp-bdm__dock">
<RuntimeSection
:lock-state="lockState"
:internal-state="internalState"
@@ -157,12 +158,13 @@ const headerName = computed(() => props.bundle.name || props.libraryName || "Bun
Save
</button>
</footer>
</div>
</div>
</template>
<style scoped>
.wp-bdm { background: var(--wp-bg2); border: 1px solid var(--wp-border); border-radius: var(--wp-radius); width: 720px; max-width: 100%; max-height: 80vh; display: flex; flex-direction: column; overflow: hidden; font-family: var(--wp-font-sans, sans-serif); font-size: 12px; color: var(--wp-text); }
.wp-bdm__head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; background: linear-gradient(180deg, color-mix(in srgb, var(--modal-accent, var(--wp-accent)) 18%, var(--wp-bg2)) 0%, var(--wp-bg2) 100%); border-bottom: 1px solid var(--wp-border); }
.wp-bdm { background: var(--wp-bg2); border: 1px solid var(--wp-border); border-radius: var(--wp-radius); width: 720px; max-width: 100%; max-height: 80vh; display: flex; flex-direction: column; overflow-y: auto; font-family: var(--wp-font-sans, sans-serif); font-size: 12px; color: var(--wp-text); }
.wp-bdm__head { position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 10px; padding: 12px 14px; background: linear-gradient(180deg, color-mix(in srgb, var(--modal-accent, var(--wp-accent)) 18%, var(--wp-bg2)) 0%, var(--wp-bg2) 100%); border-bottom: 1px solid var(--wp-border); }
.wp-bdm__head-icon { color: var(--modal-accent, var(--wp-accent)); font-size: 18px; width: 24px; text-align: center; }
.wp-bdm__title-block { flex: 1; min-width: 0; }
.wp-bdm__title-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
@@ -175,6 +177,7 @@ const headerName = computed(() => props.bundle.name || props.libraryName || "Bun
.wp-bdm__close { background: transparent; border: 0; color: var(--wp-text-dim, var(--wp-text3)); cursor: pointer; padding: 4px; }
.wp-bdm__close:hover { color: var(--wp-text); }
.wp-bdm__close .pi { font-size: 12px; }
.wp-bdm__dock { position: sticky; bottom: 0; z-index: 2; background: var(--wp-bg2); }
.wp-bdm__foot { display: flex; align-items: center; gap: 12px; padding: 10px 14px; background: var(--wp-bg3); border-top: 1px solid var(--wp-border); }
.wp-bdm__spa-link { display: inline-flex; align-items: center; gap: 5px; font: 10px var(--wp-font-sans); color: var(--wp-text-muted, var(--wp-text2)); text-decoration: none; }
.wp-bdm__spa-link:hover { color: var(--wp-accent-text, var(--wp-text)); }
@@ -109,6 +109,7 @@ function onSpaClick(): void {
:sibling-vars="siblingVars"
@update="onUpdate"
/>
<div class="wp-cbm__dock">
<RuntimeSection :module="module" @update="onUpdate" />
<footer class="wp-cbm__foot">
@@ -147,6 +148,7 @@ function onSpaClick(): void {
<button type="button" class="wp-cbm__btn" data-test="cbm-cancel" @click="emit('cancel')">Cancel</button>
<button type="button" class="wp-cbm__btn wp-cbm__btn--primary" data-test="cbm-save" @click="emit('save')">Save</button>
</footer>
</div>
</div>
</template>
@@ -160,7 +162,7 @@ function onSpaClick(): void {
max-height: 80vh;
display: flex;
flex-direction: column;
overflow: hidden;
overflow-y: auto;
font-family: var(--wp-font-sans, sans-serif);
font-size: 12px;
color: var(--wp-text);
@@ -168,6 +170,12 @@ function onSpaClick(): void {
/* Head styling lives in src/components/context/editors/_modal-head.css
* (imported once by ContextWidget). Every modal in this family shares
* the same accent-tinted gradient + chip + close button. */
.wp-cbm__dock {
position: sticky;
bottom: 0;
z-index: 2;
background: var(--wp-bg2);
}
.wp-cbm__foot {
display: flex;
align-items: center;
@@ -691,14 +691,6 @@ function onSpaClick(): void {
:stranded="hasDangling"
@update="onUpdate"
/>
<TargetReachSection
:model-value="targetSelect"
:chain-modules="chainModules"
:constraint-uid="constraintUid"
:target-wildcard-id="targetWildcardId"
:target-name="targetName"
@update:model-value="onTargetSelect"
/>
<ExceptionsSection
:module="module"
:source-values="sourceValues"
@@ -712,6 +704,15 @@ function onSpaClick(): void {
@update="onUpdate"
/>
<div class="wp-cnm__dock">
<TargetReachSection
:model-value="targetSelect"
:chain-modules="chainModules"
:constraint-uid="constraintUid"
:target-wildcard-id="targetWildcardId"
:target-name="targetName"
@update:model-value="onTargetSelect"
/>
<footer class="wp-cnm__foot">
<a
v-if="isLibraryTracked"
@@ -748,6 +749,7 @@ function onSpaClick(): void {
<button type="button" class="wp-cnm__btn" data-test="cnm-cancel" @click="emit('cancel')">Cancel</button>
<button type="button" class="wp-cnm__btn wp-cnm__btn--primary" data-test="cnm-save" @click="emit('save')">Save</button>
</footer>
</div>
<!-- Feature 2: confirm pulling the missing dependency (+ its closure)
from the community before any network/install side effect. -->
@@ -772,13 +774,19 @@ function onSpaClick(): void {
max-height: 80vh;
display: flex;
flex-direction: column;
overflow: hidden;
overflow-y: auto;
font-family: var(--wp-font-sans, sans-serif);
font-size: 12px;
color: var(--wp-text);
}
/* Head styling lives in src/components/context/editors/_modal-head.css
* (imported once by ContextWidget). */
.wp-cnm__dock {
position: sticky;
bottom: 0;
z-index: 2;
background: var(--wp-bg2);
}
.wp-cnm__foot {
display: flex;
align-items: center;
@@ -304,6 +304,7 @@ function bumpExtraFactor(idx: number, dir: 1 | -1): void {
<section class="ex" data-test="ex-section">
<div class="ex__label">Exceptions</div>
<div class="ex__list">
<div
v-for="(exc, i) in libraryExceptions"
:key="libKey(exc)"
@@ -551,6 +552,7 @@ function bumpExtraFactor(idx: number, dir: 1 | -1): void {
><i class="pi pi-trash" /></button>
</template>
</div>
</div>
<button
v-if="!stranded"
@@ -581,11 +583,18 @@ function bumpExtraFactor(idx: number, dir: 1 | -1): void {
color: var(--wp-text-dim, var(--wp-text3));
margin-bottom: 8px;
}
.ex__list {
/* Long exception lists scroll in place; the label + add button stay put. */
max-height: 34vh;
overflow-y: auto;
}
.ex__row {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 0;
/* right padding keeps the trash off the section edge + the list
* scrollbar when the exceptions list scrolls. */
padding: 4px 8px 4px 0;
font: 11px var(--wp-font-mono);
border-bottom: 1px dashed var(--wp-border-soft, var(--wp-border));
}
@@ -801,17 +810,17 @@ function bumpExtraFactor(idx: number, dir: 1 | -1): void {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
width: 20px;
height: 20px;
padding: 0;
background: color-mix(in srgb, var(--wp-text) 3%, transparent);
border: 1px solid var(--wp-border);
background: transparent;
border: 0;
border-radius: 4px;
color: var(--wp-text-dim, var(--wp-text3));
cursor: pointer;
font-size: 11px;
flex-shrink: 0;
transition: background 0.12s, border-color 0.12s, color 0.12s;
transition: background 0.12s, color 0.12s;
}
.ex__trash:hover {
background: color-mix(in srgb, var(--wp-danger, #e05252) 18%, transparent);
@@ -160,6 +160,7 @@ function onSpaClick(): void {
:via-option-pairs="viaOptionPairs"
@update="onUpdate"
/>
<div class="wp-dvm__dock">
<RuntimeSection :module="module" @update="onUpdate" />
<footer class="wp-dvm__foot">
@@ -198,6 +199,7 @@ function onSpaClick(): void {
<button type="button" class="wp-dvm__btn" data-test="dvm-cancel" @click="emit('cancel')">Cancel</button>
<button type="button" class="wp-dvm__btn wp-dvm__btn--primary" data-test="dvm-save" @click="emit('save')">Save</button>
</footer>
</div>
</div>
</template>
@@ -211,13 +213,19 @@ function onSpaClick(): void {
max-height: 80vh;
display: flex;
flex-direction: column;
overflow: hidden;
overflow-y: auto;
font-family: var(--wp-font-sans, sans-serif);
font-size: 12px;
color: var(--wp-text);
}
/* Head styling lives in src/components/context/editors/_modal-head.css
* (imported once by ContextWidget). */
.wp-dvm__dock {
position: sticky;
bottom: 0;
z-index: 2;
background: var(--wp-bg2);
}
.wp-dvm__foot {
display: flex;
align-items: center;
@@ -693,6 +693,9 @@ function opUsesValue(op: string | undefined): boolean {
display: flex;
flex-direction: column;
gap: 6px;
/* Long rule lists scroll in place so identity / footer stay visible. */
max-height: 44vh;
overflow-y: auto;
}
/* Rule card */
@@ -66,6 +66,7 @@ function onSpaClick(): void {
<IdentitySection :module="module" @update="onUpdate" />
<ValuesSection :module="module" @update="onUpdate" />
<div class="wp-fvm__dock">
<RuntimeSection :module="module" @update="onUpdate" />
<footer class="wp-fvm__foot">
@@ -105,6 +106,7 @@ function onSpaClick(): void {
<button type="button" class="wp-fvm__btn" data-test="fvm-cancel" @click="emit('cancel')">Cancel</button>
<button type="button" class="wp-fvm__btn wp-fvm__btn--primary" data-test="fvm-save" @click="emit('save')">Save</button>
</footer>
</div>
</div>
</template>
@@ -118,13 +120,19 @@ function onSpaClick(): void {
max-height: 80vh;
display: flex;
flex-direction: column;
overflow: hidden;
overflow-y: auto;
font-family: var(--wp-font-sans, sans-serif);
font-size: 12px;
color: var(--wp-text);
}
/* Head styling lives in src/components/context/editors/_modal-head.css
* (imported once by ContextWidget). */
.wp-fvm__dock {
position: sticky;
bottom: 0;
z-index: 2;
background: var(--wp-bg2);
}
.wp-fvm__foot {
display: flex;
align-items: center;
@@ -183,9 +183,14 @@ const addedCount = computed(() => draftRows.value.filter((r) => r.libraryId ===
background: var(--wp-bg-deep, var(--wp-bg));
border: 1px solid var(--wp-border);
border-radius: 3px;
overflow: hidden;
/* Long value lists scroll in place; the header row stays pinned. */
max-height: 42vh;
overflow-y: auto;
}
.vals__head {
position: sticky;
top: 0;
z-index: 1;
display: grid;
grid-template-columns: 22px 180px 1fr 28px 28px;
gap: 10px;
@@ -101,6 +101,7 @@ function onSpaClick(): void {
@update="onUpdate"
/>
<PoolSection :module="module" :via-option-pairs="viaOptionPairs" @update="onUpdate" />
<div class="wp-wcm__dock">
<RuntimeSection :module="module" @update="onUpdate" />
<footer class="wp-wcm__foot">
@@ -150,6 +151,7 @@ function onSpaClick(): void {
Save
</button>
</footer>
</div>
</div>
</template>
@@ -163,7 +165,7 @@ function onSpaClick(): void {
max-height: 80vh;
display: flex;
flex-direction: column;
overflow: hidden;
overflow-y: auto;
font-family: var(--wp-font-sans, sans-serif);
font-size: 12px;
color: var(--wp-text);
@@ -171,6 +173,12 @@ function onSpaClick(): void {
/* Head styling lives in src/components/context/editors/_modal-head.css
* (imported once by ContextWidget). Adopted from InjectorBindingModal
* so every per-instance edit modal reads as one design family. */
.wp-wcm__dock {
position: sticky;
bottom: 0;
z-index: 2;
background: var(--wp-bg2);
}
.wp-wcm__foot {
display: flex;
align-items: center;
@@ -903,9 +903,15 @@ const skewedTowards = computed(() => {
background: var(--wp-bg-deep, var(--wp-bg));
border: 1px solid var(--wp-border);
border-radius: 3px;
overflow: hidden;
/* Long option lists scroll in place so the axes / pick / footer stay
* visible; the column-header row below stays pinned via sticky. */
max-height: 40vh;
overflow-y: auto;
}
.pool__opt-head {
position: sticky;
top: 0;
z-index: 1;
display: grid;
/* Match OptionRow column widths so cell alignment stays in lockstep. */
grid-template-columns: 22px 1fr 110px 64px 96px;