perfectionist-plugins 2.0.0 → 2.4.0
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 +143 -121
- package/better-client.js +1093 -512
- package/hide-unowned-skins.js +130 -63
- package/package.json +36 -37
- package/perfectionist-utils.js +57 -4
- package/unlock-profile-skins.js +153 -67
- package/tools/betterclient-monitor.js +0 -269
package/hide-unowned-skins.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const version = "2.
|
|
1
|
+
export const version = "2.5.1";
|
|
2
2
|
|
|
3
3
|
const PLUGIN_ID = "hide-unowned-skins";
|
|
4
4
|
const STYLE_ID = "perfectionist-hide-unowned-skins-style";
|
|
@@ -18,7 +18,7 @@ const RETRY_MS = 5000;
|
|
|
18
18
|
const STATE_KEY = "perfectionist-plugins:state";
|
|
19
19
|
|
|
20
20
|
const CSS = `
|
|
21
|
-
.${HIDDEN_CLASS} { display: none !important; margin: 0 !important; padding: 0 !important; border: none !important; }
|
|
21
|
+
.${HIDDEN_CLASS} { display: none !important; margin: 0 !important; padding: 0 !important; border: none !important; pointer-events: none !important; }
|
|
22
22
|
[data-perf-dots] { justify-content: center !important; }
|
|
23
23
|
`;
|
|
24
24
|
|
|
@@ -162,13 +162,13 @@ function installFetchFilter() {
|
|
|
162
162
|
|
|
163
163
|
const wrapped = function (input, init) {
|
|
164
164
|
const url =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
165
|
+
typeof input === "string"
|
|
166
|
+
? input
|
|
167
|
+
: typeof input?.url === "string"
|
|
168
|
+
? input.url
|
|
169
|
+
: typeof input?.href === "string"
|
|
170
|
+
? input.href
|
|
171
|
+
: "";
|
|
172
172
|
const promise = original.call(this, input, init);
|
|
173
173
|
if (!filterActive || url.split("?")[0].indexOf(CAROUSEL_URI) === -1) return promise;
|
|
174
174
|
return promise.then((response) => {
|
|
@@ -224,8 +224,7 @@ function installXhrFilter() {
|
|
|
224
224
|
const kept = filterSkinList(json);
|
|
225
225
|
recordClientOrder(kept ?? json);
|
|
226
226
|
if (kept) cache.filtered = JSON.stringify(kept);
|
|
227
|
-
} catch {
|
|
228
|
-
}
|
|
227
|
+
} catch {}
|
|
229
228
|
}
|
|
230
229
|
return cache.filtered;
|
|
231
230
|
};
|
|
@@ -281,14 +280,16 @@ let selectInFlight = false;
|
|
|
281
280
|
let sessionMisses = 0;
|
|
282
281
|
let championMisses = 0;
|
|
283
282
|
let renderObserver = null;
|
|
284
|
-
|
|
285
|
-
const lastAppliedLeft = new Map();
|
|
286
|
-
const skinSuffixCache = new Map();
|
|
283
|
+
let sessionActive = false;
|
|
287
284
|
let repackTimer = null;
|
|
288
285
|
let removeStyle = null;
|
|
289
286
|
let lastSelectedDomIndex = -1;
|
|
290
287
|
let navFixAt = 0;
|
|
291
288
|
let lastSkipSignature = null;
|
|
289
|
+
let itemSkinIds = new WeakMap();
|
|
290
|
+
const repackOriginals = new Map();
|
|
291
|
+
const lastAppliedLeft = new Map();
|
|
292
|
+
const skinSuffixCache = new Map();
|
|
292
293
|
const widthCache = new Map();
|
|
293
294
|
|
|
294
295
|
function logError(scope, error) {
|
|
@@ -301,7 +302,8 @@ function skinSuffixOf(text) {
|
|
|
301
302
|
return /Skins\/Base\//i.test(text) ? 0 : null;
|
|
302
303
|
}
|
|
303
304
|
|
|
304
|
-
function
|
|
305
|
+
function skinIdFromImages(item) {
|
|
306
|
+
if (currentChampionId === null) return null;
|
|
305
307
|
for (const img of item.querySelectorAll("img[src]")) {
|
|
306
308
|
const src = img.getAttribute("src");
|
|
307
309
|
let suffix = skinSuffixCache.get(src);
|
|
@@ -315,6 +317,25 @@ function readSkinId(item) {
|
|
|
315
317
|
return suffix === null ? null : currentChampionId * 1000 + suffix;
|
|
316
318
|
}
|
|
317
319
|
|
|
320
|
+
function readSkinId(item) {
|
|
321
|
+
return itemSkinIds.get(item) ?? skinIdFromImages(item);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function effectiveOrder() {
|
|
325
|
+
return clientSkinOrder.length > 0 ? clientSkinOrder : carouselOrder;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function mapCarouselToItems(items) {
|
|
329
|
+
const order = effectiveOrder();
|
|
330
|
+
itemSkinIds = new WeakMap();
|
|
331
|
+
if (order.length === 0 || order.length !== items.length) return false;
|
|
332
|
+
items.forEach((item, index) => {
|
|
333
|
+
const id = order[index];
|
|
334
|
+
if (typeof id === "number") itemSkinIds.set(item, id);
|
|
335
|
+
});
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
|
|
318
339
|
function hideTargetFor(item) {
|
|
319
340
|
let target = item;
|
|
320
341
|
for (let depth = 0; depth < 2; depth++) {
|
|
@@ -366,6 +387,40 @@ function guardSelection(items) {
|
|
|
366
387
|
return true;
|
|
367
388
|
}
|
|
368
389
|
|
|
390
|
+
function leftToPx(item, raw) {
|
|
391
|
+
if (!raw) return 0;
|
|
392
|
+
if (raw.endsWith("px")) return parseFloat(raw);
|
|
393
|
+
if (raw.endsWith("%")) return (parseFloat(raw) / 100) * (item.parentElement?.offsetWidth || 468);
|
|
394
|
+
return 0;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function originalLeftOf(item) {
|
|
398
|
+
const original = repackOriginals.get(item);
|
|
399
|
+
return leftToPx(item, original ? original.value : item.style.getPropertyValue("left"));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function refreshOriginal(item) {
|
|
403
|
+
const current = item.style.getPropertyValue("left");
|
|
404
|
+
const priority = item.style.getPropertyPriority("left");
|
|
405
|
+
const last = lastAppliedLeft.get(item);
|
|
406
|
+
if (last !== undefined && current === last && priority === "important") return;
|
|
407
|
+
repackOriginals.set(item, { value: current, priority });
|
|
408
|
+
lastAppliedLeft.delete(item);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function widthOf(item, anchor) {
|
|
412
|
+
if (item.offsetWidth > 0) {
|
|
413
|
+
widthCache.set(item, item.offsetWidth);
|
|
414
|
+
return item.offsetWidth;
|
|
415
|
+
}
|
|
416
|
+
const measured = item.getBoundingClientRect().width;
|
|
417
|
+
if (measured > 0) {
|
|
418
|
+
widthCache.set(item, measured);
|
|
419
|
+
return measured;
|
|
420
|
+
}
|
|
421
|
+
return widthCache.get(item) ?? (item === anchor ? 117 : 73);
|
|
422
|
+
}
|
|
423
|
+
|
|
369
424
|
function repackVisibleItems(items) {
|
|
370
425
|
pruneRepack();
|
|
371
426
|
const visible = items.filter((item) => !item.classList.contains(HIDDEN_CLASS));
|
|
@@ -374,32 +429,15 @@ function repackVisibleItems(items) {
|
|
|
374
429
|
return;
|
|
375
430
|
}
|
|
376
431
|
|
|
377
|
-
const selectedIndex = items.findIndex((item) => item.classList.contains(
|
|
432
|
+
const selectedIndex = items.findIndex((item) => item.classList.contains(SELECTED_CLASS));
|
|
378
433
|
let anchor = selectedIndex >= 0 ? items[selectedIndex] : null;
|
|
379
434
|
if (!anchor && selectedSkinId !== null) {
|
|
380
435
|
anchor = visible.find((item) => readSkinId(item) === selectedSkinId) ?? null;
|
|
381
436
|
}
|
|
382
437
|
if (!anchor) anchor = visible[Math.floor(visible.length / 2)];
|
|
383
438
|
const anchorDomIndex = items.indexOf(anchor);
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
const measured = item.getBoundingClientRect().width;
|
|
387
|
-
if (measured > 0) {
|
|
388
|
-
widthCache.set(item, measured);
|
|
389
|
-
return measured;
|
|
390
|
-
}
|
|
391
|
-
return widthCache.get(item) ?? (item === anchor ? 117 : 73);
|
|
392
|
-
};
|
|
393
|
-
const toPx = (item) => {
|
|
394
|
-
const raw = item.style.left || "0";
|
|
395
|
-
if (raw.endsWith("px")) return parseFloat(raw);
|
|
396
|
-
const box = item.parentElement?.getBoundingClientRect().width || 468;
|
|
397
|
-
return (parseFloat(raw) / 100) * box;
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
const anchorLeft = toPx(anchor);
|
|
401
|
-
let rightX = anchorLeft + widthOf(anchor);
|
|
402
|
-
let leftX = anchorLeft;
|
|
439
|
+
refreshOriginal(anchor);
|
|
440
|
+
const anchorLeft = originalLeftOf(anchor);
|
|
403
441
|
|
|
404
442
|
const setLeft = (item, x) => {
|
|
405
443
|
const want = `${x}px`;
|
|
@@ -416,19 +454,23 @@ function repackVisibleItems(items) {
|
|
|
416
454
|
lastAppliedLeft.set(item, want);
|
|
417
455
|
};
|
|
418
456
|
|
|
419
|
-
|
|
420
|
-
|
|
457
|
+
let rightX = anchorLeft + widthOf(anchor, anchor);
|
|
458
|
+
for (let index = anchorDomIndex + 1; index < items.length; index++) {
|
|
459
|
+
const item = items[index];
|
|
460
|
+
if (item.classList.contains(HIDDEN_CLASS)) continue;
|
|
461
|
+
const width = widthOf(item, anchor);
|
|
462
|
+
setLeft(item, rightX);
|
|
463
|
+
rightX += width;
|
|
464
|
+
}
|
|
421
465
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
});
|
|
466
|
+
let leftX = anchorLeft;
|
|
467
|
+
for (let index = anchorDomIndex - 1; index >= 0; index--) {
|
|
468
|
+
const item = items[index];
|
|
469
|
+
if (item.classList.contains(HIDDEN_CLASS)) continue;
|
|
470
|
+
const width = widthOf(item, anchor);
|
|
471
|
+
leftX -= width;
|
|
472
|
+
setLeft(item, leftX);
|
|
473
|
+
}
|
|
432
474
|
}
|
|
433
475
|
|
|
434
476
|
function restoreRepack() {
|
|
@@ -446,6 +488,7 @@ function restoreRepack() {
|
|
|
446
488
|
|
|
447
489
|
function applyAll() {
|
|
448
490
|
const items = [...document.querySelectorAll(SKIN_ITEM_SELECTOR)];
|
|
491
|
+
mapCarouselToItems(items);
|
|
449
492
|
let hidden = 0;
|
|
450
493
|
items.forEach((item) => {
|
|
451
494
|
const skinId = currentChampionId === null ? null : readSkinId(item);
|
|
@@ -457,9 +500,10 @@ function applyAll() {
|
|
|
457
500
|
|
|
458
501
|
const dots = [...document.querySelectorAll(DOT_SELECTOR)];
|
|
459
502
|
let dotsHidden = 0;
|
|
460
|
-
const
|
|
503
|
+
const dotOrder = effectiveOrder();
|
|
504
|
+
const dotsMapped = dotOrder.length > 0 && dots.length === dotOrder.length;
|
|
461
505
|
dots.forEach((dot, index) => {
|
|
462
|
-
const skinId = dotsMapped ?
|
|
506
|
+
const skinId = dotsMapped ? dotOrder[index] : undefined;
|
|
463
507
|
const hide = ownedSkinIds !== null && skinId !== undefined && allSkinIds !== null && allSkinIds.has(skinId) && !ownedSkinIds.has(skinId);
|
|
464
508
|
if (hide) dotsHidden++;
|
|
465
509
|
dot.classList.toggle(HIDDEN_CLASS, hide);
|
|
@@ -481,6 +525,27 @@ function scheduleRepack() {
|
|
|
481
525
|
repackTimer = setTimeout(runRepack, REPACK_SETTLE_MS);
|
|
482
526
|
}
|
|
483
527
|
|
|
528
|
+
function startRenderObserver() {
|
|
529
|
+
if (!sessionActive || renderObserver || !api) return;
|
|
530
|
+
renderObserver = new MutationObserver(() => {
|
|
531
|
+
ensureSourceFilter();
|
|
532
|
+
const item = document.querySelector(SKIN_ITEM_SELECTOR);
|
|
533
|
+
if (!item && !document.querySelector(DOT_SELECTOR)) return;
|
|
534
|
+
applyAllGuarded();
|
|
535
|
+
if (item) scheduleRefresh();
|
|
536
|
+
});
|
|
537
|
+
try {
|
|
538
|
+
renderObserver.observe(document.documentElement ?? document, OBSERVER_OPTIONS);
|
|
539
|
+
} catch {
|
|
540
|
+
renderObserver = null;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function stopRenderObserver() {
|
|
545
|
+
renderObserver?.disconnect();
|
|
546
|
+
renderObserver = null;
|
|
547
|
+
}
|
|
548
|
+
|
|
484
549
|
function runRepack() {
|
|
485
550
|
repackTimer = null;
|
|
486
551
|
renderObserver?.disconnect();
|
|
@@ -490,7 +555,7 @@ function runRepack() {
|
|
|
490
555
|
if (ownedSkinIds !== null && hidden) repackVisibleItems(items);
|
|
491
556
|
else restoreRepack();
|
|
492
557
|
} finally {
|
|
493
|
-
|
|
558
|
+
startRenderObserver();
|
|
494
559
|
}
|
|
495
560
|
}
|
|
496
561
|
|
|
@@ -499,7 +564,7 @@ function applyAllGuarded() {
|
|
|
499
564
|
try {
|
|
500
565
|
applyAll();
|
|
501
566
|
} finally {
|
|
502
|
-
|
|
567
|
+
startRenderObserver();
|
|
503
568
|
}
|
|
504
569
|
}
|
|
505
570
|
|
|
@@ -656,11 +721,15 @@ function scheduleRefresh() {
|
|
|
656
721
|
function onSessionPush(raw) {
|
|
657
722
|
const msg = raw ?? {};
|
|
658
723
|
if (msg.eventType === "Delete") {
|
|
724
|
+
sessionActive = false;
|
|
725
|
+
stopRenderObserver();
|
|
659
726
|
resetState();
|
|
660
727
|
return;
|
|
661
728
|
}
|
|
662
729
|
const s = msg.data ?? msg;
|
|
663
730
|
if (!s?.myTeam) return;
|
|
731
|
+
sessionActive = true;
|
|
732
|
+
startRenderObserver();
|
|
664
733
|
updateForSession(s, null);
|
|
665
734
|
}
|
|
666
735
|
|
|
@@ -671,11 +740,17 @@ async function pollSession() {
|
|
|
671
740
|
try {
|
|
672
741
|
const session = await api.lcu.get(SESSION_URI);
|
|
673
742
|
sessionMisses = 0;
|
|
743
|
+
sessionActive = true;
|
|
744
|
+
startRenderObserver();
|
|
674
745
|
const selection = await api.lcu.get(SELECTION_URI).catch(() => null);
|
|
675
746
|
updateForSession(session, selection);
|
|
676
747
|
} catch {
|
|
677
748
|
sessionMisses++;
|
|
678
|
-
if (sessionMisses >= 3)
|
|
749
|
+
if (sessionMisses >= 3) {
|
|
750
|
+
sessionActive = false;
|
|
751
|
+
stopRenderObserver();
|
|
752
|
+
resetState();
|
|
753
|
+
}
|
|
679
754
|
} finally {
|
|
680
755
|
pollInFlight = false;
|
|
681
756
|
}
|
|
@@ -684,17 +759,9 @@ async function pollSession() {
|
|
|
684
759
|
function onEnable(utils) {
|
|
685
760
|
api = utils;
|
|
686
761
|
filterActive = true;
|
|
762
|
+
sessionActive = false;
|
|
687
763
|
removeStyle = api.addStyle(STYLE_ID, CSS);
|
|
688
764
|
|
|
689
|
-
renderObserver = new MutationObserver(() => {
|
|
690
|
-
ensureSourceFilter();
|
|
691
|
-
const item = document.querySelector(SKIN_ITEM_SELECTOR);
|
|
692
|
-
if (!item && !document.querySelector(DOT_SELECTOR)) return;
|
|
693
|
-
applyAllGuarded();
|
|
694
|
-
if (item) scheduleRefresh();
|
|
695
|
-
});
|
|
696
|
-
renderObserver.observe(document.documentElement ?? document, OBSERVER_OPTIONS);
|
|
697
|
-
|
|
698
765
|
window.addEventListener("resize", onWindowResize);
|
|
699
766
|
unsubscribe = api.subscribe(SESSION_URI, onSessionPush);
|
|
700
767
|
pollSession();
|
|
@@ -708,9 +775,9 @@ function onDisable() {
|
|
|
708
775
|
refreshTimer = null;
|
|
709
776
|
clearTimeout(repackTimer);
|
|
710
777
|
repackTimer = null;
|
|
711
|
-
|
|
778
|
+
sessionActive = false;
|
|
779
|
+
stopRenderObserver();
|
|
712
780
|
removeStyle?.();
|
|
713
|
-
renderObserver = null;
|
|
714
781
|
removeStyle = null;
|
|
715
782
|
filterActive = false;
|
|
716
783
|
|
package/package.json
CHANGED
|
@@ -1,37 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "perfectionist-plugins",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Small plugins that fix the annoying parts of League of Legends champ select. Built for Pengu Loader.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "perfectionist-utils.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./perfectionist-utils.js",
|
|
9
|
-
"./perfectionist-utils.js": "./perfectionist-utils.js",
|
|
10
|
-
"./ban-blocker.js": "./ban-blocker.js",
|
|
11
|
-
"./hide-unowned-skins.js": "./hide-unowned-skins.js",
|
|
12
|
-
"./unlock-profile-skins.js": "./unlock-profile-skins.js",
|
|
13
|
-
"./auto-claimer.js": "./auto-claimer.js",
|
|
14
|
-
"./auto-read.js": "./auto-read.js",
|
|
15
|
-
"./better-client.js": "./better-client.js"
|
|
16
|
-
},
|
|
17
|
-
"files": [
|
|
18
|
-
"perfectionist-utils.js",
|
|
19
|
-
"ban-blocker.js",
|
|
20
|
-
"hide-unowned-skins.js",
|
|
21
|
-
"unlock-profile-skins.js",
|
|
22
|
-
"auto-claimer.js",
|
|
23
|
-
"auto-read.js",
|
|
24
|
-
"better-client.js"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "perfectionist-plugins",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "Small plugins that fix the annoying parts of League of Legends champ select. Built for Pengu Loader.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "perfectionist-utils.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./perfectionist-utils.js",
|
|
9
|
+
"./perfectionist-utils.js": "./perfectionist-utils.js",
|
|
10
|
+
"./ban-blocker.js": "./ban-blocker.js",
|
|
11
|
+
"./hide-unowned-skins.js": "./hide-unowned-skins.js",
|
|
12
|
+
"./unlock-profile-skins.js": "./unlock-profile-skins.js",
|
|
13
|
+
"./auto-claimer.js": "./auto-claimer.js",
|
|
14
|
+
"./auto-read.js": "./auto-read.js",
|
|
15
|
+
"./better-client.js": "./better-client.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"perfectionist-utils.js",
|
|
19
|
+
"ban-blocker.js",
|
|
20
|
+
"hide-unowned-skins.js",
|
|
21
|
+
"unlock-profile-skins.js",
|
|
22
|
+
"auto-claimer.js",
|
|
23
|
+
"auto-read.js",
|
|
24
|
+
"better-client.js"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/SrKing-9812/Perfectionist-Plugins.git"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"pengu-loader",
|
|
32
|
+
"league-of-legends",
|
|
33
|
+
"plugin"
|
|
34
|
+
],
|
|
35
|
+
"author": "Sr. King"
|
|
36
|
+
}
|
package/perfectionist-utils.js
CHANGED
|
@@ -6,7 +6,7 @@ const PANEL_ID = "perfectionist-plugins-panel";
|
|
|
6
6
|
const STYLE_ID = "perfectionist-plugins-style";
|
|
7
7
|
const TAB_TITLE = "Perfectionism";
|
|
8
8
|
|
|
9
|
-
export const version = "2.
|
|
9
|
+
export const version = "2.4.0";
|
|
10
10
|
|
|
11
11
|
const PKG = "perfectionist-plugins";
|
|
12
12
|
const CDN = "https://cdn.jsdelivr.net/npm";
|
|
@@ -26,7 +26,7 @@ const BUNDLED_MODULES = [
|
|
|
26
26
|
id: "better-client",
|
|
27
27
|
file: "better-client.js",
|
|
28
28
|
name: "BetterClient",
|
|
29
|
-
version: "1.
|
|
29
|
+
version: "1.7.0",
|
|
30
30
|
description: "Debloats the client: blocks telemetry, merges duplicate requests, caches immutable data, decodes images asynchronously and fixes several bugs.",
|
|
31
31
|
},
|
|
32
32
|
{
|
|
@@ -40,14 +40,14 @@ const BUNDLED_MODULES = [
|
|
|
40
40
|
id: "unlock-profile-skins",
|
|
41
41
|
file: "unlock-profile-skins.js",
|
|
42
42
|
name: "Unlock Profile Skins",
|
|
43
|
-
version: "1.2.
|
|
43
|
+
version: "1.2.4",
|
|
44
44
|
description: "Unlocks every skin in the Set profile background window.",
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
id: "hide-unowned-skins",
|
|
48
48
|
file: "hide-unowned-skins.js",
|
|
49
49
|
name: "Hide Unowned Skins",
|
|
50
|
-
version: "2.
|
|
50
|
+
version: "2.5.1",
|
|
51
51
|
description: "In champion select, only shows the skins you own for the picked champion.",
|
|
52
52
|
},
|
|
53
53
|
{
|
|
@@ -127,6 +127,9 @@ const STYLE_CSS = `
|
|
|
127
127
|
#${PANEL_ID} .perf-chip:hover { color: #f0e6d2; border-color: #c89b3c; }
|
|
128
128
|
#${PANEL_ID} .perf-chip--on { color: #010a13; background: #c89b3c; border-color: #c89b3c; }
|
|
129
129
|
#${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; }
|
|
130
133
|
|
|
131
134
|
#${PANEL_ID} .perf-tools { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; margin-bottom: 14px; }
|
|
132
135
|
#${PANEL_ID} .perf-search { flex: 1 1 auto; min-width: 0; box-sizing: border-box;
|
|
@@ -341,6 +344,14 @@ function importModule(url) {
|
|
|
341
344
|
return coords?.importModule ? coords.importModule(url) : import(url);
|
|
342
345
|
}
|
|
343
346
|
|
|
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
|
+
|
|
344
355
|
async function installModule(mod) {
|
|
345
356
|
state.installed[mod.id] = true;
|
|
346
357
|
delete state.enabled[mod.id];
|
|
@@ -636,6 +647,17 @@ function buildOptionRow(mod, option, disabled, onChange) {
|
|
|
636
647
|
return row;
|
|
637
648
|
}
|
|
638
649
|
|
|
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
|
+
|
|
639
661
|
function buildChips(mod, option, locked) {
|
|
640
662
|
const block = el("div", "perf-chip-block");
|
|
641
663
|
block.title = option.description ?? "";
|
|
@@ -658,6 +680,28 @@ function buildChips(mod, option, locked) {
|
|
|
658
680
|
return block;
|
|
659
681
|
}
|
|
660
682
|
|
|
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
|
+
|
|
661
705
|
function buildOptions(mod, locked) {
|
|
662
706
|
const options = registry.get(mod.id)?.options ?? [];
|
|
663
707
|
if (options.length === 0) return null;
|
|
@@ -667,6 +711,14 @@ function buildOptions(mod, locked) {
|
|
|
667
711
|
wrap.append(buildChips(mod, option, locked));
|
|
668
712
|
continue;
|
|
669
713
|
}
|
|
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
|
+
}
|
|
670
722
|
if (!option.children?.length) {
|
|
671
723
|
wrap.append(buildOptionRow(mod, option, Boolean(locked)));
|
|
672
724
|
continue;
|
|
@@ -1020,6 +1072,7 @@ const utils = {
|
|
|
1020
1072
|
getAvailableModules,
|
|
1021
1073
|
installModule,
|
|
1022
1074
|
uninstallModule,
|
|
1075
|
+
loadPluginFile,
|
|
1023
1076
|
checkForUpdates,
|
|
1024
1077
|
watch,
|
|
1025
1078
|
waitFor,
|