You are using an outdated version of Vencord! Chances are, your issue is already fixed.
-
- Please first update using the Updater Page in Settings, or use the VencordInstaller (Update Vencord Button)
- to do so, in case you can't access the Updater page.
+
+ Please first update before asking for support!
+ You are using an externally updated Vencord version, which we do not provide support for!
+
+ Please either switch to an officially supported version of Vencord, or
+ contact your package maintainer for support instead.
+
+
+ You are using a fork of Vencord, which we do not provide support for!
+
+ Please either switch to an officially supported version of Vencord, or
+ contact your package maintainer for support instead.
+
+
,
+ onCloseCallback: () => setTimeout(() => NavigationRouter.back(), 50)
+ });
+ }
+ }
+ },
+
+ ContributorDmWarningCard: ErrorBoundary.wrap(({ userId }) => {
+ if (!isPluginDev(userId)) return null;
+ if (RelationshipStore.isFriend(userId)) return null;
+
+ return (
+
+ Please do not private message Vencord plugin developers for support!
+
+ Instead, use the Vencord support channel: {Parser.parse("https://discord.com/channels/1015060230222131221/1026515880080842772")}
+ {!ChannelStore.getChannel(SUPPORT_CHANNEL_ID) && " (Click the link to join)"}
+
+ );
+ }, { noop: true })
});
diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx
index 70e4070c..0b06d035 100644
--- a/src/plugins/betterFolders/index.tsx
+++ b/src/plugins/betterFolders/index.tsx
@@ -127,7 +127,7 @@ export default definePlugin({
},
// If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children
{
- match: /unreadMentionsIndicatorBottom,barClassName.+?}\)\]/,
+ match: /unreadMentionsIndicatorBottom,.+?}\)\]/,
replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0].isBetterFolders))"
},
// Export the isBetterFolders variable to the folders component
diff --git a/src/plugins/lastfm/index.tsx b/src/plugins/lastfm/index.tsx
index 5dfec8a3..1213ece2 100644
--- a/src/plugins/lastfm/index.tsx
+++ b/src/plugins/lastfm/index.tsx
@@ -77,7 +77,8 @@ const enum NameFormat {
ArtistFirst = "artist-first",
SongFirst = "song-first",
ArtistOnly = "artist",
- SongOnly = "song"
+ SongOnly = "song",
+ AlbumName = "album"
}
const applicationId = "1108588077900898414";
@@ -147,6 +148,10 @@ const settings = definePluginSettings({
{
label: "Use song name only",
value: NameFormat.SongOnly
+ },
+ {
+ label: "Use album name (falls back to custom status text if song has no album)",
+ value: NameFormat.AlbumName
}
],
},
@@ -313,6 +318,8 @@ export default definePlugin({
return trackData.artist;
case NameFormat.SongOnly:
return trackData.name;
+ case NameFormat.AlbumName:
+ return trackData.album || settings.store.statusName;
default:
return settings.store.statusName;
}
diff --git a/src/plugins/messageLatency/README.md b/src/plugins/messageLatency/README.md
new file mode 100644
index 00000000..8d2a776c
--- /dev/null
+++ b/src/plugins/messageLatency/README.md
@@ -0,0 +1,31 @@
+# MessageLatency
+
+Displays an indicator for messages that took ≥n seconds to send.
+
+> **NOTE**
+>
+> - This plugin only applies to messages received after opening the channel
+> - False positives can exist if the user's system clock has drifted.
+> - Grouped messages only display latency of the first message
+
+## Demo
+
+### Chat View
+
+
+
+### Clock -ve Drift
+
+
+
+### Clock +ve Drift
+
+
+
+### Connection Delay
+
+
+
+### Icons
+
+
diff --git a/src/plugins/messageLatency/index.tsx b/src/plugins/messageLatency/index.tsx
new file mode 100644
index 00000000..0b6d7503
--- /dev/null
+++ b/src/plugins/messageLatency/index.tsx
@@ -0,0 +1,147 @@
+/*
+ * 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 ErrorBoundary from "@components/ErrorBoundary";
+import { Devs } from "@utils/constants";
+import { isNonNullish } from "@utils/guards";
+import definePlugin, { OptionType } from "@utils/types";
+import { findExportedComponentLazy } from "@webpack";
+import { SnowflakeUtils, Tooltip } from "@webpack/common";
+import { Message } from "discord-types/general";
+
+type FillValue = ("status-danger" | "status-warning" | "text-muted");
+type Fill = [FillValue, FillValue, FillValue];
+type DiffKey = keyof Diff;
+
+interface Diff {
+ days: number,
+ hours: number,
+ minutes: number,
+ seconds: number;
+}
+
+const HiddenVisually = findExportedComponentLazy("HiddenVisually");
+
+export default definePlugin({
+ name: "MessageLatency",
+ description: "Displays an indicator for messages that took ≥n seconds to send",
+ authors: [Devs.arHSM],
+ settings: definePluginSettings({
+ latency: {
+ type: OptionType.NUMBER,
+ description: "Threshold in seconds for latency indicator",
+ default: 2
+ }
+ }),
+ patches: [
+ {
+ find: "showCommunicationDisabledStyles",
+ replacement: {
+ match: /(message:(\i),avatar:\i,username:\(0,\i.jsxs\)\(\i.Fragment,\{children:\[)(\i&&)/,
+ replace: "$1$self.Tooltip()({ message: $2 }),$3"
+ }
+ }
+ ],
+ stringDelta(delta: number) {
+ const diff: Diff = {
+ days: Math.round(delta / (60 * 60 * 24)),
+ hours: Math.round((delta / (60 * 60)) % 24),
+ minutes: Math.round((delta / (60)) % 60),
+ seconds: Math.round(delta % 60),
+ };
+
+ const str = (k: DiffKey) => diff[k] > 0 ? `${diff[k]} ${k}` : null;
+ const keys = Object.keys(diff) as DiffKey[];
+
+ return keys.map(str).filter(isNonNullish).join(" ") || "0 seconds";
+ },
+ latencyTooltipData(message: Message) {
+ const { id, nonce } = message;
+
+ // Message wasn't received through gateway
+ if (!isNonNullish(nonce)) return null;
+
+ const delta = Math.round((SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce)) / 1000);
+
+ // Thanks dziurwa (I hate you)
+ // This is when the user's clock is ahead
+ // Can't do anything if the clock is behind
+ const abs = Math.abs(delta);
+ const ahead = abs !== delta;
+
+ const stringDelta = this.stringDelta(abs);
+
+ // Also thanks dziurwa
+ // 2 minutes
+ const TROLL_LIMIT = 2 * 60;
+ const { latency } = this.settings.store;
+
+ const fill: Fill = delta >= TROLL_LIMIT || ahead ? ["text-muted", "text-muted", "text-muted"] : delta >= (latency * 2) ? ["status-danger", "text-muted", "text-muted"] : ["status-warning", "status-warning", "text-muted"];
+
+ return abs >= latency ? { delta: stringDelta, ahead: abs !== delta, fill } : null;
+ },
+ Tooltip() {
+ return ErrorBoundary.wrap(({ message }: { message: Message; }) => {
+
+ const d = this.latencyTooltipData(message);
+
+ if (!isNonNullish(d)) return null;
+
+ return
+ {
+ props => <>
+ {}
+ {/* Time Out indicator uses this, I think this is for a11y */}
+ Delayed Message
+ >
+ }
+ ;
+ });
+ },
+ Icon({ delta, fill, props }: {
+ delta: string;
+ fill: Fill,
+ props: {
+ onClick(): void;
+ onMouseEnter(): void;
+ onMouseLeave(): void;
+ onContextMenu(): void;
+ onFocus(): void;
+ onBlur(): void;
+ "aria-label"?: string;
+ };
+ }) {
+ return ;
+ }
+});
diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx
index 95234d5a..731e4b84 100644
--- a/src/plugins/messageLogger/index.tsx
+++ b/src/plugins/messageLogger/index.tsx
@@ -255,7 +255,7 @@ export default definePlugin({
replace: "$1" +
".update($3,m =>" +
" (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message, true)) ? m :" +
- " $2.message.content !== m.content ?" +
+ " $2.message.content !== m.editHistory?.[0]?.content && $2.message.content !== m.content ?" +
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" +
" m" +
")" +
diff --git a/src/plugins/replyTimestamp/README.md b/src/plugins/replyTimestamp/README.md
new file mode 100644
index 00000000..b7952bf3
--- /dev/null
+++ b/src/plugins/replyTimestamp/README.md
@@ -0,0 +1,5 @@
+# ReplyTimestamp
+
+Shows timestamps on the previews of replied-to messages. Pretty simple.
+
+
diff --git a/src/plugins/replyTimestamp/index.tsx b/src/plugins/replyTimestamp/index.tsx
new file mode 100644
index 00000000..05ec28b1
--- /dev/null
+++ b/src/plugins/replyTimestamp/index.tsx
@@ -0,0 +1,77 @@
+/*
+ * 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 { Devs } from "@utils/constants";
+import definePlugin from "@utils/types";
+import { findByPropsLazy } from "@webpack";
+import { Timestamp } from "@webpack/common";
+import type { Message } from "discord-types/general";
+import type { HTMLAttributes } from "react";
+
+const { getMessageTimestampId } = findByPropsLazy("getMessageTimestampId");
+const { calendarFormat, dateFormat, isSameDay } = findByPropsLazy("calendarFormat", "dateFormat", "isSameDay", "accessibilityLabelCalendarFormat");
+const MessageClasses = findByPropsLazy("separator", "latin24CompactTimeStamp");
+
+function Sep(props: HTMLAttributes) {
+ return ;
+}
+
+const enum ReferencedMessageState {
+ LOADED = 0,
+ NOT_LOADED = 1,
+ DELETED = 2,
+}
+
+type ReferencedMessage = { state: ReferencedMessageState.LOADED; message: Message; } | { state: ReferencedMessageState.NOT_LOADED | ReferencedMessageState.DELETED; };
+
+function ReplyTimestamp({
+ referencedMessage,
+ baseMessage,
+}: {
+ referencedMessage: ReferencedMessage,
+ baseMessage: Message;
+}) {
+ if (referencedMessage.state !== ReferencedMessageState.LOADED) return null;
+ const refTimestamp = referencedMessage.message.timestamp as any;
+ const baseTimestamp = baseMessage.timestamp as any;
+ return (
+
+ [
+ {isSameDay(refTimestamp, baseTimestamp)
+ ? dateFormat(refTimestamp, "LT")
+ : calendarFormat(refTimestamp)
+ }
+ ]
+
+ );
+}
+
+export default definePlugin({
+ name: "ReplyTimestamp",
+ description: "Shows a timestamp on replied-message previews",
+ authors: [Devs.Kyuuhachi],
+
+ patches: [
+ {
+ find: "renderSingleLineMessage:function()",
+ replacement: {
+ match: /(?<="aria-label":\i,children:\[)(?=\i,\i,\i\])/,
+ replace: "$self.ReplyTimestamp(arguments[0]),"
+ }
+ }
+ ],
+
+ ReplyTimestamp: ErrorBoundary.wrap(ReplyTimestamp, { noop: true }),
+});
diff --git a/src/plugins/replyTimestamp/style.css b/src/plugins/replyTimestamp/style.css
new file mode 100644
index 00000000..f4237171
--- /dev/null
+++ b/src/plugins/replyTimestamp/style.css
@@ -0,0 +1,3 @@
+.vc-reply-timestamp {
+ margin-right: 0.25em;
+}
diff --git a/src/plugins/showHiddenThings/index.ts b/src/plugins/showHiddenThings/index.ts
index e7be929b..1858582a 100644
--- a/src/plugins/showHiddenThings/index.ts
+++ b/src/plugins/showHiddenThings/index.ts
@@ -31,12 +31,22 @@ const settings = definePluginSettings({
description: "Show the invites paused tooltip in the server list.",
default: true,
},
+ showModView: {
+ type: OptionType.BOOLEAN,
+ description: "Show the member mod view context menu item in all servers.",
+ default: true,
+ },
+ disableDiscoveryFilters: {
+ type: OptionType.BOOLEAN,
+ description: "Disable filters in Server Discovery search that hide servers that don't meet discovery criteria.",
+ default: true,
+ },
});
migratePluginSettings("ShowHiddenThings", "ShowTimeouts");
export default definePlugin({
name: "ShowHiddenThings",
- tags: ["ShowTimeouts", "ShowInvitesPaused"],
+ tags: ["ShowTimeouts", "ShowInvitesPaused", "ShowModView", "DisableDiscoveryFilters"],
description: "Displays various moderator-only elements regardless of permissions.",
authors: [Devs.Dolfies],
patches: [
@@ -55,6 +65,22 @@ export default definePlugin({
match: /\i\.\i\.can\(\i\.Permissions.MANAGE_GUILD,\i\)/,
replace: "true",
},
+ },
+ {
+ find: "canAccessGuildMemberModViewWithExperiment:",
+ predicate: () => settings.store.showModView,
+ replacement: {
+ match: /return \i\.hasAny\(\i\.computePermissions\(\{user:\i,context:\i,checkElevated:!1\}\),\i\.MemberSafetyPagePermissions\)/,
+ replace: "return true",
+ }
+ },
+ {
+ find: "auto_removed:",
+ predicate: () => settings.store.disableDiscoveryFilters,
+ replacement: {
+ match: /filters:\i\.join\(" AND "\),facets:\[/,
+ replace: "facets:["
+ }
}
],
settings,
diff --git a/src/plugins/voiceDownload/index.tsx b/src/plugins/voiceDownload/index.tsx
new file mode 100644
index 00000000..8586b9f9
--- /dev/null
+++ b/src/plugins/voiceDownload/index.tsx
@@ -0,0 +1,52 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import "./style.css";
+
+import { Devs } from "@utils/constants";
+import definePlugin from "@utils/types";
+
+export default definePlugin({
+ name: "VoiceDownload",
+ description: "Adds a download to voice messages. (Opens a new browser tab)",
+ authors: [Devs.puv],
+ patches: [
+ {
+ find: "rippleContainer,children",
+ replacement: {
+ match: /\(0,\i\.jsx\).{0,150},children:.{0,50}\("source",{src:(\i)}\)}\)/,
+ replace: "[$&, $self.renderDownload($1)]"
+ }
+ }
+ ],
+
+ renderDownload(src: string) {
+ return (
+ e.stopPropagation()}
+ aria-label="Download voice message"
+ >
+
+
+ );
+ },
+
+ Icon: () => (
+
+ ),
+});
diff --git a/src/plugins/voiceDownload/style.css b/src/plugins/voiceDownload/style.css
new file mode 100644
index 00000000..2b776023
--- /dev/null
+++ b/src/plugins/voiceDownload/style.css
@@ -0,0 +1,12 @@
+.vc-voice-download {
+ width: 24px;
+ height: 24px;
+ color: var(--interactive-normal);
+ margin-left: 12px;
+ cursor: pointer;
+ position: relative;
+}
+
+.vc-voice-download:hover {
+ color: var(--interactive-active);
+}
diff --git a/src/plugins/webScreenShareFixes.web/index.ts b/src/plugins/webScreenShareFixes.web/index.ts
new file mode 100644
index 00000000..8d1ab582
--- /dev/null
+++ b/src/plugins/webScreenShareFixes.web/index.ts
@@ -0,0 +1,30 @@
+/*
+ * 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: "WebScreenShareFixes",
+ authors: [Devs.Kaitlyn],
+ description: "Removes 2500kbps bitrate cap on chromium and vesktop clients.",
+ enabledByDefault: true,
+ patches: [
+ {
+ find: "x-google-max-bitrate",
+ replacement: [
+ {
+ match: /"x-google-max-bitrate=".concat\(\i\)/,
+ replace: '"x-google-max-bitrate=".concat("80_000")'
+ },
+ {
+ match: /;level-asymmetry-allowed=1/,
+ replace: ";b=AS:800000;level-asymmetry-allowed=1"
+ }
+ ]
+ }
+ ]
+});
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 8ef56066..ddeca008 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -268,6 +268,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "Dziurwa",
id: 1001086404203389018n
},
+ arHSM: {
+ name: "arHSM",
+ id: 841509053422632990n
+ },
F53: {
name: "F53",
id: 280411966126948353n
@@ -428,6 +432,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "newwares",
id: 421405303951851520n
},
+ puv: {
+ name: "puv",
+ id: 469441552251355137n
+ },
Kodarru: {
name: "Kodarru",
id: 785227396218748949n