perfectionist-plugins 2.4.6 → 2.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/auto-accept.js CHANGED
@@ -33,8 +33,8 @@ let attempts = 0;
33
33
  let unsubscribes = [];
34
34
  let removeStyle = null;
35
35
  let domObserver = null;
36
- let domTimer = null;
37
-
36
+ let domTimer = null;
37
+
38
38
  function enabled() {
39
39
  try {
40
40
  return api?.getOption?.(PLUGIN_ID, ACCEPT_KEY, true) === true;
@@ -99,8 +99,8 @@ async function acceptNow() {
99
99
  } finally {
100
100
  sending = false;
101
101
  }
102
- }
103
-
102
+ }
103
+
104
104
  function onPhase(raw) {
105
105
  const phase = unwrap(raw);
106
106
  if (typeof phase !== "string") return;
@@ -148,8 +148,8 @@ function toggle() {
148
148
  syncButton();
149
149
  if (next) acceptNow();
150
150
  else resetReadyCheck();
151
- }
152
-
151
+ }
152
+
153
153
  function buildButton() {
154
154
  const root = document.createElement("div");
155
155
  root.id = ROOT_ID;
@@ -165,8 +165,8 @@ function buildButton() {
165
165
  event.preventDefault();
166
166
  event.stopPropagation();
167
167
  toggle();
168
- });
169
-
168
+ });
169
+
170
170
  root.append(option);
171
171
  return root;
172
172
  }
package/ban-blocker.js CHANGED
@@ -1,4 +1,4 @@
1
- export const version = "3.6.0";
1
+ export const version = "3.6.2";
2
2
 
3
3
  const PLUGIN_ID = "ban-blocker";
4
4
  const STYLE_ID = "perfectionist-ban-blocker-style";
@@ -70,6 +70,7 @@ const fetchedNames = new Set();
70
70
  const playerNames = new Map();
71
71
  const cellToPlayer = new Map();
72
72
  const buttons = new Map();
73
+ const swapSeen = new Set();
73
74
  const failedUntil = new Map();
74
75
  const failCount = new Map();
75
76
  let rows = [];
@@ -159,10 +160,19 @@ function linkPlayer(p, rowName, cellId) {
159
160
  return root;
160
161
  }
161
162
 
163
+ function pickStarted(s) {
164
+ const actions = s?.actions?.flat?.() ?? [];
165
+ return actions.some((a) => a.type === "pick" && (a.isInProgress || a.completed));
166
+ }
167
+
162
168
  function banPending(s) {
163
- const bans = (s?.actions?.flat?.() ?? []).filter((a) => a.type === "ban");
169
+ const actions = s?.actions?.flat?.() ?? [];
170
+ const bans = actions.filter((a) => a.type === "ban");
164
171
  if (!bans.length) return true;
165
- return bans.some((a) => !a.completed);
172
+ if (bans.some((a) => !a.completed)) return true;
173
+ const phase = s?.timer?.phase;
174
+ if (phase && phase !== "BAN_PICK") return false;
175
+ return !pickStarted(s);
166
176
  }
167
177
 
168
178
  function pump() {
@@ -254,6 +264,7 @@ function onSessionGone() {
254
264
  lastSession = null;
255
265
  goneAway = true;
256
266
  uiVisible = false;
267
+ swapSeen.clear();
257
268
  removeButtons();
258
269
  if (!goneTimer) {
259
270
  goneTimer = setTimeout(() => {
@@ -449,16 +460,31 @@ function isSelfRow(r) {
449
460
  return lastSession?.localPlayerCellId != null && (r.cell ?? r.slotId) === lastSession.localPlayerCellId;
450
461
  }
451
462
 
463
+ function rowKey(r) {
464
+ return String(r.slotId ?? r.cell ?? r.index);
465
+ }
466
+
452
467
  function nativeSwapIn(circle) {
453
- return !!circle.querySelector(".swap-button-component:not([data-bb])");
468
+ if (!circle) return false;
469
+ return [...circle.querySelectorAll(".swap-button-component")].some((el) => el.dataset.bb !== "1");
454
470
  }
455
471
 
456
472
  function ensureButtons() {
457
473
  const list = getRows();
458
474
  for (const r of list) {
475
+ if (!r.circle || !r.circle.isConnected) r.circle = r.el.querySelector(".champion-icon-container");
459
476
  if (!r.circle || !r.circle.isConnected) continue;
477
+ const key = rowKey(r);
460
478
  let btn = buttons.get(r.index);
461
- if (isSelfRow(r) || !nativeSwapIn(r.circle)) {
479
+ if (isSelfRow(r)) {
480
+ if (btn) {
481
+ btn.remove();
482
+ buttons.delete(r.index);
483
+ }
484
+ continue;
485
+ }
486
+ if (nativeSwapIn(r.circle)) swapSeen.add(key);
487
+ if (!swapSeen.has(key)) {
462
488
  if (btn) {
463
489
  btn.remove();
464
490
  buttons.delete(r.index);
@@ -531,6 +557,7 @@ function update() {
531
557
  renderButtons();
532
558
  } else if (uiVisible) {
533
559
  uiVisible = false;
560
+ swapSeen.clear();
534
561
  removeButtons();
535
562
  }
536
563
  }
@@ -605,6 +632,7 @@ function onDisable() {
605
632
  observer = null;
606
633
 
607
634
  uiVisible = false;
635
+ swapSeen.clear();
608
636
  goneAway = true;
609
637
  target = undefined;
610
638
  sent = 0;
@@ -78,16 +78,11 @@ function installArrayFilter() {
78
78
  return owned;
79
79
  });
80
80
  if (!changed) return result;
81
- filterStats.array = (filterStats.array || 0) + 1;
82
- filterStats.source = "array";
83
- filterStats.lastIn = result.length;
84
- filterStats.lastOut = kept.length;
85
81
  return kept;
86
82
  };
87
83
  wrapped.__perfSkinFilter = FILTER_VERSION;
88
84
  proto.filter = wrapped;
89
85
  }
90
- const filterStats = (globalThis.__perfSkinFilterStats ??= { carousel: 0, source: "", lastIn: 0, lastOut: 0 });
91
86
  let rawFetch = (typeof window !== "undefined" && window.fetch && window.fetch.__perfOriginalFetch) || null;
92
87
  let clientSkinOrder = [];
93
88
 
@@ -127,8 +122,6 @@ function filterSkinList(list) {
127
122
  })
128
123
  .filter(isOwnedSkin);
129
124
  if (filtered.length !== list.length) changed = true;
130
- filterStats.lastIn = list.length;
131
- filterStats.lastOut = filtered.length;
132
125
  return changed ? filtered : null;
133
126
  }
134
127
 
@@ -153,8 +146,6 @@ function rewriteWebSocketFrame(text) {
153
146
  if (!payload || typeof payload !== "object" || typeof payload.uri !== "string") return null;
154
147
  if (payload.uri.split("?")[0] !== CAROUSEL_URI) return null;
155
148
  const kept = filterSkinList(payload.data);
156
- filterStats.carousel++;
157
- filterStats.source = "ws";
158
149
  recordClientOrder(kept ?? payload.data);
159
150
  if (!kept) return null;
160
151
  return JSON.stringify([frame[0], frame[1], Object.assign({}, payload, { data: kept })]);
@@ -235,7 +226,6 @@ function installFetchFilter() {
235
226
  const original = window.fetch;
236
227
  if (typeof original !== "function" || original.__perfSkinFilter === FILTER_VERSION) return;
237
228
  if (!rawFetch) rawFetch = original.__perfOriginalFetch || original;
238
- globalThis.__perfNativeFetch = rawFetch;
239
229
 
240
230
  const wrapped = function (input, init) {
241
231
  const url =
@@ -261,8 +251,6 @@ function installFetchFilter() {
261
251
  .catch(() => null)
262
252
  .then((json) => {
263
253
  const kept = filterSkinList(json);
264
- filterStats.carousel++;
265
- filterStats.source = "fetch";
266
254
  recordClientOrder(kept ?? json);
267
255
  if (!kept) return response;
268
256
  return new Response(JSON.stringify(kept), {
@@ -307,8 +295,6 @@ function installXhrFilter() {
307
295
  try {
308
296
  const json = JSON.parse(raw);
309
297
  const kept = filterSkinList(json);
310
- filterStats.carousel++;
311
- filterStats.source = "xhr";
312
298
  recordClientOrder(kept ?? json);
313
299
  if (kept) cache.filtered = JSON.stringify(kept);
314
300
  } catch {
@@ -442,10 +428,6 @@ function applyAll() {
442
428
  });
443
429
  }
444
430
 
445
- function applyAllGuarded() {
446
- applyAll();
447
- }
448
-
449
431
  let applyQueued = false;
450
432
 
451
433
  function scheduleApply() {
@@ -558,7 +540,7 @@ function resetState() {
558
540
  failure = null;
559
541
  sessionMisses = 0;
560
542
  championMisses = 0;
561
- applyAllGuarded();
543
+ applyAll();
562
544
  }
563
545
 
564
546
  function loadChampion(championId) {
@@ -579,7 +561,7 @@ function loadChampion(championId) {
579
561
  topLevelOrder = topLevel;
580
562
  ownedTopLevelIds = ownedTopLevel;
581
563
  carouselOrder = carouselIds.length > 0 ? carouselIds : inventoryOrder;
582
- applyAllGuarded();
564
+ applyAll();
583
565
  },
584
566
  (error) => {
585
567
  if (token !== requestToken) return;
@@ -618,7 +600,7 @@ function updateForSession(session, selection) {
618
600
  }
619
601
  }
620
602
 
621
- applyAllGuarded();
603
+ applyAll();
622
604
  }
623
605
 
624
606
  function scheduleRefresh() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perfectionist-plugins",
3
- "version": "2.4.6",
3
+ "version": "2.4.7",
4
4
  "description": "Small plugins that fix the annoying parts of League of Legends champ select. Built for Pengu Loader.",
5
5
  "type": "module",
6
6
  "main": "perfectionist-utils.js",
@@ -5,7 +5,7 @@ const PANEL_ID = "perfectionist-plugins-panel";
5
5
  const STYLE_ID = "perfectionist-plugins-style";
6
6
  const TAB_TITLE = "Perfectionism";
7
7
 
8
- export const version = "2.4.0";
8
+ export const version = "2.4.6";
9
9
 
10
10
  const PKG = "perfectionist-plugins";
11
11
  const CDN = "https://cdn.jsdelivr.net/npm";
@@ -30,7 +30,7 @@ const BUNDLED_MODULES = [
30
30
  id: "ban-blocker",
31
31
  file: "ban-blocker.js",
32
32
  name: "Ban Blocker",
33
- version: "3.6.0",
33
+ version: "3.6.2",
34
34
  description: "Adds a BLOCK button to your teammates in champ select. While a teammate is blocked, the champion they are about to ban is prepicked for you, so their ban can never land on it.",
35
35
  },
36
36
  {
@@ -44,7 +44,7 @@ const BUNDLED_MODULES = [
44
44
  id: "hide-unowned-skins",
45
45
  file: "hide-unowned-skins.js",
46
46
  name: "Hide Unowned Skins",
47
- version: "2.5.1",
47
+ version: "2.4.3",
48
48
  description: "In champ select, the skin carousel only shows the skins you own for the champion you picked.",
49
49
  },
50
50
  {
@@ -849,10 +849,23 @@ function refreshPanel() {
849
849
  requestAnimationFrame(() => applyScrollTop(next, top));
850
850
  }
851
851
 
852
+ function settingsNavScope() {
853
+ const title =
854
+ document.querySelector(".lol-settings-nav-title:not([data-perfectionist])") ??
855
+ document.querySelector(".lol-settings-nav-title");
856
+ return title?.closest("lol-uikit-scrollable") ?? title?.parentElement ?? null;
857
+ }
858
+
859
+ function navItems() {
860
+ const scope = settingsNavScope();
861
+ if (!scope) return [];
862
+ return [...scope.querySelectorAll('lol-uikit-navigation-item, [class*="settings-nav"]')];
863
+ }
864
+
852
865
  function setNavActive(activeNav) {
853
- for (const nav of document.querySelectorAll(".lol-settings-nav")) {
866
+ for (const nav of navItems()) {
854
867
  if (nav === activeNav) {
855
- nav.setAttribute("active", "true");
868
+ if (nav.getAttribute("active") !== "true") nav.setAttribute("active", "true");
856
869
  } else if (nav.hasAttribute("active")) {
857
870
  nav.removeAttribute("active");
858
871
  }
@@ -861,6 +874,26 @@ function setNavActive(activeNav) {
861
874
 
862
875
  let panelRequested = false;
863
876
  let syncTimer = null;
877
+ let navObserver = null;
878
+ let nativeActiveBeforeOpen = null;
879
+
880
+ function suppressNativeNavActive() {
881
+ const own = document.getElementById(NAV_ID);
882
+ if (!own) return;
883
+ if (!panelRequested) {
884
+ if (own.hasAttribute("active")) own.removeAttribute("active");
885
+ return;
886
+ }
887
+ for (const nav of navItems()) {
888
+ if (nav !== own && nav.hasAttribute("active")) nav.removeAttribute("active");
889
+ }
890
+ }
891
+
892
+ function watchNavActive() {
893
+ if (navObserver || !document.body) return;
894
+ navObserver = new MutationObserver(suppressNativeNavActive);
895
+ navObserver.observe(document.body, { subtree: true, attributes: true, attributeFilter: ["active"] });
896
+ }
864
897
 
865
898
  function startSync() {
866
899
  if (syncTimer === null) syncTimer = setInterval(keepPanelInSync, 300);
@@ -885,7 +918,11 @@ function openPanel() {
885
918
  if (existing) existing.remove();
886
919
  content.append(buildPanel());
887
920
 
888
- setNavActive(document.getElementById(NAV_ID));
921
+ const ownNav = document.getElementById(NAV_ID);
922
+ if (!nativeActiveBeforeOpen) {
923
+ nativeActiveBeforeOpen = navItems().find((nav) => nav !== ownNav && nav.hasAttribute("active")) ?? null;
924
+ }
925
+ setNavActive(ownNav);
889
926
  const currentTitle = document.querySelector(".lol-settings-title-current");
890
927
  if (currentTitle) currentTitle.textContent = TAB_TITLE.toUpperCase();
891
928
  }
@@ -897,7 +934,9 @@ function closePanel() {
897
934
  panel.remove();
898
935
  const options = document.querySelector(".lol-settings-content .lol-settings-options");
899
936
  if (options) options.style.display = "";
900
- setNavActive(null);
937
+ document.getElementById(NAV_ID)?.removeAttribute("active");
938
+ if (nativeActiveBeforeOpen?.isConnected) nativeActiveBeforeOpen.setAttribute("active", "true");
939
+ nativeActiveBeforeOpen = null;
901
940
  }
902
941
 
903
942
  function keepPanelInSync() {
@@ -905,6 +944,7 @@ function keepPanelInSync() {
905
944
  if (!content) {
906
945
  panelRequested = false;
907
946
  stopSync();
947
+ document.getElementById(NAV_ID)?.removeAttribute("active");
908
948
  return;
909
949
  }
910
950
  if (!panelRequested) {
@@ -923,12 +963,7 @@ function keepPanelInSync() {
923
963
  if (options && options.style.display !== "none") options.style.display = "none";
924
964
 
925
965
  const nav = document.getElementById(NAV_ID);
926
- if (nav) {
927
- for (const other of document.querySelectorAll(".lol-settings-nav")) {
928
- if (other !== nav && other.hasAttribute("active")) other.removeAttribute("active");
929
- }
930
- if (nav.getAttribute("active") !== "true") nav.setAttribute("active", "true");
931
- }
966
+ if (nav) setNavActive(nav);
932
967
 
933
968
  const title = document.querySelector(".lol-settings-title-current");
934
969
  if (title && title.textContent !== TAB_TITLE.toUpperCase()) title.textContent = TAB_TITLE.toUpperCase();
@@ -967,6 +1002,7 @@ function isDoneButton(target) {
967
1002
 
968
1003
  function installSettingsTab() {
969
1004
  addStyle(STYLE_ID, STYLE_CSS);
1005
+ watchNavActive();
970
1006
 
971
1007
  document.addEventListener(
972
1008
  "click",