perfectionist-plugins 2.4.0 → 2.4.1
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 +0 -20
- package/better-client.js +796 -1093
- package/hide-unowned-skins.js +205 -303
- package/package.json +1 -1
- package/perfectionist-utils.js +7 -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.",
|
|
@@ -127,10 +124,6 @@ const STYLE_CSS = `
|
|
|
127
124
|
#${PANEL_ID} .perf-chip:hover { color: #f0e6d2; border-color: #c89b3c; }
|
|
128
125
|
#${PANEL_ID} .perf-chip--on { color: #010a13; background: #c89b3c; border-color: #c89b3c; }
|
|
129
126
|
#${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
127
|
#${PANEL_ID} .perf-tools { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; margin-bottom: 14px; }
|
|
135
128
|
#${PANEL_ID} .perf-search { flex: 1 1 auto; min-width: 0; box-sizing: border-box;
|
|
136
129
|
font-family: "LoL Body", Arial, "Helvetica Neue", Helvetica, sans-serif; font-size: 12px; letter-spacing: 0.3px;
|
|
@@ -145,18 +138,6 @@ const STYLE_CSS = `
|
|
|
145
138
|
#${PANEL_ID} .perf-chip:disabled { opacity: 0.45; cursor: default; }
|
|
146
139
|
#${PANEL_ID} .perf-chip:disabled:hover { color: #a09b8c; border-color: #785a28; background: #0a1428; }
|
|
147
140
|
#${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
141
|
`;
|
|
161
142
|
|
|
162
143
|
const registry = new Map();
|
|
@@ -170,8 +151,10 @@ let coords = null;
|
|
|
170
151
|
const resolved = new Map();
|
|
171
152
|
let availableModules = BUNDLED_MODULES;
|
|
172
153
|
let pendingReload = false;
|
|
154
|
+
let modulesLoaded = false;
|
|
173
155
|
let searchQuery = "";
|
|
174
156
|
let runningVersion = version;
|
|
157
|
+
let coreStarted = false;
|
|
175
158
|
|
|
176
159
|
let state = { installed: {}, enabled: {}, options: {}, masterLocked: false };
|
|
177
160
|
|
|
@@ -322,20 +305,6 @@ function isInstalled(mod) {
|
|
|
322
305
|
return installed === undefined ? registry.has(mod.id) : Boolean(installed);
|
|
323
306
|
}
|
|
324
307
|
|
|
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
308
|
function moduleUrl(file) {
|
|
340
309
|
return `${CDN}/${PKG}@${runningVersion}/${file}`;
|
|
341
310
|
}
|
|
@@ -344,14 +313,6 @@ function importModule(url) {
|
|
|
344
313
|
return coords?.importModule ? coords.importModule(url) : import(url);
|
|
345
314
|
}
|
|
346
315
|
|
|
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
316
|
async function installModule(mod) {
|
|
356
317
|
state.installed[mod.id] = true;
|
|
357
318
|
delete state.enabled[mod.id];
|
|
@@ -368,105 +329,14 @@ async function uninstallModule(mod) {
|
|
|
368
329
|
refreshPanel();
|
|
369
330
|
}
|
|
370
331
|
|
|
371
|
-
const SAFE_PHASES = new Set(["None", "Lobby", "EndOfGame"]);
|
|
372
|
-
|
|
373
332
|
function reloadClientNow() {
|
|
374
333
|
if (typeof window.reloadClient === "function") window.reloadClient();
|
|
375
334
|
else window.location.reload();
|
|
376
335
|
}
|
|
377
336
|
|
|
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
337
|
async function loadModules() {
|
|
338
|
+
if (modulesLoaded) return;
|
|
339
|
+
modulesLoaded = true;
|
|
470
340
|
for (const mod of BUNDLED_MODULES) {
|
|
471
341
|
if (mod.core || resolved.has(mod.id) || registry.has(mod.id) || !isInstalled(mod)) continue;
|
|
472
342
|
try {
|
|
@@ -647,17 +517,6 @@ function buildOptionRow(mod, option, disabled, onChange) {
|
|
|
647
517
|
return row;
|
|
648
518
|
}
|
|
649
519
|
|
|
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
520
|
function buildChips(mod, option, locked) {
|
|
662
521
|
const block = el("div", "perf-chip-block");
|
|
663
522
|
block.title = option.description ?? "";
|
|
@@ -680,28 +539,6 @@ function buildChips(mod, option, locked) {
|
|
|
680
539
|
return block;
|
|
681
540
|
}
|
|
682
541
|
|
|
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
542
|
function buildOptions(mod, locked) {
|
|
706
543
|
const options = registry.get(mod.id)?.options ?? [];
|
|
707
544
|
if (options.length === 0) return null;
|
|
@@ -711,14 +548,6 @@ function buildOptions(mod, locked) {
|
|
|
711
548
|
wrap.append(buildChips(mod, option, locked));
|
|
712
549
|
continue;
|
|
713
550
|
}
|
|
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
551
|
if (!option.children?.length) {
|
|
723
552
|
wrap.append(buildOptionRow(mod, option, Boolean(locked)));
|
|
724
553
|
continue;
|
|
@@ -1072,8 +901,6 @@ const utils = {
|
|
|
1072
901
|
getAvailableModules,
|
|
1073
902
|
installModule,
|
|
1074
903
|
uninstallModule,
|
|
1075
|
-
loadPluginFile,
|
|
1076
|
-
checkForUpdates,
|
|
1077
904
|
watch,
|
|
1078
905
|
waitFor,
|
|
1079
906
|
addStyle,
|
|
@@ -1083,7 +910,8 @@ const utils = {
|
|
|
1083
910
|
};
|
|
1084
911
|
|
|
1085
912
|
export async function init(context, coordsProvided) {
|
|
1086
|
-
if (window.PerfectionistUtils) return;
|
|
913
|
+
if (coreStarted || window.PerfectionistUtils) return;
|
|
914
|
+
coreStarted = true;
|
|
1087
915
|
coords = coordsProvided ?? null;
|
|
1088
916
|
runningVersion = coords?.version || version;
|
|
1089
917
|
|
|
@@ -1097,7 +925,6 @@ export async function init(context, coordsProvided) {
|
|
|
1097
925
|
|
|
1098
926
|
try {
|
|
1099
927
|
await loadModules();
|
|
1100
|
-
checkForUpdates();
|
|
1101
928
|
} catch (error) {
|
|
1102
929
|
logError("startup failed.", error);
|
|
1103
930
|
}
|