docs : better documentation and uploading screenshot.
BIN
.config/BetterDiscord/data/betterdiscord.asar
Normal file
10207
.config/BetterDiscord/data/stable/addon-store.json
Normal file
3668
.config/BetterDiscord/data/stable/custom.css
Normal file
20
.config/BetterDiscord/data/stable/misc.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": "1.12.7",
|
||||
"addonlistControls": {
|
||||
"plugin": {
|
||||
"view": "grid"
|
||||
},
|
||||
"theme": {
|
||||
"view": "list"
|
||||
}
|
||||
},
|
||||
"drawerStates": {
|
||||
"settings": {
|
||||
"customcss": true,
|
||||
"window": true,
|
||||
"addons": true,
|
||||
"store": true,
|
||||
"editor": false
|
||||
}
|
||||
}
|
||||
}
|
||||
16
.config/BetterDiscord/data/stable/plugins.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"StaffTag": false,
|
||||
"BDFDB": true,
|
||||
"Hide Channels": false,
|
||||
"EditUsers": false,
|
||||
"BackgroundManager": false,
|
||||
"BetterRoleColors": false,
|
||||
"ZeresPluginLibrary": false,
|
||||
"BetterAnimations": false,
|
||||
"BetterFriendList": false,
|
||||
"MemberCount": false,
|
||||
"BetterFolders": false,
|
||||
"ViewProfilePicture": true,
|
||||
"ImageUtilities": true,
|
||||
"HideDisabledEmojis": true
|
||||
}
|
||||
48
.config/BetterDiscord/data/stable/settings.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"general": {
|
||||
"voiceDisconnect": false,
|
||||
"showToasts": true,
|
||||
"mediaKeys": false,
|
||||
"bdContextMenu": true,
|
||||
"themeAttributes": true
|
||||
},
|
||||
"addons": {
|
||||
"addonErrors": false,
|
||||
"editAction": "detached",
|
||||
"checkForUpdates": true,
|
||||
"updateInterval": 4
|
||||
},
|
||||
"store": {
|
||||
"bdAddonStore": true,
|
||||
"alwaysEnable": false,
|
||||
"addonEmbeds": true
|
||||
},
|
||||
"customcss": {
|
||||
"customcss": true,
|
||||
"liveUpdate": true,
|
||||
"openAction": "settings"
|
||||
},
|
||||
"editor": {
|
||||
"lineNumbers": true,
|
||||
"minimap": true,
|
||||
"hover": true,
|
||||
"quickSuggestions": true,
|
||||
"fontSize": 14,
|
||||
"renderWhitespace": "selection"
|
||||
},
|
||||
"window": {
|
||||
"transparency": true,
|
||||
"removeMinimumSize": true,
|
||||
"frame": false,
|
||||
"inAppTrafficLights": false
|
||||
},
|
||||
"developer": {
|
||||
"debugLogs": false,
|
||||
"devTools": false,
|
||||
"debuggerHotkey": false,
|
||||
"reactDevTools": false,
|
||||
"inspectElement": false,
|
||||
"devToolsWarning": false,
|
||||
"recovery": true
|
||||
}
|
||||
}
|
||||
7
.config/BetterDiscord/data/stable/themes.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Translucence": false,
|
||||
"NotAnotherAnimeTheme": false,
|
||||
"ClearVision": true,
|
||||
"ClearVision V7 for BetterDiscord": false,
|
||||
"Glass Wave": false
|
||||
}
|
||||
25
.config/BetterDiscord/plugins/0BDFDB.config.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"all": {
|
||||
"changeLogs": {
|
||||
"BDFDB": "4.2.7",
|
||||
"BetterFriendList": "1.6.3",
|
||||
"EditUsers": "5.0.7",
|
||||
"ImageUtilities": "5.6.3",
|
||||
"StaffTag": "1.6.9"
|
||||
},
|
||||
"choices": {
|
||||
"toastPosition": "right"
|
||||
},
|
||||
"general": {
|
||||
"shareData": true,
|
||||
"showToasts": true,
|
||||
"checkForUpdates": false,
|
||||
"showSupportBadges": false,
|
||||
"useChromium": false
|
||||
},
|
||||
"hashes": {
|
||||
"0BDFDB.data.json": "a9d07bfced7360491909f31e25262dead289759c",
|
||||
"0BDFDB.raw.css": "d0b50a89fc20f5eda9939f0aaf13664ed2b920d0"
|
||||
}
|
||||
}
|
||||
}
|
||||
5496
.config/BetterDiscord/plugins/0BDFDB.data.json
Normal file
8829
.config/BetterDiscord/plugins/0BDFDB.plugin.js
Normal file
1836
.config/BetterDiscord/plugins/0BDFDB.raw.css
Normal file
79
.config/BetterDiscord/plugins/0PluginLibrary.plugin.js
Normal file
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; connect-src 'self'">
|
||||
<title>Site not found · GitHub Pages</title>
|
||||
<style type="text/css" media="screen">
|
||||
body {
|
||||
background-color: #f1f1f1;
|
||||
margin: 0;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.container { margin: 50px auto 40px auto; width: 600px; text-align: center; }
|
||||
|
||||
a { color: #4183c4; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
h1 { width: 800px; position:relative; left: -100px; letter-spacing: -1px; line-height: 60px; font-size: 60px; font-weight: 100; margin: 0px 0 50px 0; text-shadow: 0 1px 0 #fff; }
|
||||
p { color: rgba(0, 0, 0, 0.5); margin: 20px 0; line-height: 1.6; }
|
||||
|
||||
ul { list-style: none; margin: 25px 0; padding: 0; }
|
||||
li { display: table-cell; font-weight: bold; width: 1%; }
|
||||
|
||||
.logo { display: inline-block; margin-top: 35px; }
|
||||
.logo-img-2x { display: none; }
|
||||
@media
|
||||
only screen and (-webkit-min-device-pixel-ratio: 2),
|
||||
only screen and ( min--moz-device-pixel-ratio: 2),
|
||||
only screen and ( -o-min-device-pixel-ratio: 2/1),
|
||||
only screen and ( min-device-pixel-ratio: 2),
|
||||
only screen and ( min-resolution: 192dpi),
|
||||
only screen and ( min-resolution: 2dppx) {
|
||||
.logo-img-1x { display: none; }
|
||||
.logo-img-2x { display: inline-block; }
|
||||
}
|
||||
|
||||
#suggestions {
|
||||
margin-top: 35px;
|
||||
color: #ccc;
|
||||
}
|
||||
#suggestions a {
|
||||
color: #666666;
|
||||
font-weight: 200;
|
||||
font-size: 14px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<h1>404</h1>
|
||||
<p><strong>There isn't a GitHub Pages site here.</strong></p>
|
||||
|
||||
<p>
|
||||
If you're trying to publish one,
|
||||
<a href="https://help.github.com/pages/">read the full documentation</a>
|
||||
to learn how to set up <strong>GitHub Pages</strong>
|
||||
for your repository, organization, or user account.
|
||||
</p>
|
||||
|
||||
<div id="suggestions">
|
||||
<a href="https://githubstatus.com">GitHub Status</a> —
|
||||
<a href="https://twitter.com/githubstatus">@githubstatus</a>
|
||||
</div>
|
||||
|
||||
<a href="/" class="logo logo-img-1x">
|
||||
<img width="32" height="32" title="" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFMTZCRDY3REIzRjAxMUUyQUQzREIxQzRENUFFNUM5NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFMTZCRDY3RUIzRjAxMUUyQUQzREIxQzRENUFFNUM5NiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkUxNkJENjdCQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkUxNkJENjdDQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+SM9MCAAAA+5JREFUeNrEV11Ik1EY3s4+ddOp29Q5b0opCgKFsoKoi5Kg6CIhuwi6zLJLoYLopq4qsKKgi4i6CYIoU/q5iDAKs6syoS76IRWtyJ+p7cdt7sf1PGOD+e0c3dygAx/67ZzzPM95/877GYdHRg3ZjMXFxepQKNS6sLCwJxqNNuFpiMfjVs4ZjUa/pmmjeD6VlJS8NpvNT4QQ7mxwjSsJiEQim/1+/9lgMHgIr5ohuxG1WCw9Vqv1clFR0dCqBODElV6v90ogEDjGdYbVjXhpaendioqK07CIR7ZAqE49PT09BPL2PMgTByQGsYiZlQD4uMXtdr+JxWINhgINYhGT2MsKgMrm2dnZXgRXhaHAg5jEJodUAHxux4LudHJE9RdEdA+i3Juz7bGHe4mhE9FNrgwBCLirMFV9Okh5eflFh8PR5nK5nDabrR2BNJlKO0T35+Li4n4+/J+/JQCxhmu5h3uJoXNHPbmWZAHMshWB8l5/ipqammaAf0zPDDx1ONV3vurdidqwAQL+pEc8sLcAe1CCvQ3YHxIW8Pl85xSWNC1hADDIv0rIE/o4J0k3kww4xSlwIhcq3EFFOm7KN/hUGOQkt0CFa5WpNJlMvxBEz/IVQAxg/ZRZl9wiHA63yDYieM7DnLP5CiAGsC7I5sgtYKJGWe2A8seFqgFJrJjEPY1Cn3pJ8/9W1e5VWsFDTEmFrBcoDhZJEQkXuhICMyKpjhahqN21hRYATKfUOlDmkygrR4o4C0VOLGJKrOITKB4jijzdXygBKixyC5TDQdnk/Pz8qRw6oOWGlsTKGOQW6OH6FBWsyePxdOXLTgxiyebILZCjz+GLgMIKnXNzc49YMlcRdHXcSwxFVgTInQhC9G33UhNoJLuqq6t345p9y3eUy8OTk5PjAHuI9uo4b07FBaOhsu0A4Unc+T1TU1Nj3KsSSE5yJ65jqF2DDd8QqWYmAZrIM2VlZTdnZmb6AbpdV9V6ec9znf5Q7HjYumdRE0JOp3MjitO4SFa+cZz8Umqe3TCbSLvdfkR/kWDdNQl5InuTcysOcpFT35ZrbBxx4p3JAHlZVVW1D/634VRt+FvLBgK/v5LV9WS+10xMTEwtRw7XvqOL+e2Q8V3AYIOIAXQ26/heWVnZCVfcyKHg2CBgTpmPmjYM8l24GyaUHyaIh7XwfR9ErE8qHoDfn2LTNAVC0HX6MFcBIP8Bi+6F6cdW/DICkANRfx99fEYFQ7Nph5i/uQiA214gno7K+guhaiKg9gC62+M8eR7XsBsYJ4ilam60Fb7r7uAj8wFyuwM1oIOWgfmDy6RXEEQzJMPe23DXrVS7rtyD3Df8z/FPgAEAzWU5Ku59ZAUAAAAASUVORK5CYII=">
|
||||
</a>
|
||||
|
||||
<a href="/" class="logo logo-img-2x">
|
||||
<img width="32" height="32" title="" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpEQUM1QkUxRUI0MUMxMUUyQUQzREIxQzRENUFFNUM5NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpEQUM1QkUxRkI0MUMxMUUyQUQzREIxQzRENUFFNUM5NiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkUxNkJENjdGQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkUxNkJENjgwQjNGMDExRTJBRDNEQjFDNEQ1QUU1Qzk2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+hfPRaQAAB6lJREFUeNrsW2mME2UYbodtt+2222u35QheoCCYGBQligIJgkZJNPzgigoaTEj8AdFEMfADfyABkgWiiWcieK4S+QOiHAYUj2hMNKgYlEujpNttu9vttbvdw+chU1K6M535pt3ubHCSyezR+b73eb73+t7vrfXsufOW4bz6+vom9/b23ovnNNw34b5xYGAgODg46Mbt4mesVmsWd1qSpHhdXd2fuP/Afcput5/A88xwymcdBgLqenp6FuRyuWV4zu/v759QyWBjxoz5t76+/gun09mK5xFyakoCAPSaTCazNpvNPoYVbh6O1YKGRF0u13sNDQ27QMzfpiAAKj0lnU6/gBVfAZW2WWpwwVzy0IgP3G73FpjI6REhAGA9qVRqA1b9mVoBVyIC2tDi8Xg24+dUzQiAbS/s7Ox8G2o/3mKCC+Zw0efzPQEfcVjYrARX3dbV1bUtHo8fMgt42f+Mp0yUTVQbdWsAHVsikdiHkHaPxcQXQufXgUBgMRxme9U0AAxfH4vFvjM7eF6UkbJS5qoQwEQGA57Ac5JllFyUVZZ5ckUEgMVxsK2jlSYzI+QXJsiyjzNEAJyJAzb/KQa41jJKL8pODMQiTEAymXw5n8/P0IjD3bh7Rgog59aanxiIRTVvV/oj0tnHca/WMrVwODwB3raTGxzkBg/gnZVapFV62Wy2n5AO70HM/5wbJ0QnXyQSaVPDIuNZzY0V3ntHMwxiwHA0Gj2Np7ecIBDgaDAYXKCQJM1DhrgJ3nhulcPbl8j4NmHe46X/g60fwbz3aewjkqFQaAqebWU1AOqyQwt8Id6qEHMc97zu7u7FGGsn7HAiVuosVw7P35C1nccdgSCxop1dHeZswmfHMnxBo6ZTk+jN8dl/vF7vWofDsa+MLN9oEUBMxOb3+1eoEsBVw6Zmua49r8YmhAKDiEPcMwBsxMiqQ+ixzPFxZyqRpXARG/YOr1ObFJ0gUskXBbamcR1OKmMUvDxHRAu8/LmY3jFLMUpFqz9HxG65smYJdyKyECOxDiEAe/p1gjF2oonivZAsxVgl2daa4EQWCW6J55qFAFFZiJWYLxNQy2qOSUzGRsyXCUDIeliwAHEO4WSlWQBRFoZakXcKmCXmyXAKs0Ve9vl8q42WoIYpJU4hV3hKcNs8m9gl7p/xQ73eF5kB4j5mNrWmTJRNwAzqiV1CxjVTZCIkEq+Z1bZFZSN2CenmVAFVy4Plz8xKAGWjjAKFk6lCBMDR/MJjLLMSQNm43xAiQKTaA+9/wewhDjL+JVI1kkTSSOTcKbMTwPqESAot6dn6Fr1gHwVJju6IRuyiByPuUUBAg5DGkAgBmxlvdgIEK9gDkohdY/BJo4CAG0R8miRSsGABkgVQs4KXu098IgUXSSRsFAoKZiVAVDY2WUiiPTjYRi41KwGisrGsLtlsth8Fiwnz2fBkQvWfRtlE3iF2yW63/yCacXZ1dW02GwGyTFaRd4idJnCKHRaCxYRHoG5LTKT6SyiToP1fJHbmAYPYRR0UnZQtMnA6s0zg+GZBlt0Gdo7EPHgpE3Q6nZ8YyLhc8Xj8MJh/aKTAY+5FPAKHLE7RdwuYJZmNwzyCMkBCYyKROJBMJl9B/PXXCjjmCmDOVzH3fiPpObEWGqoKe4EBl8v1hlqsdLvd23mkxHM9pc9kMpmno9HoeTii7ewbHEZPPx1ztLS1tV3AnGuMjiNjvbQFuHw6zDo5By7dTPAQNBgMLrRarTkSls1mnwT7uwp9virx9QzbW/HuV/j5d/b+6jniKlllP8lkeONJDk+dq9GsQTnC4fB1heO0K47Hwe7WdDr9nAKgXwOBwHI+C45Htj1d6sd429TUNEcmUdc+PRaLHcvn87dXW4ugzdsaGxufL94NFv9zi1J7GVbhlvb2dnaJ3SVrxfc+n2+NTsZ7/H7/Mr3g5XdSIHyJSH1PZ+7fToyl2+ErqilgZ4NaLYB9goVGaHjR93Hv1ZrU4XDsFT20kH3PObzbWk0CgG1jacVIUnAQb9F+VexyLMzkpcLv0IJV7AHQIOCAUYHx7v5qgScmYHtTqSAyZLEJTK22Bie4iq3xsqpm4SAf9Hq9a2DnJ4uLK3SEULcdRvp3i3zHySqpficxEdsQc1NrlYXXvR+O7qASSezXB+h1SuUomgg9LL8BUoV4749EIolKh+EiqWmqVEZlDgHks2pxHw7xTqUQw9J5NcAXOK10AGIoZ6Zli6JY6Z1Q461KoZ4NiKLHarW+KDsxlDUPHZ5zPQZqUVDPJsTqb5n9malbpAh8C2XXDLl62+WZIDFRUlNVOiwencnNU3aQEkL+cDMSoLvZo2fQB7AJssNAuFuvorlDVVkkg2I87+jo2K2QAVphDrfyViK5VqtO34OkaxXCp+7drdDBCAdubm6eidX+2WwqT5komwh4YQLk+H4aE93h8Xg2gvHekQZOGSgLZTLyDTLJ4Lx9/KZWKBSainT4Iy3FqQBfnUZR42PKQFksBr9QKVXCPusD3OiA/RkQ5kP8qV/Jl1WywAp/6+dcmPM2zL1UrUahe4JqfnWWKXIul3uUbfP8njAFLW1OFr3gdFtZ72cNH+PtQT7/brW+NXqJAHh0y9V8/U/A1U7AfwIMAD7mS3pCbuWJAAAAAElFTkSuQmCC">
|
||||
</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"currentVersionInfo": {
|
||||
"version": "1.2.7",
|
||||
"hasShownChangelog": true
|
||||
},
|
||||
"hasShownV2Modal": true
|
||||
}
|
||||
11
.config/BetterDiscord/plugins/BetterFolders.config.json
Normal file
587
.config/BetterDiscord/plugins/BetterFolders.plugin.js
Normal file
@@ -0,0 +1,587 @@
|
||||
/**
|
||||
* @name BetterFolders
|
||||
* @version 3.6.2
|
||||
* @author Zerthox
|
||||
* @authorLink https://github.com/Zerthox
|
||||
* @description Adds new functionality to server folders. Custom Folder Icons. Close other folders on open.
|
||||
* @website https://github.com/Zerthox/BetterDiscord-Plugins
|
||||
* @source https://github.com/Zerthox/BetterDiscord-Plugins/tree/master/src/BetterFolders
|
||||
**/
|
||||
|
||||
/*@cc_on @if (@_jscript)
|
||||
var pluginName = WScript.ScriptName.split(".")[0];
|
||||
var shell = WScript.CreateObject("WScript.Shell");
|
||||
shell.Popup(
|
||||
"Do NOT run scripts from the internet with the Windows Script Host!\nMove this file to your BetterDiscord plugins folder.",
|
||||
0,
|
||||
pluginName + ": Warning!",
|
||||
0x1030
|
||||
);
|
||||
var fso = new ActiveXObject("Scripting.FileSystemObject");
|
||||
var pluginsPath = shell.expandEnvironmentStrings("%appdata%\\BetterDiscord\\plugins");
|
||||
if (!fso.FolderExists(pluginsPath)) {
|
||||
var popup = shell.Popup(
|
||||
"Unable to find BetterDiscord on your computer.\nOpen the download page of BetterDiscord?",
|
||||
0,
|
||||
pluginName + ": BetterDiscord not found",
|
||||
0x34
|
||||
);
|
||||
if (popup === 6) {
|
||||
shell.Exec("explorer \"https://betterdiscord.app\"");
|
||||
}
|
||||
} else if (WScript.ScriptFullName === pluginsPath + "\\" + WScript.ScriptName) {
|
||||
shell.Popup(
|
||||
"This plugin is already in the correct folder.\nNavigate to the \"Plugins\" settings tab in Discord and enable it there.",
|
||||
0,
|
||||
pluginName,
|
||||
0x40
|
||||
);
|
||||
} else {
|
||||
var popup = shell.Popup(
|
||||
"Open the BetterDiscord plugins folder?",
|
||||
0,
|
||||
pluginName,
|
||||
0x34
|
||||
);
|
||||
if (popup === 6) {
|
||||
shell.Exec("explorer " + pluginsPath);
|
||||
}
|
||||
}
|
||||
WScript.Quit();
|
||||
@else @*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let meta = null;
|
||||
const getMeta = () => {
|
||||
if (meta) {
|
||||
return meta;
|
||||
}
|
||||
else {
|
||||
throw Error("Accessing meta before initialization");
|
||||
}
|
||||
};
|
||||
const setMeta = (newMeta) => {
|
||||
meta = newMeta;
|
||||
};
|
||||
|
||||
const load = (key) => BdApi.Data.load(getMeta().name, key);
|
||||
const save = (key, value) => BdApi.Data.save(getMeta().name, key, value);
|
||||
|
||||
const checkObjectValues = (target) => target !== window && target instanceof Object && target.constructor?.prototype !== target;
|
||||
const byEntry = (filter, every = false) => {
|
||||
return ((target, ...args) => {
|
||||
if (checkObjectValues(target)) {
|
||||
const values = Object.values(target);
|
||||
return values.length > 0 && values[every ? "every" : "some"]((value) => filter(value, ...args));
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
const byName$1 = (name) => {
|
||||
return (target) => (target?.displayName ?? target?.constructor?.displayName) === name;
|
||||
};
|
||||
const byKeys$1 = (...keys) => {
|
||||
return (target) => target instanceof Object && keys.every((key) => key in target);
|
||||
};
|
||||
const byProtos = (...protos) => {
|
||||
return (target) => target instanceof Object && target.prototype instanceof Object && protos.every((proto) => proto in target.prototype);
|
||||
};
|
||||
const bySource$1 = (...fragments) => {
|
||||
return (target) => {
|
||||
while (target instanceof Object && "$$typeof" in target) {
|
||||
target = target.render ?? target.type;
|
||||
}
|
||||
if (target instanceof Function) {
|
||||
const source = target.toString();
|
||||
const renderSource = target.prototype?.render?.toString();
|
||||
return fragments.every((fragment) => typeof fragment === "string" ? (source.includes(fragment) || renderSource?.includes(fragment)) : (fragment(source) || renderSource && fragment(renderSource)));
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const confirm = (title, content, options = {}) => BdApi.UI.showConfirmationModal(title, content, options);
|
||||
const mappedProxy = (target, mapping) => {
|
||||
const map = new Map(Object.entries(mapping));
|
||||
return new Proxy(target, {
|
||||
get(target, prop) {
|
||||
return target[map.get(prop) ?? prop];
|
||||
},
|
||||
set(target, prop, value) {
|
||||
target[map.get(prop) ?? prop] = value;
|
||||
return true;
|
||||
},
|
||||
deleteProperty(target, prop) {
|
||||
delete target[map.get(prop) ?? prop];
|
||||
map.delete(prop);
|
||||
return true;
|
||||
},
|
||||
has(target, prop) {
|
||||
return map.has(prop) || prop in target;
|
||||
},
|
||||
ownKeys() {
|
||||
return [...map.keys(), ...Object.keys(target)];
|
||||
},
|
||||
getOwnPropertyDescriptor(target, prop) {
|
||||
return Object.getOwnPropertyDescriptor(target, map.get(prop) ?? prop);
|
||||
},
|
||||
defineProperty(target, prop, attributes) {
|
||||
Object.defineProperty(target, map.get(prop) ?? prop, attributes);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const find = (filter, { resolve = true, entries = false } = {}) => BdApi.Webpack.getModule(filter, {
|
||||
defaultExport: resolve,
|
||||
searchExports: entries
|
||||
});
|
||||
const byName = (name, options) => find(byName$1(name), options);
|
||||
const byKeys = (keys, options) => find(byKeys$1(...keys), options);
|
||||
const bySource = (contents, options) => find(bySource$1(...contents), options);
|
||||
const resolveKey = (target, filter) => [target, Object.entries(target ?? {}).find(([, value]) => filter(value))?.[0]];
|
||||
const findWithKey = (filter) => resolveKey(find(byEntry(filter)), filter);
|
||||
const demangle = (mapping, required, proxy = false) => {
|
||||
const req = required ?? Object.keys(mapping);
|
||||
const found = find((target) => (checkObjectValues(target)
|
||||
&& req.every((req) => Object.values(target).some((value) => mapping[req](value)))));
|
||||
return proxy ? mappedProxy(found, Object.fromEntries(Object.entries(mapping).map(([key, filter]) => [
|
||||
key,
|
||||
Object.entries(found ?? {}).find(([, value]) => filter(value))?.[0]
|
||||
]))) : Object.fromEntries(Object.entries(mapping).map(([key, filter]) => [
|
||||
key,
|
||||
Object.values(found ?? {}).find((value) => filter(value))
|
||||
]));
|
||||
};
|
||||
let controller = new AbortController();
|
||||
const waitFor = (filter, { resolve = true, entries = false } = {}) => BdApi.Webpack.waitForModule(filter, {
|
||||
signal: controller.signal,
|
||||
defaultExport: resolve,
|
||||
searchExports: entries
|
||||
});
|
||||
const abort = () => {
|
||||
controller.abort();
|
||||
controller = new AbortController();
|
||||
};
|
||||
|
||||
const COLOR = "#3a71c1";
|
||||
const print = (output, ...data) => output(`%c[${getMeta().name}] %c${getMeta().version ? `(v${getMeta().version})` : ""}`, `color: ${COLOR}; font-weight: 700;`, "color: #666; font-size: .8em;", ...data);
|
||||
const log = (...data) => print(console.log, ...data);
|
||||
const warn = (...data) => print(console.warn, ...data);
|
||||
const error = (...data) => print(console.error, ...data);
|
||||
|
||||
const patch = (type, object, method, callback, options) => {
|
||||
const original = object?.[method];
|
||||
if (!(original instanceof Function)) {
|
||||
throw TypeError(`patch target ${original} is not a function`);
|
||||
}
|
||||
const cancel = BdApi.Patcher[type](getMeta().name, object, method, options.once ? (...args) => {
|
||||
const result = callback(cancel, original, ...args);
|
||||
cancel();
|
||||
return result;
|
||||
} : (...args) => callback(cancel, original, ...args));
|
||||
if (!options.silent) {
|
||||
log(`Patched ${options.name ?? String(method)}`);
|
||||
}
|
||||
return cancel;
|
||||
};
|
||||
const instead = (object, method, callback, options = {}) => patch("instead", object, method, (cancel, original, context, args) => callback({ cancel, original, context, args }), options);
|
||||
const after = (object, method, callback, options = {}) => patch("after", object, method, (cancel, original, context, args, result) => callback({ cancel, original, context, args, result }), options);
|
||||
let menuPatches = [];
|
||||
const unpatchAll = () => {
|
||||
if (menuPatches.length + BdApi.Patcher.getPatchesByCaller(getMeta().name).length > 0) {
|
||||
for (const cancel of menuPatches) {
|
||||
cancel();
|
||||
}
|
||||
menuPatches = [];
|
||||
BdApi.Patcher.unpatchAll(getMeta().name);
|
||||
log("Unpatched all");
|
||||
}
|
||||
};
|
||||
|
||||
const inject = (styles) => {
|
||||
if (typeof styles === "string") {
|
||||
BdApi.DOM.addStyle(getMeta().name, styles);
|
||||
}
|
||||
};
|
||||
const clear = () => BdApi.DOM.removeStyle(getMeta().name);
|
||||
|
||||
const ClientActions = /* @__PURE__ */ byKeys(["toggleGuildFolderExpand"]);
|
||||
|
||||
const { useStateFromStores } = /* @__PURE__ */ demangle({
|
||||
default: byKeys$1("Store", "connectStores"),
|
||||
Dispatcher: byProtos("dispatch"),
|
||||
Store: byProtos("emitChange"),
|
||||
BatchedStoreListener: byProtos("attach", "detach"),
|
||||
useStateFromStores: bySource$1("useStateFromStores")
|
||||
}, ["Store", "Dispatcher", "useStateFromStores"]);
|
||||
|
||||
const SortedGuildStore = /* @__PURE__ */ byName("SortedGuildStore");
|
||||
const ExpandedGuildFolderStore = /* @__PURE__ */ byName("ExpandedGuildFolderStore");
|
||||
|
||||
const { React } = BdApi;
|
||||
const classNames = /* @__PURE__ */ find((exports) => exports instanceof Object && exports.default === exports && Object.keys(exports).length === 1);
|
||||
|
||||
const Button = /* @__PURE__ */ byKeys(["Colors", "Link"], { entries: true });
|
||||
|
||||
const Flex = /* @__PURE__ */ byKeys(["Child", "Justify", "Align"], { entries: true });
|
||||
|
||||
const { FormSection, FormItem, FormText,
|
||||
FormDivider, FormSwitch} = /* @__PURE__ */ demangle({
|
||||
FormSection: bySource$1("titleClassName:", ".sectionTitle"),
|
||||
FormItem: bySource$1("titleClassName:", "required:"),
|
||||
FormTitle: bySource$1("faded:", "required:"),
|
||||
FormText: (target) => target.Types?.INPUT_PLACEHOLDER,
|
||||
FormDivider: bySource$1(".divider", "style:"),
|
||||
FormSwitch: bySource$1("tooltipNote:"),
|
||||
FormNotice: bySource$1("imageData:", ".formNotice")
|
||||
}, ["FormSection", "FormItem", "FormDivider"]);
|
||||
|
||||
const margins = /* @__PURE__ */ byKeys(["marginBottom40", "marginTop4"]);
|
||||
|
||||
const RadioGroup = /* @__PURE__ */ bySource(["radioPosition:", "radioItemClassName:", "options:"], { entries: true });
|
||||
|
||||
const ImageInput = /* @__PURE__ */ find((target) => typeof target.defaultProps?.multiple === "boolean" && typeof target.defaultProps?.maxFileSizeBytes === "number");
|
||||
|
||||
const replaceElement = (target, replace) => {
|
||||
target.type = replace.type;
|
||||
target.key = replace.key ?? target.key;
|
||||
target.props = replace.props;
|
||||
};
|
||||
const queryTree = (node, predicate) => {
|
||||
const worklist = [node].flat();
|
||||
while (worklist.length !== 0) {
|
||||
const node = worklist.shift();
|
||||
if (React.isValidElement(node)) {
|
||||
if (predicate(node)) {
|
||||
return node;
|
||||
}
|
||||
const children = node?.props?.children;
|
||||
if (children) {
|
||||
worklist.push(...[children].flat());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const getFiber = (node) => {
|
||||
const key = Object.keys(node).find((key) => key.startsWith("__reactFiber"));
|
||||
return node?.[key];
|
||||
};
|
||||
const queryFiber = (fiber, predicate, direction = "up" , depth = 30) => {
|
||||
if (depth < 0) {
|
||||
return null;
|
||||
}
|
||||
if (predicate(fiber)) {
|
||||
return fiber;
|
||||
}
|
||||
if (direction === "up" || direction === "both" ) {
|
||||
let count = 0;
|
||||
let parent = fiber.return;
|
||||
while (parent && count < depth) {
|
||||
if (predicate(parent)) {
|
||||
return parent;
|
||||
}
|
||||
count++;
|
||||
parent = parent.return;
|
||||
}
|
||||
}
|
||||
if (direction === "down" || direction === "both" ) {
|
||||
let child = fiber.child;
|
||||
while (child) {
|
||||
const result = queryFiber(child, predicate, "down" , depth - 1);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
child = child.sibling;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const findOwner = (fiber, depth = 50) => {
|
||||
return queryFiber(fiber, (node) => node?.stateNode instanceof React.Component, "up" , depth);
|
||||
};
|
||||
const forceFullRerender = (fiber) => new Promise((resolve) => {
|
||||
const owner = findOwner(fiber);
|
||||
if (owner) {
|
||||
const { stateNode } = owner;
|
||||
instead(stateNode, "render", () => null, { once: true, silent: true });
|
||||
stateNode.forceUpdate(() => stateNode.forceUpdate(() => resolve(true)));
|
||||
}
|
||||
else {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
const SettingsContainer = ({ name, children, onReset }) => (React.createElement(FormSection, null,
|
||||
children,
|
||||
onReset ? (React.createElement(React.Fragment, null,
|
||||
React.createElement(FormDivider, { className: classNames(margins.marginTop20, margins.marginBottom20) }),
|
||||
React.createElement(Flex, { justify: Flex.Justify.END },
|
||||
React.createElement(Button, { size: Button.Sizes.SMALL, onClick: () => confirm(name, "Reset all settings?", {
|
||||
onConfirm: () => onReset()
|
||||
}) }, "Reset")))) : null));
|
||||
|
||||
class SettingsStore {
|
||||
constructor(defaults, onLoad) {
|
||||
this.listeners = new Set();
|
||||
this.update = (settings) => {
|
||||
Object.assign(this.current, typeof settings === "function" ? settings(this.current) : settings);
|
||||
this._dispatch(true);
|
||||
};
|
||||
this.addReactChangeListener = this.addListener;
|
||||
this.removeReactChangeListener = this.removeListener;
|
||||
this.defaults = defaults;
|
||||
this.onLoad = onLoad;
|
||||
}
|
||||
load() {
|
||||
this.current = { ...this.defaults, ...load("settings") };
|
||||
this.onLoad?.();
|
||||
this._dispatch(false);
|
||||
}
|
||||
_dispatch(save$1) {
|
||||
for (const listener of this.listeners) {
|
||||
listener(this.current);
|
||||
}
|
||||
if (save$1) {
|
||||
save("settings", this.current);
|
||||
}
|
||||
}
|
||||
reset() {
|
||||
this.current = { ...this.defaults };
|
||||
this._dispatch(true);
|
||||
}
|
||||
delete(...keys) {
|
||||
for (const key of keys) {
|
||||
delete this.current[key];
|
||||
}
|
||||
this._dispatch(true);
|
||||
}
|
||||
useCurrent() {
|
||||
return useStateFromStores([this], () => this.current, undefined, () => false);
|
||||
}
|
||||
useSelector(selector, deps, compare) {
|
||||
return useStateFromStores([this], () => selector(this.current), deps, compare);
|
||||
}
|
||||
useState() {
|
||||
return useStateFromStores([this], () => [
|
||||
this.current,
|
||||
this.update
|
||||
]);
|
||||
}
|
||||
useStateWithDefaults() {
|
||||
return useStateFromStores([this], () => [
|
||||
this.current,
|
||||
this.defaults,
|
||||
this.update
|
||||
]);
|
||||
}
|
||||
useListener(listener, deps) {
|
||||
React.useEffect(() => {
|
||||
this.addListener(listener);
|
||||
return () => this.removeListener(listener);
|
||||
}, deps ?? [listener]);
|
||||
}
|
||||
addListener(listener) {
|
||||
this.listeners.add(listener);
|
||||
return listener;
|
||||
}
|
||||
removeListener(listener) {
|
||||
this.listeners.delete(listener);
|
||||
}
|
||||
removeAllListeners() {
|
||||
this.listeners.clear();
|
||||
}
|
||||
}
|
||||
const createSettings = (defaults, onLoad) => new SettingsStore(defaults, onLoad);
|
||||
|
||||
const createPlugin = (plugin) => (meta) => {
|
||||
setMeta(meta);
|
||||
const { start, stop, styles, Settings, SettingsPanel } = (plugin instanceof Function ? plugin(meta) : plugin);
|
||||
Settings?.load();
|
||||
return {
|
||||
start() {
|
||||
log("Enabled");
|
||||
inject(styles);
|
||||
start?.();
|
||||
},
|
||||
stop() {
|
||||
abort();
|
||||
unpatchAll();
|
||||
clear();
|
||||
stop?.();
|
||||
log("Disabled");
|
||||
},
|
||||
getSettingsPanel: SettingsPanel ? () => (React.createElement(SettingsContainer, { name: meta.name, onReset: Settings ? () => Settings.reset() : null },
|
||||
React.createElement(SettingsPanel, null))) : null
|
||||
};
|
||||
};
|
||||
|
||||
const Settings = createSettings({
|
||||
closeOnOpen: false,
|
||||
folders: {}
|
||||
});
|
||||
|
||||
const css = ".customIcon-BetterFolders {\n box-sizing: border-box;\n border-radius: var(--radius-lg);\n width: var(--guildbar-folder-size);\n height: var(--guildbar-folder-size);\n padding: var(--custom-folder-preview-padding);\n background-size: contain;\n background-position: center;\n background-repeat: no-repeat;\n}";
|
||||
const styles = {
|
||||
customIcon: "customIcon-BetterFolders"
|
||||
};
|
||||
|
||||
const folderStyles = byKeys(["folderIcon", "folderIconWrapper", "folderPreviewWrapper"]);
|
||||
const renderIcon = (data) => (React.createElement("div", { className: styles.customIcon, style: { backgroundImage: data?.icon ? `url(${data.icon})` : null } }));
|
||||
const BetterFolderIcon = ({ data, childProps, FolderIcon }) => {
|
||||
if (FolderIcon) {
|
||||
const result = FolderIcon(childProps);
|
||||
if (data?.icon) {
|
||||
const replace = renderIcon(data);
|
||||
const iconWrapper = queryTree(result, (node) => node?.props?.className === folderStyles.folderIconWrapper);
|
||||
if (iconWrapper) {
|
||||
replaceElement(iconWrapper, replace);
|
||||
}
|
||||
else {
|
||||
error("Failed to find folderIconWrapper element");
|
||||
}
|
||||
if (data.always) {
|
||||
const previewWrapper = queryTree(result, (node) => node?.props?.className === folderStyles.folderPreviewWrapper);
|
||||
if (previewWrapper) {
|
||||
replaceElement(previewWrapper, replace);
|
||||
}
|
||||
else {
|
||||
error("Failed to find folderPreviewWrapper element");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const compareFolderData = (a, b) => a?.icon === b?.icon && a?.always === b?.always;
|
||||
const ConnectedBetterFolderIcon = ({ folderId, ...props }) => {
|
||||
const data = Settings.useSelector((current) => current.folders[folderId], [folderId], compareFolderData);
|
||||
return React.createElement(BetterFolderIcon, { data: data, ...props });
|
||||
};
|
||||
|
||||
const BetterFolderUploader = ({ icon, always, onChange }) => (React.createElement(React.Fragment, null,
|
||||
React.createElement(Flex, { align: Flex.Align.CENTER },
|
||||
React.createElement(Button, { color: Button.Colors.WHITE, look: Button.Looks.OUTLINED },
|
||||
"Upload Image",
|
||||
React.createElement(ImageInput, { onChange: (img) => onChange({ icon: img, always }) })),
|
||||
React.createElement(FormText, { type: "description", style: { margin: "0 10px 0 40px" } }, "Preview:"),
|
||||
renderIcon({ icon})),
|
||||
React.createElement(FormSwitch, { hideBorder: true, className: margins.marginTop8, value: always, onChange: (checked) => onChange({ icon, always: checked }) }, "Always display icon")));
|
||||
|
||||
const folderModalPatch = ({ context, result }) => {
|
||||
const { folderId } = context.props;
|
||||
const { state } = context;
|
||||
const form = queryTree(result, (node) => node?.type === "form");
|
||||
if (!form) {
|
||||
warn("Unable to find form");
|
||||
return;
|
||||
}
|
||||
if (!state.iconType) {
|
||||
const { icon = null, always = false } = Settings.current.folders[folderId] ?? {};
|
||||
Object.assign(state, {
|
||||
iconType: icon ? "custom" : "default" ,
|
||||
icon,
|
||||
always
|
||||
});
|
||||
}
|
||||
const { children } = form.props;
|
||||
const { className } = children[0].props;
|
||||
children.push(React.createElement(FormItem, { title: "Icon", className: className },
|
||||
React.createElement(RadioGroup, { value: state.iconType, options: [
|
||||
{ value: "default" , name: "Default Icon" },
|
||||
{ value: "custom" , name: "Custom Icon" }
|
||||
], onChange: ({ value }) => context.setState({ iconType: value }) })));
|
||||
if (state.iconType === "custom" ) {
|
||||
const tree = SortedGuildStore.getGuildsTree();
|
||||
children.push(React.createElement(FormItem, { title: "Custom Icon", className: className },
|
||||
React.createElement(BetterFolderUploader, { icon: state.icon, always: state.always, folderNode: tree.nodes[folderId], onChange: ({ icon, always }) => context.setState({ icon, always }) })));
|
||||
}
|
||||
const button = queryTree(result, (node) => node?.props?.type === "submit");
|
||||
const original = button.props.onClick;
|
||||
button.props.onClick = (...args) => {
|
||||
original(...args);
|
||||
const { folders } = Settings.current;
|
||||
if (state.iconType === "custom" && state.icon) {
|
||||
folders[folderId] = { icon: state.icon, always: state.always };
|
||||
Settings.update({ folders });
|
||||
}
|
||||
else if ((state.iconType === "default" || !state.icon) && folders[folderId]) {
|
||||
delete folders[folderId];
|
||||
Settings.update({ folders });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const guildStyles = byKeys(["guilds", "base"]);
|
||||
const getGuildsOwner = () => findOwner(getFiber(document.getElementsByClassName(guildStyles.guilds)?.[0]));
|
||||
const triggerRerender = async (guildsFiber) => {
|
||||
if (await forceFullRerender(guildsFiber)) {
|
||||
log("Rerendered guilds");
|
||||
}
|
||||
else {
|
||||
warn("Unable to rerender guilds");
|
||||
}
|
||||
};
|
||||
const index = createPlugin({
|
||||
start() {
|
||||
let FolderIcon = null;
|
||||
const guildsOwner = getGuildsOwner();
|
||||
const FolderIconWrapper = findWithKey(bySource$1("folderIconWrapper"));
|
||||
after(...FolderIconWrapper, ({ args: [props], result }) => {
|
||||
const icon = queryTree(result, (node) => node?.props?.folderNode);
|
||||
if (!icon) {
|
||||
return error("Unable to find FolderIcon component");
|
||||
}
|
||||
if (!FolderIcon) {
|
||||
log("Found FolderIcon component");
|
||||
FolderIcon = icon.type;
|
||||
}
|
||||
const replace = React.createElement(ConnectedBetterFolderIcon, { folderId: props.folderNode.id, childProps: icon.props, FolderIcon: FolderIcon });
|
||||
replaceElement(icon, replace);
|
||||
}, { name: "FolderIconWrapper" });
|
||||
triggerRerender(guildsOwner);
|
||||
after(ClientActions, "toggleGuildFolderExpand", ({ original, args: [folderId] }) => {
|
||||
if (Settings.current.closeOnOpen) {
|
||||
for (const id of ExpandedGuildFolderStore.getExpandedFolders()) {
|
||||
if (id !== folderId) {
|
||||
original(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
waitFor(bySource$1(".folderName", ".onClose"), { entries: true }).then((FolderSettingsModal) => {
|
||||
if (FolderSettingsModal) {
|
||||
after(FolderSettingsModal.prototype, "render", folderModalPatch, { name: "GuildFolderSettingsModal" });
|
||||
}
|
||||
});
|
||||
},
|
||||
stop() {
|
||||
triggerRerender(getGuildsOwner());
|
||||
},
|
||||
styles: css,
|
||||
Settings,
|
||||
SettingsPanel: () => {
|
||||
const [{ closeOnOpen }, setSettings] = Settings.useState();
|
||||
return (React.createElement(FormSwitch, { note: "Close other folders when opening a new folder", hideBorder: true, value: closeOnOpen, onChange: (checked) => {
|
||||
if (checked) {
|
||||
for (const id of Array.from(ExpandedGuildFolderStore.getExpandedFolders()).slice(1)) {
|
||||
ClientActions.toggleGuildFolderExpand(id);
|
||||
}
|
||||
}
|
||||
setSettings({ closeOnOpen: checked });
|
||||
} }, "Close on open"));
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = index;
|
||||
|
||||
/*@end @*/
|
||||
13
.config/BetterDiscord/plugins/BetterFriendList.config.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"all": {
|
||||
"general": {
|
||||
"addTotalAmount": true,
|
||||
"addBlockedCategory": true,
|
||||
"addIgnoredCategory": true,
|
||||
"addFavorizedCategory": true,
|
||||
"addHiddenCategory": true,
|
||||
"addSortOptions": true,
|
||||
"addMutualGuild": true
|
||||
}
|
||||
}
|
||||
}
|
||||
901
.config/BetterDiscord/plugins/BetterFriendList.plugin.js
Normal file
@@ -0,0 +1,901 @@
|
||||
/**
|
||||
* @name BetterFriendList
|
||||
* @author DevilBro
|
||||
* @authorId 278543574059057154
|
||||
* @version 1.6.3
|
||||
* @description Adds extra Controls to the Friends Page, for example sort by Name/Status, Search and Amount Numbers, new Tabs
|
||||
* @invite Jx3TjNS
|
||||
* @donate https://www.paypal.me/MircoWittrien
|
||||
* @patreon https://www.patreon.com/MircoWittrien
|
||||
* @website https://mwittrien.github.io/
|
||||
* @source https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/BetterFriendList/
|
||||
* @updateUrl https://mwittrien.github.io/BetterDiscordAddons/Plugins/BetterFriendList/BetterFriendList.plugin.js
|
||||
*/
|
||||
|
||||
module.exports = (_ => {
|
||||
const changeLog = {
|
||||
|
||||
};
|
||||
|
||||
return !window.BDFDB_Global || (!window.BDFDB_Global.loaded && !window.BDFDB_Global.started) ? class {
|
||||
constructor (meta) {for (let key in meta) this[key] = meta[key];}
|
||||
getName () {return this.name;}
|
||||
getAuthor () {return this.author;}
|
||||
getVersion () {return this.version;}
|
||||
getDescription () {return `The Library Plugin needed for ${this.name} is missing. Open the Plugin Settings to download it. \n\n${this.description}`;}
|
||||
|
||||
downloadLibrary () {
|
||||
BdApi.Net.fetch("https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js").then(r => {
|
||||
if (!r || r.status != 200) throw new Error();
|
||||
else return r.text();
|
||||
}).then(b => {
|
||||
if (!b) throw new Error();
|
||||
else return require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0BDFDB.plugin.js"), b, _ => BdApi.UI.showToast("Finished downloading BDFDB Library", {type: "success"}));
|
||||
}).catch(error => {
|
||||
BdApi.UI.alert("Error", "Could not download BDFDB Library Plugin. Try again later or download it manually from GitHub: https://mwittrien.github.io/downloader/?library");
|
||||
});
|
||||
}
|
||||
|
||||
load () {
|
||||
if (!window.BDFDB_Global || !Array.isArray(window.BDFDB_Global.pluginQueue)) window.BDFDB_Global = Object.assign({}, window.BDFDB_Global, {pluginQueue: []});
|
||||
if (!window.BDFDB_Global.downloadModal) {
|
||||
window.BDFDB_Global.downloadModal = true;
|
||||
BdApi.UI.showConfirmationModal("Library Missing", `The Library Plugin needed for ${this.name} is missing. Please click "Download Now" to install it.`, {
|
||||
confirmText: "Download Now",
|
||||
cancelText: "Cancel",
|
||||
onCancel: _ => {delete window.BDFDB_Global.downloadModal;},
|
||||
onConfirm: _ => {
|
||||
delete window.BDFDB_Global.downloadModal;
|
||||
this.downloadLibrary();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!window.BDFDB_Global.pluginQueue.includes(this.name)) window.BDFDB_Global.pluginQueue.push(this.name);
|
||||
}
|
||||
start () {this.load();}
|
||||
stop () {}
|
||||
getSettingsPanel () {
|
||||
let template = document.createElement("template");
|
||||
template.innerHTML = `<div style="color: var(--text-primary); font-size: 16px; font-weight: 300; white-space: pre; line-height: 22px;">The Library Plugin needed for ${this.name} is missing.\nPlease click <a style="font-weight: 500;">Download Now</a> to install it.</div>`;
|
||||
template.content.firstElementChild.querySelector("a").addEventListener("click", this.downloadLibrary);
|
||||
return template.content.firstElementChild;
|
||||
}
|
||||
} : (([Plugin, BDFDB]) => {
|
||||
var rerenderTimeout, sortKey, sortReversed;
|
||||
|
||||
const customSections = {
|
||||
BLOCKED: "BLOCKED",
|
||||
FAVORITES: "FAVORIZED_FRIENDS",
|
||||
HIDDEN: "HIDDEN_FRIENDS",
|
||||
IGNORED: "IGNORED"
|
||||
};
|
||||
const placeHolderId = "PLACEHOLDER_BETTERFRIENDLIST";
|
||||
|
||||
var favorizedFriends = [], hiddenFriends = [];
|
||||
var currentSection = null;
|
||||
|
||||
const statusSortOrder = {
|
||||
online: 0,
|
||||
streaming: 1,
|
||||
idle: 2,
|
||||
dnd: 3,
|
||||
offline: 4,
|
||||
invisible: 5,
|
||||
unknown: 6
|
||||
};
|
||||
|
||||
return class BetterFriendList extends Plugin {
|
||||
onLoad () {
|
||||
this.defaults = {
|
||||
general: {
|
||||
addTotalAmount: {value: true, description: "Adds total Amount for All/Requested/Blocked"},
|
||||
addBlockedCategory: {value: true, description: "Adds Blocked Category"},
|
||||
addIgnoredCategory: {value: true, description: "Adds Ignored Category"},
|
||||
addFavorizedCategory: {value: true, description: "Adds Favorites Category"},
|
||||
addHiddenCategory: {value: true, description: "Adds Hidden Category"},
|
||||
addSortOptions: {value: true, description: "Adds Sort Options"},
|
||||
addMutualGuild: {value: true, description: "Adds mutual Servers in Friend List"}
|
||||
}
|
||||
};
|
||||
|
||||
this.modulePatches = {
|
||||
before: [
|
||||
"AnalyticsContext",
|
||||
"PeopleListSectionedLazy",
|
||||
"PeopleListSectionedNonLazy",
|
||||
"TabBar"
|
||||
],
|
||||
after: [
|
||||
"PeopleListItem",
|
||||
"TabBar"
|
||||
],
|
||||
componentDidMount: [
|
||||
"PeopleListItem"
|
||||
],
|
||||
componentWillUnmount: [
|
||||
"PeopleListItem"
|
||||
]
|
||||
};
|
||||
|
||||
this.css = `
|
||||
${BDFDB.dotCNS.peoplestabbar + BDFDB.dotCN.peoplesbadge} {
|
||||
background-color: var(--background-accent);
|
||||
margin-left: 6px;
|
||||
}
|
||||
${BDFDB.dotCN._betterfriendlisttitle} {
|
||||
width: 200px;
|
||||
}
|
||||
${BDFDB.dotCN._betterfriendlistnamecell} {
|
||||
width: 200px;
|
||||
}
|
||||
${BDFDB.dotCNS.peoplespeoplecolumn + BDFDB.dotCN.searchbar} {
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
${BDFDB.dotCN.peoplesuser} {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
${BDFDB.dotCN.peoplesactions} {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
${BDFDB.dotCN._betterfriendlistmutualguilds} {
|
||||
flex: 0 0 200px;
|
||||
margin-left: 13px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
onStart () {
|
||||
sortKey = null;
|
||||
sortReversed = false;
|
||||
currentSection = null;
|
||||
|
||||
this.forceUpdateAll();
|
||||
}
|
||||
|
||||
onStop () {
|
||||
this.forceUpdateAll();
|
||||
}
|
||||
|
||||
getSettingsPanel (collapseStates = {}) {
|
||||
let settingsPanel;
|
||||
return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, {
|
||||
collapseStates: collapseStates,
|
||||
children: _ => {
|
||||
let settingsItems = [];
|
||||
|
||||
for (let key in this.defaults.general) settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
|
||||
type: "Switch",
|
||||
plugin: this,
|
||||
keys: ["general", key],
|
||||
label: this.defaults.general[key].description,
|
||||
value: this.settings.general[key]
|
||||
}));
|
||||
|
||||
return settingsItems;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSettingsClosed () {
|
||||
if (this.SettingsUpdated) {
|
||||
delete this.SettingsUpdated;
|
||||
this.forceUpdateAll();
|
||||
}
|
||||
}
|
||||
|
||||
forceUpdateAll () {
|
||||
favorizedFriends = BDFDB.DataUtils.load(this, "favorizedFriends");
|
||||
favorizedFriends = !BDFDB.ArrayUtils.is(favorizedFriends) ? [] : favorizedFriends;
|
||||
hiddenFriends = BDFDB.DataUtils.load(this, "hiddenFriends");
|
||||
hiddenFriends = !BDFDB.ArrayUtils.is(hiddenFriends) ? [] : hiddenFriends;
|
||||
|
||||
BDFDB.PatchUtils.forceAllUpdates(this);
|
||||
this.rerenderList();
|
||||
}
|
||||
|
||||
onUserContextMenu (e) {
|
||||
if (!e.instance.props.user || !BDFDB.LibraryStores.RelationshipStore.isFriend(e.instance.props.user.id)) return;
|
||||
let favorized = favorizedFriends.indexOf(e.instance.props.user.id) > -1;
|
||||
let hidden = hiddenFriends.indexOf(e.instance.props.user.id) > -1;
|
||||
let [children, index] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: "remove-friend"});
|
||||
if (index > -1) children.splice(index + 1, 0, this.settings.general.addFavorizedCategory && BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
|
||||
label: favorized ? this.labels.context_unfavorizefriend : this.labels.context_favorizefriend,
|
||||
id: BDFDB.ContextMenuUtils.createItemId(this.name, favorized ? "unfavorize-friend" : "favorize-friend"),
|
||||
action: _ => {
|
||||
if (favorized) BDFDB.ArrayUtils.remove(favorizedFriends, e.instance.props.user.id, true);
|
||||
else {
|
||||
favorizedFriends.push(e.instance.props.user.id);
|
||||
BDFDB.ArrayUtils.remove(hiddenFriends, e.instance.props.user.id, true);
|
||||
}
|
||||
BDFDB.DataUtils.save(favorizedFriends, this, "favorizedFriends");
|
||||
BDFDB.DataUtils.save(hiddenFriends, this, "hiddenFriends");
|
||||
this.rerenderList();
|
||||
}
|
||||
}), this.settings.general.addHiddenCategory && BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
|
||||
label: hidden ? this.labels.context_unhidefriend : this.labels.context_hidefriend,
|
||||
id: BDFDB.ContextMenuUtils.createItemId(this.name, hidden ? "unhide-friend" : "hide-friend"),
|
||||
action: _ => {
|
||||
if (hidden) BDFDB.ArrayUtils.remove(hiddenFriends, e.instance.props.user.id, true);
|
||||
else {
|
||||
BDFDB.ArrayUtils.remove(favorizedFriends, e.instance.props.user.id, true);
|
||||
hiddenFriends.push(e.instance.props.user.id);
|
||||
}
|
||||
BDFDB.DataUtils.save(favorizedFriends, this, "favorizedFriends");
|
||||
BDFDB.DataUtils.save(hiddenFriends, this, "hiddenFriends");
|
||||
this.rerenderList();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
processTabBar (e) {
|
||||
if (e.instance.props.children && e.instance.props.children.some(c => c && c.props && c.props.id == BDFDB.DiscordConstants.FriendsSections.ADD_FRIEND)) {
|
||||
let relationships = BDFDB.LibraryStores.RelationshipStore.getMutableRelationships(), relationshipCount = {};
|
||||
for (let type in BDFDB.DiscordConstants.RelationshipTypes) relationshipCount[type] = 0;
|
||||
relationships.forEach((type, id) => {
|
||||
if (!this.settings.general.addHiddenCategory || (hiddenFriends.indexOf(id) == -1 || type != BDFDB.DiscordConstants.RelationshipTypes.FRIEND)) relationshipCount[type]++;
|
||||
});
|
||||
relationshipCount.IGNORED = BDFDB.LibraryStores.RelationshipStore.getIgnoredIDs().length;
|
||||
currentSection = e.instance.props.selectedItem;
|
||||
let hasFriends = relationshipCount[BDFDB.DiscordConstants.RelationshipTypes.FRIEND] > 0;
|
||||
if (!e.returnvalue) {
|
||||
e.instance.props.children = e.instance.props.children.filter(c => c && c.props.id != customSections.FAVORITES && c.props.id != customSections.HIDDEN);
|
||||
if (this.settings.general.addFavorizedCategory && hasFriends) e.instance.props.children.splice(e.instance.props.children.findIndex(c => c && c.props.id == BDFDB.DiscordConstants.FriendsSections.ONLINE) + 1, 0, BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TabBar.Item, {
|
||||
id: customSections.FAVORITES,
|
||||
className: BDFDB.disCN.peoplestabbaritem,
|
||||
children: this.labels.favorites
|
||||
}));
|
||||
let index = e.instance.props.children.findIndex(c => c && c.props.id == BDFDB.DiscordConstants.FriendsSections.PENDING);
|
||||
if (index == -1) index = e.instance.props.children.findIndex(c => c && c.props.id == customSections.FAVORITES);
|
||||
if (index == -1) index = e.instance.props.children.findIndex(c => c && c.props.id == BDFDB.DiscordConstants.FriendsSections.ONLINE);
|
||||
if (this.settings.general.addHiddenCategory && hasFriends) e.instance.props.children.splice(index + 1, 0, BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TabBar.Item, {
|
||||
id: customSections.HIDDEN,
|
||||
className: BDFDB.disCN.peoplestabbaritem,
|
||||
children: this.labels.hidden
|
||||
}));
|
||||
if (this.settings.general.addIgnoredCategory) e.instance.props.children.splice(index + 1, 0, BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TabBar.Item, {
|
||||
id: customSections.IGNORED,
|
||||
className: BDFDB.disCN.peoplestabbaritem,
|
||||
children: this.labels.ignored
|
||||
}));
|
||||
if (this.settings.general.addBlockedCategory) e.instance.props.children.splice(index + 1, 0, BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TabBar.Item, {
|
||||
id: customSections.BLOCKED,
|
||||
className: BDFDB.disCN.peoplestabbaritem,
|
||||
children: this.labels.blocked
|
||||
}));
|
||||
}
|
||||
else {
|
||||
if (this.settings.general.addTotalAmount) {
|
||||
for (let child of e.returnvalue.props.children) if (child && child.props.id != BDFDB.DiscordConstants.FriendsSections.ADD_FRIEND) {
|
||||
let newChildren = [child.props.children].flat().filter(n => !n || !n.props || n.props.count == undefined);
|
||||
switch (child.props.id) {
|
||||
case BDFDB.DiscordConstants.FriendsSections.ALL:
|
||||
newChildren.push(this.createBadge(relationshipCount[BDFDB.DiscordConstants.RelationshipTypes.FRIEND]));
|
||||
break;
|
||||
case customSections.FAVORITES:
|
||||
newChildren.push(this.createBadge(favorizedFriends.filter(id => relationships.get(id) == BDFDB.DiscordConstants.RelationshipTypes.FRIEND).length));
|
||||
break;
|
||||
case BDFDB.DiscordConstants.FriendsSections.ONLINE:
|
||||
newChildren.push(this.createBadge(Array.from(relationships).filter(n => n[1] == BDFDB.DiscordConstants.RelationshipTypes.FRIEND && !(this.settings.general.addHiddenCategory && hiddenFriends.indexOf(n[0]) > -1) && BDFDB.LibraryStores.PresenceStore.getStatus(n[0]) != BDFDB.LibraryComponents.StatusComponents.Types.OFFLINE).length));
|
||||
break;
|
||||
case BDFDB.DiscordConstants.FriendsSections.PENDING:
|
||||
newChildren.push(this.createBadge(relationshipCount[BDFDB.DiscordConstants.RelationshipTypes.PENDING_INCOMING], this.labels.incoming, relationshipCount[BDFDB.DiscordConstants.RelationshipTypes.PENDING_INCOMING] > 0));
|
||||
newChildren.push(this.createBadge(relationshipCount[BDFDB.DiscordConstants.RelationshipTypes.PENDING_OUTGOING], this.labels.outgoing));
|
||||
break;
|
||||
case customSections.BLOCKED:
|
||||
newChildren.push(this.createBadge(relationshipCount[BDFDB.DiscordConstants.RelationshipTypes.BLOCKED]));
|
||||
break;
|
||||
case customSections.IGNORED:
|
||||
newChildren.push(this.createBadge(relationshipCount.IGNORED));
|
||||
break;
|
||||
case customSections.HIDDEN:
|
||||
newChildren.push(this.createBadge(hiddenFriends.filter(id => relationships.get(id) == BDFDB.DiscordConstants.RelationshipTypes.FRIEND).length));
|
||||
break;
|
||||
}
|
||||
child.props.children = newChildren;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processAnalyticsContext (e) {
|
||||
if (e.instance.props.section != BDFDB.DiscordConstants.AnalyticsSections.FRIENDS_LIST) return;
|
||||
let body = BDFDB.ReactUtils.findChild(e.instance, {filter: n => n && n.props && n.props.renderRow && n.props.rows});
|
||||
if (!body) return;
|
||||
let users = body.props.rows.flat(10);
|
||||
let filteredUsers = users;
|
||||
if (this.settings.general.addFavorizedCategory && currentSection == customSections.FAVORITES) filteredUsers = filteredUsers.filter(n => n && n.user && favorizedFriends.indexOf(n.user.id) > -1);
|
||||
if (this.settings.general.addHiddenCategory) {
|
||||
if (currentSection == customSections.HIDDEN) filteredUsers = filteredUsers.filter(n => n && n.user && hiddenFriends.indexOf(n.user.id) > -1);
|
||||
else filteredUsers = filteredUsers.filter(n => n && n.user && hiddenFriends.indexOf(n.user.id) == -1);
|
||||
}
|
||||
if (this.settings.general.addBlockedCategory && currentSection == customSections.BLOCKED || this.settings.general.addIgnoredCategory && currentSection == customSections.IGNORED) {
|
||||
filteredUsers = currentSection == customSections.IGNORED ? BDFDB.LibraryStores.RelationshipStore.getIgnoredIDs() : this.getBlockedIDs();
|
||||
for (let className of [BDFDB.disCN.peopleslistsearchbar, BDFDB.disCN.peopleslistempty]) {
|
||||
let [children, index] = BDFDB.ReactUtils.findParent(e.instance, {props: [["className", className]]});
|
||||
if (index > -1) children[index] = null;
|
||||
}
|
||||
body.props.hasSearchQuery = false;
|
||||
}
|
||||
if (this.settings.general.addBlockedCategory || this.settings.general.addIgnoredCategory) {
|
||||
let [children, index] = BDFDB.ReactUtils.findParent(e.instance, {filter: n => n.type && n.type.toLocaleString().indexOf("blockedIgnoredSettingsNotice") > -1});
|
||||
if (index > -1) children[index] = null;
|
||||
}
|
||||
let renderSection = body.props.renderSection;
|
||||
body.props.renderSection = BDFDB.TimeUtils.suppress((...args) => {
|
||||
let returnValue = renderSection(...args);
|
||||
let title = returnValue.props.children.props.title, customTitle = null;
|
||||
if (this.settings.general.addFavorizedCategory && currentSection == customSections.FAVORITES) customTitle = this.labels.favorites;
|
||||
else if (this.settings.general.addHiddenCategory && currentSection == customSections.HIDDEN) customTitle = this.labels.hidden;
|
||||
else if (this.settings.general.addBlockedCategory && currentSection == customSections.BLOCKED) customTitle = this.labels.blocked;
|
||||
else if (this.settings.general.addIgnoredCategory && currentSection == customSections.IGNORED) customTitle = this.labels.ignored;
|
||||
returnValue.props.children.props.title = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
|
||||
align: BDFDB.LibraryComponents.Flex.Align.CENTER,
|
||||
children: [
|
||||
BDFDB.ReactUtils.createElement("div", {
|
||||
className: BDFDB.disCN._betterfriendlisttitle,
|
||||
children: customTitle ? `${customTitle} - ${filteredUsers.filter(u => u && u.key != placeHolderId).length}` : title.replace(users.length, filteredUsers.filter(u => u && u.key != placeHolderId).length)
|
||||
}),
|
||||
this.settings.general.addSortOptions && [
|
||||
{key: "nicknameLower", label: BDFDB.LanguageUtils.LanguageStrings.USER_SETTINGS_LABEL_USERNAME},
|
||||
{key: "statusIndex", label: BDFDB.LanguageUtils.LibraryStrings.status}
|
||||
].filter(n => n).map(data => BDFDB.ReactUtils.createElement("div", {
|
||||
className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.tableheadercellwrapper, BDFDB.disCN.tableheadercell, BDFDB.disCN._betterfriendlistnamecell, sortKey == data.key && BDFDB.disCN.tableheadercellsorted, BDFDB.disCN.tableheadercellclickable),
|
||||
children: BDFDB.ReactUtils.createElement("div", {
|
||||
className: BDFDB.disCN.tableheadercellcontent,
|
||||
children: [
|
||||
data.label,
|
||||
sortKey == data.key && BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
|
||||
className: BDFDB.disCN.tableheadersorticon,
|
||||
name: BDFDB.LibraryComponents.SvgIcon.Names[sortReversed ? "ARROW_UP" : "ARROW_DOWN"]
|
||||
})
|
||||
].filter(n => n)
|
||||
}),
|
||||
onClick: event => {
|
||||
if (sortKey == data.key) {
|
||||
if (!sortReversed) sortReversed = true;
|
||||
else {
|
||||
sortKey = null;
|
||||
sortReversed = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sortKey = data.key;
|
||||
sortReversed = false;
|
||||
}
|
||||
this.rerenderList();
|
||||
}
|
||||
}))
|
||||
].flat(10).filter(n => n)
|
||||
});
|
||||
return returnValue;
|
||||
}, "Error in Section Render of PeopleList!", this);
|
||||
}
|
||||
|
||||
processPeopleListSectionedLazy (e) {
|
||||
this.processPeopleListSectionedNonLazy(e);
|
||||
}
|
||||
|
||||
processPeopleListSectionedNonLazy (e) {
|
||||
if (this.settings.general.addFavorizedCategory && currentSection == customSections.FAVORITES) e.instance.props.rows = [].concat(e.instance.props.rows).map(section => [].concat(section).filter(entry => entry && entry.user && favorizedFriends.indexOf(entry.user.id) > -1));
|
||||
if (this.settings.general.addHiddenCategory) {
|
||||
if (currentSection == customSections.HIDDEN) e.instance.props.rows = [].concat(e.instance.props.rows).map(section => [].concat(section).filter(entry => entry && entry.user && hiddenFriends.indexOf(entry.user.id) > -1));
|
||||
else if (([].concat(e.instance.props.rows).flat(10)[0] || {}).type == BDFDB.DiscordConstants.RelationshipTypes.FRIEND) e.instance.props.rows = [].concat(e.instance.props.rows).map(section => [].concat(section).filter(entry => entry && entry.user && hiddenFriends.indexOf(entry.user.id) == -1));
|
||||
}
|
||||
if (this.settings.general.addBlockedCategory && currentSection == customSections.BLOCKED || this.settings.general.addIgnoredCategory && currentSection == customSections.IGNORED) {
|
||||
let ignoredSection = currentSection == customSections.IGNORED;
|
||||
let userIDs = ignoredSection ? BDFDB.LibraryStores.RelationshipStore.getIgnoredIDs() : this.getBlockedIDs();
|
||||
let RelationshipConstructor = e.instance.props.rows.flat(10)[0] && e.instance.props.rows.flat(10)[0].constructor || class RelationshipConstructor {
|
||||
get comparator() {
|
||||
return [this.type, 1, this.nickname || this.user && this.user.global && this.user.global.toLowerCase() || this.usernameLower]
|
||||
}
|
||||
constructor(e) {
|
||||
for (let prop of Object.keys(e)) this[prop] = e[prop];
|
||||
}
|
||||
};
|
||||
e.instance.props.rows = [userIDs.map(id => {
|
||||
let user = BDFDB.LibraryStores.UserStore.getUser(id);
|
||||
return new RelationshipConstructor({
|
||||
activities: [],
|
||||
applicationId: undefined,
|
||||
applicationStream: null,
|
||||
giftIntentType: undefined,
|
||||
ignoredUser: ignoredSection,
|
||||
isGameRelationship: false,
|
||||
isMobile: false,
|
||||
key: id,
|
||||
mutualGuilds: [],
|
||||
mutualGuildsLength: 0,
|
||||
nickname: undefined,
|
||||
spam: false,
|
||||
status: BDFDB.UserUtils.getStatus(id) || "offline",
|
||||
type: BDFDB.DiscordConstants.RelationshipTypes.BLOCKED,
|
||||
user: user ? user : new BDFDB.DiscordObjects.User({id: id, username: BDFDB.LanguageUtils.LanguageStrings.UNKNOWN_USER}),
|
||||
userId: id,
|
||||
usernameLower: user ? user.username.toLowerCase() : BDFDB.LanguageUtils.LanguageStrings.UNKNOWN_USER
|
||||
});
|
||||
})];
|
||||
}
|
||||
if (sortKey && e.instance.props.rows.flat(10).length) e.instance.props.rows = [].concat(e.instance.props.rows).map(section => {
|
||||
let newSection = [].concat(section);
|
||||
newSection = BDFDB.ArrayUtils.keySort(newSection.map(entry => Object.assign({}, entry, {
|
||||
statusIndex: statusSortOrder[entry.status],
|
||||
nicknameLower: entry.nickname ? entry.nickname.toLowerCase() : entry.usernameLower
|
||||
})), sortKey);
|
||||
if (sortReversed) newSection.reverse();
|
||||
if (!newSection.length) {
|
||||
let placeholder = new BDFDB.DiscordObjects.User({
|
||||
id: placeHolderId,
|
||||
username: placeHolderId
|
||||
});
|
||||
if (placeholder) newSection.push(new BDFDB.DiscordObjects.Relationship({
|
||||
activities: [],
|
||||
applicationStream: null,
|
||||
isMobile: false,
|
||||
key: placeHolderId,
|
||||
mutualGuilds: [],
|
||||
mutualGuildsLength: 0,
|
||||
status: "offline",
|
||||
type: BDFDB.DiscordConstants.RelationshipTypes.NONE,
|
||||
user: placeholder,
|
||||
usernameLower: placeholder.usernameNormalized
|
||||
}));
|
||||
}
|
||||
return newSection;
|
||||
});
|
||||
}
|
||||
|
||||
processPeopleListItem (e) {
|
||||
if (e.node) {
|
||||
BDFDB.TimeUtils.clear(rerenderTimeout);
|
||||
rerenderTimeout = BDFDB.TimeUtils.timeout(_ => BDFDB.PatchUtils.forceAllUpdates(this, "TabBar"), 1000);
|
||||
}
|
||||
else {
|
||||
if (e.instance.props.user.id == placeHolderId) return null;
|
||||
let childrenRender = e.returnvalue.props.children;
|
||||
e.returnvalue.props.children = BDFDB.TimeUtils.suppress((...args) => {
|
||||
let returnValue = childrenRender(...args);
|
||||
if (BDFDB.LibraryStores.RelationshipStore.isBlocked(e.instance.props.user.id) || BDFDB.LibraryStores.RelationshipStore.isIgnored(e.instance.props.user.id)) {
|
||||
let actions = BDFDB.ReactUtils.findChild(returnValue, {props: [["className", BDFDB.disCN.peoplesactions]]});
|
||||
if (actions) actions.props.children.pop();
|
||||
}
|
||||
if (this.settings.general.addMutualGuild) {
|
||||
let mutualGuilds = BDFDB.ArrayUtils.removeCopies([].concat(BDFDB.LibraryStores.GuildMemberStore.memberOf(e.instance.props.user.id), (BDFDB.LibraryStores.UserProfileStore.getMutualGuilds(e.instance.props.user.id) || []).map(n => n && n.guild && n.guild.id)).flat()).filter(n => n);
|
||||
if (mutualGuilds && mutualGuilds.length) {
|
||||
let guildsIds = BDFDB.LibraryStores.SortedGuildStore.getFlattenedGuildIds();
|
||||
let [children, index] = BDFDB.ReactUtils.findParent(returnValue, {filter: n => n && n.props && n.props.subText && n.props.user});
|
||||
if (index > -1) children.splice(index + 1, 0, BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.GuildSummaryItem, {
|
||||
className: BDFDB.disCN._betterfriendlistmutualguilds,
|
||||
guilds: mutualGuilds.sort((x, y) => guildsIds.indexOf(x) < guildsIds.indexOf(y) ? -1 : 1).map(BDFDB.LibraryStores.GuildStore.getGuild),
|
||||
showTooltip: true,
|
||||
max: 10
|
||||
}, true));
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}, "Error in PeopleListItem Render!", this);
|
||||
}
|
||||
}
|
||||
|
||||
createBadge (amount, text, red) {
|
||||
let badge = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Badges.NumberBadge, {
|
||||
className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.peoplesbadge),
|
||||
count: amount,
|
||||
disableColor: !red
|
||||
});
|
||||
return text ? BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
|
||||
text: text,
|
||||
tooltipConfig: {
|
||||
type: "bottom"
|
||||
},
|
||||
children: badge
|
||||
}) : badge;
|
||||
}
|
||||
|
||||
getBlockedIDs () {
|
||||
return Array.from(BDFDB.LibraryStores.RelationshipStore.getMutableRelationships()).filter(n => n[1] == BDFDB.DiscordConstants.RelationshipTypes.BLOCKED).map(n => n[0]);
|
||||
}
|
||||
|
||||
rerenderList () {
|
||||
let selectedButton = document.querySelector(BDFDB.dotCNS.dmchannel + BDFDB.dotCNS.namecontainerselected + "a");
|
||||
if (selectedButton) selectedButton.click();
|
||||
}
|
||||
|
||||
setLabelsByLanguage () {
|
||||
switch (BDFDB.LanguageUtils.getLanguage().id) {
|
||||
case "bg": // Bulgarian
|
||||
return {
|
||||
blocked: "Блокиран",
|
||||
context_favorizefriend: "Добавете приятел към любими",
|
||||
context_hidefriend: "Скрий приятел",
|
||||
context_unfavorizefriend: "Премахване на приятел от любимите",
|
||||
context_unhidefriend: "Разкрий приятел",
|
||||
favorites: "Любими",
|
||||
hidden: "Скрити",
|
||||
ignored: "Игнориран",
|
||||
incoming: "Входящи",
|
||||
outgoing: "Изходящи"
|
||||
};
|
||||
case "cs": // Czech
|
||||
return {
|
||||
blocked: "Blokované",
|
||||
context_favorizefriend: "Přidat přítele do oblíbených",
|
||||
context_hidefriend: "Skrýt přítele",
|
||||
context_unfavorizefriend: "Odebrat přítele z oblíbených",
|
||||
context_unhidefriend: "Odkrýt přítele",
|
||||
favorites: "Oblíbené",
|
||||
hidden: "Skrytý",
|
||||
ignored: "Ignorováno",
|
||||
incoming: "Přicházející",
|
||||
outgoing: "Odchozí"
|
||||
};
|
||||
case "da": // Danish
|
||||
return {
|
||||
blocked: "Blokeret",
|
||||
context_favorizefriend: "Føj ven til favoritter",
|
||||
context_hidefriend: "Skjul ven",
|
||||
context_unfavorizefriend: "Fjern ven fra favoritter",
|
||||
context_unhidefriend: "Skjul ven",
|
||||
favorites: "Favoritter",
|
||||
hidden: "Skjult",
|
||||
ignored: "Ignoreret",
|
||||
incoming: "Indgående",
|
||||
outgoing: "Udgående"
|
||||
};
|
||||
case "de": // German
|
||||
return {
|
||||
blocked: "Blockiert",
|
||||
context_favorizefriend: "Freund zu Favoriten hinzufügen",
|
||||
context_hidefriend: "Freund ausblenden",
|
||||
context_unfavorizefriend: "Freund aus Favoriten entfernen",
|
||||
context_unhidefriend: "Freund einblenden",
|
||||
favorites: "Favoriten",
|
||||
hidden: "Versteckt",
|
||||
ignored: "Ignoriert",
|
||||
incoming: "Eingehend",
|
||||
outgoing: "Ausgehend"
|
||||
};
|
||||
case "el": // Greek
|
||||
return {
|
||||
blocked: "Μπλοκαρισμένος",
|
||||
context_favorizefriend: "Προσθήκη φίλου στους αγαπημένους",
|
||||
context_hidefriend: "Απόκρυψη φίλου",
|
||||
context_unfavorizefriend: "Κατάργηση φίλου από τούς αγαπημένους",
|
||||
context_unhidefriend: "Επανεμφάνιση φίλου",
|
||||
favorites: "Αγαπημένοι",
|
||||
hidden: "Σε απόκρυψη",
|
||||
ignored: "Αγνοημένος",
|
||||
incoming: "Εισερχόμενος",
|
||||
outgoing: "Εξερχόμενος"
|
||||
};
|
||||
case "es": // Spanish
|
||||
return {
|
||||
blocked: "Obstruido",
|
||||
context_favorizefriend: "Agregar amigo a favoritos",
|
||||
context_hidefriend: "Ocultar amigo",
|
||||
context_unfavorizefriend: "Quitar amigo de favoritos",
|
||||
context_unhidefriend: "Mostrar amigo",
|
||||
favorites: "Favoritos",
|
||||
hidden: "Oculto",
|
||||
ignored: "Ignorado",
|
||||
incoming: "Entrante",
|
||||
outgoing: "Saliente"
|
||||
};
|
||||
case "es-419": // Spanish (Latin America)
|
||||
return {
|
||||
blocked: "Obstruido",
|
||||
context_favorizefriend: "Agregar amigo a los favoritos",
|
||||
context_hidefriend: "Esconder a amigo",
|
||||
context_unfavorizefriend: "Eliminar amigo de los favoritos",
|
||||
context_unhidefriend: "Amigo dehide",
|
||||
favorites: "Favoritos",
|
||||
hidden: "Oculto",
|
||||
ignored: "Ignorado",
|
||||
incoming: "Entrante",
|
||||
outgoing: "Extrovertido"
|
||||
};
|
||||
case "fi": // Finnish
|
||||
return {
|
||||
blocked: "Estetty",
|
||||
context_favorizefriend: "Lisää ystävä suosikkeihin",
|
||||
context_hidefriend: "Piilota ystävä",
|
||||
context_unfavorizefriend: "Poista ystävä suosikeista",
|
||||
context_unhidefriend: "Näytä ystävä",
|
||||
favorites: "Suosikit",
|
||||
hidden: "Piilotettu",
|
||||
ignored: "Sivuutettu",
|
||||
incoming: "Saapuva",
|
||||
outgoing: "Lähtevä"
|
||||
};
|
||||
case "fr": // French
|
||||
return {
|
||||
blocked: "Bloqué",
|
||||
context_favorizefriend: "Ajouter un ami aux favoris",
|
||||
context_hidefriend: "Masquer l'ami",
|
||||
context_unfavorizefriend: "Supprimer un ami des favoris",
|
||||
context_unhidefriend: "Afficher l'ami",
|
||||
favorites: "Favoris",
|
||||
hidden: "Caché",
|
||||
ignored: "Ignoré",
|
||||
incoming: "Entrant",
|
||||
outgoing: "Sortant"
|
||||
};
|
||||
case "hi": // Hindi
|
||||
return {
|
||||
blocked: "अवरोधित",
|
||||
context_favorizefriend: "मित्र को पसंदीदा में जोड़ें",
|
||||
context_hidefriend: "दोस्त छुपाएं",
|
||||
context_unfavorizefriend: "मित्र को पसंदीदा से हटाएं",
|
||||
context_unhidefriend: "मित्र दिखाएँ",
|
||||
favorites: "पसंदीदा",
|
||||
hidden: "छिपा हुआ",
|
||||
ignored: "अवहेलना करना",
|
||||
incoming: "आने वाली",
|
||||
outgoing: "निवर्तमान"
|
||||
};
|
||||
case "hr": // Croatian
|
||||
return {
|
||||
blocked: "Blokiran",
|
||||
context_favorizefriend: "Dodaj prijatelja u favorite",
|
||||
context_hidefriend: "Sakrij prijatelja",
|
||||
context_unfavorizefriend: "Ukloni prijatelja iz omiljenih",
|
||||
context_unhidefriend: "Otkrij prijatelja",
|
||||
favorites: "Favoriti",
|
||||
hidden: "Skriven",
|
||||
ignored: "Zanemaren",
|
||||
incoming: "Dolazni",
|
||||
outgoing: "Odlazni"
|
||||
};
|
||||
case "hu": // Hungarian
|
||||
return {
|
||||
blocked: "Zárolt",
|
||||
context_favorizefriend: "Ismerős hozzáadása a kedvencekhez",
|
||||
context_hidefriend: "Barát elrejtése",
|
||||
context_unfavorizefriend: "Ismerős eltávolítása a kedvencekből",
|
||||
context_unhidefriend: "Barát megjelenítése",
|
||||
favorites: "Kedvencek",
|
||||
hidden: "Rejtett",
|
||||
ignored: "Figyelmen kívül hagyott",
|
||||
incoming: "Beérkező",
|
||||
outgoing: "Kimenő"
|
||||
};
|
||||
case "it": // Italian
|
||||
return {
|
||||
blocked: "Bloccato",
|
||||
context_favorizefriend: "Aggiungi amico ai preferiti",
|
||||
context_hidefriend: "Nascondi amico",
|
||||
context_unfavorizefriend: "Rimuovi amico dai preferiti",
|
||||
context_unhidefriend: "Scopri amico",
|
||||
favorites: "Preferiti",
|
||||
hidden: "Nascosto",
|
||||
ignored: "Ignorato",
|
||||
incoming: "In arrivo",
|
||||
outgoing: "Estroverso"
|
||||
};
|
||||
case "ja": // Japanese
|
||||
return {
|
||||
blocked: "ブロックされています",
|
||||
context_favorizefriend: "お気に入りに友達を追加する",
|
||||
context_hidefriend: "友達を隠す",
|
||||
context_unfavorizefriend: "お気に入りから友達を削除する",
|
||||
context_unhidefriend: "友達を再表示",
|
||||
favorites: "お気に入り",
|
||||
hidden: "隠し",
|
||||
ignored: "無視した",
|
||||
incoming: "着信",
|
||||
outgoing: "発信"
|
||||
};
|
||||
case "ko": // Korean
|
||||
return {
|
||||
blocked: "막힌",
|
||||
context_favorizefriend: "즐겨찾기에 친구 추가",
|
||||
context_hidefriend: "친구 숨기기",
|
||||
context_unfavorizefriend: "즐겨찾기에서 친구 제거",
|
||||
context_unhidefriend: "친구 숨기기 해제",
|
||||
favorites: "즐겨찾기",
|
||||
hidden: "숨겨진",
|
||||
ignored: "무시했습니다",
|
||||
incoming: "들어오는",
|
||||
outgoing: "나가는"
|
||||
};
|
||||
case "lt": // Lithuanian
|
||||
return {
|
||||
blocked: "Užblokuotas",
|
||||
context_favorizefriend: "Pridėti draugą prie mėgstamiausių",
|
||||
context_hidefriend: "Slėpti draugą",
|
||||
context_unfavorizefriend: "Pašalinti draugą iš mėgstamiausių",
|
||||
context_unhidefriend: "Nerodyti draugo",
|
||||
favorites: "Mėgstamiausi",
|
||||
hidden: "Paslėpta",
|
||||
ignored: "Ignoruojamas",
|
||||
incoming: "Gaunamasis",
|
||||
outgoing: "Išeinantis"
|
||||
};
|
||||
case "nl": // Dutch
|
||||
return {
|
||||
blocked: "Geblokkeerd",
|
||||
context_favorizefriend: "Vriend toevoegen aan favorieten",
|
||||
context_hidefriend: "Vriend verbergen",
|
||||
context_unfavorizefriend: "Vriend uit favorieten verwijderen",
|
||||
context_unhidefriend: "Vriend zichtbaar maken",
|
||||
favorites: "Favorieten",
|
||||
hidden: "Verborgen",
|
||||
ignored: "Genegeerd",
|
||||
incoming: "Inkomend",
|
||||
outgoing: "Uitgaand"
|
||||
};
|
||||
case "no": // Norwegian
|
||||
return {
|
||||
blocked: "Blokkert",
|
||||
context_favorizefriend: "Legg til en venn i favoritter",
|
||||
context_hidefriend: "Skjul venn",
|
||||
context_unfavorizefriend: "Fjern venn fra favoritter",
|
||||
context_unhidefriend: "Skjul venn",
|
||||
favorites: "Favoritter",
|
||||
hidden: "Skjult",
|
||||
ignored: "Ignorert",
|
||||
incoming: "Innkommende",
|
||||
outgoing: "Utgående"
|
||||
};
|
||||
case "pl": // Polish
|
||||
return {
|
||||
blocked: "Zablokowany",
|
||||
context_favorizefriend: "Dodaj znajomego do ulubionych",
|
||||
context_hidefriend: "Ukryj znajomego",
|
||||
context_unfavorizefriend: "Usuń znajomego z ulubionych",
|
||||
context_unhidefriend: "Pokaż znajomego",
|
||||
favorites: "Ulubione",
|
||||
hidden: "Ukryci",
|
||||
ignored: "Ignorowane",
|
||||
incoming: "Przychodzące",
|
||||
outgoing: "Wychodzące"
|
||||
};
|
||||
case "pt-BR": // Portuguese (Brazil)
|
||||
return {
|
||||
blocked: "Bloqueado",
|
||||
context_favorizefriend: "Adicionar amigo aos favoritos",
|
||||
context_hidefriend: "Esconder Amigo",
|
||||
context_unfavorizefriend: "Remover amigo dos favoritos",
|
||||
context_unhidefriend: "Reexibir amigo",
|
||||
favorites: "Favoritos",
|
||||
hidden: "Escondido",
|
||||
ignored: "Ignorado",
|
||||
incoming: "Entrada",
|
||||
outgoing: "Extrovertido"
|
||||
};
|
||||
case "ro": // Romanian
|
||||
return {
|
||||
blocked: "Blocat",
|
||||
context_favorizefriend: "Adaugă prieten la favorite",
|
||||
context_hidefriend: "Ascunde prietenul",
|
||||
context_unfavorizefriend: "Scoateți prietenul din favorite",
|
||||
context_unhidefriend: "Afișează prietenul",
|
||||
favorites: "Favorite",
|
||||
hidden: "Ascuns",
|
||||
ignored: "Ignorat",
|
||||
incoming: "Primite",
|
||||
outgoing: "De ieșire"
|
||||
};
|
||||
case "ru": // Russian
|
||||
return {
|
||||
blocked: "Заблокированный",
|
||||
context_favorizefriend: "Добавить друга в избранное",
|
||||
context_hidefriend: "Скрыть друга",
|
||||
context_unfavorizefriend: "Удалить друга из избранного",
|
||||
context_unhidefriend: "Показать друга",
|
||||
favorites: "Избранное",
|
||||
hidden: "Скрытый",
|
||||
ignored: "Игнорируется",
|
||||
incoming: "Входящий",
|
||||
outgoing: "Исходящий"
|
||||
};
|
||||
case "sv": // Swedish
|
||||
return {
|
||||
blocked: "Blockerad",
|
||||
context_favorizefriend: "Lägg till vän till favoriter",
|
||||
context_hidefriend: "Dölj vän",
|
||||
context_unfavorizefriend: "Ta bort vän från favoriter",
|
||||
context_unhidefriend: "Göm din vän",
|
||||
favorites: "Favoriter",
|
||||
hidden: "Dold",
|
||||
ignored: "Ignorerad",
|
||||
incoming: "Inkommande",
|
||||
outgoing: "Utgående"
|
||||
};
|
||||
case "th": // Thai
|
||||
return {
|
||||
blocked: "ที่ถูกปิดกั้น",
|
||||
context_favorizefriend: "เพิ่มเพื่อนในรายการโปรด",
|
||||
context_hidefriend: "ซ่อนเพื่อน",
|
||||
context_unfavorizefriend: "ลบเพื่อนออกจากรายการโปรด",
|
||||
context_unhidefriend: "เลิกซ่อนเพื่อน",
|
||||
favorites: "รายการโปรด",
|
||||
hidden: "ซ่อนเร้น",
|
||||
ignored: "เพิกเฉย",
|
||||
incoming: "ขาเข้า",
|
||||
outgoing: "ขาออก"
|
||||
};
|
||||
case "tr": // Turkish
|
||||
return {
|
||||
blocked: "Engellenmiş",
|
||||
context_favorizefriend: "Favorilere arkadaş ekle",
|
||||
context_hidefriend: "Arkadaşı Gizle",
|
||||
context_unfavorizefriend: "Arkadaşını favorilerden kaldır",
|
||||
context_unhidefriend: "Arkadaşı Göster",
|
||||
favorites: "Favoriler",
|
||||
hidden: "Gizli",
|
||||
ignored: "Göz ardı edilen",
|
||||
incoming: "Gelen",
|
||||
outgoing: "Dışa dönük"
|
||||
};
|
||||
case "uk": // Ukrainian
|
||||
return {
|
||||
blocked: "Заблокований",
|
||||
context_favorizefriend: "Додати друга у вибране",
|
||||
context_hidefriend: "Сховати друга",
|
||||
context_unfavorizefriend: "Видалити друга з вибраного",
|
||||
context_unhidefriend: "Показати друга",
|
||||
favorites: "Вибране",
|
||||
hidden: "Прихований",
|
||||
ignored: "Ігнорований",
|
||||
incoming: "Вхідні",
|
||||
outgoing: "Вихідний"
|
||||
};
|
||||
case "vi": // Vietnamese
|
||||
return {
|
||||
blocked: "Bị chặn",
|
||||
context_favorizefriend: "Thêm bạn bè vào danh sách yêu thích",
|
||||
context_hidefriend: "Ẩn bạn bè",
|
||||
context_unfavorizefriend: "Xóa bạn bè khỏi danh sách yêu thích",
|
||||
context_unhidefriend: "Bỏ ẩn bạn bè",
|
||||
favorites: "Yêu thích",
|
||||
hidden: "Ẩn",
|
||||
ignored: "Bỏ qua",
|
||||
incoming: "Mới đến",
|
||||
outgoing: "Hướng ngoaị"
|
||||
};
|
||||
case "zh-CN": // Chinese (China)
|
||||
return {
|
||||
blocked: "阻止",
|
||||
context_favorizefriend: "添加好友到收藏夹",
|
||||
context_hidefriend: "隐藏好友",
|
||||
context_unfavorizefriend: "从收藏夹中移除好友",
|
||||
context_unhidefriend: "取消隐藏好友",
|
||||
favorites: "收藏夹",
|
||||
hidden: "隐藏",
|
||||
ignored: "被忽略",
|
||||
incoming: "导入",
|
||||
outgoing: "导出"
|
||||
};
|
||||
case "zh-TW": // Chinese (Taiwan)
|
||||
return {
|
||||
blocked: "阻止",
|
||||
context_favorizefriend: "新增好友到我的最愛",
|
||||
context_hidefriend: "隱藏好友",
|
||||
context_unfavorizefriend: "從我的最愛中移除好友",
|
||||
context_unhidefriend: "取消隱藏好友",
|
||||
favorites: "我的最愛",
|
||||
hidden: "隱藏",
|
||||
ignored: "被忽略",
|
||||
incoming: "匯入",
|
||||
outgoing: "匯出"
|
||||
};
|
||||
default: // English
|
||||
return {
|
||||
blocked: "Blocked",
|
||||
context_favorizefriend: "Add Friend to Favorites",
|
||||
context_hidefriend: "Hide Friend",
|
||||
context_unfavorizefriend: "Remove Friend from Favorites",
|
||||
context_unhidefriend: "Unhide Friend",
|
||||
favorites: "Favorites",
|
||||
hidden: "Hidden",
|
||||
ignored: "Ignored",
|
||||
incoming: "Incoming",
|
||||
outgoing: "Outgoing"
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
})(window.BDFDB_Global.PluginUtils.buildPlugin(changeLog));
|
||||
})();
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"currentVersionInfo": {
|
||||
"version": "0.10.4",
|
||||
"hasShownChangelog": true
|
||||
}
|
||||
}
|
||||
33
.config/BetterDiscord/plugins/EditUsers.config.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"all": {
|
||||
"places": {
|
||||
"contextMenu": true,
|
||||
"chatTextarea": true,
|
||||
"chatWindow": true,
|
||||
"reactions": true,
|
||||
"mentions": true,
|
||||
"memberList": true,
|
||||
"voiceChat": true,
|
||||
"recentDms": true,
|
||||
"dmsList": true,
|
||||
"dmHeader": true,
|
||||
"dmCalls": true,
|
||||
"typing": true,
|
||||
"friendList": true,
|
||||
"inviteList": true,
|
||||
"activity": true,
|
||||
"userPanel": true,
|
||||
"userPopout": true,
|
||||
"userProfile": true,
|
||||
"autocompletes": true,
|
||||
"quickSwitcher": true,
|
||||
"searchPopout": true,
|
||||
"userAccount": true,
|
||||
"appTitle": true
|
||||
},
|
||||
"types": {
|
||||
"servers": true,
|
||||
"dms": true
|
||||
}
|
||||
}
|
||||
}
|
||||
2590
.config/BetterDiscord/plugins/EditUsers.plugin.js
Normal file
365
.config/BetterDiscord/plugins/HideChannels.plugin.js
Normal file
@@ -0,0 +1,365 @@
|
||||
/**
|
||||
* @name Hide Channels
|
||||
* @author Farcrada
|
||||
* @version 2.2.13
|
||||
* @description Hide channel list from view.
|
||||
*
|
||||
* @invite qH6UWCwfTu
|
||||
* @website https://github.com/Farcrada/DiscordPlugins
|
||||
* @source https://github.com/Farcrada/DiscordPlugins/edit/master/Hide-Channels/HideChannels.plugin.js
|
||||
* @updateUrl https://raw.githubusercontent.com/Farcrada/DiscordPlugins/master/Hide-Channels/HideChannels.plugin.js
|
||||
*/
|
||||
|
||||
/** @type {typeof import("react")} */
|
||||
const React = BdApi.React;
|
||||
|
||||
const { Webpack, Webpack: { Filters }, Data, DOM, Patcher } = BdApi,
|
||||
|
||||
config = {
|
||||
constants: {
|
||||
//The names we need for CSS
|
||||
cssStyle: "HideChannelsStyle",
|
||||
hideElementsName: "hideChannelElement",
|
||||
buttonID: "toggleChannels",
|
||||
buttonHidden: "channelsHidden",
|
||||
buttonVisible: "channelsVisible",
|
||||
avatarOverlap: "avatarOverlap",
|
||||
panelsButtonHidden: "panelsButtonHidden"
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = class HideChannels {
|
||||
constructor(meta) { config.info = meta; }
|
||||
|
||||
start() {
|
||||
try {
|
||||
console.log(config)
|
||||
//React components for settings
|
||||
this.WindowInfoStore = Webpack.getModule(Filters.byKeys("isFocused", "isElementFullScreen"));
|
||||
|
||||
this.KeybindToCombo = Webpack.getModule(Filters.byStrings("numpad plus"), { searchExports: true });
|
||||
this.KeybindToString = Webpack.getModule(Filters.byStrings(".join(\"+\")"), { searchExports: true });
|
||||
|
||||
this.FormSwitch = Webpack.getModule(Filters.byStrings('labelRow', 'checked'), { searchExports: true });
|
||||
this.FormItem = Webpack.getModule(m => Filters.byStrings('titleId', 'errorId', 'setIsFocused')(m?.render), { searchExports: true });
|
||||
|
||||
//The sidebar to "minimize"/hide
|
||||
this.sidebarClass = Webpack.getModule(Filters.byKeys("container", "base")).sidebarList;
|
||||
this.headerBarClass = Webpack.getModule(Filters.byKeys("chat", "title")).title;
|
||||
this.baseClass = Webpack.getModule(Filters.byKeys("container", "base")).base;
|
||||
this.avatarWrapper = Webpack.getModule(Filters.byKeys("avatarWrapper")).avatarWrapper;
|
||||
this.panelsButton = Webpack.getModule(Filters.byKeys("avatarWrapper")).buttons;
|
||||
|
||||
//And the keybind
|
||||
this.animation = Data.load(config.info.slug, "animation") ?? true;
|
||||
this.keybindSetting = this.checkKeybindLoad(Data.load(config.info.slug, "keybind"));
|
||||
this.keybind = this.keybindSetting.split('+');
|
||||
|
||||
//Predefine for the eventlistener
|
||||
this.currentlyPressed = {};
|
||||
|
||||
this.generateCSS();
|
||||
|
||||
//Render the button and we're off to the races!
|
||||
const filter = f => f?.Icon && f.Title,
|
||||
modules = Webpack.getModule(m => Object.values(m).some(filter), { first: false });
|
||||
for (const module of modules) {
|
||||
const HeaderBar = [module, Object.keys(module).find(k => filter(module[k]))];
|
||||
this.patchTitleBar(HeaderBar);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
try {
|
||||
console.error("Attempting to stop after starting error...", err)
|
||||
this.stop();
|
||||
}
|
||||
catch (err) {
|
||||
console.error(config.info.name + ".stop()", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getSettingsPanel() {
|
||||
//Settings window is lazy loaded so we need to cache this after it's been loaded (i.e. open settings).
|
||||
//This also allows for a (delayed) call to retrieve a way to prompt a Form
|
||||
if (!this.KeybindRecorder)
|
||||
this.KeybindRecorder = Webpack.getModule(m => m.prototype?.cleanUp);
|
||||
|
||||
//Return our keybind settings wrapped in a form item
|
||||
return () => {
|
||||
const [animation, setanimation] = React.useState(this.animation);
|
||||
|
||||
return [
|
||||
React.createElement(this.FormSwitch, {
|
||||
value: animation,
|
||||
note: "Enable the hide animation. Useful if the animation is \"unstatisfactory\".",
|
||||
onChange: (newState) => {
|
||||
//Save new state
|
||||
this.animation = newState;
|
||||
Data.save(config.info.slug, "animation", newState);
|
||||
setanimation(newState);
|
||||
|
||||
//Update CSS to reflect new settings.
|
||||
this.generateCSS()
|
||||
}
|
||||
}, "Enable Hide Animation"),
|
||||
React.createElement(this.FormItem, {
|
||||
//tag: "h5",
|
||||
title: "Toggle by keybind:"
|
||||
},
|
||||
//Containing a keybind recorder.
|
||||
React.createElement(this.KeybindRecorder, {
|
||||
//The `keyup` and `keydown` events register the Ctrl key different
|
||||
//We need to accomodate for that
|
||||
defaultValue: this.KeybindToCombo(this.keybindSetting.replace("control", "ctrl")),
|
||||
onChange: (e) => {
|
||||
//Convert the keybind to current locale
|
||||
//Once again accomodate for event differences
|
||||
const keybindString = this.KeybindToString(e).toLowerCase().replace("ctrl", "control");
|
||||
|
||||
//Set the keybind and save it.
|
||||
Data.save(config.info.slug, "keybind", keybindString);
|
||||
//And the keybindSetting
|
||||
this.keybindSetting = keybindString;
|
||||
this.keybind = keybindString.split('+');
|
||||
}
|
||||
}))];
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
Patcher.unpatchAll(config.info.slug);
|
||||
|
||||
//Our CSS
|
||||
DOM.removeStyle(config.constants.cssStyle);
|
||||
|
||||
//And if there are remnants of css left,
|
||||
//make sure we remove the class from the sidebar to ensure visual confirmation.
|
||||
let sidebar = document.querySelector(`.${this.sidebarClass}`);
|
||||
if (sidebar?.classList.contains(config.constants.hideElementsName))
|
||||
sidebar.classList.remove(config.constants.hideElementsName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object[]} headerBar The module and the export's name (as a string) that contains it
|
||||
*/
|
||||
patchTitleBar(headerBar) {
|
||||
Patcher.before(config.info.slug, ...headerBar, (thisObject, methodArguments, returnValue) => {
|
||||
//When elements are being re-rendered we need to check if there actually is a place for us.
|
||||
//Along with that we need to check if what we're adding to is an array.
|
||||
if (Array.isArray(methodArguments[0]?.children))
|
||||
if (methodArguments[0].children.some?.(child =>
|
||||
//Make sure we're on the "original" headerbar and not that of a Voice channel's chat, or thread.
|
||||
child?.props?.channel ||
|
||||
//Group chat
|
||||
child?.props?.children?.some?.(child => child?.props?.channel !== undefined) ||
|
||||
//The friends page
|
||||
child?.type?.Header ||
|
||||
//The Nitro page
|
||||
child?.props?.children === "Nitro" ||
|
||||
//The Shop page
|
||||
child?.props?.children?.some?.(child => child?.props?.children === "Shop") ||
|
||||
//Home page of certain servers. This is gonna be broken next update, calling it.
|
||||
child?.props?.children?.some?.(grandChild => typeof grandChild === 'string')))
|
||||
|
||||
//Make sure our component isn't already present.
|
||||
if (!methodArguments[0].children.some?.(child => child?.key === config.info.slug))
|
||||
//And since we want to be on the most left of the header bar for style we unshift into the array.
|
||||
methodArguments[0].children.unshift?.(React.createElement(this.hideChannelComponent, { key: config.info.slug }));
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* React component for our button.
|
||||
* @returns React element
|
||||
*/
|
||||
hideChannelComponent = () => {
|
||||
//Only fetch the sidebar on a rerender.
|
||||
const sidebarNode = document.querySelector(`.${this.sidebarClass}`),
|
||||
//When a state updates, it rerenders.
|
||||
[hidden, setHidden] = React.useState(
|
||||
//Check on a rerender where our side bar is so we can correctly reflect this.
|
||||
sidebarNode?.classList.contains(config.constants.hideElementsName));
|
||||
//Avatar wrapper element
|
||||
const sidebarAvatar = document.querySelector(`.${this.avatarWrapper}`);
|
||||
const panelsButton = document.querySelector(`.${this.panelsButton}`);
|
||||
|
||||
/**
|
||||
* Use this to make a despensable easy to use listener with React.
|
||||
* @param {string} eventName The name of the event to listen for.
|
||||
* @param {callback} callback Function to call when said event is triggered.
|
||||
* @param {boolean} bubbling Handle bubbling or not
|
||||
* @param {object} [target] The object to attach our listener to.
|
||||
*/
|
||||
function useListener(eventName, callback, bubbling, target = window) {
|
||||
React.useEffect(() => {
|
||||
//ComponentDidMount
|
||||
target.addEventListener(eventName, callback, bubbling);
|
||||
//ComponentWillUnmount
|
||||
return () => target.removeEventListener(eventName, callback, bubbling);
|
||||
});
|
||||
}
|
||||
|
||||
function useWindowChangeListener(windowStore, callback) {
|
||||
React.useEffect(() => {
|
||||
windowStore.addChangeListener(callback);
|
||||
return () => windowStore.removeChangeListener(callback);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} sidebar Sidebar node we want to toggle.
|
||||
* @returns The passed state in reverse.
|
||||
*/
|
||||
function toggleSidebar(sidebar) {
|
||||
/**
|
||||
* Adds and removes our CSS to make our sidebar appear and disappear.
|
||||
* @param {boolean} state State that determines the toggle.
|
||||
* @returns The passed state in reverse.
|
||||
*/
|
||||
return state => {
|
||||
//If it is showing, we need to hide it.
|
||||
if (!state) {
|
||||
//We hide it through CSS by adding a class.
|
||||
sidebar?.classList.add(config.constants.hideElementsName);
|
||||
sidebarAvatar?.classList.add(config.constants.avatarOverlap);
|
||||
panelsButton?.classList.add(config.constants.panelsButtonHidden);
|
||||
} else {
|
||||
//If it is hidden, we need to show it.
|
||||
sidebar?.classList.remove(config.constants.hideElementsName);
|
||||
sidebarAvatar?.classList.remove(config.constants.avatarOverlap);
|
||||
panelsButton?.classList.remove(config.constants.panelsButtonHidden);
|
||||
}
|
||||
return !state;
|
||||
};
|
||||
}
|
||||
|
||||
//Keydown event
|
||||
useListener("keydown", e => {
|
||||
//Since we made this an object,
|
||||
//we can make new properties with `[]`
|
||||
if (e?.key?.toLowerCase)
|
||||
this.currentlyPressed[e.key.toLowerCase()] = true;
|
||||
|
||||
//Account for bubbling
|
||||
}, true);
|
||||
|
||||
//Keyup event
|
||||
useListener("keyup", e => {
|
||||
//Check if every currentlyPessed is in our saved keybind.
|
||||
if (this.keybind.every(key => this.currentlyPressed[key.toLowerCase()] === true))
|
||||
//Toggle the sidebar and rerender on toggle; change the state
|
||||
setHidden(toggleSidebar(sidebarNode));
|
||||
|
||||
//Current key goes up, so...
|
||||
this.currentlyPressed[e.key.toLowerCase()] = false;
|
||||
|
||||
//Account for bubbling
|
||||
}, true);
|
||||
|
||||
//Lose focus event
|
||||
useWindowChangeListener(this.WindowInfoStore, () => {
|
||||
//Clear when it gets back into focus
|
||||
if (this.WindowInfoStore.isFocused())
|
||||
this.currentlyPressed = {};
|
||||
});
|
||||
|
||||
//Return our element.
|
||||
return React.createElement("div", {
|
||||
//Styling
|
||||
id: config.constants.buttonID,
|
||||
//The icon
|
||||
className: hidden ? config.constants.buttonHidden : config.constants.buttonVisible,
|
||||
//Toggle the sidebar and rerender on toggle; change the state.
|
||||
onClick: () => setHidden(toggleSidebar(sidebarNode))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given keybind for validity. If not valid returns a default keybind.
|
||||
* @param {String|Array.<number>|Array.<Array.<number>>} keybindToLoad The keybind to filter and load in.
|
||||
* @param {String} [defaultKeybind] A default keybind to fall back on in case of invalidity.
|
||||
* @returns Will return the keybind or return a default keybind.
|
||||
*/
|
||||
checkKeybindLoad(keybindToLoad, defaultKeybind = "control+h") {
|
||||
defaultKeybind = defaultKeybind.toLowerCase().replace("ctrl", "control");
|
||||
|
||||
//If no keybind
|
||||
if (!keybindToLoad)
|
||||
return defaultKeybind;
|
||||
|
||||
//Error sensitive, so just plump it into a try-catch
|
||||
try {
|
||||
//If it's already a string, double check it
|
||||
if (typeof (keybindToLoad) === typeof (defaultKeybind)) {
|
||||
keybindToLoad = keybindToLoad.toLowerCase().replace("control", "ctrl");
|
||||
//Does it go into a combo? (i.e.: is it the correct format?)
|
||||
if (this.KeybindToCombo(keybindToLoad))
|
||||
return keybindToLoad.replace("ctrl", "control");
|
||||
else
|
||||
return defaultKeybind;
|
||||
}
|
||||
else
|
||||
//If it's not a string, check if it's a combo.
|
||||
if (this.KeybindToString(keybindToLoad))
|
||||
return this.KeybindToString(keybindToLoad).toLowerCase().replace("ctrl", "control");
|
||||
}
|
||||
catch (e) { return defaultKeybind; }
|
||||
}
|
||||
|
||||
generateCSS() {
|
||||
//Check if there is any CSS we have already, and remove it.
|
||||
DOM.removeStyle(config.constants.cssStyle);
|
||||
|
||||
//Now inject our (new) CSS
|
||||
DOM.addStyle(config.constants.cssStyle, `
|
||||
/* Button CSS */
|
||||
#${config.constants.buttonID} {
|
||||
min-width: 24px;
|
||||
height: 24px;
|
||||
background-position: center !important;
|
||||
background-size: 100% !important;
|
||||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* How the button looks */
|
||||
.theme-dark #${config.constants.buttonID}.${config.constants.buttonVisible} {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2ZmZiIgd2lkdGg9IjE4cHgiIGhlaWdodD0iMThweCI+PHBhdGggZD0iTTE4LjQxIDE2LjU5TDEzLjgyIDEybDQuNTktNC41OUwxNyA2bC02IDYgNiA2ek02IDZoMnYxMkg2eiIvPjxwYXRoIGQ9Ik0yNCAyNEgwVjBoMjR2MjR6IiBmaWxsPSJub25lIi8+PC9zdmc+) no-repeat;
|
||||
}
|
||||
.theme-dark #${config.constants.buttonID}.${config.constants.buttonHidden} {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2ZmZiIgd2lkdGg9IjE4cHgiIGhlaWdodD0iMThweCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTUuNTkgNy40MUwxMC4xOCAxMmwtNC41OSA0LjU5TDcgMThsNi02LTYtNnpNMTYgNmgydjEyaC0yeiIvPjwvc3ZnPg==) no-repeat;
|
||||
}
|
||||
/* In light theme */
|
||||
.theme-light #${config.constants.buttonID}.${config.constants.buttonVisible} {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzRmNTY2MCIgd2lkdGg9IjE4cHgiIGhlaWdodD0iMThweCI+PHBhdGggZD0iTTE4LjQxIDE2LjU5TDEzLjgyIDEybDQuNTktNC41OUwxNyA2bC02IDYgNiA2ek02IDZoMnYxMkg2eiIvPjxwYXRoIGQ9Ik0yNCAyNEgwVjBoMjR2MjR6IiBmaWxsPSJub25lIi8+PC9zdmc+) no-repeat;
|
||||
}
|
||||
.theme-light #${config.constants.buttonID}.${config.constants.buttonHidden} {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzRmNTY2MCIgd2lkdGg9IjE4cHgiIGhlaWdodD0iMThweCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTUuNTkgNy40MUwxMC4xOCAxMmwtNC41OSA0LjU5TDcgMThsNi02LTYtNnpNMTYgNmgydjEyaC0yeiIvPjwvc3ZnPg==) no-repeat;
|
||||
}
|
||||
|
||||
/* Attached CSS to sidebar */
|
||||
html .${config.constants.hideElementsName}.${config.constants.hideElementsName} {
|
||||
width: 0 !important;
|
||||
}
|
||||
html .${config.constants.avatarOverlap}.${config.constants.avatarOverlap}{
|
||||
z-index: 1;
|
||||
}
|
||||
html .${config.constants.panelsButtonHidden}.${config.constants.panelsButtonHidden}{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Don't have square border at top left when channels are hidden */
|
||||
.${this.baseClass} {
|
||||
border-radius: 8px 0 0 !important;
|
||||
}
|
||||
|
||||
/* Set animations */
|
||||
.${this.sidebarClass} {
|
||||
${this.animation ? "transition: width 400ms ease;" : ""}
|
||||
overflow: hidden;
|
||||
}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"version": "0.1.0"
|
||||
}
|
||||
278
.config/BetterDiscord/plugins/HideDisabledEmojis.plugin.js
Normal file
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* @name HideDisabledEmojis
|
||||
* @description Hides disabled emojis from the emoji picker.
|
||||
* @version 0.1.0
|
||||
* @author Zerebos
|
||||
* @authorId 249746236008169473
|
||||
* @website https://github.com/zerebos/BetterDiscordAddons/tree/master/Plugins/HideDisabledEmojis
|
||||
* @source https://github.com/zerebos/BetterDiscordAddons/blob/master/Plugins/HideDisabledEmojis/HideDisabledEmojis.plugin.js
|
||||
*/
|
||||
|
||||
/*@cc_on
|
||||
@if (@_jscript)
|
||||
|
||||
// Offer to self-install for clueless users that try to run this directly.
|
||||
var shell = WScript.CreateObject("WScript.Shell");
|
||||
var fs = new ActiveXObject("Scripting.FileSystemObject");
|
||||
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
|
||||
var pathSelf = WScript.ScriptFullName;
|
||||
// Put the user at ease by addressing them in the first person
|
||||
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
|
||||
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
|
||||
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40);
|
||||
} else if (!fs.FolderExists(pathPlugins)) {
|
||||
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
|
||||
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
|
||||
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
|
||||
// Show the user where to put plugins in the future
|
||||
shell.Exec("explorer " + pathPlugins);
|
||||
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40);
|
||||
}
|
||||
WScript.Quit();
|
||||
|
||||
@else@*/
|
||||
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/plugins/HideDisabledEmojis/index.ts
|
||||
var HideDisabledEmojis_exports = {};
|
||||
__export(HideDisabledEmojis_exports, {
|
||||
default: () => HideDisabledEmojis
|
||||
});
|
||||
module.exports = __toCommonJS(HideDisabledEmojis_exports);
|
||||
|
||||
// src/common/plugin.ts
|
||||
var Plugin = class {
|
||||
meta;
|
||||
manifest;
|
||||
settings;
|
||||
defaultSettings;
|
||||
LocaleManager;
|
||||
get strings() {
|
||||
if (!this.manifest.strings) return {};
|
||||
const locale = this.LocaleManager?.locale.split("-")[0] ?? "en";
|
||||
if (this.manifest.strings.hasOwnProperty(locale)) return this.manifest.strings[locale];
|
||||
if (this.manifest.strings.hasOwnProperty("en")) return this.manifest.strings.en;
|
||||
return this.manifest.strings;
|
||||
}
|
||||
constructor(meta, zplConfig) {
|
||||
this.meta = meta;
|
||||
this.manifest = zplConfig;
|
||||
if (typeof this.manifest.config !== "undefined") {
|
||||
this.defaultSettings = {};
|
||||
for (let s = 0; s < this.manifest.config.length; s++) {
|
||||
const current = this.manifest.config[s];
|
||||
if (current.type != "category") {
|
||||
this.defaultSettings[current.id] = current.value;
|
||||
} else {
|
||||
for (let si = 0; si < current.settings.length; si++) {
|
||||
const subCurrent = current.settings[si];
|
||||
this.defaultSettings[subCurrent.id] = subCurrent.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.settings = BdApi.Utils.extend({}, this.defaultSettings);
|
||||
}
|
||||
const currentVersionInfo = BdApi.Data.load(this.meta.name, "version");
|
||||
if (currentVersionInfo !== this.meta.version) {
|
||||
this.#showChangelog();
|
||||
BdApi.Data.save(this.meta.name, "version", this.meta.version);
|
||||
}
|
||||
if (this.manifest.strings) this.LocaleManager = BdApi.Webpack.getByKeys("locale", "initialize");
|
||||
if (this.manifest.config && !this.getSettingsPanel) {
|
||||
this.getSettingsPanel = () => {
|
||||
this.#updateConfig();
|
||||
return BdApi.UI.buildSettingsPanel({
|
||||
onChange: (_, id, value) => {
|
||||
this.settings[id] = value;
|
||||
this.saveSettings();
|
||||
},
|
||||
settings: this.manifest.config
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
async start() {
|
||||
BdApi.Logger.info(this.meta.name, `version ${this.meta.version} has started.`);
|
||||
if (this.defaultSettings) this.settings = this.loadSettings();
|
||||
if (typeof this.onStart == "function") this.onStart();
|
||||
}
|
||||
stop() {
|
||||
BdApi.Logger.info(this.meta.name, `version ${this.meta.version} has stopped.`);
|
||||
if (typeof this.onStop == "function") this.onStop();
|
||||
}
|
||||
#showChangelog() {
|
||||
if (typeof this.manifest.changelog == "undefined") return;
|
||||
const changelog = {
|
||||
title: this.meta.name + " Changelog",
|
||||
subtitle: `v${this.meta.version}`,
|
||||
changes: []
|
||||
};
|
||||
if (!Array.isArray(this.manifest.changelog)) Object.assign(changelog, this.manifest.changelog);
|
||||
else changelog.changes = this.manifest.changelog;
|
||||
BdApi.UI.showChangelogModal(changelog);
|
||||
}
|
||||
saveSettings() {
|
||||
BdApi.Data.save(this.meta.name, "settings", this.settings);
|
||||
}
|
||||
loadSettings() {
|
||||
return BdApi.Utils.extend({}, this.defaultSettings ?? {}, BdApi.Data.load(this.meta.name, "settings"));
|
||||
}
|
||||
#updateConfig() {
|
||||
if (!this.manifest.config) return;
|
||||
for (const setting of this.manifest.config) {
|
||||
if (setting.type !== "category") {
|
||||
setting.value = this.settings[setting.id] ?? setting.value;
|
||||
} else {
|
||||
for (const subsetting of setting.settings) {
|
||||
subsetting.value = this.settings[subsetting.id] ?? subsetting.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buildSettingsPanel(onChange) {
|
||||
this.#updateConfig();
|
||||
return BdApi.UI.buildSettingsPanel({
|
||||
onChange: (groupId, id, value) => {
|
||||
this.settings[id] = value;
|
||||
onChange?.(groupId, id, value);
|
||||
this.saveSettings();
|
||||
},
|
||||
settings: this.manifest.config
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// src/plugins/HideDisabledEmojis/config.ts
|
||||
var manifest = {
|
||||
info: {
|
||||
name: "HideDisabledEmojis",
|
||||
authors: [{
|
||||
name: "Zerebos",
|
||||
discord_id: "249746236008169473",
|
||||
github_username: "zerebos",
|
||||
twitter_username: "IAmZerebos"
|
||||
}],
|
||||
version: "0.1.0",
|
||||
description: "Hides disabled emojis from the emoji picker.",
|
||||
github: "https://github.com/zerebos/BetterDiscordAddons/tree/master/Plugins/HideDisabledEmojis",
|
||||
github_raw: "https://github.com/zerebos/BetterDiscordAddons/blob/master/Plugins/HideDisabledEmojis/HideDisabledEmojis.plugin.js"
|
||||
},
|
||||
changelog: [
|
||||
{
|
||||
title: "What's New?",
|
||||
type: "added",
|
||||
items: [
|
||||
"No longer dependent on ZeresPluginLibrary!"
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Bugs Squashed",
|
||||
type: "fixed",
|
||||
items: [
|
||||
"Correctly hides emojis in the picker.",
|
||||
"Also hides categories in the sidebar that have no emojis available to be used.",
|
||||
"Hides other nitro promo in the emoji picker."
|
||||
]
|
||||
}
|
||||
],
|
||||
main: "index.ts"
|
||||
};
|
||||
var config_default = manifest;
|
||||
|
||||
// src/plugins/HideDisabledEmojis/index.ts
|
||||
var { Patcher, Webpack, Logger, Utils } = BdApi;
|
||||
var EmojiInfo = Webpack.getByKeys("isEmojiDisabled", "isEmojiFiltered");
|
||||
var HideDisabledEmojis = class extends Plugin {
|
||||
constructor(meta) {
|
||||
super(meta, config_default);
|
||||
}
|
||||
async onStart() {
|
||||
if (!EmojiInfo) return Logger.error(this.meta.name, "Important modules needed not found");
|
||||
Patcher.after(this.meta.name, EmojiInfo, "isEmojiFiltered", (thisObject, methodArguments, returnValue) => {
|
||||
return returnValue || EmojiInfo.isEmojiDisabled(methodArguments[0]);
|
||||
});
|
||||
const [memoModule, key] = BdApi.Webpack.getWithKey(BdApi.Webpack.Filters.byStrings("topEmojis", "getDisambiguatedEmojiContext"));
|
||||
if (key && memoModule) {
|
||||
Patcher.before(this.meta.name, memoModule, key, (_, args) => {
|
||||
if (args[1] == null) {
|
||||
args[1] = {
|
||||
getGuildId: () => null
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
const doFiltering = (props) => {
|
||||
props.rowCountBySection = props.rowCountBySection.filter((c, i) => c || props.collapsedSections.has(props.sectionDescriptors[i].sectionId));
|
||||
props.sectionDescriptors = props.sectionDescriptors.filter((s) => s.count || props.collapsedSections.has(s.sectionId));
|
||||
const wasFiltered = props.emojiGrid.filtered;
|
||||
props.emojiGrid = props.emojiGrid.filter((r) => r.length > 0);
|
||||
if (wasFiltered) props.emojiGrid.filtered = true;
|
||||
};
|
||||
const PickerWrapMemo = Webpack.getModule((m) => m?.type?.render?.toString?.()?.includes("EMOJI_PICKER_POPOUT"));
|
||||
if (!PickerWrapMemo) return;
|
||||
Patcher.after(this.meta.name, PickerWrapMemo.type, "render", (_, [inputProps], ret) => {
|
||||
const pickerChild = Utils.findInTree(ret, (m) => !!m?.props?.emojiGrid, { walkable: ["props", "children"] });
|
||||
if (!pickerChild?.type?.type) return;
|
||||
ret.props.children.props.page = "DM Channel";
|
||||
if (pickerChild.type.type.__patched) return;
|
||||
Patcher.before(this.meta.name, pickerChild.type, "type", (__, [props]) => {
|
||||
if (!props.rowCountBySection) return;
|
||||
if (props.emojiGrid.filtered) return doFiltering(props);
|
||||
props.emojiGrid.filtered = true;
|
||||
let row = 0;
|
||||
for (let s = 0; s < props.sectionDescriptors.length; s++) {
|
||||
const section = props.sectionDescriptors[s];
|
||||
const rowCount = props.rowCountBySection[s];
|
||||
const rowEnd = row + rowCount - 1;
|
||||
let countLeft = 0;
|
||||
let rowsLeft = 0;
|
||||
for (let r = row; r <= rowEnd; r++) {
|
||||
props.emojiGrid[r] = props.emojiGrid[r].filter((e) => {
|
||||
const hasDisabled = Object.hasOwn(e, "isDisabled");
|
||||
const isDisabled = !e.isDisabled;
|
||||
const typeCheck = e.type !== 1;
|
||||
const pickerCheck = inputProps?.pickerIntention !== 1;
|
||||
return hasDisabled && isDisabled && (typeCheck || pickerCheck);
|
||||
});
|
||||
const remaining = props.emojiGrid[r].length;
|
||||
if (remaining) {
|
||||
rowsLeft = rowsLeft + 1;
|
||||
countLeft = countLeft + remaining;
|
||||
}
|
||||
}
|
||||
section.count = countLeft;
|
||||
props.rowCountBySection[s] = rowsLeft;
|
||||
row = rowEnd + 1;
|
||||
}
|
||||
doFiltering(props);
|
||||
});
|
||||
pickerChild.type.type.__patched = true;
|
||||
});
|
||||
const [catModule, catKey] = BdApi.Webpack.getWithKey(BdApi.Webpack.Filters.byStrings("useEmojiCategories"));
|
||||
Patcher.after(this.meta.name, catModule, catKey, (_, [intention, channel], ret) => {
|
||||
return ret.filter((c) => c.type !== "GUILD" || !c.isNitroLocked && c.emojis?.some((e) => !EmojiInfo.isEmojiFiltered({ emoji: e, channel, intention })));
|
||||
});
|
||||
}
|
||||
onStop() {
|
||||
Patcher.unpatchAll(this.meta.name);
|
||||
}
|
||||
};
|
||||
|
||||
/*@end@*/
|
||||
69
.config/BetterDiscord/plugins/ImageUtilities.config.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"all": {
|
||||
"detailsSettings": {
|
||||
"footnote": true,
|
||||
"tooltip": false,
|
||||
"tooltipDelay": 0
|
||||
},
|
||||
"engines": {
|
||||
"_all": true,
|
||||
"Baidu": true,
|
||||
"Bing": true,
|
||||
"Google": true,
|
||||
"GoogleLens": true,
|
||||
"ImgOps": true,
|
||||
"IQDB": true,
|
||||
"Reddit": true,
|
||||
"SauceNAO": true,
|
||||
"Sogou": true,
|
||||
"TinEye": true,
|
||||
"WhatAnime": true,
|
||||
"Yandex": true
|
||||
},
|
||||
"galleryFilter": {
|
||||
"3gp": true,
|
||||
"avi": true,
|
||||
"flv": true,
|
||||
"jpeg": true,
|
||||
"jpg": true,
|
||||
"gif": true,
|
||||
"mov": true,
|
||||
"mp4": true,
|
||||
"mpeg-1": true,
|
||||
"mpeg-2": true,
|
||||
"ogg": true,
|
||||
"png": true,
|
||||
"svg": true,
|
||||
"webm": true,
|
||||
"webp": true,
|
||||
"wmv": true
|
||||
},
|
||||
"general": {
|
||||
"nsfwMode": false
|
||||
},
|
||||
"places": {
|
||||
"userAvatars": true,
|
||||
"groupIcons": true,
|
||||
"guildIcons": true,
|
||||
"streamPreviews": true,
|
||||
"emojis": true
|
||||
},
|
||||
"rescaleSettings": {
|
||||
"messages": "NONE",
|
||||
"imageViewer": "NONE",
|
||||
"rescaleEmbeds": true
|
||||
},
|
||||
"viewerSettings": {
|
||||
"zoomMode": true,
|
||||
"galleryMode": true,
|
||||
"details": true
|
||||
},
|
||||
"zoomSettings": {
|
||||
"clickMode": false,
|
||||
"lensSize": 200,
|
||||
"pixelMode": false,
|
||||
"zoomLevel": 2,
|
||||
"zoomSpeed": 0.1
|
||||
}
|
||||
}
|
||||
}
|
||||
2269
.config/BetterDiscord/plugins/ImageUtilities.plugin.js
Normal file
6
.config/BetterDiscord/plugins/MemberCount.config.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"currentVersionInfo": {
|
||||
"version": "2.2.24",
|
||||
"hasShownChangelog": true
|
||||
}
|
||||
}
|
||||
41
.config/BetterDiscord/plugins/StaffTag.config.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"all": {
|
||||
"customTitles": {
|
||||
"owner": "",
|
||||
"groupOwner": "",
|
||||
"forumCreator": "",
|
||||
"threadCreator": "",
|
||||
"admin": "",
|
||||
"management": ""
|
||||
},
|
||||
"general": {
|
||||
"useCrown": true,
|
||||
"useRoleColor": true,
|
||||
"useBlackFont": false,
|
||||
"ignoreBots": false,
|
||||
"ignoreMyself": false
|
||||
},
|
||||
"tagPlaces": {
|
||||
"chat": true,
|
||||
"memberList": true,
|
||||
"voiceList": true,
|
||||
"userPopout": true,
|
||||
"userProfile": true
|
||||
},
|
||||
"tagTypes": {
|
||||
"owners": true,
|
||||
"groupOwners": true,
|
||||
"threadCreators": true,
|
||||
"forumCreators": true,
|
||||
"admins": true,
|
||||
"managementG": true,
|
||||
"managementC": true,
|
||||
"managementT": true,
|
||||
"managementE": true,
|
||||
"managementR": true,
|
||||
"managementU": true,
|
||||
"managementV": true,
|
||||
"managementM": true
|
||||
}
|
||||
}
|
||||
}
|
||||
581
.config/BetterDiscord/plugins/StaffTag.plugin.js
Normal file
@@ -0,0 +1,581 @@
|
||||
/**
|
||||
* @name StaffTag
|
||||
* @author DevilBro
|
||||
* @authorId 278543574059057154
|
||||
* @version 1.6.9
|
||||
* @description Adds a Crown/Tag to Server Owners (or Admins/Management)
|
||||
* @invite Jx3TjNS
|
||||
* @donate https://www.paypal.me/MircoWittrien
|
||||
* @patreon https://www.patreon.com/MircoWittrien
|
||||
* @website https://mwittrien.github.io/
|
||||
* @source https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/StaffTag/
|
||||
* @updateUrl https://mwittrien.github.io/BetterDiscordAddons/Plugins/StaffTag/StaffTag.plugin.js
|
||||
*/
|
||||
|
||||
module.exports = (_ => {
|
||||
const changeLog = {
|
||||
|
||||
};
|
||||
|
||||
return !window.BDFDB_Global || (!window.BDFDB_Global.loaded && !window.BDFDB_Global.started) ? class {
|
||||
constructor (meta) {for (let key in meta) this[key] = meta[key];}
|
||||
getName () {return this.name;}
|
||||
getAuthor () {return this.author;}
|
||||
getVersion () {return this.version;}
|
||||
getDescription () {return `The Library Plugin needed for ${this.name} is missing. Open the Plugin Settings to download it. \n\n${this.description}`;}
|
||||
|
||||
downloadLibrary () {
|
||||
BdApi.Net.fetch("https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js").then(r => {
|
||||
if (!r || r.status != 200) throw new Error();
|
||||
else return r.text();
|
||||
}).then(b => {
|
||||
if (!b) throw new Error();
|
||||
else return require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0BDFDB.plugin.js"), b, _ => BdApi.UI.showToast("Finished downloading BDFDB Library", {type: "success"}));
|
||||
}).catch(error => {
|
||||
BdApi.UI.alert("Error", "Could not download BDFDB Library Plugin. Try again later or download it manually from GitHub: https://mwittrien.github.io/downloader/?library");
|
||||
});
|
||||
}
|
||||
|
||||
load () {
|
||||
if (!window.BDFDB_Global || !Array.isArray(window.BDFDB_Global.pluginQueue)) window.BDFDB_Global = Object.assign({}, window.BDFDB_Global, {pluginQueue: []});
|
||||
if (!window.BDFDB_Global.downloadModal) {
|
||||
window.BDFDB_Global.downloadModal = true;
|
||||
BdApi.UI.showConfirmationModal("Library Missing", `The Library Plugin needed for ${this.name} is missing. Please click "Download Now" to install it.`, {
|
||||
confirmText: "Download Now",
|
||||
cancelText: "Cancel",
|
||||
onCancel: _ => {delete window.BDFDB_Global.downloadModal;},
|
||||
onConfirm: _ => {
|
||||
delete window.BDFDB_Global.downloadModal;
|
||||
this.downloadLibrary();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!window.BDFDB_Global.pluginQueue.includes(this.name)) window.BDFDB_Global.pluginQueue.push(this.name);
|
||||
}
|
||||
start () {this.load();}
|
||||
stop () {}
|
||||
getSettingsPanel () {
|
||||
let template = document.createElement("template");
|
||||
template.innerHTML = `<div style="color: var(--text-primary); font-size: 16px; font-weight: 300; white-space: pre; line-height: 22px;">The Library Plugin needed for ${this.name} is missing.\nPlease click <a style="font-weight: 500;">Download Now</a> to install it.</div>`;
|
||||
template.content.firstElementChild.querySelector("a").addEventListener("click", this.downloadLibrary);
|
||||
return template.content.firstElementChild;
|
||||
}
|
||||
} : (([Plugin, BDFDB]) => {
|
||||
const userTypes = {
|
||||
NONE: 0,
|
||||
MANAGEMENT: 1,
|
||||
ADMIN: 2,
|
||||
FORUM_CREATOR: 3,
|
||||
THREAD_CREATOR: 3,
|
||||
GROUP_OWNER: 5,
|
||||
OWNER: 6
|
||||
};
|
||||
|
||||
const labelMap = {
|
||||
[userTypes.NONE]: "",
|
||||
[userTypes.MANAGEMENT]: "management",
|
||||
[userTypes.ADMIN]: "admin",
|
||||
[userTypes.FORUM_CREATOR]: "forumCreator",
|
||||
[userTypes.THREAD_CREATOR]: "threadCreator",
|
||||
[userTypes.GROUP_OWNER]: "groupOwner",
|
||||
[userTypes.OWNER]: "owner"
|
||||
};
|
||||
|
||||
const classNameMap = {
|
||||
[userTypes.NONE]: "",
|
||||
[userTypes.MANAGEMENT]: "_stafftagmanagementicon",
|
||||
[userTypes.ADMIN]: "_stafftagadminicon",
|
||||
[userTypes.FORUM_CREATOR]: "_stafftagforumcreatoricon",
|
||||
[userTypes.THREAD_CREATOR]: "_stafftagthreadcreatoricon",
|
||||
[userTypes.GROUP_OWNER]: "_stafftaggroupownericon",
|
||||
[userTypes.OWNER]: "_stafftagownericon"
|
||||
};
|
||||
|
||||
return class StaffTag extends Plugin {
|
||||
onLoad () {
|
||||
|
||||
this.modulePatches = {
|
||||
before: [
|
||||
"MessageUsername"
|
||||
],
|
||||
after: [
|
||||
"NameContainerDecorators",
|
||||
"UserHeaderUsername",
|
||||
"VoiceUser"
|
||||
]
|
||||
};
|
||||
|
||||
this.defaults = {
|
||||
general: {
|
||||
useCrown: {value: true, description: "Uses the Crown Icon instead of the Bot Tag Style"},
|
||||
useRoleColor: {value: true, description: "Uses the Role Color instead of the default Blurple"},
|
||||
useBlackFont: {value: false, description: "Uses black Font instead of darkening the Role Color on bright Colors"},
|
||||
ignoreBots: {value: false, description: "Doesn't add the Owner/Admin/Management Tag for Bots"},
|
||||
ignoreMyself: {value: false, description: "Doesn't add the Owner/Admin/Management Tag for yourself"}
|
||||
},
|
||||
tagTypes: {
|
||||
owners: {value: true, description: "Server Owner Tag"},
|
||||
groupOwners: {value: true, description: "Group Owner Tag"},
|
||||
threadCreators: {value: true, description: "Thread Creator Tag"},
|
||||
forumCreators: {value: true, description: "Forum Creator Tag"},
|
||||
admins: {value: true, description: "Admin Tag (Admin Permissions)"},
|
||||
managementG: {value: true, description: "Management Tag (Server Management)"},
|
||||
managementC: {value: true, description: "Management Tag (Channel Management)"},
|
||||
managementT: {value: true, description: "Management Tag (Threads Management)"},
|
||||
managementE: {value: true, description: "Management Tag (Events Management)"},
|
||||
managementR: {value: true, description: "Management Tag (Role Management)"},
|
||||
managementU: {value: true, description: "Management Tag (User Management 'Kick/Ban')"},
|
||||
managementV: {value: true, description: "Management Tag (Voice Management 'Mute/Deafen/Move')"},
|
||||
managementM: {value: true, description: "Management Tag (Message Management)"}
|
||||
},
|
||||
tagPlaces: {
|
||||
chat: {value: true, description: "Messages"},
|
||||
memberList: {value: true, description: "Member List"},
|
||||
voiceList: {value: true, description: "Voice User List"},
|
||||
userPopout: {value: true, description: "User Popouts"},
|
||||
userProfile: {value: true, description: "User Profile Modal"},
|
||||
},
|
||||
customTitles: {
|
||||
owner: {value: "", placeholder: "Owner", description: "Server Owner Tags"},
|
||||
groupOwner: {value: "", placeholder: "Group Owner", description: "Group Owner Tags"},
|
||||
forumCreator: {value: "", placeholder: "Creator", description: "Forum Creator Tags"},
|
||||
threadCreator: {value: "", placeholder: "Creator", description: "Thread Creator Tags"},
|
||||
admin: {value: "", placeholder: "Admin", description: "Admin Tags"},
|
||||
management: {value: "", placeholder: "Management", description: "Management Tags"}
|
||||
}
|
||||
};
|
||||
|
||||
this.css = `
|
||||
${BDFDB.dotCN.memberownericon + BDFDB.dotCN._stafftagadminicon} {
|
||||
color: #aaa9ad;
|
||||
}
|
||||
${BDFDB.dotCN.memberownericon + BDFDB.dotCN._stafftagmanagementicon} {
|
||||
color: #88540b;
|
||||
}
|
||||
${BDFDB.dotCN.memberownericon + BDFDB.dotCN._stafftagforumcreatoricon},
|
||||
${BDFDB.dotCN.memberownericon + BDFDB.dotCN._stafftagthreadcreatoricon} {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
${BDFDB.dotCN.memberownericon} {
|
||||
top: 0px;
|
||||
}
|
||||
${BDFDB.dotCN.memberownericon} + ${BDFDB.dotCN.memberownericon} {
|
||||
display: none;
|
||||
}
|
||||
${BDFDB.dotCNS.message + BDFDB.dotCN.memberownericon} {
|
||||
top: 2px;
|
||||
}
|
||||
${BDFDB.dotCNS.messagecompact + BDFDB.dotCN.memberownericon} {
|
||||
top: 1px;
|
||||
margin-left: 0;
|
||||
margin-right: 4px;
|
||||
}
|
||||
${BDFDB.dotCNS.messagerepliedmessage + BDFDB.dotCN.memberownericon},
|
||||
${BDFDB.dotCNS.messagethreadaccessory + BDFDB.dotCN.memberownericon} {
|
||||
top: 0px;
|
||||
margin-left: 0;
|
||||
margin-right: 4px;
|
||||
}
|
||||
${BDFDB.dotCNS.voiceuser + BDFDB.dotCN.memberownericon}:last-child {
|
||||
margin-right: 4px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
onStart () {
|
||||
this.forceUpdateAll();
|
||||
}
|
||||
|
||||
onStop () {
|
||||
this.forceUpdateAll();
|
||||
}
|
||||
|
||||
getSettingsPanel (collapseStates = {}) {
|
||||
let settingsPanel;
|
||||
return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, {
|
||||
collapseStates: collapseStates,
|
||||
children: _ => {
|
||||
let settingsItems = [];
|
||||
|
||||
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
|
||||
title: "Settings",
|
||||
collapseStates: collapseStates,
|
||||
children: Object.keys(this.defaults.general).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
|
||||
type: "Switch",
|
||||
plugin: this,
|
||||
key: key,
|
||||
keys: ["general", key],
|
||||
label: this.defaults.general[key].description,
|
||||
value: this.settings.general[key]
|
||||
}))
|
||||
}));
|
||||
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
|
||||
title: "Tag Settings",
|
||||
collapseStates: collapseStates,
|
||||
children: [
|
||||
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
|
||||
title: "Add Tags for:",
|
||||
dividerBottom: true,
|
||||
children: Object.keys(this.defaults.tagTypes).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
|
||||
type: "Switch",
|
||||
plugin: this,
|
||||
keys: ["tagTypes", key],
|
||||
label: this.defaults.tagTypes[key].description,
|
||||
value: this.settings.tagTypes[key]
|
||||
}))
|
||||
}),
|
||||
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
|
||||
title: "Add Tags in:",
|
||||
children: Object.keys(this.defaults.tagPlaces).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
|
||||
type: "Switch",
|
||||
plugin: this,
|
||||
keys: ["tagPlaces", key],
|
||||
label: this.defaults.tagPlaces[key].description,
|
||||
value: this.settings.tagPlaces[key]
|
||||
}))
|
||||
})
|
||||
]
|
||||
}));
|
||||
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
|
||||
title: "Custom Title Settings",
|
||||
collapseStates: collapseStates,
|
||||
children: Object.keys(this.defaults.customTitles).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
|
||||
type: "TextInput",
|
||||
plugin: this,
|
||||
keys: ["customTitles", key],
|
||||
label: this.defaults.customTitles[key].description,
|
||||
basis: "50%",
|
||||
value: this.settings.customTitles[key],
|
||||
placeholder: this.defaults.customTitles[key].placeholder
|
||||
}))
|
||||
}));
|
||||
|
||||
return settingsItems;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSettingsClosed () {
|
||||
if (this.SettingsUpdated) {
|
||||
delete this.SettingsUpdated;
|
||||
this.forceUpdateAll();
|
||||
}
|
||||
}
|
||||
|
||||
forceUpdateAll () {
|
||||
BDFDB.PatchUtils.forceAllUpdates(this);
|
||||
BDFDB.MessageUtils.rerenderAll();
|
||||
}
|
||||
|
||||
processNameContainerDecorators (e) {
|
||||
if (!e.instance.props.user) return;
|
||||
let channelId = e.instance.props.channel && e.instance.props.channel.id || BDFDB.LibraryStores.SelectedChannelStore.getChannelId();
|
||||
let userType = this.getUserType(e.instance.props.user, channelId);
|
||||
if (userType && this.settings.tagPlaces.memberList) {
|
||||
this.injectStaffTag(e.returnvalue.props.children, e.instance.props.user, userType, 1, {
|
||||
channelId: channelId,
|
||||
tagClass: BDFDB.disCN.bottagmember
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
processMessageUsername (e) {
|
||||
if (!e.instance.props.message || !this.settings.tagPlaces.chat || !e.instance.props.decorations) return;
|
||||
const author = e.instance.props.userOverride || e.instance.props.message.author;
|
||||
let userType = this.getUserType(author, e.instance.props.message.channel_id);
|
||||
if (!userType) return;
|
||||
if (!BDFDB.ArrayUtils.is(e.instance.props.decorations[0])) e.instance.props.decorations[0] = [e.instance.props.decorations[0]].filter(n => n);
|
||||
this.injectStaffTag(e.instance.props.decorations[0], author, userType, 0, {
|
||||
channelId: e.instance.props.message.channel_id,
|
||||
tagClass: e.instance.props.compact ? BDFDB.disCN.messagebottagcompact : BDFDB.disCN.messagebottagcozy,
|
||||
useRem: true
|
||||
});
|
||||
}
|
||||
|
||||
processVoiceUser (e) {
|
||||
if (e.instance.props.user && this.settings.tagPlaces.voiceList) {
|
||||
let userType = this.getUserType(e.instance.props.user, e.instance.props.channel && e.instance.props.channel.id);
|
||||
if (!userType) return;
|
||||
let content = BDFDB.ReactUtils.findChild(e.returnvalue, {props: [["className", BDFDB.disCN.voicecontent]]});
|
||||
if (content) this.injectStaffTag(content.props.children, e.instance.props.user, userType, 3, {
|
||||
channelId: e.instance.props.channel && e.instance.props.channel.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
processNameTag (e) {
|
||||
if (!e.instance.props.user || !e.instance.props.className) return;
|
||||
let userType = this.getUserType(e.instance.props.user);
|
||||
if (!userType) return;
|
||||
let inject = false, tagClass = "";
|
||||
if (e.instance.props.className.indexOf(BDFDB.disCN.userpopoutheadertagwithnickname) > -1) {
|
||||
inject = this.settings.tagPlaces.userPopout;
|
||||
tagClass = BDFDB.disCNS.userpopoutheaderbottag + BDFDB.disCN.bottagnametag;
|
||||
}
|
||||
else if (e.instance.props.className.indexOf(BDFDB.disCN.userprofilenametag) > -1) {
|
||||
inject = this.settings.tagPlaces.userProfile;
|
||||
tagClass = BDFDB.disCN.bottagnametag;
|
||||
}
|
||||
if (inject) this.injectStaffTag(e.returnvalue.props.children, e.instance.props.user, userType, 2, {
|
||||
tagClass: tagClass,
|
||||
useRem: e.instance.props.useRemSizes,
|
||||
inverted: e.instance.props.invertBotTagColor
|
||||
});
|
||||
}
|
||||
|
||||
processUserHeaderUsername (e) {
|
||||
let themeType = BDFDB.ObjectUtils.get(e.instance, "props.tags.props.themeType");
|
||||
if (!e.instance.props.user || (themeType == BDFDB.DiscordConstants.ProfileTypes.POPOUT || themeType == BDFDB.DiscordConstants.ProfileTypes.SIDEBAR) && !this.settings.tagPlaces.userPopout || (themeType == BDFDB.DiscordConstants.ProfileTypes.MODAL || themeType == BDFDB.DiscordConstants.ProfileTypes.MODAL_V2) && !this.settings.tagPlaces.userProfile) return;
|
||||
let userType = this.getUserType(e.instance.props.user, e.instance.props.channel && e.instance.props.channel.id);
|
||||
if (!userType) return;
|
||||
let [children, index] = BDFDB.ReactUtils.findParent(e.returnvalue, {props: [["className", BDFDB.disCN.userheadernickname]]});
|
||||
if (index > -1) {
|
||||
if (!BDFDB.ArrayUtils.is(children[index].props.children)) children[index].props.children = [children[index].props.children].flat(10);
|
||||
this.injectStaffTag(children[index].props.children, e.instance.props.user, userType, 2, {
|
||||
tagClass: BDFDB.disCNS.userheaderbottag + BDFDB.disCN.bottagnametag,
|
||||
inverted: typeof e.instance.getMode == "function" && e.instance.getMode() !== "Normal"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
injectStaffTag (children, user, userType, insertIndex, config = {}) {
|
||||
if (!BDFDB.ArrayUtils.is(children) || !user) return;
|
||||
let [_, index] = BDFDB.ReactUtils.findParent(children, {props: [["text", [BDFDB.LanguageUtils.LanguageStrings.GROUP_OWNER, BDFDB.LanguageUtils.LanguageStrings.GUILD_OWNER]]]});
|
||||
if (index > -1) children[index] = null;
|
||||
let channel = BDFDB.LibraryStores.ChannelStore.getChannel(config.channelId || BDFDB.LibraryStores.SelectedChannelStore.getChannelId());
|
||||
let member = channel && this.settings.general.useRoleColor ? (BDFDB.LibraryStores.GuildMemberStore.getMember(channel.guild_id, user.id) || {}) : {};
|
||||
|
||||
let fallbackLabel = this.settings.general.useCrown && this.getLabelFallback(userType);
|
||||
let label = this.getLabel(userType, fallbackLabel);
|
||||
let labelExtra = userType == userTypes.FORUM_CREATOR ? BDFDB.LanguageUtils.LanguageStrings.BOT_TAG_FORUM_ORIGINAL_POSTER_TOOLTIP : userType == userTypes.MANAGEMENT && this.getManagementLabel(user);
|
||||
|
||||
let tag = null;
|
||||
if (this.settings.general.useCrown) tag = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
|
||||
text: labelExtra ? `${label} (${labelExtra})` : label,
|
||||
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
|
||||
className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN.memberownericon, classNameMap[userType] && BDFDB.disCN[classNameMap[userType]]),
|
||||
name: BDFDB.LibraryComponents.SvgIcon.Names.CROWN,
|
||||
"aria-label": fallbackLabel
|
||||
})
|
||||
});
|
||||
else {
|
||||
let tagColor = BDFDB.ColorUtils.convert(member.colorString, "RGBA");
|
||||
let isBright = BDFDB.ColorUtils.isBright(tagColor);
|
||||
tagColor = isBright ? (this.settings.general.useBlackFont ? tagColor : BDFDB.ColorUtils.change(tagColor, -0.3)) : tagColor;
|
||||
tag = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.BotTag, {
|
||||
className: config.tagClass,
|
||||
useRemSizes: config.useRem,
|
||||
invertColor: config.inverted,
|
||||
style: {
|
||||
backgroundColor: config.inverted ? (isBright && this.settings.general.useBlackFont ? "black" : null) : tagColor,
|
||||
color: !config.inverted ? (isBright && this.settings.general.useBlackFont ? "black" : null) : tagColor
|
||||
},
|
||||
tag: label
|
||||
});
|
||||
if (labelExtra) tag = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
|
||||
text: labelExtra,
|
||||
children: tag
|
||||
});
|
||||
}
|
||||
children.splice(insertIndex, 0, tag);
|
||||
}
|
||||
|
||||
getLabelFallback (userType) {
|
||||
switch (userType) {
|
||||
case userTypes.OWNER: return BDFDB.LanguageUtils.LanguageStrings.GUILD_OWNER;
|
||||
case userTypes.GROUP_OWNER: return BDFDB.LanguageUtils.LanguageStrings.GROUP_OWNER;
|
||||
case userTypes.FORUM_CREATOR: return BDFDB.LanguageUtils.LanguageStrings.BOT_TAG_FORUM_ORIGINAL_POSTER;
|
||||
case userTypes.THREAD_CREATOR: return this.labels.creator.replace("{{var0}}", BDFDB.LanguageUtils.LanguageStrings.THREAD);
|
||||
case userTypes.ADMIN: return BDFDB.LanguageUtils.LanguageStrings.ADMINISTRATOR;
|
||||
case userTypes.MANAGEMENT: return this.labels.management;
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
getLabel (userType, fallback) {
|
||||
let type = labelMap[userType];
|
||||
if (!type) return fallback || "";
|
||||
else if (!fallback) return this.settings.customTitles[type] || this.defaults.customTitles[type].placeholder;
|
||||
else return this.settings.customTitles[type] && this.settings.customTitles[type].toLowerCase() != this.defaults.customTitles[type].placeholder.toLowerCase() ? this.settings.customTitles[type] : fallback;
|
||||
}
|
||||
|
||||
getManagementLabel (user) {
|
||||
return [
|
||||
this.settings.tagTypes.managementG && BDFDB.UserUtils.can("MANAGE_GUILD", user.id) && BDFDB.LanguageUtils.LibraryStrings.server,
|
||||
this.settings.tagTypes.managementC && BDFDB.UserUtils.can("MANAGE_CHANNELS", user.id) && BDFDB.LanguageUtils.LanguageStrings.CHANNELS,
|
||||
this.settings.tagTypes.managementT && BDFDB.UserUtils.can("MANAGE_THREADS", user.id) && BDFDB.LanguageUtils.LanguageStrings.THREADS,
|
||||
this.settings.tagTypes.managementE && BDFDB.UserUtils.can("MANAGE_EVENTS", user.id) && BDFDB.LanguageUtils.LanguageStrings.GUILD_EVENTS,
|
||||
this.settings.tagTypes.managementR && BDFDB.UserUtils.can("MANAGE_ROLES", user.id) && BDFDB.LanguageUtils.LanguageStrings.ROLES,
|
||||
this.settings.tagTypes.managementU && (BDFDB.UserUtils.can("BAN_MEMBERS", user.id) || BDFDB.UserUtils.can("KICK_MEMBERS", user.id)) && BDFDB.LanguageUtils.LanguageStrings.MEMBERS,
|
||||
this.settings.tagTypes.managementV && (BDFDB.UserUtils.can("MUTE_MEMBERS", user.id) || BDFDB.UserUtils.can("DEAFEN_MEMBERS", user.id) || BDFDB.UserUtils.can("MOVE_MEMBERS", user.id)) && BDFDB.LanguageUtils.LanguageStrings.VOICE_AND_VIDEO,
|
||||
this.settings.tagTypes.managementM && BDFDB.UserUtils.can("MANAGE_MESSAGES", user.id) && BDFDB.LanguageUtils.LanguageStrings.MESSAGES
|
||||
].filter(n => n).join(", ");
|
||||
}
|
||||
|
||||
getUserType (user, channelId) {
|
||||
if (!user || this.settings.general.ignoreBots && user.bot || this.settings.general.ignoreMyself && user.id == BDFDB.UserUtils.me.id) return userTypes.NONE;
|
||||
const channel = BDFDB.LibraryStores.ChannelStore.getChannel(channelId || BDFDB.LibraryStores.SelectedChannelStore.getChannelId());
|
||||
if (!channel) return userTypes.NONE;
|
||||
const guild = BDFDB.LibraryStores.GuildStore.getGuild(channel.guild_id);
|
||||
|
||||
if (this.settings.tagTypes.owners && guild && guild.ownerId == user.id) return userTypes.OWNER;
|
||||
else if (this.settings.tagTypes.groupOwners && channel.ownerId == user.id && channel.isGroupDM()) return userTypes.GROUP_OWNER;
|
||||
else if (this.settings.tagTypes.forumCreators && channel.ownerId == user.id && BDFDB.ChannelUtils.isForumPost(channel)) return userTypes.FORUM_CREATOR;
|
||||
else if (this.settings.tagTypes.threadCreators && channel.ownerId == user.id && BDFDB.ChannelUtils.isThread(channel) && !BDFDB.ChannelUtils.isForumPost(channel)) return userTypes.THREAD_CREATOR;
|
||||
else if (this.settings.tagTypes.admins && BDFDB.UserUtils.can("ADMINISTRATOR", user.id)) return userTypes.ADMIN;
|
||||
else if (this.settings.tagTypes.managementG && BDFDB.UserUtils.can("MANAGE_GUILD", user.id) || this.settings.tagTypes.managementC && BDFDB.UserUtils.can("MANAGE_CHANNELS", user.id) || this.settings.tagTypes.managementR && BDFDB.UserUtils.can("MANAGE_ROLES", user.id) || this.settings.tagTypes.managementU && (BDFDB.UserUtils.can("BAN_MEMBERS", user.id) || BDFDB.UserUtils.can("KICK_MEMBERS", user.id)) || this.settings.tagTypes.managementM && BDFDB.UserUtils.can("MANAGE_MESSAGES", user.id)) return userTypes.MANAGEMENT;
|
||||
return userTypes.NONE;
|
||||
}
|
||||
|
||||
setLabelsByLanguage () {
|
||||
switch (BDFDB.LanguageUtils.getLanguage().id) {
|
||||
case "bg": // Bulgarian
|
||||
return {
|
||||
management: "Управление",
|
||||
creator: "Cъздател {{var0}}"
|
||||
};
|
||||
case "cs": // Czech
|
||||
return {
|
||||
management: "Řízení",
|
||||
creator: "{{var0}} autor"
|
||||
};
|
||||
case "da": // Danish
|
||||
return {
|
||||
management: "Ledelse",
|
||||
creator: "{{var0}} skaber"
|
||||
};
|
||||
case "de": // German
|
||||
return {
|
||||
management: "Verwaltung",
|
||||
creator: "{{var0}}ersteller"
|
||||
};
|
||||
case "el": // Greek
|
||||
return {
|
||||
management: "Διαχείριση",
|
||||
creator: "{{var0}} δημιουργός"
|
||||
};
|
||||
case "es": // Spanish
|
||||
return {
|
||||
management: "Administración",
|
||||
creator: "{{var0}} creador"
|
||||
};
|
||||
case "fi": // Finnish
|
||||
return {
|
||||
management: "Johto",
|
||||
creator: "{{var0}} luoja"
|
||||
};
|
||||
case "fr": // French
|
||||
return {
|
||||
management: "La gestion",
|
||||
creator: "{{var0}}créateur"
|
||||
};
|
||||
case "hi": // Hindi
|
||||
return {
|
||||
management: "प्रबंध",
|
||||
creator: "{{var0}}निर्माता"
|
||||
};
|
||||
case "hr": // Croatian
|
||||
return {
|
||||
management: "Upravljanje",
|
||||
creator: "{{var0}} kreator"
|
||||
};
|
||||
case "hu": // Hungarian
|
||||
return {
|
||||
management: "Menedzsment",
|
||||
creator: "{{var0}} alkotója"
|
||||
};
|
||||
case "it": // Italian
|
||||
return {
|
||||
management: "Gestione",
|
||||
creator: "{{var0}}creatore"
|
||||
};
|
||||
case "ja": // Japanese
|
||||
return {
|
||||
management: "管理",
|
||||
creator: "{{var0}}作成者"
|
||||
};
|
||||
case "ko": // Korean
|
||||
return {
|
||||
management: "조치",
|
||||
creator: "{{var0}}창조자"
|
||||
};
|
||||
case "lt": // Lithuanian
|
||||
return {
|
||||
management: "Valdymas",
|
||||
creator: "{{var0}} kūrėjas"
|
||||
};
|
||||
case "nl": // Dutch
|
||||
return {
|
||||
management: "Beheer",
|
||||
creator: "{{var0}}maker"
|
||||
};
|
||||
case "no": // Norwegian
|
||||
return {
|
||||
management: "Ledelse",
|
||||
creator: "{{var0}} skaperen"
|
||||
};
|
||||
case "pl": // Polish
|
||||
return {
|
||||
management: "Zarządzanie",
|
||||
creator: "{{var0}}twórca"
|
||||
};
|
||||
case "pt-BR": // Portuguese (Brazil)
|
||||
return {
|
||||
management: "Gestão",
|
||||
creator: "{{var0}} criador"
|
||||
};
|
||||
case "ro": // Romanian
|
||||
return {
|
||||
management: "Administrare",
|
||||
creator: "{{var0}} creator"
|
||||
};
|
||||
case "ru": // Russian
|
||||
return {
|
||||
management: "Управление",
|
||||
creator: "Cоздатель {{var0}}"
|
||||
};
|
||||
case "sv": // Swedish
|
||||
return {
|
||||
management: "Förvaltning",
|
||||
creator: "{{var0}} skapare"
|
||||
};
|
||||
case "th": // Thai
|
||||
return {
|
||||
management: "การจัดการ",
|
||||
creator: "{{var0}}ผู้สร้าง"
|
||||
};
|
||||
case "tr": // Turkish
|
||||
return {
|
||||
management: "Yönetim",
|
||||
creator: "{{var0}}yaratıcı"
|
||||
};
|
||||
case "uk": // Ukrainian
|
||||
return {
|
||||
management: "Управління",
|
||||
creator: "{{var0}} творець"
|
||||
};
|
||||
case "vi": // Vietnamese
|
||||
return {
|
||||
management: "Sự quản lý",
|
||||
creator: "Người tạo {{var0}}"
|
||||
};
|
||||
case "zh-CN": // Chinese (China)
|
||||
return {
|
||||
management: "管理",
|
||||
creator: "{{var0}} 创建者"
|
||||
};
|
||||
case "zh-TW": // Chinese (Taiwan)
|
||||
return {
|
||||
management: "管理",
|
||||
creator: "{{var0}} 建立者"
|
||||
};
|
||||
default: // English
|
||||
return {
|
||||
management: "Management",
|
||||
creator: "{{var0}} Creator"
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
})(window.BDFDB_Global.PluginUtils.buildPlugin(changeLog));
|
||||
})();
|
||||
808
.config/BetterDiscord/plugins/ViewProfilePicture.plugin.js
Normal file
@@ -0,0 +1,808 @@
|
||||
/**
|
||||
* @name ViewProfilePicture
|
||||
* @description Adds a button to the user popout and profile that allows you to view the Avatar and banner.
|
||||
* @version 1.3.0
|
||||
* @author Skamt
|
||||
* @website https://github.com/Skamt/BDAddons/tree/main/ViewProfilePicture
|
||||
* @source https://raw.githubusercontent.com/Skamt/BDAddons/main/ViewProfilePicture/ViewProfilePicture.plugin.js
|
||||
*/
|
||||
|
||||
// common/Utils/EventEmitter.js
|
||||
var EventEmitter_default = class {
|
||||
constructor() {
|
||||
this.listeners = {};
|
||||
}
|
||||
isInValid(event, handler) {
|
||||
return typeof event !== "string" || typeof handler !== "function";
|
||||
}
|
||||
once(event, handler) {
|
||||
if (this.isInValid(event, handler)) return;
|
||||
if (!this.listeners[event]) this.listeners[event] = /* @__PURE__ */ new Set();
|
||||
const wrapper = () => {
|
||||
handler();
|
||||
this.off(event, wrapper);
|
||||
};
|
||||
this.listeners[event].add(wrapper);
|
||||
}
|
||||
on(event, handler) {
|
||||
if (this.isInValid(event, handler)) return;
|
||||
if (!this.listeners[event]) this.listeners[event] = /* @__PURE__ */ new Set();
|
||||
this.listeners[event].add(handler);
|
||||
return () => this.off(event, handler);
|
||||
}
|
||||
off(event, handler) {
|
||||
if (this.isInValid(event, handler)) return;
|
||||
if (!this.listeners[event]) return;
|
||||
this.listeners[event].delete(handler);
|
||||
if (this.listeners[event].size !== 0) return;
|
||||
delete this.listeners[event];
|
||||
}
|
||||
emit(event, ...payload) {
|
||||
if (!this.listeners[event]) return;
|
||||
for (const listener of this.listeners[event]) {
|
||||
try {
|
||||
listener.apply(null, payload);
|
||||
} catch (err) {
|
||||
console.error(`Could not run listener for ${event}`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// common/Utils/Plugin.js
|
||||
var Events = {
|
||||
START: "START",
|
||||
STOP: "STOP"
|
||||
};
|
||||
var Plugin_default = new class extends EventEmitter_default {
|
||||
start() {
|
||||
this.emit(Events.START);
|
||||
}
|
||||
stop() {
|
||||
this.emit(Events.STOP);
|
||||
}
|
||||
}();
|
||||
|
||||
// config:@Config
|
||||
var Config_default = {
|
||||
"info": {
|
||||
"name": "ViewProfilePicture",
|
||||
"version": "1.3.0",
|
||||
"description": "Adds a button to the user popout and profile that allows you to view the Avatar and banner.",
|
||||
"source": "https://raw.githubusercontent.com/Skamt/BDAddons/main/ViewProfilePicture/ViewProfilePicture.plugin.js",
|
||||
"github": "https://github.com/Skamt/BDAddons/tree/main/ViewProfilePicture",
|
||||
"authors": [{
|
||||
"name": "Skamt"
|
||||
}]
|
||||
},
|
||||
"settings": {
|
||||
"showOnHover": false,
|
||||
"bannerColor": false
|
||||
}
|
||||
};
|
||||
|
||||
// common/Api.js
|
||||
var Api = new BdApi(Config_default.info.name);
|
||||
var UI = /* @__PURE__ */ (() => Api.UI)();
|
||||
var DOM = /* @__PURE__ */ (() => Api.DOM)();
|
||||
var Data = /* @__PURE__ */ (() => Api.Data)();
|
||||
var React = /* @__PURE__ */ (() => Api.React)();
|
||||
var Patcher = /* @__PURE__ */ (() => Api.Patcher)();
|
||||
var Logger = /* @__PURE__ */ (() => Api.Logger)();
|
||||
var Webpack = /* @__PURE__ */ (() => Api.Webpack)();
|
||||
var findInTree = /* @__PURE__ */ (() => Api.Utils.findInTree)();
|
||||
|
||||
// common/Utils/StylesLoader.js
|
||||
var styleLoader = {
|
||||
_styles: [],
|
||||
push(styles) {
|
||||
this._styles.push(styles);
|
||||
}
|
||||
};
|
||||
Plugin_default.on(Events.START, () => {
|
||||
DOM.addStyle(styleLoader._styles.join("\n"));
|
||||
});
|
||||
Plugin_default.on(Events.STOP, () => {
|
||||
DOM.removeStyle();
|
||||
});
|
||||
var StylesLoader_default = styleLoader;
|
||||
|
||||
// src/ViewProfilePicture/styles.css
|
||||
StylesLoader_default.push(`/* View Profile Button */
|
||||
.VPP-Button {
|
||||
background: hsl(var(--black-500-hsl) / 0.7);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.VPP-float {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
z-index: 987;
|
||||
}
|
||||
|
||||
.VPP-Button svg {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
/* Bigger icon on profile */
|
||||
.VPP-settings svg,
|
||||
.VPP-profile svg {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.VPP-Button:hover {
|
||||
background: hsl(var(--black-500-hsl) / 0.85);
|
||||
}
|
||||
|
||||
/* div replacement if No banner */
|
||||
.VPP-NoBanner {
|
||||
width: 70vw;
|
||||
height: 50vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Carousel Modal */
|
||||
.VPP-carousel-modal {
|
||||
background: #0000;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.VPP-carousel {
|
||||
position: static;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.VPP-carousel button {
|
||||
margin: 0 15px;
|
||||
opacity: 0.8;
|
||||
background: var(--background-base-low);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Copy color button */
|
||||
.VPP-copy-color-container {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.VPP-copy-color-label,
|
||||
.VPP-copy-color {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
line-height: 30px;
|
||||
transition: opacity 0.15s ease;
|
||||
opacity: 0.5;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.VPP-copy-color:hover {
|
||||
opacity: 1;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.VPP-separator {
|
||||
line-height: 30px;
|
||||
opacity: 0.5;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.VPP-copy-color-label {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.VPP-hover {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.VPP-container:hover .VPP-hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.VPP-colorFormat-options {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.VPP-colorFormat-options > div {
|
||||
flex: 1;
|
||||
}
|
||||
`);
|
||||
|
||||
// common/Utils/index.js
|
||||
function fit({ width, height, gap = 0.8 }) {
|
||||
const ratio = Math.min(innerWidth / width, innerHeight / height);
|
||||
width = Math.round(width * ratio);
|
||||
height = Math.round(height * ratio);
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
maxHeight: height * gap,
|
||||
maxWidth: width * gap
|
||||
};
|
||||
}
|
||||
|
||||
function concateClassNames(...args) {
|
||||
return args.filter(Boolean).join(" ");
|
||||
}
|
||||
var promiseHandler = (promise) => promise.then((data) => [void 0, data]).catch((err) => [err]);
|
||||
|
||||
function copy(data) {
|
||||
DiscordNative.clipboard.copy(data);
|
||||
}
|
||||
var nop = () => {};
|
||||
|
||||
function getImageDimensions(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve({
|
||||
width: img.width,
|
||||
height: img.height
|
||||
});
|
||||
img.onerror = reject;
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
// common/Components/ErrorBoundary/index.jsx
|
||||
var ErrorBoundary = class extends React.Component {
|
||||
state = { hasError: false, error: null, info: null };
|
||||
componentDidCatch(error, info) {
|
||||
this.setState({ error, info, hasError: true });
|
||||
const errorMessage = `
|
||||
${error?.message || ""}${(info?.componentStack || "").split("\n").slice(0, 20).join("\n")}`;
|
||||
console.error(`%c[${Config_default?.info?.name || "Unknown Plugin"}] %cthrew an exception at %c[${this.props.id}]
|
||||
`, "color: #3a71c1;font-weight: bold;", "", "color: red;font-weight: bold;", errorMessage);
|
||||
}
|
||||
renderErrorBoundary() {
|
||||
return /* @__PURE__ */ React.createElement("div", { style: { background: "#292c2c", padding: "20px", borderRadius: "10px" } }, /* @__PURE__ */ React.createElement("b", { style: { color: "#e0e1e5" } }, "An error has occured while rendering ", /* @__PURE__ */ React.createElement("span", { style: { color: "orange" } }, this.props.id)));
|
||||
}
|
||||
renderFallback() {
|
||||
if (React.isValidElement(this.props.fallback)) {
|
||||
if (this.props.passMetaProps)
|
||||
this.props.fallback.props = {
|
||||
id: this.props.id,
|
||||
plugin: Config_default?.info?.name || "Unknown Plugin",
|
||||
...this.props.fallback.props
|
||||
};
|
||||
return this.props.fallback;
|
||||
}
|
||||
return /* @__PURE__ */ React.createElement(
|
||||
this.props.fallback, {
|
||||
id: this.props.id,
|
||||
plugin: Config_default?.info?.name || "Unknown Plugin"
|
||||
}
|
||||
);
|
||||
}
|
||||
render() {
|
||||
if (!this.state.hasError) return this.props.children;
|
||||
return this.props.fallback ? this.renderFallback() : this.renderErrorBoundary();
|
||||
}
|
||||
};
|
||||
|
||||
// common/Components/icons/ErrorIcon/index.jsx
|
||||
var ErrorIcon_default = (props) => /* @__PURE__ */ React.createElement("div", { ...props }, /* @__PURE__ */ React.createElement(
|
||||
"svg", {
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
viewBox: "0 0 24 24",
|
||||
fill: "red",
|
||||
width: "18",
|
||||
height: "18"
|
||||
},
|
||||
/* @__PURE__ */
|
||||
React.createElement(
|
||||
"path", {
|
||||
d: "M0 0h24v24H0z",
|
||||
fill: "none"
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */
|
||||
React.createElement("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" })
|
||||
));
|
||||
|
||||
// common/Utils/Logger.js
|
||||
Logger.patchError = (patchId) => {
|
||||
console.error(`%c[${Config_default.info.name}] %cCould not find module for %c[${patchId}]`, "color: #3a71c1;font-weight: bold;", "", "color: red;font-weight: bold;");
|
||||
};
|
||||
var Logger_default = Logger;
|
||||
|
||||
// common/Webpack.js
|
||||
var getModule = /* @__PURE__ */ (() => Webpack.getModule)();
|
||||
var Filters = /* @__PURE__ */ (() => Webpack.Filters)();
|
||||
var getMangled = /* @__PURE__ */ (() => Webpack.getMangled)();
|
||||
var getStore = /* @__PURE__ */ (() => Webpack.getStore)();
|
||||
|
||||
function reactRefMemoFilter(type, ...args) {
|
||||
const filter = Filters.byStrings(...args);
|
||||
return (target) => target[type] && filter(target[type]);
|
||||
}
|
||||
|
||||
function getModuleAndKey(filter, options) {
|
||||
let module2;
|
||||
const target = getModule((entry, m) => filter(entry) ? module2 = m : false, options);
|
||||
module2 = module2?.exports;
|
||||
if (!module2) return;
|
||||
const key = Object.keys(module2).find((k) => module2[k] === target);
|
||||
if (!key) return;
|
||||
return { module: module2, key };
|
||||
}
|
||||
|
||||
// common/React.js
|
||||
var useState = /* @__PURE__ */ (() => React.useState)();
|
||||
var useMemo = /* @__PURE__ */ (() => React.useMemo)();
|
||||
var React_default = /* @__PURE__ */ (() => React)();
|
||||
|
||||
// common/DiscordModules/zustand.js
|
||||
var { zustand } = getMangled(Filters.bySource("useSyncExternalStoreWithSelector", "useDebugValue", "subscribe"), {
|
||||
_: Filters.byStrings("subscribe"),
|
||||
zustand: () => true
|
||||
});
|
||||
var subscribeWithSelector = getModule(Filters.byStrings("equalityFn", "fireImmediately"), { searchExports: true });
|
||||
var zustand_default = zustand;
|
||||
|
||||
// common/Utils/Settings.js
|
||||
var SettingsStoreSelectors = {};
|
||||
var persistMiddleware = (config) => (set, get, api) => config((args) => (set(args), Data.save("settings", get().getRawState())), get, api);
|
||||
var SettingsStore = Object.assign(
|
||||
zustand_default(
|
||||
persistMiddleware(
|
||||
subscribeWithSelector((set, get) => {
|
||||
const settingsObj = /* @__PURE__ */ Object.create(null);
|
||||
for (const [key, value] of Object.entries({
|
||||
...Config_default.settings,
|
||||
...Data.load("settings")
|
||||
})) {
|
||||
settingsObj[key] = value;
|
||||
settingsObj[`set${key}`] = (newValue) => set({
|
||||
[key]: newValue });
|
||||
SettingsStoreSelectors[key] = (state) => state[key];
|
||||
}
|
||||
settingsObj.getRawState = () => {
|
||||
return Object.entries(get()).filter(([, val]) => typeof val !== "function").reduce((acc, [key, val]) => {
|
||||
acc[key] = val;
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
return settingsObj;
|
||||
})
|
||||
)
|
||||
), {
|
||||
useSetting: function(key) {
|
||||
return this((state) => [state[key], state[`set${key}`]]);
|
||||
},
|
||||
selectors: SettingsStoreSelectors
|
||||
}
|
||||
);
|
||||
Object.defineProperty(SettingsStore, "state", {
|
||||
configurable: false,
|
||||
get() {
|
||||
return this.getState();
|
||||
}
|
||||
});
|
||||
var Settings_default = SettingsStore;
|
||||
|
||||
// common/Utils/Modals/styles.css
|
||||
StylesLoader_default.push(`.transparent-background.transparent-background{
|
||||
background: transparent;
|
||||
border:unset;
|
||||
}`);
|
||||
|
||||
// MODULES-AUTO-LOADER:@Modules/ModalRoot
|
||||
var ModalRoot_default = getModule(Filters.byStrings("rootWithShadow", "MODAL"), { searchExports: true });
|
||||
|
||||
// MODULES-AUTO-LOADER:@Modules/ModalSize
|
||||
var ModalSize_default = getModule(Filters.byKeys("DYNAMIC", "SMALL", "LARGE"), { searchExports: true });
|
||||
|
||||
// common/Utils/Modals/index.jsx
|
||||
var ModalActions = /* @__PURE__ */ getMangled("onCloseRequest:null!=", {
|
||||
openModal: /* @__PURE__ */ Filters.byStrings("onCloseRequest:null!="),
|
||||
closeModal: /* @__PURE__ */ Filters.byStrings(".setState", ".getState()[")
|
||||
});
|
||||
var openModal = (children, tag, { className, ...modalRootProps } = {}) => {
|
||||
const id = `${tag ? `${tag}-` : ""}modal`;
|
||||
return ModalActions.openModal((props) => {
|
||||
return /* @__PURE__ */ React.createElement(
|
||||
ErrorBoundary, {
|
||||
id,
|
||||
plugin: Config_default.info.name
|
||||
},
|
||||
/* @__PURE__ */
|
||||
React.createElement(
|
||||
ModalRoot_default, {
|
||||
onClick: props.onClose,
|
||||
transitionState: props.transitionState,
|
||||
className: concateClassNames("transparent-background", className),
|
||||
size: ModalSize_default.DYNAMIC,
|
||||
...modalRootProps
|
||||
},
|
||||
React.cloneElement(children, { ...props })
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// MODULES-AUTO-LOADER:@Modules/Tooltip
|
||||
var Tooltip_default = getModule(Filters.byPrototypeKeys("renderTooltip"), { searchExports: true });
|
||||
|
||||
// common/Components/Tooltip/index.jsx
|
||||
var Tooltip_default2 = ({ note, position, children }) => {
|
||||
return /* @__PURE__ */ React.createElement(
|
||||
Tooltip_default, {
|
||||
text: note,
|
||||
position: position || "top"
|
||||
},
|
||||
(props) => React.cloneElement(children, {
|
||||
...props,
|
||||
...children.props
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// common/Components/icons/ImageIcon/index.jsx
|
||||
function ImageIcon(props) {
|
||||
return /* @__PURE__ */ React.createElement(
|
||||
"svg", {
|
||||
fill: "currentColor",
|
||||
width: "24",
|
||||
height: "24",
|
||||
viewBox: "-50 -50 484 484",
|
||||
...props
|
||||
},
|
||||
/* @__PURE__ */
|
||||
React.createElement("path", { d: "M341.333,0H42.667C19.093,0,0,19.093,0,42.667v298.667C0,364.907,19.093,384,42.667,384h298.667 C364.907,384,384,364.907,384,341.333V42.667C384,19.093,364.907,0,341.333,0z M42.667,320l74.667-96l53.333,64.107L245.333,192l96,128H42.667z" })
|
||||
);
|
||||
}
|
||||
|
||||
// common/Utils/ImageModal/styles.css
|
||||
StylesLoader_default.push(`.downloadLink {
|
||||
color: white !important;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
/* line-height: 18px;*/
|
||||
text-decoration: none;
|
||||
transition: opacity.15s ease;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.imageModalwrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.imageModalOptions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
`);
|
||||
|
||||
// MODULES-AUTO-LOADER:@Stores/AccessibilityStore
|
||||
var AccessibilityStore_default = getStore("AccessibilityStore");
|
||||
|
||||
// common/Utils/ImageModal/index.jsx
|
||||
var RenderLinkComponent = getModule((m) => m.type?.toString?.().includes("MASKED_LINK"), { searchExports: false });
|
||||
var ImageModal = getModule(reactRefMemoFilter("type", "renderLinkComponent"), { searchExports: true });
|
||||
|
||||
function h(e, t) {
|
||||
let n = arguments.length > 2 && void 0 !== arguments[2] && arguments[2];
|
||||
true === n || AccessibilityStore_default.useReducedMotion ? e.set(t) : e.start(t);
|
||||
}
|
||||
var useSomeScalingHook = getModule(Filters.byStrings("reducedMotion.enabled", "useSpring", "respect-motion-settings"), { searchExports: true });
|
||||
var context = getModule((a) => a?._currentValue?.scale, { searchExports: true });
|
||||
var ImageComponent = ({ url, ...rest }) => {
|
||||
const [x, P] = useState(false);
|
||||
const [M, w] = useSomeScalingHook(() => ({
|
||||
scale: AccessibilityStore_default.useReducedMotion ? 1 : 0.9,
|
||||
x: 0,
|
||||
y: 0,
|
||||
config: {
|
||||
friction: 30,
|
||||
tension: 300
|
||||
}
|
||||
}));
|
||||
const contextVal = useMemo(
|
||||
() => ({
|
||||
scale: M.scale,
|
||||
x: M.x,
|
||||
y: M.y,
|
||||
setScale(e, t) {
|
||||
h(M.scale, e, null == t ? void 0 : t.immediate);
|
||||
},
|
||||
setOffset(e, t, n) {
|
||||
h(M.x, e, null == n ? void 0 : n.immediate), h(M.y, t, null == n ? void 0 : n.immediate);
|
||||
},
|
||||
zoomed: x,
|
||||
setZoomed(e) {
|
||||
P(e), h(M.scale, e ? 2.5 : 1), e || (h(M.x, 0), h(M.y, 0));
|
||||
}
|
||||
}),
|
||||
[x, M]
|
||||
);
|
||||
return /* @__PURE__ */ React_default.createElement(context.Provider, { value: contextVal }, /* @__PURE__ */ React_default.createElement("div", { className: "imageModalwrapper" }, /* @__PURE__ */ React_default.createElement(
|
||||
ImageModal, {
|
||||
maxWidth: rest.maxWidth,
|
||||
maxHeight: rest.maxHeight,
|
||||
media: {
|
||||
...rest,
|
||||
type: "IMAGE",
|
||||
url,
|
||||
proxyUrl: url
|
||||
}
|
||||
}
|
||||
), !x && /* @__PURE__ */ React_default.createElement("div", { className: "imageModalOptions" }, /* @__PURE__ */ React_default.createElement(
|
||||
RenderLinkComponent, {
|
||||
className: "downloadLink",
|
||||
href: url
|
||||
},
|
||||
"Open in Browser"
|
||||
))));
|
||||
};
|
||||
|
||||
// MODULES-AUTO-LOADER:@Modules/Color
|
||||
var Color_default = getModule(Filters.byKeys("Color", "hex", "hsl"), { searchExports: false });
|
||||
|
||||
// MODULES-AUTO-LOADER:@Stores/ThemeStore
|
||||
var ThemeStore_default = getStore("ThemeStore");
|
||||
|
||||
// common/Utils/Toast.js
|
||||
function showToast(content, type) {
|
||||
UI.showToast(`[${Config_default.info.name}] ${content}`, { timeout: 5e3, type });
|
||||
}
|
||||
var Toast_default = {
|
||||
success(content) {
|
||||
showToast(content, "success");
|
||||
},
|
||||
info(content) {
|
||||
showToast(content, "info");
|
||||
},
|
||||
warning(content) {
|
||||
showToast(content, "warning");
|
||||
},
|
||||
error(content) {
|
||||
showToast(content, "error");
|
||||
}
|
||||
};
|
||||
|
||||
// src/ViewProfilePicture/components/ColorModalComponent.jsx
|
||||
var DesignSystem = getModule((a) => a?.unsafe_rawColors?.PRIMARY_800?.resolve);
|
||||
|
||||
function resolveColor() {
|
||||
if (!DesignSystem?.unsafe_rawColors?.PRIMARY_800) return "#111214";
|
||||
return DesignSystem.unsafe_rawColors?.PRIMARY_800.resolve({
|
||||
theme: ThemeStore_default.theme,
|
||||
saturation: AccessibilityStore_default.saturation
|
||||
}).hex();
|
||||
}
|
||||
|
||||
function copyColor(type, color) {
|
||||
let c = color;
|
||||
try {
|
||||
switch (type) {
|
||||
case "hex":
|
||||
c = Color_default(color).hex();
|
||||
break;
|
||||
case "rgba":
|
||||
c = Color_default(color).css("rgba");
|
||||
break;
|
||||
case "hsla":
|
||||
c = Color_default(color).css("hsla");
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
copy(c);
|
||||
Toast_default.success(`${c} Copied!`);
|
||||
}
|
||||
}
|
||||
|
||||
function SimpleColorModal({ color }) {
|
||||
return /* @__PURE__ */ React_default.createElement(
|
||||
"div", {
|
||||
onClick: (e) => e.stopPropagation(),
|
||||
className: "VPP-NoBanner",
|
||||
style: { backgroundColor: Color_default(color).css() }
|
||||
},
|
||||
/* @__PURE__ */
|
||||
React_default.createElement("div", { className: "VPP-copy-color-container" }, /* @__PURE__ */ React_default.createElement("span", { className: "VPP-copy-color-label" }, "Copy Color:"), ["hex", false, "rgba", false, "hsla"].map(
|
||||
(name) => name ? /* @__PURE__ */ React_default.createElement(
|
||||
"a", {
|
||||
className: "VPP-copy-color",
|
||||
onClick: (e) => {
|
||||
e.stopPropagation();
|
||||
copyColor(name, color);
|
||||
}
|
||||
},
|
||||
name
|
||||
) : /* @__PURE__ */ React_default.createElement("span", { className: "VPP-separator" }, "|")
|
||||
))
|
||||
);
|
||||
}
|
||||
var {
|
||||
module: { ZP: palletHook }
|
||||
} = getModuleAndKey(Filters.byStrings("toHexString", "toHsl", "palette"), { searchExports: true }) || {};
|
||||
|
||||
function ColorModal({ displayProfile, user }) {
|
||||
const color = palletHook(user.getAvatarURL(displayProfile.guildId, 80));
|
||||
return /* @__PURE__ */ React_default.createElement(SimpleColorModal, { color: color || resolveColor() });
|
||||
}
|
||||
var ColorModalComponent_default = {
|
||||
SimpleColorModal,
|
||||
ColorModal
|
||||
};
|
||||
|
||||
// MODULES-AUTO-LOADER:@Modules/ModalCarousel
|
||||
var ModalCarousel_default = getModule(Filters.byPrototypeKeys("navigateTo", "preloadImage"), { searchExports: false });
|
||||
|
||||
// src/ViewProfilePicture/components/ModalCarousel.jsx
|
||||
var ModalCarousel_default2 = class extends ModalCarousel_default {
|
||||
preloadNextImages() {}
|
||||
};
|
||||
|
||||
// MODULES-AUTO-LOADER:@Modules/Spinner
|
||||
var Spinner_default = getModule((a) => a?.Type?.CHASING_DOTS, { searchExports: true });
|
||||
|
||||
// src/ViewProfilePicture/components/ViewProfilePictureButtonComponent.jsx
|
||||
function Banner({ url, src }) {
|
||||
const [loaded, setLoaded] = React_default.useState(false);
|
||||
const dimsRef = React_default.useRef();
|
||||
React_default.useEffect(() => {
|
||||
(async () => {
|
||||
const [err, dims] = await promiseHandler(getImageDimensions(src));
|
||||
dimsRef.current = fit(err ? {} : dims);
|
||||
setLoaded(true);
|
||||
})();
|
||||
}, []);
|
||||
if (!loaded) return /* @__PURE__ */ React_default.createElement(Spinner_default, { type: Spinner_default.Type.SPINNING_CIRCLE });
|
||||
return /* @__PURE__ */ React_default.createElement(
|
||||
ImageComponent, {
|
||||
url,
|
||||
...dimsRef.current
|
||||
}
|
||||
);
|
||||
}
|
||||
var ViewProfilePictureButtonComponent_default = ({ className, user, displayProfile }) => {
|
||||
const showOnHover = Settings_default(Settings_default.selectors.showOnHover);
|
||||
const handler = () => {
|
||||
const avatarURL = user.getAvatarURL(displayProfile.guildId, 4096, true);
|
||||
const bannerURL = displayProfile.getBannerURL({ canAnimate: true, size: 4096 });
|
||||
const color = displayProfile.accentColor || displayProfile.primaryColor;
|
||||
const items = [
|
||||
/* @__PURE__ */
|
||||
React_default.createElement(
|
||||
ImageComponent, {
|
||||
url: avatarURL,
|
||||
...fit({ width: 4096, height: 4096 })
|
||||
}
|
||||
),
|
||||
bannerURL && /* @__PURE__ */ React_default.createElement(
|
||||
Banner, {
|
||||
url: bannerURL,
|
||||
src: displayProfile.getBannerURL({ canAnimate: true, size: 20 })
|
||||
}
|
||||
),
|
||||
(!bannerURL || Settings_default.getState().bannerColor) && (color ? /* @__PURE__ */ React_default.createElement(ColorModalComponent_default.SimpleColorModal, { color }) : /* @__PURE__ */ React_default.createElement(
|
||||
ColorModalComponent_default.ColorModal, {
|
||||
user,
|
||||
displayProfile
|
||||
}
|
||||
))
|
||||
].filter(Boolean).map((item) => ({ component: item }));
|
||||
openModal(
|
||||
/* @__PURE__ */
|
||||
React_default.createElement(
|
||||
ModalCarousel_default2, {
|
||||
startWith: 0,
|
||||
className: "VPP-carousel",
|
||||
items
|
||||
}
|
||||
),
|
||||
"VPP-carousel", { className: "VPP-carousel-modal" }
|
||||
);
|
||||
};
|
||||
return /* @__PURE__ */ React_default.createElement(Tooltip_default2, { note: "View profile picture" }, /* @__PURE__ */ React_default.createElement(
|
||||
"div", {
|
||||
onClick: handler,
|
||||
className: concateClassNames(className, showOnHover && "VPP-hover")
|
||||
},
|
||||
/* @__PURE__ */
|
||||
React_default.createElement(ImageIcon, null)
|
||||
));
|
||||
};
|
||||
|
||||
// src/ViewProfilePicture/patches/patchVPPButton.jsx
|
||||
var UserProfileModalforwardRef = getModule(Filters.byKeys("Overlay", "render"));
|
||||
var typeFilter = Filters.byStrings("div", "wrapper", "children");
|
||||
Plugin_default.on(Events.START, () => {
|
||||
if (!UserProfileModalforwardRef) return Logger_default.patchError("patchVPPButton");
|
||||
const unpatch = Patcher.after(UserProfileModalforwardRef, "render", (_, [props], ret) => {
|
||||
const target = findInTree(ret, (a) => typeFilter(a?.type), { walkable: ["props", "children"] }) || findInTree(ret, (a) => a?.type === "header" || a?.props?.className?.startsWith("profileHeader"), { walkable: ["props", "children"] });
|
||||
if (!target) return;
|
||||
ret.props.className = `${ret.props.className} VPP-container`;
|
||||
target.props.children.unshift(
|
||||
/* @__PURE__ */
|
||||
React.createElement(
|
||||
ErrorBoundary, {
|
||||
id: "ViewProfilePictureButtonComponent",
|
||||
plugin: Config_default.info.name,
|
||||
fallback: /* @__PURE__ */ React.createElement(ErrorIcon_default, { className: "VPP-Button" })
|
||||
},
|
||||
/* @__PURE__ */
|
||||
React.createElement(
|
||||
ViewProfilePictureButtonComponent_default, {
|
||||
className: concateClassNames("VPP-Button", !typeFilter(target?.type) && "VPP-float"),
|
||||
user: props.user,
|
||||
displayProfile: props.displayProfile
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
Plugin_default.once(Events.STOP, unpatch);
|
||||
});
|
||||
|
||||
// MODULES-AUTO-LOADER:@Modules/FormSwitch
|
||||
var FormSwitch_default = getModule(Filters.byStrings("note", "tooltipNote"), { searchExports: true });
|
||||
|
||||
// common/Components/Switch/index.jsx
|
||||
var Switch_default = FormSwitch_default || function SwitchComponentFallback(props) {
|
||||
return /* @__PURE__ */ React.createElement("div", { style: { color: "#fff" } }, props.children, /* @__PURE__ */ React.createElement(
|
||||
"input", {
|
||||
type: "checkbox",
|
||||
checked: props.value,
|
||||
onChange: (e) => props.onChange(e.target.checked)
|
||||
}
|
||||
));
|
||||
};
|
||||
|
||||
// common/Components/SettingSwtich/index.jsx
|
||||
function SettingSwtich({ settingKey, note, onChange = nop, hideBorder = false, description, ...rest }) {
|
||||
const [val, set] = Settings_default.useSetting(settingKey);
|
||||
return /* @__PURE__ */ React.createElement(
|
||||
Switch_default, {
|
||||
...rest,
|
||||
value: val,
|
||||
note,
|
||||
hideBorder,
|
||||
onChange: (e) => {
|
||||
set(e);
|
||||
onChange(e);
|
||||
}
|
||||
},
|
||||
description || settingKey
|
||||
);
|
||||
}
|
||||
|
||||
// src/ViewProfilePicture/components/SettingComponent.jsx
|
||||
function SettingComponent() {
|
||||
return [{
|
||||
settingKey: "showOnHover",
|
||||
note: "By default hide ViewProfilePicture button and show on hover.",
|
||||
description: "Show on hover"
|
||||
},
|
||||
{
|
||||
settingKey: "bannerColor",
|
||||
note: "Always include banner color in carousel, even if a banner is present.",
|
||||
description: "Include banner color."
|
||||
}
|
||||
].map(SettingSwtich);
|
||||
}
|
||||
|
||||
// src/ViewProfilePicture/index.jsx
|
||||
Plugin_default.getSettingsPanel = () => /* @__PURE__ */ React_default.createElement(SettingComponent, null);
|
||||
module.exports = () => Plugin_default;
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"currentVersionInfo": {
|
||||
"version": "2.0.23",
|
||||
"hasShownChangelog": true
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
[colors]
|
||||
background=#0E1125
|
||||
foreground=#e4e6ec
|
||||
primary=#699AD3
|
||||
secondary=#D1ACAF
|
||||
tertiary=#9DAECF
|
||||
highlight=#B0CBE7
|
||||
accent=#CDD5EA
|
||||
border=#929BB1
|
||||
|
||||
[options]
|
||||
name=Pywal
|
||||
author=Rion Zaphkiel
|
||||
@@ -1,5 +1,5 @@
|
||||
[color]
|
||||
gradient = 1
|
||||
gradient_count = 2
|
||||
gradient_color_1 = '#929ca3'
|
||||
gradient_color_2 = '#717C82'
|
||||
gradient_color_1 = '#a79e9c'
|
||||
gradient_color_2 = '#A06A68'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
"type": "kitty-direct",
|
||||
"source": "/home/rionzaphkiel/.config/fastfetch/logo/john_wuthering.png",
|
||||
"source": "/home/rionzaphkiel/.config/fastfetch/logo/john_arknights.png",
|
||||
"padding": {
|
||||
"top": 1,
|
||||
"left": 2
|
||||
@@ -52,7 +52,7 @@
|
||||
{
|
||||
"type": "command",
|
||||
"key": "wife ",
|
||||
"format": "Artoria Pendragon",
|
||||
"format": "Wiš'adel",
|
||||
"keyColor": "white"
|
||||
}
|
||||
]
|
||||
|
||||
|
Before Width: | Height: | Size: 647 KiB After Width: | Height: | Size: 669 KiB |
@@ -1,4 +1,4 @@
|
||||
@import url("file:///home/rionzaphkiel/.cache/wal/colors-waybar.css"); /* ambil palet Pywal */
|
||||
@import url("file:///home/rionzaphkiel/.cache/wal/colors-waybar.css");
|
||||
|
||||
/* === Global === */
|
||||
* {
|
||||
@@ -9,12 +9,11 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* === Window utama === */
|
||||
/* === Main Window === */
|
||||
window {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
|
||||
/* Campuran background + nuansa @color1 */
|
||||
background:
|
||||
radial-gradient(circle at top left, alpha(@color1,0.35), transparent 70%),
|
||||
radial-gradient(circle at top right, alpha(@color1,0.35), transparent 70%),
|
||||
@@ -66,7 +65,7 @@ window {
|
||||
color: @foreground;
|
||||
}
|
||||
|
||||
/* Item terpilih (highlight nuansa pywal) */
|
||||
/* Highlight item */
|
||||
#entry:selected {
|
||||
background-color: alpha(@color1, 0.2); /* warna highlight */
|
||||
border: 1px solid @color1;
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
@import url("file:///home/rionzaphkiel/.cache/wal/colors-waybar.css");
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
}
|
||||
100% {
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
all:unset;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 18px;
|
||||
outline: none;
|
||||
border: none;
|
||||
text-shadow:none;
|
||||
background-color:transparent;
|
||||
}
|
||||
|
||||
window {
|
||||
all:unset;
|
||||
padding: 20px;
|
||||
border-radius: 0px;
|
||||
background-color: alpha(@background,.5);
|
||||
}
|
||||
#inner-box {
|
||||
margin: 2px;
|
||||
padding: 5px
|
||||
border: none;
|
||||
}
|
||||
#outer-box {
|
||||
border: none;
|
||||
}
|
||||
#scroll {
|
||||
margin: 0px;
|
||||
padding: 30px;
|
||||
border: none;
|
||||
}
|
||||
#input {
|
||||
all:unset;
|
||||
margin-left:20px;
|
||||
margin-right:20px;
|
||||
margin-top:20px;
|
||||
padding: 20px;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: @text;
|
||||
box-shadow: 1px 1px 5px rgba(0,0,0, .5);
|
||||
border-radius:10;
|
||||
background-color: alpha(@background,.2);
|
||||
}
|
||||
#input image {
|
||||
border: none;
|
||||
color: @red;
|
||||
padding-right:10px;
|
||||
}
|
||||
#input * {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#input:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
|
||||
border-radius:10;
|
||||
}
|
||||
#text {
|
||||
margin: 5px;
|
||||
border: none;
|
||||
color: @color1;
|
||||
outline: none;
|
||||
}
|
||||
#text {
|
||||
margin: 5px;
|
||||
border: none;
|
||||
color: @text;
|
||||
outline: none;
|
||||
}
|
||||
#entry {
|
||||
border: none;
|
||||
margin: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
#entry arrow {
|
||||
border: none;
|
||||
color: @lavender;
|
||||
|
||||
}
|
||||
#entry:selected {
|
||||
box-shadow: 1px 1px 5px rgba(255,255,255, .03);
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
background-color:transparent;
|
||||
}
|
||||
#entry:selected #text {
|
||||
color: @mauve;
|
||||
}
|
||||
#entry:drop(active) {
|
||||
background-color: @lavender !important;
|
||||
}
|
||||
@@ -13,5 +13,4 @@ sed -i "s|^gradient_color_1 = '.*'|gradient_color_1 = '$COLOR1'|" "$CAVA_CONFIG"
|
||||
sed -i "s|^gradient_color_2 = '.*'|gradient_color_2 = '$COLOR2'|" "$CAVA_CONFIG"
|
||||
|
||||
# Auto-Restart CAVA (if it's running)
|
||||
killall -USR1 cava
|
||||
|
||||
killall -USR1 cava
|
||||
@@ -3,12 +3,12 @@
|
||||
# Path
|
||||
WALCSS="$HOME/.cache/wal/colors.css"
|
||||
BDCSS="$HOME/.config/BetterDiscord/data/stable/custom.css"
|
||||
TEMPLATE="$HOME/.config/bd-template.css"
|
||||
HEADER="$HOME/.config/bd-header.css"
|
||||
TEMPLATE="$HOME/.config/BetterDiscord/bd-template.css"
|
||||
HEADER="$HOME/.config/BetterDiscord/bd-header.css"
|
||||
|
||||
# Overwrite Better Discord Custom CSS using pywal color palette
|
||||
cat "$HEADER" > "$BDCSS"
|
||||
echo "" >> "$BDCSS"
|
||||
cat "$WALCSS" >> "$BDCSS"
|
||||
echo "" >> "$BDCSS"
|
||||
cat "$TEMPLATE" >> "$BDCSS"
|
||||
cat "$TEMPLATE" >> "$BDCSS"
|
||||
34
.script/fastfetch_auto.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==== Fastfetch Auto ====
|
||||
|
||||
CFG="$HOME/.config/fastfetch/config.jsonc"
|
||||
FLAG="$HOME/.config/fastfetch/.reload_flag"
|
||||
|
||||
clear
|
||||
tput civis # hide cursor
|
||||
|
||||
# Make sure cursor comes back if script exits
|
||||
cleanup() {
|
||||
tput cnorm
|
||||
clear
|
||||
}
|
||||
trap cleanup INT TERM EXIT
|
||||
|
||||
fastfetch
|
||||
|
||||
while true; do
|
||||
# check keyboard input (1 char, timeout 1s)
|
||||
if read -r -n 1 -t 1 key; then
|
||||
if [[ $key == "q" ]]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
|
||||
# check config or flag file
|
||||
if [ -f "$FLAG" ] || [ "$CFG" -nt "$CFG" ]; then
|
||||
clear
|
||||
fastfetch
|
||||
rm -f "$FLAG"
|
||||
fi
|
||||
done
|
||||
121
.zshrc
Normal file
@@ -0,0 +1,121 @@
|
||||
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
|
||||
# Initialization code that may require console input (password prompts, [y/n]
|
||||
# confirmations, etc.) must go above this block; everything else may go below.
|
||||
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
|
||||
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
|
||||
fi
|
||||
|
||||
# If you come from bash you might have to change your $PATH.
|
||||
export PATH=$HOME/.local/bin/important-script:$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH
|
||||
|
||||
|
||||
# Path to your Oh My Zsh installation.
|
||||
export ZSH="$HOME/.oh-my-zsh"
|
||||
|
||||
# Set name of the theme to load --- if set to "random", it will
|
||||
# load a random theme each time Oh My Zsh is loaded, in which case,
|
||||
# to know which specific one was loaded, run: echo $RANDOM_THEME
|
||||
# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes
|
||||
ZSH_THEME="powerlevel10k/powerlevel10k"
|
||||
|
||||
# Set list of themes to pick from when loading at random
|
||||
# Setting this variable when ZSH_THEME=random will cause zsh to load
|
||||
# a theme from this variable instead of looking in $ZSH/themes/
|
||||
# If set to an empty array, this variable will have no effect.
|
||||
# ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" )
|
||||
|
||||
# Uncomment the following line to use case-sensitive completion.
|
||||
# CASE_SENSITIVE="true"
|
||||
|
||||
# Uncomment the following line to use hyphen-insensitive completion.
|
||||
# Case-sensitive completion must be off. _ and - will be interchangeable.
|
||||
# HYPHEN_INSENSITIVE="true"
|
||||
|
||||
# Uncomment one of the following lines to change the auto-update behavior
|
||||
# zstyle ':omz:update' mode disabled # disable automatic updates
|
||||
# zstyle ':omz:update' mode auto # update automatically without asking
|
||||
# zstyle ':omz:update' mode reminder # just remind me to update when it's time
|
||||
|
||||
# Uncomment the following line to change how often to auto-update (in days).
|
||||
# zstyle ':omz:update' frequency 13
|
||||
|
||||
# Uncomment the following line if pasting URLs and other text is messed up.
|
||||
# DISABLE_MAGIC_FUNCTIONS="true"
|
||||
|
||||
# Uncomment the following line to disable colors in ls.
|
||||
# DISABLE_LS_COLORS="true"
|
||||
|
||||
# Uncomment the following line to disable auto-setting terminal title.
|
||||
# DISABLE_AUTO_TITLE="true"
|
||||
|
||||
# Uncomment the following line to enable command auto-correction.
|
||||
# ENABLE_CORRECTION="true"
|
||||
|
||||
# Uncomment the following line to display red dots whilst waiting for completion.
|
||||
# You can also set it to another string to have that shown instead of the default red dots.
|
||||
# e.g. COMPLETION_WAITING_DOTS="%F{yellow}waiting...%f"
|
||||
# Caution: this setting can cause issues with multiline prompts in zsh < 5.7.1 (see #5765)
|
||||
# COMPLETION_WAITING_DOTS="true"
|
||||
|
||||
# Uncomment the following line if you want to disable marking untracked files
|
||||
# under VCS as dirty. This makes repository status check for large repositories
|
||||
# much, much faster.
|
||||
# DISABLE_UNTRACKED_FILES_DIRTY="true"
|
||||
|
||||
# Uncomment the following line if you want to change the command execution time
|
||||
# stamp shown in the history command output.
|
||||
# You can set one of the optional three formats:
|
||||
# "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd"
|
||||
# or set a custom format using the strftime function format specifications,
|
||||
# see 'man strftime' for details.
|
||||
# HIST_STAMPS="mm/dd/yyyy"
|
||||
|
||||
# Would you like to use another custom folder than $ZSH/custom?
|
||||
# ZSH_CUSTOM=/path/to/new-custom-folder
|
||||
|
||||
# Which plugins would you like to load?
|
||||
# Standard plugins can be found in $ZSH/plugins/
|
||||
# Custom plugins may be added to $ZSH_CUSTOM/plugins/
|
||||
# Example format: plugins=(rails git textmate ruby lighthouse)
|
||||
# Add wisely, as too many plugins slow down shell startup.
|
||||
plugins=(git zsh-autosuggestions zsh-syntax-highlighting)
|
||||
|
||||
source $ZSH/oh-my-zsh.sh
|
||||
|
||||
# User configuration
|
||||
|
||||
# export MANPATH="/usr/local/man:$MANPATH"
|
||||
|
||||
# You may need to manually set your language environment
|
||||
# export LANG=en_US.UTF-8
|
||||
|
||||
# Preferred editor for local and remote sessions
|
||||
# if [[ -n $SSH_CONNECTION ]]; then
|
||||
# export EDITOR='vim'
|
||||
# else
|
||||
# export EDITOR='nvim'
|
||||
# fi
|
||||
|
||||
# Compilation flags
|
||||
# export ARCHFLAGS="-arch $(uname -m)"
|
||||
|
||||
# Set personal aliases, overriding those provided by Oh My Zsh libs,
|
||||
# plugins, and themes. Aliases can be placed here, though Oh My Zsh
|
||||
# users are encouraged to define aliases within a top-level file in
|
||||
# the $ZSH_CUSTOM folder, with .zsh extension. Examples:
|
||||
# - $ZSH_CUSTOM/aliases.zsh
|
||||
# - $ZSH_CUSTOM/macos.zsh
|
||||
# For a full list of active aliases, run `alias`.
|
||||
#
|
||||
# Example aliases
|
||||
# alias zshconfig="mate ~/.zshrc"
|
||||
# alias ohmyzsh="mate ~/.oh-my-zsh"
|
||||
|
||||
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
|
||||
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
|
||||
|
||||
# Muat warna dari Pywal di bagian paling akhir
|
||||
[ -f ~/.cache/wal/sequences ] && cat ~/.cache/wal/sequences
|
||||
|
||||
alias ffa='~/.local/bin/fastfetch_auto.sh'
|
||||
alias reales='~/.realesrgan/realesrgan-ncnn-vulkan'
|
||||
431
README.md
@@ -1,58 +1,409 @@
|
||||
# rion-ricing
|
||||
Hey quick overview here. This contains My **GNOME** Dotfiles for a Hyprland-looks Setup. This is my way to cope for the Endfield that never comes, hopefully there'll be news on The TGS. Later on i'll make the details how to do my config, but for now i'm too lazy to make a good docs for that lol.
|
||||
<div align="center">
|
||||
|
||||
[](https://git.io/typing-svg)
|
||||
|
||||
</div>
|
||||
|
||||
# Showcase
|
||||
[](https://youtu.be/tJPglNR69ok)
|
||||
|
||||
***ENJOY!!***
|
||||
This contains My Fedora Linux dotfiles for a Hyprland-looks in **GNOME** setup.
|
||||
|
||||
-RionZaphkiel
|
||||
**ENJOY!**
|
||||
|
||||
-Rion Zaphkiel
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> All Configurations were meant to be used with `Pywal`
|
||||
>
|
||||
|
||||
# Extension
|
||||
---
|
||||
|
||||
These are the GNOME Shell extensions I use. The table below shows the extension name, what it does, and a short note or recommended configuration.
|
||||
## GNOME Extensions
|
||||
|
||||
| Extension | Purpose / Usage | Notes / Recommended Settings |
|
||||
|---|---|---|
|
||||
| **Blur My Shell** | Adds configurable blur to GNOME Shell surfaces (top bar, overview, dialogs). | Tweak blur strength in the extension settings. May increase GPU usage slightly. |
|
||||
| **Clipboard Indicator** | Simple clipboard manager accessible from the top bar; shows history and lets you pin/clear entries. | Set history size and enable/disable syncing to avoid leaking sensitive clipboard items. |
|
||||
| **Just Perfection** | Powerful UI customizer for GNOME Shell (hide panel items, tweak overview, clock, etc.). | Backup configuration before big changes; useful to hide elements you don’t need. |
|
||||
| **Media Controls** | Improves media playback controls in the top bar (MPRIS integration). | Works with most music/video players; enable show/hide behavior in prefs. |
|
||||
| **Quick Settings Tweaks** | Adds extra toggles and small UI tweaks to the quick settings (system menu). | Choose which quick toggles you want; some toggles require additional packages. |
|
||||
| **System Monitor** | Shows CPU, memory, disk or network stats in the top bar or quick settings. | Shows your vitals in top panel |
|
||||
| **Unblank Lock Screen** | Prevents the lock screen from fully blanking (keeps wallpaper or UI visible). | Useful for demos/screenshots — be mindful of security / screen privacy. |
|
||||
| **Workspace Indicator** | Displays current workspace information (number or name) on the top bar. | Handy for multi-workspace workflow; configure numbering vs names in prefs. |
|
||||
| **Move Workspace Indicator** | Adds quick controls/indicators to move windows between workspaces. | Lets you move workspace indicator to the left (Enable the workspace indicator first) |
|
||||
| **Open Bar** | Adds an extra customizable bar/dock for quick access to apps and widgets. | ⚠️ **Warning:** may cause a small lag when changing wallpaper (the extension redraws UI). If you notice stutter while switching wallpapers, try disabling Open Bar |
|
||||
<details>
|
||||
<summary>🪟 Forge</summary>
|
||||
|
||||
# Scripts
|
||||
### Description
|
||||
Tiling and window manager for GNOME.
|
||||
|
||||
These are the custom scripts I use to integrate Pywal, wallpapers, and my applications.
|
||||
### Settings
|
||||
You can follow my settings to get the same style in screenshot below, or you can tweak it as you will.
|
||||
|
||||
| Script | Purpose / Usage | Notes |
|
||||
|---|---|---|
|
||||
| **wallpaper-picker.sh** | Wallpaper selector using Wofi with thumbnails. Automatically applies Pywal colors, updates GNOME wallpaper, sets Fastfetch logo, and runs hooks. | Requires **ImageMagick**, **jq**, **wofi**, and Pywal installed. Open Bar GNOME extension may cause slight lag when changing wallpaper. |
|
||||
| **cava-pywalsync.sh** | Syncs Cava’s gradient colors with Pywal’s palette. Automatically restarts Cava if it’s running. | Needs `jq` and `Pywal`. |
|
||||
| **discord-pywalsync.sh** | Updates BetterDiscord custom CSS with Pywal colors by combining `colors.css`, header, and template. | ⚠️ Overwrites `custom.css` in BetterDiscord — keep a backup of personal changes. |
|
||||
| **wlogout-pywalsync.sh** | Creates a blurred wallpaper background for Wlogout and updates its stylesheet with Pywal theme. | Needs **ImageMagick**. |
|
||||
| **scaler-wallpaper.sh** | Ensures all wallpapers are scaled to **1920×1080**. Converts to `.png` and moves the original file into `~/Pictures/.backup` with incremental naming. | You can adjust the resolution based on your monitor resolution. |
|
||||
> <details>
|
||||
> <summary>📸 Show Screenshot</summary>
|
||||
>
|
||||
>
|
||||
>
|
||||
> </details>
|
||||
|
||||
# Applications
|
||||
---
|
||||
</details>
|
||||
|
||||
These are the main applications I use in my setup, most of which are themed with **Pywal** for consistency.
|
||||
|
||||
| Application | Purpose / Usage | Notes |
|
||||
|---|---|---|
|
||||
| **BetterDiscord** | Extends the Discord client with plugins and themes for customization. | ⚠️ Third-party mod, not officially supported by Discord. |
|
||||
| **btop** | A modern terminal resource monitor for CPU, memory, disks, and processes. | Config in `~/.config/btop/`. |
|
||||
| **Cava** | Terminal-based audio visualizer. | Integrated with Pywal for dynamic color themes. |
|
||||
| **Fastfetch** | Fast system information tool (like neofetch). | Config in `~/.config/fastfetch/config.jsonc`. |
|
||||
| **Pywal** | Generates color palettes from wallpapers and applies them system-wide. | Use my `wallpaper-picker.sh` and the hooks for auto theming according to the wallpaper. |
|
||||
| **Pywalfox** | Browser extension and companion script that syncs Pywal colors to Firefox and Librewolf. | Needs the `pywalfox` helper script installed (`pip install pywalfox`). |
|
||||
| **WezTerm** | GPU-accelerated terminal emulator with Lua configuration. | Config in `~/.config/wezterm/wezterm.lua`. |
|
||||
| **Wlogout** | A simple logout/shutdown/suspend menu. | Config in `~/.config/wlogout/`. |
|
||||
| **Wofi** | Application launcher and dmenu replacement. | Config in `~/.config/wofi/`. |
|
||||
<details>
|
||||
<summary>🎛️ Just Perfection</summary>
|
||||
|
||||
### Description
|
||||
Tweak Tool to Customize GNOME Shell, Change the Behavior and Disable UI Elements.
|
||||
|
||||
### Settings
|
||||
You can follow my settings to get the same style in screenshot below, or you can tweak it as you will.
|
||||
|
||||
> <details>
|
||||
> <summary>📸 Show Screenshot</summary>
|
||||
>
|
||||
>
|
||||
>
|
||||
> </details>
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>🎵 Media Controls</summary>
|
||||
|
||||
### Description
|
||||
Show controls and information of the currently playing media in the panel.
|
||||
|
||||
### Settings
|
||||
You can follow my settings to get the same style in screenshot below, or you can tweak it as you will.
|
||||
|
||||
> <details>
|
||||
> <summary>📸 Show Screenshot</summary>
|
||||
>
|
||||
>
|
||||
>
|
||||
> </details>
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
<details>
|
||||
<summary>🍹 Open Bar</summary>
|
||||
|
||||
### Description
|
||||
Top Bar / Top Panel , Menus , Dash / Dock , Gnome Shell , Gtk Apps theming. Open the bar and let the colors flow.
|
||||
|
||||
### Settings
|
||||
You can follow my settings to get the same style in screenshot below, or you can tweak it as you will.
|
||||
|
||||
> <details>
|
||||
> <summary>📸 Show Screenshot</summary>
|
||||
>
|
||||
>
|
||||
>
|
||||
> </details>
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
<details>
|
||||
<summary>⚡ Quick Settings Tweaks</summary>
|
||||
|
||||
### Description
|
||||
Enhances the quick settings menu with extra toggles and controls.
|
||||
|
||||
### Settings
|
||||
You can follow my settings to get the same style in screenshot below, or you can tweak it as you will.
|
||||
|
||||
> <details>
|
||||
> <summary>📸 Show Screenshot</summary>
|
||||
>
|
||||
>
|
||||
>
|
||||
> </details>
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Other cool extension</summary>
|
||||
|
||||
- Clipboard Indicator : The most popular clipboard manager for GNOME
|
||||
|
||||
- Workspace Indicator : Put an indicator on the panel signaling in which workspace you are, and give you the possibility of switching to another one.
|
||||
|
||||
- Move Workspace Indicator : Replace native Activities Indicator by Workspace Indicator. Nothing else. Obviously, you have to install and activate official Workspace Indicator extension.
|
||||
|
||||
- System Monitor : Monitor system from the top bar
|
||||
|
||||
- Unblank lock screen : Unblank lock screen. Helping for ricing showcase
|
||||
</details>
|
||||
|
||||
|
||||
## Applications
|
||||
<details>
|
||||
<summary>🎨 BetterDiscord</summary>
|
||||
|
||||
### Overview
|
||||
- ✔️ BetterDiscord is a client mod with endless flexibility and addons. The only limit to the customization is your own imagination.
|
||||
- 🧩 Extending the platform is as easy as clicking install on a plugin or theme.
|
||||
- 🎨 BetterDiscord will help you have a beautiful and more useful user experience on Discord.
|
||||
|
||||
### Installation
|
||||
1. Install [BetterDiscord](https://docs.betterdiscord.app/users/getting-started/installation#manual-installation).
|
||||
2. Restart Discord.
|
||||
|
||||
### Configuration
|
||||
1. On Settings > BetterDiscord > Enable Custom CSS and Enable Transparency.
|
||||
2. Paste the `.config/BetterDiscord` into `~/.config/BetterDiscord`
|
||||
3. `discord-pywalsync.sh` is used to overwrite the css color with pywal generated color palette. The script calls as a hook when running `wallpaper-picker.sh` so it automatically matches the color whenever you change your wallpaper.
|
||||
|
||||
### Notes
|
||||
⚠️ BetterDiscord is third-party and not officially supported by Discord. Use at your own risk.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>📊 btop</summary>
|
||||
|
||||
### Overview
|
||||
Resource monitor that shows usage and stats for processor, memory, disks, network and processes.
|
||||
|
||||
### Installation
|
||||
1. Install [btop](https://github.com/aristocratos/btop?tab=readme-ov-file#installation)
|
||||
|
||||
### Configuration
|
||||
1. Paste the `.config/btop` into `~/.config/btop`
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🎶 Cava</summary>
|
||||
|
||||
### Overview
|
||||
Cross-platform Audio Visualizer. Cava is a bar spectrum audio visualizer for terminal or desktop (SDL).
|
||||
|
||||
### Installation
|
||||
1. Install [cava](https://github.com/karlstav/cava?tab=readme-ov-file#installing)
|
||||
|
||||
### Configuration
|
||||
1. Paste the `.config/cava` into `~/.config/cava`
|
||||
2. `cava-pywalsync.sh` is used to match the cava color with pywal generated color palette. The script calls as a hook when running `wallpaper-picker.sh` so it automatically matches the color whenever you change your wallpaper.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>⚡ Fastfetch</summary>
|
||||
|
||||
### Overview
|
||||
Fastfetch is a neofetch-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and SunOS.
|
||||
|
||||
### Installation
|
||||
1. Install [fastfetch](https://github.com/fastfetch-cli/fastfetch?tab=readme-ov-file#installation)
|
||||
|
||||
### Configuration
|
||||
1. Paste the `.config/fastfetch` into `~/.config/fastfetch`
|
||||
2. `wallpaper-picker.sh` changes the logo and the wife name, based on the wallpaper name. i.e. if the file name is `Arknight_Theresa`, then the logo will set into `john_arknight` and the wife name into `Theresa`.
|
||||
3. `fastfetch_auto.sh` is a script that detects the change of fastfetch config and reloads it, so you don’t have to call fastfetch multiple times.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🌈 Pywal</summary>
|
||||
|
||||
### Overview
|
||||
Pywal is a tool that generates a color palette from the dominant colors in an image. It then applies the colors system-wide and on-the-fly in all of your favourite programs.
|
||||
|
||||
### Installation
|
||||
1. Install [pywal](https://github.com/dylanaraps/pywal/wiki/Installation)
|
||||
|
||||
### Configuration
|
||||
1. Paste the `.config/wal` into `~/.config/wal`
|
||||
2. `wallpaper-picker.sh` calls pywal to generate color palette based on the wallpaper name (if the theme exists) or the dominant color of your wallpaper. i.e. I have `.config/wal/themes/Arknights.json` so if I set my wallpaper to `Arknights_Amiya` it applies the predefined theme rather than the dominant color of the wallpaper. It also calls `.config/wal/hooks/hooks.sh`.
|
||||
3. `hooks.sh` syncs the theme across all of the applications that are being used.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🖥️ WezTerm</summary>
|
||||
|
||||
### Overview
|
||||
WezTerm is a powerful cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust.
|
||||
|
||||
### Installation
|
||||
1. Install [wezterm](https://wezterm.org/installation)
|
||||
|
||||
### Configuration
|
||||
1. Paste the `.config/wezterm` into `~/.config/wezterm`
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🚪 Wlogout</summary>
|
||||
|
||||
### Overview
|
||||
A Wayland-based logout menu.
|
||||
|
||||
### Installation
|
||||
1. Install [wlogout](https://github.com/ArtsyMacaw/wlogout)
|
||||
|
||||
### Configuration
|
||||
1. Paste the `.config/wlogout` into `~/.config/wlogout`
|
||||
2. `wlogout-pywalsync.sh` is used to get your set wallpaper, and makes it blur to use as a background in wlogout.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🔍 Wofi</summary>
|
||||
|
||||
### Overview
|
||||
Wofi is a launcher/menu program for wlroots-based Wayland compositors such as sway.
|
||||
|
||||
### Installation
|
||||
1. Install [wofi](https://github.com/SimplyCEO/wofi?tab=readme-ov-file#building)
|
||||
|
||||
### Configuration
|
||||
1. Paste the `.config/wofi` into `~/.config/wofi`
|
||||
2. `wallpaper-picker.sh` calls wofi to show the thumbnail of available wallpapers — don’t forget to set the right path to your wallpaper directory.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>✨ Other cool applications</summary>
|
||||
|
||||
- 🎵 **kew**: Listen to music in the terminal.
|
||||
- 💻 **CMatrix**: A terminal screensaver that simulates the “Matrix rain” effect, just like in the movie.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## Script
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> To fully utilize the scripts, you need to install a few dependencies:
|
||||
>
|
||||
> ```
|
||||
> jq
|
||||
> ImageMagick
|
||||
> wofi
|
||||
> pywal
|
||||
> ```
|
||||
>
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>🖼 wallpaper-picker.sh</summary>
|
||||
|
||||
### What it does
|
||||
- Provides a wallpaper picker using **Wofi** with image thumbnails.
|
||||
- Automatically applies Pywal colors, updates GNOME wallpaper, changes Fastfetch logo, and runs hooks.
|
||||
- Integrates with **Pywalfox** to update Firefox theme.
|
||||
|
||||
### Notes on Color Backends
|
||||
Pywal supports different color extraction backends that slightly change the generated palette.
|
||||
You can switch backends by adding the `--backend` argument after `"$WAL_BIN" -i "$SELECTED"` in `wallpaper-picker.sh`.
|
||||
|
||||
- **wal (default)** → balanced palette, optimized for terminals.
|
||||
- **colorz** → stronger contrast, fewer dominant colors. (Looks great on Chiori wallpapers).
|
||||
- **haishoku** → softer palettes with lower contrast.
|
||||
|
||||
This setup is optimized for the default **wal** backend.
|
||||
However, you can further customize the derived color variables if you’re not satisfied with the results:
|
||||
|
||||
- 🎶 For **Cava**, change the gradient source in `cava-pywalsync.sh`, e.g.
|
||||
```bash
|
||||
COLOR1=$(jq -r '.colors.color8' "$WAL_COLORS")
|
||||
```
|
||||
to
|
||||
```bash
|
||||
COLOR1=$(jq -r '.colors.color5' "$WAL_COLORS")
|
||||
```
|
||||
|
||||
- 💬 For Discord, edit the variables inside `~/.config/BetterDiscord/bd-template.css`, replacing var(--color) with another Pywal color of your choice.
|
||||
|
||||
#### Installing extra backends
|
||||
|
||||
```bash
|
||||
pip install colorz
|
||||
pip install haishoku
|
||||
```
|
||||
|
||||
#### Color Backend Palette Comparison
|
||||
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🎶 cava-pywalsync.sh</summary>
|
||||
|
||||
### What it does
|
||||
- Syncs **Cava’s** gradient colors with Pywal’s generated color scheme.
|
||||
- Automatically restarts Cava to apply the new colors.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>💬 discord-pywalsync.sh</summary>
|
||||
|
||||
### What it does
|
||||
- Updates **BetterDiscord’s** `custom.css` file using Pywal colors.
|
||||
- Combines your header, Pywal CSS, and template into one file.
|
||||
- Ensures Discord follows the same color scheme as the rest of your rice.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🖼 scaler-wallpaper.sh</summary>
|
||||
|
||||
### What it does
|
||||
- Ensures all wallpapers are scaled to **1920x1080** resolution.
|
||||
- Converts them to `.png` format if necessary.
|
||||
- Moves the original wallpaper to `~/Pictures/.backup` with incremental names for safe keeping.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🚪 wlogout-pywalsync.sh</summary>
|
||||
|
||||
### What it does
|
||||
- Generates a blurred and darkened version of your current wallpaper for **Wlogout** background.
|
||||
- Updates `style.css` for Wlogout with Pywal colors.
|
||||
- Keeps logout menu consistent with your rice.
|
||||
|
||||
---
|
||||
</details>
|
||||
|
||||
|
||||
## Help
|
||||
|
||||
A quick setup
|
||||
|
||||
1. Clone the repo
|
||||
|
||||
2. Symlink the Config
|
||||
|
||||
3. Symlink the Script
|
||||
|
||||
3. Chmod the Script.
|
||||
|
||||
Later, I'll make the video on how to setup from the start (in nobara).
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
<div align=center>
|
||||
|
||||
[](https://git.io/typing-svg)
|
||||
|
||||
</div>
|
||||
BIN
images/better-discord/bd-1.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
images/better-discord/bd-2.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
images/blur-my-shell/blur_1.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
images/blur-my-shell/blur_2.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
images/blur-my-shell/blur_3.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
images/blur-my-shell/blur_4.png
Normal file
|
After Width: | Height: | Size: 196 KiB |
BIN
images/blur-my-shell/blur_5.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
images/btop/btop_1.png
Normal file
|
After Width: | Height: | Size: 927 KiB |
BIN
images/btop/btop_2.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
images/cava/cava_1.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
images/cava/cava_2.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
images/color-backend/cb_1_colorz.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
images/color-backend/cb_1_default.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
images/color-backend/cb_1_haishoku.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
images/color-backend/cb_2_colorz.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
images/color-backend/cb_2_default.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
images/color-backend/cb_haishoku.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
images/fastfetch/ff_1.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
images/fastfetch/ff_2.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
images/fastfetch/ff_3.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
images/forge/forge_1.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
images/forge/forge_2.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
images/just-perfection/jp_1.png
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
images/just-perfection/jp_2.png
Normal file
|
After Width: | Height: | Size: 209 KiB |
BIN
images/just-perfection/jp_3.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
images/just-perfection/jp_4.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
images/just-perfection/jp_5.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
images/media-controls/mc_1.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
images/media-controls/mc_2.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
images/media-controls/mc_3.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
images/media-controls/mc_4.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
images/media-controls/mc_5.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
images/open-bar/bar_1.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
images/open-bar/bar_2.png
Normal file
|
After Width: | Height: | Size: 167 KiB |
BIN
images/open-bar/bar_3.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
images/quick-settings-tweak/qs_1.png
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
images/quick-settings-tweak/qs_2.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
images/quick-settings-tweak/qs_3.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
images/quick-settings-tweak/qs_4.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
images/wlogout/wlogout_1.png
Normal file
|
After Width: | Height: | Size: 688 KiB |
BIN
images/wlogout/wlogout_2.png
Normal file
|
After Width: | Height: | Size: 631 KiB |
BIN
images/wofi/wofi_1.png
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
BIN
images/wofi/wofi_2.png
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
images/wofi/wofi_wallpaper_1.png
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
BIN
images/wofi/wofi_wallpaper_2.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |