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/README.md +1 -35
- package/better-client.js +796 -1093
- package/hide-unowned-skins.js +205 -303
- package/package.json +1 -1
- package/perfectionist-utils.js +21 -180
package/package.json
CHANGED
package/perfectionist-utils.js
CHANGED
|
@@ -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:
|
|
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
|
}
|