perfectionist-plugins 2.4.4 → 2.4.6

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/README.md CHANGED
@@ -22,8 +22,8 @@ Built for [Pengu Loader](https://pengu.lol)
22
22
  | 🛡️ **Ban Blocker** | 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 on your own pick turn, so their ban can never land on it. |
23
23
  | 🔓 **Unlock Profile Skins** | In the **Set profile background** window, every skin shows as unlocked, even event skins (Signature). Browse the full list and pick any skin you like. |
24
24
  | 🎨 **Hide Unowned Skins** | In champ select, the skin carousel only shows the skins you own for the champion you picked. The arrows and dots only move through owned skins, and the ones that are left keep their normal spacing. |
25
- | ✅ **Auto Accept** | Accepts the match for you when the ready check pops up. Add an optional delay, and switch it on or off from the lobby footer. |
26
- | 🚪 **Dodge Helper** | Adds a button to leave champ select: **DODGE** in ranked Solo/Duo and Flex, with the estimated LP you will lose, and **QUIT** in every other mode. |
25
+ | ✅ **Auto Accept** | Accepts the match for you when the ready check pops up. |
26
+ | 🚪 **Dodge Helper** | Adds a **DODGE** button in ranked Solo/Duo and Flex and a **QUIT** button in other modes, with the LP and queue-penalty cost of the next dodge. |
27
27
  | 🎁 **Auto Claimer** | Claims rewards for you the moment they become claimable: event tracks, missions and the TFT Battle Pass. Three optional extras: auto-buy shop items Riot lists for 1 BE, auto-open mystery loot (icons, ward skins), and auto-open chests with keys. |
28
28
  | 📬 **Auto Read** | Marks quests, loot, Clash, TFT and client notices as read, so no badge or dot stays lit. |
29
29
 
package/auto-accept.js CHANGED
@@ -1,18 +1,16 @@
1
1
  const PLUGIN_ID = "auto-accept";
2
- export const version = "1.1.1";
2
+ export const version = "1.1.3";
3
3
 
4
4
  const PHASE_URI = "/lol-gameflow/v1/gameflow-phase";
5
5
  const READY_CHECK_URI = "/lol-matchmaking/v1/ready-check";
6
6
  const ACCEPT_URI = "/lol-matchmaking/v1/ready-check/accept";
7
- const OPTION_KEY = "autoAccept";
8
- const DELAY_KEY = "perfectionist:auto-accept:delay";
9
- const MAX_DELAY = 15000;
7
+ const ACCEPT_KEY = "autoAccept";
10
8
  const RETRY_MS = 700;
11
9
  const MAX_ATTEMPTS = 4;
10
+ const GAME_SELECT_GUARD_MS = 1500;
12
11
  const STYLE_ID = "perf-auto-accept-style";
13
12
  const ROOT_ID = "perfAutoAccept";
14
13
  const TOGGLE_ID = "perfAutoAcceptToggle";
15
- const DELAY_INPUT_ID = "perfAutoAcceptDelay";
16
14
 
17
15
  const CSS = `#perfAutoAccept {
18
16
  display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important;
@@ -24,24 +22,7 @@ const CSS = `#perfAutoAccept {
24
22
  font-family: var(--font-body, "LoL Body", Arial, sans-serif) !important;
25
23
  font-size: 12px !important; letter-spacing: 0.3px !important; color: #a09b8c !important;
26
24
  }
27
- #perfAutoAccept .auto-accept-button[selected] .auto-accept-button-text { color: #f0e6d2 !important; }
28
- #perfAutoAccept .auto-accept-delay {
29
- display: flex !important; align-items: center !important; box-sizing: border-box !important;
30
- width: 82px !important; height: 24px !important; margin-left: 0 !important;
31
- background: #1e2328 !important; border: 1px solid #785a28 !important; border-radius: 0 !important;
32
- transition: border-color 0.15s ease, background-color 0.15s ease !important;
33
- }
34
- #perfAutoAccept .auto-accept-delay:hover { border-color: #c89b3c !important; }
35
- #perfAutoAccept .auto-accept-delay:focus-within { border-color: #c89b3c !important; background: #0a1428 !important; }
36
- #perfAutoAccept .auto-accept-delay input {
37
- width: 100% !important; height: 100% !important; box-sizing: border-box !important;
38
- background: transparent !important; border: none !important; outline: none !important; box-shadow: none !important;
39
- color: #f0e6d2 !important; font-family: var(--font-body, "LoL Body", Arial, sans-serif) !important;
40
- font-size: 12px !important; letter-spacing: 0.3px !important; text-align: center !important; padding: 0 6px !important;
41
- }
42
- #perfAutoAccept .auto-accept-delay input::placeholder { color: #5b5a56 !important; }
43
- #perfAutoAccept .auto-accept-delay input::-webkit-outer-spin-button,
44
- #perfAutoAccept .auto-accept-delay input::-webkit-inner-spin-button { -webkit-appearance: none !important; margin: 0 !important; }`;
25
+ #perfAutoAccept .auto-accept-button[selected] .auto-accept-button-text { color: #f0e6d2 !important; }`;
45
26
 
46
27
  let api = null;
47
28
  let active = false;
@@ -52,29 +33,11 @@ let attempts = 0;
52
33
  let unsubscribes = [];
53
34
  let removeStyle = null;
54
35
  let domObserver = null;
55
- let domTimer = null;
56
- let delayValue = readDelay();
57
-
58
- function readDelay() {
59
- try {
60
- const raw = Number(localStorage.getItem(DELAY_KEY));
61
- if (!Number.isFinite(raw) || raw < 0) return 0;
62
- return Math.min(Math.floor(raw), MAX_DELAY);
63
- } catch {
64
- return 0;
65
- }
66
- }
67
-
68
- function writeDelay(value) {
69
- delayValue = value;
70
- try {
71
- localStorage.setItem(DELAY_KEY, String(value));
72
- } catch {}
73
- }
74
-
36
+ let domTimer = null;
37
+
75
38
  function enabled() {
76
39
  try {
77
- return api?.getOption?.(PLUGIN_ID, OPTION_KEY) === true;
40
+ return api?.getOption?.(PLUGIN_ID, ACCEPT_KEY, true) === true;
78
41
  } catch {
79
42
  return false;
80
43
  }
@@ -136,19 +99,13 @@ async function acceptNow() {
136
99
  } finally {
137
100
  sending = false;
138
101
  }
139
- }
140
-
141
- function queueAccept() {
142
- if (!active || !enabled() || accepted || sending || timer) return;
143
- if (delayValue > 0) timer = setTimeout(acceptNow, delayValue);
144
- else acceptNow();
145
- }
146
-
102
+ }
103
+
147
104
  function onPhase(raw) {
148
105
  const phase = unwrap(raw);
149
106
  if (typeof phase !== "string") return;
150
107
  if (phase === "ReadyCheck") {
151
- if (!accepted) queueAccept();
108
+ if (!accepted) acceptNow();
152
109
  return;
153
110
  }
154
111
  resetReadyCheck();
@@ -166,13 +123,13 @@ function onReadyCheck(raw) {
166
123
  resetReadyCheck();
167
124
  return;
168
125
  }
169
- queueAccept();
126
+ acceptNow();
170
127
  }
171
128
 
172
129
  async function refresh() {
173
130
  const phase = unwrap(await api.lcu.get(PHASE_URI).catch(() => null));
174
131
  if (!active) return;
175
- if (phase === "ReadyCheck") queueAccept();
132
+ if (phase === "ReadyCheck") acceptNow();
176
133
  else resetReadyCheck();
177
134
  }
178
135
 
@@ -181,31 +138,18 @@ function syncButton() {
181
138
  if (!option) return;
182
139
  if (enabled()) option.setAttribute("selected", "true");
183
140
  else option.removeAttribute("selected");
184
- const field = document.getElementById(DELAY_INPUT_ID);
185
- if (field && document.activeElement !== field) field.value = String(delayValue);
186
141
  }
187
142
 
188
143
  function toggle() {
189
144
  const next = !enabled();
190
145
  try {
191
- api.setOption(PLUGIN_ID, OPTION_KEY, next);
146
+ api.setOption(PLUGIN_ID, ACCEPT_KEY, next, false);
192
147
  } catch {}
193
148
  syncButton();
194
- if (next) queueAccept();
149
+ if (next) acceptNow();
195
150
  else resetReadyCheck();
196
- }
197
-
198
- function onDelayInput(event) {
199
- const raw = Number(event.target.value);
200
- const next = Math.min(Math.max(Number.isFinite(raw) ? Math.floor(raw) : 0, 0), MAX_DELAY);
201
- writeDelay(next);
202
- if (event.type === "change") event.target.value = String(next);
203
- if (active && enabled() && timer) {
204
- clearTimer();
205
- queueAccept();
206
- }
207
- }
208
-
151
+ }
152
+
209
153
  function buildButton() {
210
154
  const root = document.createElement("div");
211
155
  root.id = ROOT_ID;
@@ -221,29 +165,15 @@ function buildButton() {
221
165
  event.preventDefault();
222
166
  event.stopPropagation();
223
167
  toggle();
224
- });
225
-
226
- const input = document.createElement("lol-uikit-flat-input");
227
- input.className = "auto-accept-delay";
228
- const field = document.createElement("input");
229
- field.id = DELAY_INPUT_ID;
230
- field.type = "number";
231
- field.min = "0";
232
- field.max = String(MAX_DELAY);
233
- field.step = "100";
234
- field.placeholder = "ms";
235
- field.title = "Delay before accepting, in milliseconds";
236
- field.value = String(delayValue);
237
- field.addEventListener("input", onDelayInput);
238
- field.addEventListener("change", onDelayInput);
239
- field.addEventListener("click", (event) => event.stopPropagation());
240
- input.append(field);
241
-
242
- root.append(option, input);
168
+ });
169
+
170
+ root.append(option);
243
171
  return root;
244
172
  }
245
173
 
246
174
  let buttonRetry = null;
175
+ let gameSelectGuardUntil = 0;
176
+ let gameSelectClickHandler = null;
247
177
 
248
178
  function footer() {
249
179
  return (
@@ -253,6 +183,7 @@ function footer() {
253
183
  }
254
184
 
255
185
  function gameSelectOpen() {
186
+ if (Date.now() < gameSelectGuardUntil) return true;
256
187
  try {
257
188
  return !!document.querySelector(".parties-game-select-screen.game-select-show");
258
189
  } catch {
@@ -260,6 +191,15 @@ function gameSelectOpen() {
260
191
  }
261
192
  }
262
193
 
194
+ function onGameSelectClick(event) {
195
+ const target = event.target;
196
+ if (!(target instanceof Element)) return;
197
+ if (!target.closest(".parties-game-select-screen, .play-button-container, .lobby-back-button")) return;
198
+ gameSelectGuardUntil = Date.now() + GAME_SELECT_GUARD_MS;
199
+ clearTimeout(buttonRetry);
200
+ buttonRetry = setTimeout(ensureButton, GAME_SELECT_GUARD_MS + 100);
201
+ }
202
+
263
203
  function ensureButton() {
264
204
  if (!active) return;
265
205
  clearTimeout(buttonRetry);
@@ -290,6 +230,10 @@ function onEnable(utils) {
290
230
  domObserver = new MutationObserver(scheduleEnsure);
291
231
  domObserver.observe(document.documentElement ?? document, { childList: true, subtree: true });
292
232
 
233
+ gameSelectClickHandler = onGameSelectClick;
234
+ document.addEventListener("pointerdown", gameSelectClickHandler, true);
235
+ document.addEventListener("click", gameSelectClickHandler, true);
236
+
293
237
  for (const uri of [PHASE_URI, READY_CHECK_URI]) {
294
238
  try {
295
239
  const off = utils.subscribe(uri, uri === PHASE_URI ? onPhase : onReadyCheck);
@@ -316,40 +260,28 @@ function onDisable() {
316
260
  domTimer = null;
317
261
  clearTimeout(buttonRetry);
318
262
  buttonRetry = null;
263
+ if (gameSelectClickHandler) {
264
+ document.removeEventListener("pointerdown", gameSelectClickHandler, true);
265
+ document.removeEventListener("click", gameSelectClickHandler, true);
266
+ }
267
+ gameSelectClickHandler = null;
268
+ gameSelectGuardUntil = 0;
319
269
  document.getElementById(ROOT_ID)?.remove();
320
270
  removeStyle?.();
321
271
  removeStyle = null;
322
272
  api = null;
323
273
  }
324
274
 
325
- function onOptionChange(key) {
326
- if (key !== OPTION_KEY) return;
327
- syncButton();
328
- if (enabled()) queueAccept();
329
- else resetReadyCheck();
330
- }
331
-
332
275
  export function init() {
333
276
  const registerWith = (utils) => {
334
277
  utils.registerPlugin({
335
278
  id: PLUGIN_ID,
336
279
  name: "Auto Accept",
337
280
  description:
338
- "Accepts the ready check automatically, with an optional delay. The same switch is shown natively in the lobby footer.",
281
+ "Accepts the ready check automatically. Use the Auto Accept switch in the lobby footer to turn it on or off.",
339
282
  version,
340
- defaultEnabled: true,
341
- options: [
342
- {
343
- key: OPTION_KEY,
344
- label: "Auto accept matches",
345
- description:
346
- "Accepts the ready check for you. The Auto Accept switch in the lobby footer toggles this same setting.",
347
- default: true,
348
- },
349
- ],
350
283
  onEnable,
351
284
  onDisable,
352
- onOptionChange,
353
285
  });
354
286
  };
355
287
 
@@ -360,4 +292,4 @@ export function init() {
360
292
  addEventListener("perfectionist:utils-ready", (event) => registerWith(event.detail ?? window.PerfectionistUtils), {
361
293
  once: true,
362
294
  });
363
- }
295
+ }
package/auto-claimer.js CHANGED
@@ -362,7 +362,6 @@ export function init() {
362
362
  name: "Auto Claimer",
363
363
  description: "Automatically claims battle pass, event and mission rewards the moment they become claimable.",
364
364
  version,
365
- defaultEnabled: true,
366
365
  options: [
367
366
  {
368
367
  key: "buy1Be",
@@ -402,4 +401,4 @@ export function init() {
402
401
  return;
403
402
  }
404
403
  addEventListener("perfectionist:utils-ready", (e) => registerWith(e.detail ?? window.PerfectionistUtils), { once: true });
405
- }
404
+ }
package/auto-read.js CHANGED
@@ -358,7 +358,6 @@ export function init() {
358
358
  name: "Auto Read",
359
359
  description: "Automatically marks quests, loot, Clash, TFT and client notices as read so no badge or dot stays lit.",
360
360
  version,
361
- defaultEnabled: true,
362
361
  options: [
363
362
  {
364
363
  type: "chips",
@@ -388,4 +387,4 @@ export function init() {
388
387
  return;
389
388
  }
390
389
  addEventListener("perfectionist:utils-ready", (e) => registerWith(e.detail ?? window.PerfectionistUtils), { once: true });
391
- }
390
+ }
package/ban-blocker.js CHANGED
@@ -632,7 +632,6 @@ export function init() {
632
632
  name: "Ban Blocker",
633
633
  description: "During bans, blocking a teammate instantly claims the champion they hover to ban.",
634
634
  version,
635
- defaultEnabled: true,
636
635
  onEnable,
637
636
  onDisable,
638
637
  });
@@ -647,4 +646,4 @@ export function init() {
647
646
  (e) => registerWith(e.detail ?? window.PerfectionistUtils),
648
647
  { once: true },
649
648
  );
650
- }
649
+ }
package/better-client.js CHANGED
@@ -286,8 +286,6 @@ function createFetch(env) {
286
286
  storeInCache(key, ttl, response);
287
287
  }, () => {});
288
288
  }
289
- // Every caller gets its own clone; the original response stays unread so
290
- // concurrent callers can clone it after one caller consumes the body.
291
289
  return request.then((response) => (response?.clone ? response.clone() : response));
292
290
  }
293
291
 
@@ -780,7 +778,6 @@ export function init() {
780
778
  description:
781
779
  "Debloats the client: blocks telemetry and analytics calls, merges duplicate requests, caches static client data, decodes images asynchronously and keeps settings, chat and friend requests usable during a game.",
782
780
  version,
783
- defaultEnabled: true,
784
781
  options: OPTIONS,
785
782
  onEnable,
786
783
  onDisable,
@@ -793,4 +790,4 @@ export function init() {
793
790
  return;
794
791
  }
795
792
  addEventListener("perfectionist:utils-ready", (e) => registerWith(e.detail ?? window.PerfectionistUtils), { once: true });
796
- }
793
+ }