perfectionist-plugins 2.4.0 → 2.4.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perfectionist-plugins",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
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",
@@ -1,6 +1,5 @@
1
1
  const READY_EVENT = "perfectionist:utils-ready";
2
2
  const STATE_KEY = "perfectionist-plugins:state";
3
- const CACHE_KEY = "perfectionist-plugins:cache";
4
3
  const NAV_ID = "perfectionist-plugins-nav";
5
4
  const PANEL_ID = "perfectionist-plugins-panel";
6
5
  const STYLE_ID = "perfectionist-plugins-style";
@@ -10,13 +9,11 @@ export const version = "2.4.0";
10
9
 
11
10
  const PKG = "perfectionist-plugins";
12
11
  const CDN = "https://cdn.jsdelivr.net/npm";
13
- const REGISTRY_URL = `https://registry.npmjs.org/${PKG}/latest`;
14
- const CORE_FILE = "perfectionist-utils.js";
15
12
 
16
13
  const BUNDLED_MODULES = [
17
14
  {
18
15
  id: "perfectionist-utils",
19
- file: CORE_FILE,
16
+ file: "perfectionist-utils.js",
20
17
  name: "Perfectionism Core",
21
18
  version,
22
19
  description: "Shared core for the Perfectionism plugins.",
@@ -64,6 +61,20 @@ const BUNDLED_MODULES = [
64
61
  version: "1.2.0",
65
62
  description: "Automatically marks quests, loot, Clash, TFT and client notices as read so no badge or dot stays lit.",
66
63
  },
64
+ {
65
+ id: "auto-accept",
66
+ file: "auto-accept.js",
67
+ name: "Auto Accept",
68
+ version: "1.1.0",
69
+ description: "Accepts the ready check automatically, with an optional delay and a native switch in the lobby footer.",
70
+ },
71
+ {
72
+ id: "dodge-helper",
73
+ file: "dodge-helper.js",
74
+ name: "Dodge Helper",
75
+ version: "1.4.0",
76
+ description: "Adds a DODGE/QUIT button to champion select: DODGE with the estimated LP loss for ranked queues, QUIT for every other mode.",
77
+ },
67
78
  ];
68
79
 
69
80
  const STYLE_CSS = `
@@ -127,10 +138,6 @@ const STYLE_CSS = `
127
138
  #${PANEL_ID} .perf-chip:hover { color: #f0e6d2; border-color: #c89b3c; }
128
139
  #${PANEL_ID} .perf-chip--on { color: #010a13; background: #c89b3c; border-color: #c89b3c; }
129
140
  #${PANEL_ID} .perf-chip--on:hover { background: #f0e6d2; }
130
- #${PANEL_ID} .perf-info { margin-top: 11px; padding-top: 8px; border-top: 1px solid rgba(120, 90, 40, 0.35); }
131
- #${PANEL_ID} .perf-info-line { font-size: 10px; line-height: 16px; letter-spacing: 0.2px; color: #a09b8c;
132
- white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
133
-
134
141
  #${PANEL_ID} .perf-tools { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; margin-bottom: 14px; }
135
142
  #${PANEL_ID} .perf-search { flex: 1 1 auto; min-width: 0; box-sizing: border-box;
136
143
  font-family: "LoL Body", Arial, "Helvetica Neue", Helvetica, sans-serif; font-size: 12px; letter-spacing: 0.3px;
@@ -145,18 +152,6 @@ const STYLE_CSS = `
145
152
  #${PANEL_ID} .perf-chip:disabled { opacity: 0.45; cursor: default; }
146
153
  #${PANEL_ID} .perf-chip:disabled:hover { color: #a09b8c; border-color: #785a28; background: #0a1428; }
147
154
  #${PANEL_ID} .perf-chip--on:disabled:hover { color: #010a13; border-color: #c89b3c; background: #c89b3c; }
148
-
149
- .perf-update { position: fixed; inset: 0; z-index: 100000; display: flex; align-items: center;
150
- justify-content: center; background: rgba(1, 10, 19, 0.72); }
151
- .perf-update-box { width: 430px; box-sizing: border-box; padding: 22px 26px;
152
- background: #010a13; border: 1px solid #785a28; box-shadow: 0 0 32px rgba(0, 0, 0, 0.8); }
153
- .perf-update-title { font-family: "LoL Display", "Times New Roman", Times, Baskerville, Georgia, serif;
154
- font-size: 15px; font-weight: 700; letter-spacing: 1.05px; text-transform: uppercase; color: #f0e6d2; }
155
- .perf-update-status { font-family: "LoL Body", Arial, sans-serif; font-size: 12px; letter-spacing: 0.3px;
156
- color: #a09b8c; margin-top: 12px; line-height: 1.5; }
157
- .perf-update-track { height: 6px; margin-top: 16px; background: #0a1428; border: 1px solid #785a28; }
158
- .perf-update-fill { height: 100%; width: 0; background: #c89b3c; transition: width 0.25s; }
159
- .perf-update-close { margin-top: 18px; }
160
155
  `;
161
156
 
162
157
  const registry = new Map();
@@ -170,8 +165,10 @@ let coords = null;
170
165
  const resolved = new Map();
171
166
  let availableModules = BUNDLED_MODULES;
172
167
  let pendingReload = false;
168
+ let modulesLoaded = false;
173
169
  let searchQuery = "";
174
170
  let runningVersion = version;
171
+ let coreStarted = false;
175
172
 
176
173
  let state = { installed: {}, enabled: {}, options: {}, masterLocked: false };
177
174
 
@@ -322,20 +319,6 @@ function isInstalled(mod) {
322
319
  return installed === undefined ? registry.has(mod.id) : Boolean(installed);
323
320
  }
324
321
 
325
- function newerVersion(a, b) {
326
- const pa = String(a ?? "0")
327
- .split(".")
328
- .map(Number);
329
- const pb = String(b ?? "0")
330
- .split(".")
331
- .map(Number);
332
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
333
- const diff = (pa[i] || 0) - (pb[i] || 0);
334
- if (diff !== 0) return diff > 0;
335
- }
336
- return false;
337
- }
338
-
339
322
  function moduleUrl(file) {
340
323
  return `${CDN}/${PKG}@${runningVersion}/${file}`;
341
324
  }
@@ -344,14 +327,6 @@ function importModule(url) {
344
327
  return coords?.importModule ? coords.importModule(url) : import(url);
345
328
  }
346
329
 
347
- // Lets a feature module (BetterClient → Pages Remake) pull its code only when enabled.
348
- // A local override keeps the dev harness on freshly edited files instead of the CDN copy.
349
- function loadPluginFile(file) {
350
- const local = typeof window !== "undefined" ? window.PerfectionistLocalBase : null;
351
- const url = local ? new URL(file, local).href : moduleUrl(file);
352
- return importModule(url);
353
- }
354
-
355
330
  async function installModule(mod) {
356
331
  state.installed[mod.id] = true;
357
332
  delete state.enabled[mod.id];
@@ -368,105 +343,14 @@ async function uninstallModule(mod) {
368
343
  refreshPanel();
369
344
  }
370
345
 
371
- const SAFE_PHASES = new Set(["None", "Lobby", "EndOfGame"]);
372
-
373
346
  function reloadClientNow() {
374
347
  if (typeof window.reloadClient === "function") window.reloadClient();
375
348
  else window.location.reload();
376
349
  }
377
350
 
378
- async function reloadWhenSafe() {
379
- for (;;) {
380
- let phase = null;
381
- try {
382
- phase = await apiGet("/lol-gameflow/v1/gameflow-phase");
383
- } catch {
384
- phase = null;
385
- }
386
- if (phase == null || SAFE_PHASES.has(phase)) break;
387
- await new Promise((resolve) => setTimeout(resolve, 5000));
388
- }
389
- reloadClientNow();
390
- }
391
-
392
- function apiGet(path) {
393
- return fetch(path, { headers: { Accept: "application/json" } }).then((response) => {
394
- if (!response.ok) throw new Error(`${path} responded ${response.status}`);
395
- return response.status === 204 ? null : response.json();
396
- });
397
- }
398
-
399
- function buildUpdateWindow() {
400
- const root = el("div", "perf-update");
401
- const box = el("div", "perf-update-box");
402
- box.append(el("div", "perf-update-title", "Installing Update"));
403
- const status = el("div", "perf-update-status", "Downloading...");
404
- const track = el("div", "perf-update-track");
405
- const fill = el("div", "perf-update-fill");
406
- track.append(fill);
407
- box.append(status, track);
408
- root.append(box);
409
- return {
410
- root,
411
- setStatus: (text) => {
412
- status.textContent = text;
413
- },
414
- setProgress: (fraction) => {
415
- fill.style.width = `${Math.round(Math.max(0, Math.min(1, fraction)) * 100)}%`;
416
- },
417
- fail: (text) => {
418
- status.textContent = text;
419
- const close = el("button", "perf-btn perf-update-close", "Close");
420
- close.addEventListener("click", () => root.remove());
421
- box.append(close);
422
- },
423
- };
424
- }
425
-
426
- async function runUpdate(target) {
427
- const win = buildUpdateWindow();
428
- (document.body ?? document.documentElement).append(win.root);
429
- const files = [{ name: "Perfectionism Core", file: CORE_FILE }, ...BUNDLED_MODULES.filter((m) => !m.core)];
430
- const sources = {};
431
- for (let i = 0; i < files.length; i++) {
432
- win.setStatus(`Downloading ${files[i].name}...`);
433
- win.setProgress(i / files.length);
434
- try {
435
- const response = await fetch(`${CDN}/${PKG}@${target}/${files[i].file}`);
436
- if (response.status === 404 && files[i].id) continue;
437
- if (!response.ok) throw new Error(`download failed (${response.status})`);
438
- const text = await response.text();
439
- if (!text || !/export/.test(text)) throw new Error("the downloaded file looks invalid");
440
- if (files[i].id) sources[files[i].id] = text;
441
- } catch (error) {
442
- logError(`could not update to ${target}.`, error);
443
- win.fail(`Could not update to ${target}. It will be retried on the next client start.`);
444
- return;
445
- }
446
- }
447
- try {
448
- localStorage.setItem(CACHE_KEY, JSON.stringify({ version: target, files: sources }));
449
- } catch {}
450
- win.setProgress(1);
451
- runningVersion = target;
452
- saveState();
453
- win.setStatus("Update installed. Restarting the client...");
454
- reloadWhenSafe();
455
- }
456
-
457
- async function checkForUpdates() {
458
- if (!/^\d+\.\d+\.\d+$/.test(runningVersion)) return;
459
- let target = null;
460
- try {
461
- const response = await fetch(REGISTRY_URL, { headers: { Accept: "application/json" }, cache: "no-store" });
462
- if (response.ok) target = (await response.json())?.version ?? null;
463
- } catch {
464
- return;
465
- }
466
- if (target && newerVersion(target, runningVersion)) runUpdate(target);
467
- }
468
-
469
351
  async function loadModules() {
352
+ if (modulesLoaded) return;
353
+ modulesLoaded = true;
470
354
  for (const mod of BUNDLED_MODULES) {
471
355
  if (mod.core || resolved.has(mod.id) || registry.has(mod.id) || !isInstalled(mod)) continue;
472
356
  try {
@@ -647,17 +531,6 @@ function buildOptionRow(mod, option, disabled, onChange) {
647
531
  return row;
648
532
  }
649
533
 
650
- function buildInfo(option) {
651
- const block = el("div", "perf-info");
652
- if (option.label) block.append(el("div", "perf-chips-label", option.label));
653
- let lines = [];
654
- try {
655
- lines = option.lines?.() ?? [];
656
- } catch {}
657
- for (const line of lines) block.append(el("div", "perf-info-line", String(line)));
658
- return block;
659
- }
660
-
661
534
  function buildChips(mod, option, locked) {
662
535
  const block = el("div", "perf-chip-block");
663
536
  block.title = option.description ?? "";
@@ -680,28 +553,6 @@ function buildChips(mod, option, locked) {
680
553
  return block;
681
554
  }
682
555
 
683
- function buildAction(option, locked) {
684
- const button = el("button", "perf-btn", option.label);
685
- button.type = "button";
686
- button.title = option.description ?? "";
687
- button.disabled = Boolean(locked);
688
- button.addEventListener("click", async () => {
689
- const original = button.textContent;
690
- button.disabled = true;
691
- try {
692
- await option.onClick?.();
693
- button.textContent = "Copied";
694
- } catch {
695
- button.textContent = "Copy failed";
696
- }
697
- setTimeout(() => {
698
- button.textContent = original;
699
- button.disabled = Boolean(locked);
700
- }, 1600);
701
- });
702
- return button;
703
- }
704
-
705
556
  function buildOptions(mod, locked) {
706
557
  const options = registry.get(mod.id)?.options ?? [];
707
558
  if (options.length === 0) return null;
@@ -711,14 +562,6 @@ function buildOptions(mod, locked) {
711
562
  wrap.append(buildChips(mod, option, locked));
712
563
  continue;
713
564
  }
714
- if (option.type === "info") {
715
- wrap.append(buildInfo(option));
716
- continue;
717
- }
718
- if (option.type === "action") {
719
- wrap.append(buildAction(option, locked));
720
- continue;
721
- }
722
565
  if (!option.children?.length) {
723
566
  wrap.append(buildOptionRow(mod, option, Boolean(locked)));
724
567
  continue;
@@ -1072,8 +915,6 @@ const utils = {
1072
915
  getAvailableModules,
1073
916
  installModule,
1074
917
  uninstallModule,
1075
- loadPluginFile,
1076
- checkForUpdates,
1077
918
  watch,
1078
919
  waitFor,
1079
920
  addStyle,
@@ -1083,7 +924,8 @@ const utils = {
1083
924
  };
1084
925
 
1085
926
  export async function init(context, coordsProvided) {
1086
- if (window.PerfectionistUtils) return;
927
+ if (coreStarted || window.PerfectionistUtils) return;
928
+ coreStarted = true;
1087
929
  coords = coordsProvided ?? null;
1088
930
  runningVersion = coords?.version || version;
1089
931
 
@@ -1097,7 +939,6 @@ export async function init(context, coordsProvided) {
1097
939
 
1098
940
  try {
1099
941
  await loadModules();
1100
- checkForUpdates();
1101
942
  } catch (error) {
1102
943
  logError("startup failed.", error);
1103
944
  }