perfectionist-plugins 1.0.1 → 1.0.3

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
@@ -48,31 +48,19 @@ Built for [Pengu Loader](https://pengu.lol)
48
48
  2. Move the `Perfectionism` folder into your Pengu Loader `plugins` folder.
49
49
  3. Restart the League Client.
50
50
 
51
- That's it. The folder holds one tiny loader file — the plugins themselves come from the
51
+ That's it. The folder holds one tiny loader file. The plugins themselves come from the
52
52
  [`perfectionist-plugins`](https://www.npmjs.com/package/perfectionist-plugins) npm package the
53
53
  first time the client starts. Install, remove and toggle plugins in **Settings → Perfectionism**.
54
54
 
55
- <br>
56
-
57
55
  ## Updates
58
56
 
59
- Every time the client starts, the loader checks npm for a newer version and runs it — publishing
57
+ Every time the client starts, the loader checks npm for a newer version and runs it. Publishing
60
58
  a new version of the npm package **is** the update. If a new version appears while the client is
61
59
  already open, the suite downloads it with a progress window and restarts the client at a safe
62
60
  moment (never in the middle of a game). You never replace files by hand.
63
61
 
64
62
  <br>
65
63
 
66
- ## Publishing an update (maintainers)
67
-
68
- 1. Bump `version` in `package.json`.
69
- 2. Run `npm publish` from the repo root (first time only: `npm login` first).
70
-
71
- That's the whole release process — every install picks the new version up on its own. Never edit
72
- anything inside the `Perfectionism` folder: that one file stays the same for everyone.
73
-
74
- <br>
75
-
76
64
  ---
77
65
 
78
66
  <div align="center">
@@ -1,4 +1,4 @@
1
- export const version = "2.1.0";
1
+ export const version = "2.2.0";
2
2
 
3
3
  const PLUGIN_ID = "hide-unowned-skins";
4
4
  const STYLE_ID = "perfectionist-hide-unowned-skins-style";
@@ -11,7 +11,7 @@ const CAROUSEL_URI = "/lol-champ-select/v1/skin-carousel-skins";
11
11
  const CURRENT_SUMMONER_URI = "/lol-summoner/v1/current-summoner";
12
12
  const SUMMONER_TTL = 10 * 60 * 1000;
13
13
  const INVENTORY_TTL = 60 * 1000;
14
- const SESSION_POLL_MS = 1000;
14
+ const REFRESH_DEBOUNCE_MS = 350;
15
15
  const RETRY_MS = 5000;
16
16
  const STATE_KEY = "perfectionist-plugins:state";
17
17
 
@@ -34,7 +34,7 @@ let clientSkinOrder = [];
34
34
  function readDefaultEnabled() {
35
35
  try {
36
36
  const saved = JSON.parse(localStorage.getItem(STATE_KEY) || "{}");
37
- return saved[PLUGIN_ID] !== false;
37
+ return saved.enabled?.[PLUGIN_ID] !== false;
38
38
  } catch {
39
39
  return true;
40
40
  }
@@ -57,6 +57,7 @@ function filterSkinList(list) {
57
57
  }
58
58
 
59
59
  function recordClientOrder(list) {
60
+ scheduleRefresh();
60
61
  if (Array.isArray(list)) {
61
62
  clientSkinOrder = list.map((skin) => (skin && typeof skin.id === "number" ? skin.id : null)).filter((id) => id !== null);
62
63
  }
@@ -262,7 +263,8 @@ let carouselOrder = [];
262
263
  let selectedSkinId = null;
263
264
  let failure = null;
264
265
  let requestToken = 0;
265
- let pollTimer = null;
266
+ let refreshTimer = null;
267
+ let unsubscribe = null;
266
268
  let pollInFlight = false;
267
269
  let selectInFlight = false;
268
270
  let sessionMisses = 0;
@@ -452,7 +454,7 @@ function resetState() {
452
454
  failure = null;
453
455
  sessionMisses = 0;
454
456
  championMisses = 0;
455
- applyAll();
457
+ applyAllGuarded();
456
458
  }
457
459
 
458
460
  function loadChampion(championId) {
@@ -467,7 +469,7 @@ function loadChampion(championId) {
467
469
  if (token !== requestToken) return;
468
470
  ownedSkinIds = owned;
469
471
  carouselOrder = carouselIds.length > 0 ? carouselIds : inventoryOrder;
470
- applyAll();
472
+ applyAllGuarded();
471
473
  },
472
474
  (error) => {
473
475
  if (token !== requestToken) return;
@@ -507,7 +509,23 @@ function updateForSession(session, selection) {
507
509
  }
508
510
  }
509
511
 
510
- applyAll();
512
+ applyAllGuarded();
513
+ }
514
+
515
+ function scheduleRefresh() {
516
+ clearTimeout(refreshTimer);
517
+ refreshTimer = setTimeout(pollSession, REFRESH_DEBOUNCE_MS);
518
+ }
519
+
520
+ function onSessionPush(raw) {
521
+ const msg = raw ?? {};
522
+ if (msg.eventType === "Delete") {
523
+ resetState();
524
+ return;
525
+ }
526
+ const s = msg.data ?? msg;
527
+ if (!s?.myTeam) return;
528
+ updateForSession(s, null);
511
529
  }
512
530
 
513
531
  async function pollSession() {
@@ -515,11 +533,9 @@ async function pollSession() {
515
533
  ensureSourceFilter();
516
534
  pollInFlight = true;
517
535
  try {
518
- const [session, selection] = await Promise.all([
519
- api.lcu.get(SESSION_URI),
520
- api.lcu.get(SELECTION_URI).catch(() => null),
521
- ]);
536
+ const session = await api.lcu.get(SESSION_URI);
522
537
  sessionMisses = 0;
538
+ const selection = await api.lcu.get(SELECTION_URI).catch(() => null);
523
539
  updateForSession(session, selection);
524
540
  } catch {
525
541
  sessionMisses++;
@@ -534,16 +550,21 @@ function onEnable(utils) {
534
550
  filterActive = true;
535
551
  removeStyle = api.addStyle(STYLE_ID, CSS);
536
552
 
537
- renderObserver = new MutationObserver(() => applyAllGuarded());
553
+ renderObserver = new MutationObserver(() => {
554
+ applyAllGuarded();
555
+ if (document.querySelector(SKIN_ITEM_SELECTOR)) scheduleRefresh();
556
+ });
538
557
  renderObserver.observe(document.documentElement ?? document, OBSERVER_OPTIONS);
539
558
 
540
- pollTimer = setInterval(pollSession, SESSION_POLL_MS);
559
+ unsubscribe = api.subscribe(SESSION_URI, onSessionPush);
541
560
  pollSession();
542
561
  }
543
562
 
544
563
  function onDisable() {
545
- clearInterval(pollTimer);
546
- pollTimer = null;
564
+ unsubscribe?.();
565
+ unsubscribe = null;
566
+ clearTimeout(refreshTimer);
567
+ refreshTimer = null;
547
568
  renderObserver?.disconnect();
548
569
  removeStyle?.();
549
570
  renderObserver = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perfectionist-plugins",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
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",
@@ -33,7 +33,7 @@ const BUNDLED_MODULES = [
33
33
  id: "hide-unowned-skins",
34
34
  file: "hide-unowned-skins.js",
35
35
  name: "Hide Unowned Skins",
36
- version: "2.1.0",
36
+ version: "2.2.0",
37
37
  description: "In champion select, only shows the skins you own for the picked champion.",
38
38
  },
39
39
  ];
@@ -113,11 +113,6 @@ async function loadState() {
113
113
  state = { installed: saved.installed, enabled: saved.enabled ?? {} };
114
114
  return;
115
115
  }
116
- for (const mod of BUNDLED_MODULES) {
117
- if (mod.core) continue;
118
- state.installed[mod.id] = true;
119
- state.enabled[mod.id] = mod.defaultEnabled ?? true;
120
- }
121
116
  saveState();
122
117
  }
123
118
 
@@ -315,6 +310,7 @@ async function runUpdate(target) {
315
310
  win.setProgress(i / files.length);
316
311
  try {
317
312
  const response = await fetch(`${CDN}/${PKG}@${target}/${files[i].file}`);
313
+ if (response.status === 404 && files[i].id) continue;
318
314
  if (!response.ok) throw new Error(`download failed (${response.status})`);
319
315
  const text = await response.text();
320
316
  if (!text || !/export/.test(text)) throw new Error("the downloaded file looks invalid");