docs : better documentation and uploading screenshot.

This commit is contained in:
SeraphimeZelel
2025-09-07 01:19:15 +07:00
parent aa093592e7
commit f583c4a3e4
88 changed files with 39362 additions and 167 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
}
}
}

View 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
}

View 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
}
}

View File

@@ -0,0 +1,7 @@
{
"Translucence": false,
"NotAnotherAnimeTheme": false,
"ClearVision": true,
"ClearVision V7 for BetterDiscord": false,
"Glass Wave": false
}

View 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"
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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 &middot; 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> &mdash;
<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>

View File

@@ -0,0 +1,7 @@
{
"currentVersionInfo": {
"version": "1.2.7",
"hasShownChangelog": true
},
"hasShownV2Modal": true
}

File diff suppressed because one or more lines are too long

View 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 @*/

View File

@@ -0,0 +1,13 @@
{
"all": {
"general": {
"addTotalAmount": true,
"addBlockedCategory": true,
"addIgnoredCategory": true,
"addFavorizedCategory": true,
"addHiddenCategory": true,
"addSortOptions": true,
"addMutualGuild": true
}
}
}

View 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));
})();

View File

@@ -0,0 +1,6 @@
{
"currentVersionInfo": {
"version": "0.10.4",
"hasShownChangelog": true
}
}

View 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
}
}
}

File diff suppressed because it is too large Load Diff

View 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;
}`);
}
}

View File

@@ -0,0 +1,3 @@
{
"version": "0.1.0"
}

View 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@*/

View 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
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
{
"currentVersionInfo": {
"version": "2.2.24",
"hasShownChangelog": true
}
}

View 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
}
}
}

View 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));
})();

View 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;

View File

@@ -0,0 +1,6 @@
{
"currentVersionInfo": {
"version": "2.0.23",
"hasShownChangelog": true
}
}

View File

@@ -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

View File

@@ -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'

View File

@@ -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"
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 647 KiB

After

Width:  |  Height:  |  Size: 669 KiB

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
View 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
View 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
View File

@@ -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">
[![Typing SVG](https://readme-typing-svg.demolab.com?font=JetBrains+Mono&size=30&duration=2000&pause=2500&color=9A0000&center=true&vCenter=true&width=435&lines=Rion's+Dotfiles)](https://git.io/typing-svg)
</div>
# Showcase
[![Watch the video](https://img.youtube.com/vi/tJPglNR69ok/hqdefault.jpg)](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 dont 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 Cavas gradient colors with Pywals palette. Automatically restarts Cava if its 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 dont 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 — dont 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 youre 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 **Cavas** gradient colors with Pywals generated color scheme.
- Automatically restarts Cava to apply the new colors.
---
</details>
<details>
<summary>💬 discord-pywalsync.sh</summary>
### What it does
- Updates **BetterDiscords** `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>
[![Typing SVG](https://readme-typing-svg.demolab.com?font=JetBrains+Mono&size=30&duration=2000&pause=2500&color=DFCB00&center=true&vCenter=true&width=435&lines=Endfield+Copium+Corner)](https://git.io/typing-svg)
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

BIN
images/btop/btop_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 KiB

BIN
images/btop/btop_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
images/cava/cava_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
images/cava/cava_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
images/fastfetch/ff_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
images/fastfetch/ff_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
images/fastfetch/ff_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
images/forge/forge_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

BIN
images/forge/forge_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
images/open-bar/bar_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
images/open-bar/bar_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

BIN
images/open-bar/bar_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 KiB

BIN
images/wofi/wofi_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

BIN
images/wofi/wofi_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB