From 00276cad7ceba8bb35c6cb551c866e8e2fe34f54 Mon Sep 17 00:00:00 2001 From: niko <57009359+hauntii@users.noreply.github.com> Date: Sat, 31 Aug 2024 21:45:57 -0400 Subject: [PATCH 1/8] TimeBarAllActivities: Support new activity cards (#2813) --- src/plugins/timeBarAllActivities/README.md | 5 ++ src/plugins/timeBarAllActivities/index.ts | 35 ---------- src/plugins/timeBarAllActivities/index.tsx | 76 ++++++++++++++++++++++ src/utils/constants.ts | 4 ++ 4 files changed, 85 insertions(+), 35 deletions(-) create mode 100644 src/plugins/timeBarAllActivities/README.md delete mode 100644 src/plugins/timeBarAllActivities/index.ts create mode 100644 src/plugins/timeBarAllActivities/index.tsx diff --git a/src/plugins/timeBarAllActivities/README.md b/src/plugins/timeBarAllActivities/README.md new file mode 100644 index 00000000..59f0451c --- /dev/null +++ b/src/plugins/timeBarAllActivities/README.md @@ -0,0 +1,5 @@ +# TimeBarAllActivities + +Adds the Spotify time bar to all activities if they have start and end timestamps. + +![](https://github.com/user-attachments/assets/9fbbe33c-8218-43c9-8b8d-f907a4e809fe) diff --git a/src/plugins/timeBarAllActivities/index.ts b/src/plugins/timeBarAllActivities/index.ts deleted file mode 100644 index dcb809fd..00000000 --- a/src/plugins/timeBarAllActivities/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2022 Vendicated and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ - -import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; - -export default definePlugin({ - name: "TimeBarAllActivities", - description: "Adds the Spotify time bar to all activities if they have start and end timestamps", - authors: [Devs.fawn], - patches: [ - { - find: "}renderTimeBar(", - replacement: { - match: /renderTimeBar\((.{1,3})\){.{0,50}?let/, - replace: "renderTimeBar($1){let" - } - } - ], -}); diff --git a/src/plugins/timeBarAllActivities/index.tsx b/src/plugins/timeBarAllActivities/index.tsx new file mode 100644 index 00000000..04f7ff9e --- /dev/null +++ b/src/plugins/timeBarAllActivities/index.tsx @@ -0,0 +1,76 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { findComponentByCodeLazy } from "@webpack"; + +interface Activity { + timestamps?: ActivityTimestamps; +} + +interface ActivityTimestamps { + start?: string; + end?: string; +} + +const ActivityTimeBar = findComponentByCodeLazy(".Millis.HALF_SECOND", ".bar", ".progress"); + +export const settings = definePluginSettings({ + hideActivityDetailText: { + type: OptionType.BOOLEAN, + description: "Hide the large title text next to the activity", + default: true, + }, + hideActivityTimerBadges: { + type: OptionType.BOOLEAN, + description: "Hide the timer badges next to the activity", + default: true, + } +}); + +export default definePlugin({ + name: "TimeBarAllActivities", + description: "Adds the Spotify time bar to all activities if they have start and end timestamps", + authors: [Devs.fawn, Devs.niko], + settings, + patches: [ + { + find: ".Messages.USER_ACTIVITY_PLAYING", + replacement: [ + // Insert Spotify time bar component + { + match: /\(0,.{0,30}activity:(\i),className:\i\.badges\}\)/g, + replace: "$&,$self.getTimeBar($1)" + }, + // Hide the large title on listening activities, to make them look more like Spotify (also visible from hovering over the large icon) + { + match: /(\i).type===(\i\.\i)\.WATCHING/, + replace: "($self.settings.store.hideActivityDetailText&&$self.isActivityTimestamped($1)&&$1.type===$2.LISTENING)||$&" + } + ] + }, + // Hide the "badge" timers that count the time since the activity starts + { + find: ".TvIcon).otherwise", + replacement: { + match: /null!==\(\i=null===\(\i=(\i)\.timestamps\).{0,50}created_at/, + replace: "($self.settings.store.hideActivityTimerBadges&&$self.isActivityTimestamped($1))?null:$&" + } + } + ], + + isActivityTimestamped(activity: Activity) { + return activity.timestamps != null && activity.timestamps.start != null && activity.timestamps.end != null; + }, + + getTimeBar(activity: Activity) { + if (this.isActivityTimestamped(activity)) { + return ; + } + } +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 77d3951b..ae1943b0 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -554,6 +554,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "SerStars", id: 861631850681729045n, }, + niko: { + name: "niko", + id: 341377368075796483n, + }, } satisfies Record); // iife so #__PURE__ works correctly From d5eaae9d5137d4eb2c77517080e1ee554ab2f5ba Mon Sep 17 00:00:00 2001 From: Obsidian <108832807+Obsidianninja11@users.noreply.github.com> Date: Sat, 31 Aug 2024 18:02:27 -0800 Subject: [PATCH 2/8] new plugin CopyFileContents ~Easily copy text file attachments contents (#2790) --- src/components/Icons.tsx | 8 ++-- src/plugins/CopyFileContents/README.md | 5 +++ src/plugins/CopyFileContents/index.tsx | 60 ++++++++++++++++++++++++++ src/plugins/CopyFileContents/style.css | 8 ++++ src/utils/constants.ts | 4 ++ 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 src/plugins/CopyFileContents/README.md create mode 100644 src/plugins/CopyFileContents/index.tsx create mode 100644 src/plugins/CopyFileContents/style.css diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx index 7ba078d3..fa142a18 100644 --- a/src/components/Icons.tsx +++ b/src/components/Icons.tsx @@ -65,8 +65,7 @@ export function LinkIcon({ height = 24, width = 24, className }: IconProps) { } /** - * Discord's copy icon, as seen in the user popout right of the username when clicking - * your own username in the bottom left user panel + * Discord's copy icon, as seen in the user panel popout on the right of the username and in large code blocks */ export function CopyIcon(props: IconProps) { return ( @@ -76,8 +75,9 @@ export function CopyIcon(props: IconProps) { viewBox="0 0 24 24" > - - + + + ); diff --git a/src/plugins/CopyFileContents/README.md b/src/plugins/CopyFileContents/README.md new file mode 100644 index 00000000..18dc2d3d --- /dev/null +++ b/src/plugins/CopyFileContents/README.md @@ -0,0 +1,5 @@ +# CopyFileContents + +Adds a button to text file attachments to copy their contents. + +![](https://github.com/user-attachments/assets/b1a0f6f4-106f-4953-94d9-4c5ef5810bca) diff --git a/src/plugins/CopyFileContents/index.tsx b/src/plugins/CopyFileContents/index.tsx new file mode 100644 index 00000000..13b64917 --- /dev/null +++ b/src/plugins/CopyFileContents/index.tsx @@ -0,0 +1,60 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./style.css"; + +import ErrorBoundary from "@components/ErrorBoundary"; +import { CopyIcon, NoEntrySignIcon } from "@components/Icons"; +import { Devs } from "@utils/constants"; +import { copyWithToast } from "@utils/misc"; +import definePlugin from "@utils/types"; +import { Tooltip, useState } from "@webpack/common"; + +const CheckMarkIcon = () => { + return + + ; +}; + +export default definePlugin({ + name: "CopyFileContents", + description: "Adds a button to text file attachments to copy their contents", + authors: [Devs.Obsidian, Devs.Nuckyz], + patches: [ + { + find: ".Messages.PREVIEW_BYTES_LEFT.format(", + replacement: { + match: /\.footerGap.+?url:\i,fileName:\i,fileSize:\i}\),(?<=fileContents:(\i),bytesLeft:(\i).+?)/g, + replace: "$&$self.addCopyButton({fileContents:$1,bytesLeft:$2})," + } + } + ], + + addCopyButton: ErrorBoundary.wrap(({ fileContents, bytesLeft }: { fileContents: string, bytesLeft: number; }) => { + const [recentlyCopied, setRecentlyCopied] = useState(false); + + return ( + 0 ? "File too large to copy" : "Copy File Contents"}> + {tooltipProps => ( +
{ + if (!recentlyCopied && bytesLeft <= 0) { + copyWithToast(fileContents); + setRecentlyCopied(true); + setTimeout(() => setRecentlyCopied(false), 2000); + } + }} + > + {recentlyCopied ? : bytesLeft > 0 ? : } +
+ )} +
+ ); + }, { noop: true }), +}); diff --git a/src/plugins/CopyFileContents/style.css b/src/plugins/CopyFileContents/style.css new file mode 100644 index 00000000..c643cf0f --- /dev/null +++ b/src/plugins/CopyFileContents/style.css @@ -0,0 +1,8 @@ +.vc-cfc-button { + color: var(--interactive-normal); + cursor: pointer; +} + +.vc-cfc-button:hover { + color: var(--interactive-hover); +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index ae1943b0..60c20325 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -550,6 +550,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Lumap", id: 585278686291427338n, }, + Obsidian: { + name: "Obsidian", + id: 683171006717755446n, + }, SerStars: { name: "SerStars", id: 861631850681729045n, From 81eabc7ee2504d501fe962d914961038831af496 Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Sat, 31 Aug 2024 22:13:22 -0400 Subject: [PATCH 3/8] PatchHelper: Add Copy as Codeblock button (#2820) --- src/components/VencordSettings/PatchHelperTab.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index e09a1dbf..fd33c09d 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -382,6 +382,7 @@ function PatchHelper() { Code + )} From 273981deb74039c0aa8252f5c981d21f7352a412 Mon Sep 17 00:00:00 2001 From: ImBanana Date: Sun, 1 Sep 2024 05:33:07 +0300 Subject: [PATCH 4/8] new plugin StickerPaste ~ Insert stickers instead of sending (#2781) --- src/plugins/stickerPaste/README.md | 3 +++ src/plugins/stickerPaste/index.ts | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/plugins/stickerPaste/README.md create mode 100644 src/plugins/stickerPaste/index.ts diff --git a/src/plugins/stickerPaste/README.md b/src/plugins/stickerPaste/README.md new file mode 100644 index 00000000..a2917027 --- /dev/null +++ b/src/plugins/stickerPaste/README.md @@ -0,0 +1,3 @@ +# StickerPaste + +Makes picking a sticker in the sticker picker insert it into the chatbox instead of instantly sending. diff --git a/src/plugins/stickerPaste/index.ts b/src/plugins/stickerPaste/index.ts new file mode 100644 index 00000000..8a008207 --- /dev/null +++ b/src/plugins/stickerPaste/index.ts @@ -0,0 +1,24 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "StickerPaste", + description: "Makes picking a sticker in the sticker picker insert it into the chatbox instead of instantly sending", + authors: [Devs.ImBanana], + + patches: [ + { + find: ".stickers,previewSticker:", + replacement: { + match: /if\(\i\.\i\.getUploadCount/, + replace: "return true;$&", + } + } + ] +}); From e07a4e19e65b556b0e398a39499b85b88699756b Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Sat, 31 Aug 2024 23:08:33 -0400 Subject: [PATCH 5/8] VolumeBooster: Support browser and Vesktop (#2730) --- src/plugins/volumeBooster/README.md | 9 +++ .../index.ts | 61 +++++++++++++++++-- src/utils/constants.ts | 4 ++ 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 src/plugins/volumeBooster/README.md rename src/plugins/{volumeBooster.discordDesktop => volumeBooster}/index.ts (64%) diff --git a/src/plugins/volumeBooster/README.md b/src/plugins/volumeBooster/README.md new file mode 100644 index 00000000..62bd5ab5 --- /dev/null +++ b/src/plugins/volumeBooster/README.md @@ -0,0 +1,9 @@ +# Volume Booster + +Allows you to boost the volume over 200% on desktop and over 100% on other clients. + +Works on users, bots, and streams! + +![the volume being moved up to 270% on vesktop](https://github.com/user-attachments/assets/793e012e-c069-4fa4-a3d5-61c2f55edd3e) + +![the volume being moved up to 297% on a stream](https://github.com/user-attachments/assets/77463eb9-2537-4821-a3ab-82f60633ccbc) diff --git a/src/plugins/volumeBooster.discordDesktop/index.ts b/src/plugins/volumeBooster/index.ts similarity index 64% rename from src/plugins/volumeBooster.discordDesktop/index.ts rename to src/plugins/volumeBooster/index.ts index b455c97e..3ab47b19 100644 --- a/src/plugins/volumeBooster.discordDesktop/index.ts +++ b/src/plugins/volumeBooster/index.ts @@ -31,10 +31,27 @@ const settings = definePluginSettings({ } }); +interface StreamData { + audioContext: AudioContext, + audioElement: HTMLAudioElement, + emitter: any, + // added by this plugin + gainNode?: GainNode, + id: string, + levelNode: AudioWorkletNode, + sinkId: string, + stream: MediaStream, + streamSourceNode?: MediaStreamAudioSourceNode, + videoStreamId: string, + _mute: boolean, + _speakingFlags: number, + _volume: number; +} + export default definePlugin({ name: "VolumeBooster", - authors: [Devs.Nuckyz], - description: "Allows you to set the user and stream volume above the default maximum.", + authors: [Devs.Nuckyz, Devs.sadan], + description: "Allows you to set the user and stream volume above the default maximum", settings, patches: [ @@ -45,12 +62,28 @@ export default definePlugin({ ].map(find => ({ find, replacement: { - match: /(?<=maxValue:\i\.\i)\?(\d+?):(\d+?)(?=,)/, - replace: (_, higherMaxVolume, minorMaxVolume) => "" - + `?${higherMaxVolume}*$self.settings.store.multiplier` - + `:${minorMaxVolume}*$self.settings.store.multiplier` + match: /(?<=maxValue:)\i\.\i\?(\d+?):(\d+?)(?=,)/, + replace: (_, higherMaxVolume, minorMaxVolume) => `${higherMaxVolume}*$self.settings.store.multiplier` } })), + // Patches needed for web/vesktop + { + find: "streamSourceNode", + predicate: () => IS_WEB, + group: true, + replacement: [ + // Remove rounding algorithm + { + match: /Math\.max.{0,30}\)\)/, + replace: "arguments[0]" + }, + // Patch the volume + { + match: /\.volume=this\._volume\/100;/, + replace: ".volume=0.00;$self.patchVolume(this);" + } + ] + }, // Prevent Audio Context Settings sync from trying to sync with values above 200, changing them to 200 before we send to Discord { find: "AudioContextSettingsMigrated", @@ -83,4 +116,20 @@ export default definePlugin({ ] } ], + + patchVolume(data: StreamData) { + if (data.stream.getAudioTracks().length === 0) return; + + data.streamSourceNode ??= data.audioContext.createMediaStreamSource(data.stream); + + if (!data.gainNode) { + const gain = data.gainNode = data.audioContext.createGain(); + data.streamSourceNode.connect(gain); + gain.connect(data.audioContext.destination); + } + + data.gainNode.gain.value = data._mute + ? 0 + : data._volume / 100; + } }); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 60c20325..7fb2b6bf 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -534,6 +534,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Joona", id: 297410829589020673n }, + sadan: { + name: "sadan", + id: 521819891141967883n, + }, Kylie: { name: "Cookie", id: 721853658941227088n From b595a3e33c4a39e67a36dc9041ed2aa90c30aaea Mon Sep 17 00:00:00 2001 From: vxray <41696677+vxray@users.noreply.github.com> Date: Sun, 1 Sep 2024 05:20:22 +0200 Subject: [PATCH 6/8] OpenInApp: Add support for localization in Spotify URL regex (#2776) --- src/plugins/openInApp/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/openInApp/index.ts b/src/plugins/openInApp/index.ts index 1a68e8f5..2e332465 100644 --- a/src/plugins/openInApp/index.ts +++ b/src/plugins/openInApp/index.ts @@ -33,7 +33,7 @@ interface URLReplacementRule { // Do not forget to add protocols to the ALLOWED_PROTOCOLS constant const UrlReplacementRules: Record = { spotify: { - match: /^https:\/\/open\.spotify\.com\/(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/, + match: /^https:\/\/open\.spotify\.com\/(?:intl-[a-z]{2}\/)?(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/, replace: (_, type, id) => `spotify://${type}/${id}`, description: "Open Spotify links in the Spotify app", shortlinkMatch: /^https:\/\/spotify\.link\/.+$/, From 968e688c106ea7b45c1bb522c73104239d3663a6 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 1 Sep 2024 00:53:46 -0300 Subject: [PATCH 7/8] Bump to 1.9.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0fdda265..65a2f451 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.9.8", + "version": "1.9.9", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 74fd85bd3d7a86c495534f08c6c508f379514b23 Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:39:19 -0400 Subject: [PATCH 8/8] VolumeBooster: Fix on Vesktop (#2828) Also fixed an IgnoreActivities patch and added a README to it --- .../README.md | 0 .../index.tsx | 0 .../style.css | 0 src/plugins/ignoreActivities/README.md | 13 +++++++++++++ src/plugins/ignoreActivities/index.tsx | 10 +++++++++- src/plugins/volumeBooster/index.ts | 2 +- 6 files changed, 23 insertions(+), 2 deletions(-) rename src/plugins/{CopyFileContents => copyFileContents}/README.md (100%) rename src/plugins/{CopyFileContents => copyFileContents}/index.tsx (100%) rename src/plugins/{CopyFileContents => copyFileContents}/style.css (100%) create mode 100644 src/plugins/ignoreActivities/README.md diff --git a/src/plugins/CopyFileContents/README.md b/src/plugins/copyFileContents/README.md similarity index 100% rename from src/plugins/CopyFileContents/README.md rename to src/plugins/copyFileContents/README.md diff --git a/src/plugins/CopyFileContents/index.tsx b/src/plugins/copyFileContents/index.tsx similarity index 100% rename from src/plugins/CopyFileContents/index.tsx rename to src/plugins/copyFileContents/index.tsx diff --git a/src/plugins/CopyFileContents/style.css b/src/plugins/copyFileContents/style.css similarity index 100% rename from src/plugins/CopyFileContents/style.css rename to src/plugins/copyFileContents/style.css diff --git a/src/plugins/ignoreActivities/README.md b/src/plugins/ignoreActivities/README.md new file mode 100644 index 00000000..abc75720 --- /dev/null +++ b/src/plugins/ignoreActivities/README.md @@ -0,0 +1,13 @@ +# IgnoreActivities + +Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings. + +![](https://github.com/user-attachments/assets/f0c19060-0ecf-4f1c-8165-a5aa40143c82) + +![](https://github.com/user-attachments/assets/73c3fa7a-5b90-41ee-a4d6-91fa76458b74) + +![](https://github.com/user-attachments/assets/1ab3fe73-3911-48d1-8a08-e976af614b41) + +The activity stays showing as a detected game even if ignored, differently from the stock Toggle Detection button from Discord: + +![](https://github.com/user-attachments/assets/08ea60c3-3a31-42de-ae4c-7535fbf1b45a) diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index 9e6c21bd..02261b5b 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -237,7 +237,7 @@ function isActivityTypeIgnored(type: number, id?: string) { export default definePlugin({ name: "IgnoreActivities", authors: [Devs.Nuckyz, Devs.Kylie], - description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below.", + description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below", dependencies: ["UserSettingsAPI"], settings, @@ -266,6 +266,7 @@ export default definePlugin({ replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),` } }, + // Discord has 3 different components for activities. Currently, the last is the one being used { find: ".activityTitleText,variant", replacement: { @@ -279,6 +280,13 @@ export default definePlugin({ match: /\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),/, replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),` } + }, + { + find: ".promotedLabelWrapperNonBanner,children", + replacement: { + match: /\.appDetailsHeaderContainer.+?children:\i.*?}\),(?<=application:(\i).+?)/, + replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),` + } } ], diff --git a/src/plugins/volumeBooster/index.ts b/src/plugins/volumeBooster/index.ts index 3ab47b19..02a955a8 100644 --- a/src/plugins/volumeBooster/index.ts +++ b/src/plugins/volumeBooster/index.ts @@ -69,7 +69,7 @@ export default definePlugin({ // Patches needed for web/vesktop { find: "streamSourceNode", - predicate: () => IS_WEB, + predicate: () => !IS_DISCORD_DESKTOP, group: true, replacement: [ // Remove rounding algorithm