dikdimon commited on
Commit
5508c8a
·
verified ·
1 Parent(s): a2cc497

Upload 2 files

Browse files
!0000-a1111-fix/javascript/fix.js ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function () {
2
+ 'use strict';
3
+
4
+ // =========================================================================
5
+ // 0000-a1111-fix/javascript/fix.js
6
+ //
7
+ // Порядок загрузки гарантируется двумя механизмами:
8
+ //
9
+ // 1. Имя '0000-a1111-fix' — A1111 сортирует user extensions alphabetically
10
+ // (sorted(os.listdir(...))), поэтому мы первые среди пользовательских
11
+ // расширений в папке extensions/.
12
+ //
13
+ // 2. metadata.ini [javascript/fix.js] Before = sd-webui-tabs-extension —
14
+ // A1111 строит topological sort через list_scripts() и учитывает
15
+ // Before/After зависимости для JS файлов.
16
+ //
17
+ // Что НЕ покрывается нашей timing-обёрткой:
18
+ // Builtin extensions (extensions-builtin/) грузятся ДО пользовательских.
19
+ // Три builtin используют onUiLoaded: canvas-zoom-and-pan (async),
20
+ // mobile (1 строка), prompt-bracket-checker (4 строки).
21
+ // Все три быстрые — они не являются источником проблем при 50+ расширениях.
22
+ // Источник медленных callbacks — пользовательские расширения (ControlNet и др.),
23
+ // и именно их мы покрываем полностью.
24
+ //
25
+ // Три исправления:
26
+ //
27
+ // [FIX-1] scheduleAfterUiUpdateCallbacks — max-wait 1000ms.
28
+ // Оригинальный debounce сбрасывается при каждой мутации.
29
+ // При 50+ расширениях DOM меняется 2-5 сек непрерывно —
30
+ // uiAfterUpdateCallbacks не вызываются всё это время.
31
+ //
32
+ // [FIX-2] onUiLoaded — тайминг через performance.now().
33
+ // executeCallbacks синхронный — setTimeout прервать нельзя.
34
+ // Измеряем время до/после каждого callback и предупреждаем о медленных.
35
+ //
36
+ // [FIX-3] inputAccordionChecked + tabs extension.
37
+ // tabs_parser.js: content.visibleCheckbox = {} (plain Object, не HTMLInputElement)
38
+ // v2 баг: проверка `el.visibleCheckbox && typeof fn === 'function'` — оба
39
+ // условия TRUE на стабе ({} truthy в JS, стрелочная функция — function).
40
+ // v3 fix: instanceof HTMLInputElement — стаб не проходит.
41
+ // =========================================================================
42
+
43
+
44
+ // ─── [FIX-1] scheduleAfterUiUpdateCallbacks с max-wait ───────────────────
45
+
46
+ function applyFix1() {
47
+ if (typeof scheduleAfterUiUpdateCallbacks === 'undefined') {
48
+ console.warn('[a1111-fix] scheduleAfterUiUpdateCallbacks not found, skipping FIX-1');
49
+ return;
50
+ }
51
+
52
+ const DEBOUNCE_MS = 200;
53
+ const MAX_WAIT_MS = 1000;
54
+
55
+ let debounceTimer = null;
56
+ let maxWaitTimer = null;
57
+
58
+ function runAfterUpdateCallbacks() {
59
+ clearTimeout(debounceTimer);
60
+ clearTimeout(maxWaitTimer);
61
+ debounceTimer = null;
62
+ maxWaitTimer = null;
63
+ if (typeof executeCallbacks === 'function' && typeof uiAfterUpdateCallbacks !== 'undefined') {
64
+ executeCallbacks(uiAfterUpdateCallbacks);
65
+ }
66
+ }
67
+
68
+ window.scheduleAfterUiUpdateCallbacks = function () {
69
+ clearTimeout(debounceTimer);
70
+ debounceTimer = setTimeout(runAfterUpdateCallbacks, DEBOUNCE_MS);
71
+
72
+ if (!maxWaitTimer) {
73
+ maxWaitTimer = setTimeout(runAfterUpdateCallbacks, MAX_WAIT_MS);
74
+ }
75
+ };
76
+
77
+ console.log('[a1111-fix] FIX-1: max-wait=' + MAX_WAIT_MS + 'ms applied');
78
+ }
79
+
80
+
81
+ // ─── [FIX-2] Тайминг onUiLoaded callbacks ────────────────────────────────
82
+ // Покрывает: все user extension callbacks (ControlNet, adetailer и т.д.)
83
+ // НЕ покрывает: builtin callbacks (canvas-zoom, mobile, bracket-checker)
84
+ // — они быстрые и не являются источником проблем.
85
+
86
+ function applyFix2() {
87
+ if (typeof onUiLoaded === 'undefined') {
88
+ console.warn('[a1111-fix] onUiLoaded not found, skipping FIX-2');
89
+ return;
90
+ }
91
+
92
+ const _originalOnUiLoaded = window.onUiLoaded;
93
+ const SLOW_THRESHOLD_MS = 100;
94
+
95
+ window.onUiLoaded = function (callback) {
96
+ const wrappedCallback = function (arg) {
97
+ const name = callback.name || '(anonymous)';
98
+ const t0 = performance.now();
99
+ try {
100
+ callback(arg);
101
+ } catch (e) {
102
+ console.error('[a1111-fix] onUiLoaded callback crashed:', name, e);
103
+ } finally {
104
+ const elapsed = performance.now() - t0;
105
+ if (elapsed > SLOW_THRESHOLD_MS) {
106
+ console.warn(
107
+ `[a1111-fix] SLOW onUiLoaded: "${name}" took ${elapsed.toFixed(0)}ms`
108
+ );
109
+ }
110
+ }
111
+ };
112
+ _originalOnUiLoaded(wrappedCallback);
113
+ };
114
+
115
+ console.log('[a1111-fix] FIX-2: onUiLoaded timing applied (warn if >' + SLOW_THRESHOLD_MS + 'ms)');
116
+ }
117
+
118
+
119
+ // ─── [FIX-3] inputAccordionChecked — исправленная проверка стаба ─────────
120
+ //
121
+ // tabs_parser.js вешает стаб:
122
+ // content.visibleCheckbox = {} <- plain Object
123
+ // content.onVisibleCheckboxChange = () => {} <- no-op arrow function
124
+ //
125
+ // v2 баг: обе проверки TRUE на стабе в JS:
126
+ // {} — truthy (любой объект truthy в JS)
127
+ // typeof (() => {}) === 'function' — true
128
+ // -> fallback никогда не достигался
129
+ //
130
+ // v3 fix: instanceof HTMLInputElement
131
+ // Реальный: document.createElement('INPUT') -> instanceof HTMLInputElement = TRUE
132
+ // Стаб: {} -> instanceof HTMLInputElement = FALSE
133
+ //
134
+ // gradioApp().getElementById() — как в core A1111 (inputAccordion.js).
135
+ // При shadow DOM gradioApp() возвращает shadowRoot, но его getElementById
136
+ // переопределён на document.getElementById (script.js строки 6-8).
137
+
138
+ function applyFix3() {
139
+ if (typeof inputAccordionChecked === 'undefined') {
140
+ console.warn('[a1111-fix] inputAccordionChecked not found, skipping FIX-3');
141
+ return;
142
+ }
143
+
144
+ const _orig = window.inputAccordionChecked;
145
+
146
+ window.inputAccordionChecked = function (id, checked) {
147
+ const el = gradioApp().getElementById(id);
148
+
149
+ if (!el) {
150
+ console.warn('[a1111-fix] inputAccordionChecked: element not found, id:', id);
151
+ return;
152
+ }
153
+
154
+ // Реальный accordion: visibleCheckbox = document.createElement('INPUT')
155
+ // Стаб tabs extension: visibleCheckbox = {}
156
+ // instanceof HTMLInputElement — единственная надёжная проверка
157
+ if (el.visibleCheckbox instanceof HTMLInputElement) {
158
+ _orig(id, checked);
159
+ return;
160
+ }
161
+
162
+ // Стаб обнаружен. tabs_parser.js переименовал оригинальный аккордеон:
163
+ // accordion.id = '.' + originalId
164
+ const realEl = gradioApp().getElementById('.' + id);
165
+
166
+ if (realEl && realEl.visibleCheckbox instanceof HTMLInputElement
167
+ && typeof realEl.onVisibleCheckboxChange === 'function') {
168
+ console.log('[a1111-fix] FIX-3: tabs stub at "' + id + '", forwarding to ".' + id + '"');
169
+ realEl.visibleCheckbox.checked = checked;
170
+ realEl.onVisibleCheckboxChange();
171
+ return;
172
+ }
173
+
174
+ console.error(
175
+ '[a1111-fix] FIX-3: cannot resolve accordion for id:', id,
176
+ '\n el.visibleCheckbox:', el.visibleCheckbox,
177
+ '\n el.visibleCheckbox instanceof HTMLInputElement:', el.visibleCheckbox instanceof HTMLInputElement,
178
+ '\n realEl at ".' + id + '":', realEl
179
+ );
180
+ };
181
+
182
+ console.log('[a1111-fix] FIX-3: inputAccordionChecked with instanceof check applied');
183
+ }
184
+
185
+
186
+ applyFix1();
187
+ applyFix2();
188
+ applyFix3();
189
+
190
+ console.log('[a1111-fix] v3 ready');
191
+
192
+ })();
!0000-a1111-fix/metadata.ini ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [Extension]
2
+ Name = 0000-a1111-fix
3
+
4
+ ; Порядок загрузки JS через topological sort A1111.
5
+ ;
6
+ ; ВАЖНО: canonical name extension'а в A1111 = имя ПАПКИ (не [Extension] Name).
7
+ ; Код extensions.py строка 55: self.canonical_name = canonical_name.lower().strip()
8
+ ; где canonical_name = extension_dirname — это ВСЕГДА имя папки, поле Name игнорируется.
9
+ ;
10
+ ; tabs extension устанавливается двумя способами:
11
+ ; git clone -> папка называется "sd-webui-tabs-extension"
12
+ ; скачать zip с GitHub -> папка называется "sd-webui-tabs-extension-main"
13
+ ;
14
+ ; Перечисляем оба варианта. Несуществующие Before-цели A1111 молча игнорирует
15
+ ; (scripts.py строки 438-445: если scripts.get() и loaded_extensions_scripts.get()
16
+ ; вернули None — пропускается без ошибки).
17
+
18
+ [javascript/fix.js]
19
+ Before = sd-webui-tabs-extension sd-webui-tabs-extension-main