From bf68a8a3e859f60d4c6dc47fd38c6ba4922f0661 Mon Sep 17 00:00:00 2001 From: Mia <62818119+xNasuni@users.noreply.github.com> Date: Sun, 8 Jun 2025 10:55:12 -0400 Subject: [PATCH 1/5] MessageClickActions: make delete key detection consistent on lost focus (#3470) Co-authored-by: Vending Machine --- src/plugins/messageClickActions/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/messageClickActions/index.ts b/src/plugins/messageClickActions/index.ts index 19ccaa95..a07c924a 100644 --- a/src/plugins/messageClickActions/index.ts +++ b/src/plugins/messageClickActions/index.ts @@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore } from "@webpack/common"; +import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore, WindowStore } from "@webpack/common"; const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage"); const EditStore = findByPropsLazy("isEditing", "isEditingAny"); @@ -28,6 +28,7 @@ const EditStore = findByPropsLazy("isEditing", "isEditingAny"); let isDeletePressed = false; const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true); const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false); +const focusChanged = () => !WindowStore.isFocused() && (isDeletePressed = false); const settings = definePluginSettings({ enableDeleteOnClick: { @@ -62,11 +63,13 @@ export default definePlugin({ start() { document.addEventListener("keydown", keydown); document.addEventListener("keyup", keyup); + WindowStore.addChangeListener(focusChanged); }, stop() { document.removeEventListener("keydown", keydown); document.removeEventListener("keyup", keyup); + WindowStore.removeChangeListener(focusChanged); }, onMessageClick(msg: any, channel, event) { From a386736dccfda73bf0ed2964108961a8b8286206 Mon Sep 17 00:00:00 2001 From: Cookie <52550063+Covkie@users.noreply.github.com> Date: Sun, 8 Jun 2025 11:47:59 -0400 Subject: [PATCH 2/5] WebScreenShareFixes: only apply stereo parameter to video audio (#3474) --- src/plugins/webScreenShareFixes.web/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/webScreenShareFixes.web/index.ts b/src/plugins/webScreenShareFixes.web/index.ts index 1d2be2c0..2616dd0c 100644 --- a/src/plugins/webScreenShareFixes.web/index.ts +++ b/src/plugins/webScreenShareFixes.web/index.ts @@ -25,8 +25,8 @@ export default definePlugin({ replace: ";b=AS:800000;level-asymmetry-allowed=1" }, { - match: "useinbandfec=1", - replace: "useinbandfec=1;stereo=1;sprop-stereo=1" + match: /;usedtx=".concat\((\i)\?"0":"1"\)/, + replace: '$&.concat($1?";stereo=1;sprop-stereo=1":"")' } ] } From 6d47a340b1c7106ccb7b5117abd9457ad7c4bdcb Mon Sep 17 00:00:00 2001 From: Vending Machine Date: Sun, 8 Jun 2025 19:45:52 +0200 Subject: [PATCH 3/5] QuickReply: correctly handle new & deleted messages (#3473) --- src/plugins/messageClickActions/index.ts | 6 +- src/plugins/quickReply/index.ts | 114 ++++++++++++----------- 2 files changed, 63 insertions(+), 57 deletions(-) diff --git a/src/plugins/messageClickActions/index.ts b/src/plugins/messageClickActions/index.ts index a07c924a..723ece12 100644 --- a/src/plugins/messageClickActions/index.ts +++ b/src/plugins/messageClickActions/index.ts @@ -21,6 +21,7 @@ import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore, WindowStore } from "@webpack/common"; +import NoReplyMentionPlugin from "plugins/noReplyMention"; const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage"); const EditStore = findByPropsLazy("isEditing", "isEditingAny"); @@ -92,9 +93,8 @@ export default definePlugin({ if (msg.hasFlag(EPHEMERAL)) return; const isShiftPress = event.shiftKey && !settings.store.requireModifier; - const NoReplyMention = Vencord.Plugins.plugins.NoReplyMention as any as typeof import("../noReplyMention").default; - const shouldMention = Vencord.Plugins.isPluginEnabled("NoReplyMention") - ? NoReplyMention.shouldMention(msg, isShiftPress) + const shouldMention = Vencord.Plugins.isPluginEnabled(NoReplyMentionPlugin.name) + ? NoReplyMentionPlugin.shouldMention(msg, isShiftPress) : !isShiftPress; FluxDispatcher.dispatch({ diff --git a/src/plugins/quickReply/index.ts b/src/plugins/quickReply/index.ts index f6ca5b45..5a6b45f9 100644 --- a/src/plugins/quickReply/index.ts +++ b/src/plugins/quickReply/index.ts @@ -16,19 +16,20 @@ * along with this program. If not, see . */ -import { definePluginSettings, Settings } from "@api/Settings"; +import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { ChannelStore, ComponentDispatch, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common"; +import { ChannelStore, ComponentDispatch, FluxDispatcher as Dispatcher, MessageActions, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common"; import { Message } from "discord-types/general"; +import NoBlockedMessagesPlugin from "plugins/noBlockedMessages"; +import NoReplyMentionPlugin from "plugins/noReplyMention"; -const Kangaroo = findByPropsLazy("jumpToMessage"); const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked"); const isMac = navigator.platform.includes("Mac"); // bruh -let replyIdx = -1; -let editIdx = -1; +let currentlyReplyingId: string | null = null; +let currentlyEditingId: string | null = null; const enum MentionOptions { @@ -69,36 +70,29 @@ export default definePlugin({ flux: { DELETE_PENDING_REPLY() { - replyIdx = -1; + currentlyReplyingId = null; }, MESSAGE_END_EDIT() { - editIdx = -1; + currentlyEditingId = null; + }, + CHANNEL_SELECT() { + currentlyReplyingId = null; + currentlyEditingId = null; }, MESSAGE_START_EDIT: onStartEdit, CREATE_PENDING_REPLY: onCreatePendingReply } }); -function calculateIdx(messages: Message[], id: string) { - const idx = messages.findIndex(m => m.id === id); - return idx === -1 - ? idx - : messages.length - idx - 1; -} - -function onStartEdit({ channelId, messageId, _isQuickEdit }: any) { +function onStartEdit({ messageId, _isQuickEdit }: any) { if (_isQuickEdit) return; - - const meId = UserStore.getCurrentUser().id; - - const messages = MessageStore.getMessages(channelId)._array.filter(m => m.author.id === meId); - editIdx = calculateIdx(messages, messageId); + currentlyEditingId = messageId; } function onCreatePendingReply({ message, _isQuickReply }: { message: Message; _isQuickReply: boolean; }) { if (_isQuickReply) return; - replyIdx = calculateIdx(MessageStore.getMessages(message.channel_id)._array, message.id); + currentlyReplyingId = message.id; } const isCtrl = (e: KeyboardEvent) => isMac ? e.metaKey : e.ctrlKey; @@ -123,10 +117,10 @@ function jumpIfOffScreen(channelId: string, messageId: string) { const vh = Math.max(document.documentElement.clientHeight, window.innerHeight); const rect = element.getBoundingClientRect(); - const isOffscreen = rect.bottom < 200 || rect.top - vh >= -200; + const isOffscreen = rect.bottom < 150 || rect.top - vh >= -150; if (isOffscreen) { - Kangaroo.jumpToMessage({ + MessageActions.jumpToMessage({ channelId, messageId, flash: false, @@ -137,43 +131,48 @@ function jumpIfOffScreen(channelId: string, messageId: string) { function getNextMessage(isUp: boolean, isReply: boolean) { let messages: Array = MessageStore.getMessages(SelectedChannelStore.getChannelId())._array; - if (!isReply) { // we are editing so only include own - const meId = UserStore.getCurrentUser().id; - messages = messages.filter(m => m.author.id === meId); - } - if (Vencord.Plugins.isPluginEnabled("NoBlockedMessages")) { - messages = messages.filter(m => !RelationshipStore.isBlocked(m.author.id)); - } + const meId = UserStore.getCurrentUser().id; + const hasNoBlockedMessages = Vencord.Plugins.isPluginEnabled(NoBlockedMessagesPlugin.name); - const mutate = (i: number) => isUp - ? Math.min(messages.length - 1, i + 1) - : Math.max(-1, i - 1); + messages = messages.filter(m => { + if (m.deleted) return false; + if (!isReply && m.author.id !== meId) return false; // editing only own messages + if (hasNoBlockedMessages && NoBlockedMessagesPlugin.shouldIgnoreMessage(m)) return false; - const findNextNonDeleted = (i: number) => { - do { - i = mutate(i); - } while (i !== -1 && messages[messages.length - i - 1]?.deleted === true); - return i; + return true; + }); + + const findNextNonDeleted = (id: string | null) => { + if (id === null) return messages[messages.length - 1]; + + const idx = messages.findIndex(m => m.id === id); + if (idx === -1) return messages[messages.length - 1]; + + const i = isUp ? idx - 1 : idx + 1; + return messages[i] ?? null; }; - let i: number; - if (isReply) - replyIdx = i = findNextNonDeleted(replyIdx); - else - editIdx = i = findNextNonDeleted(editIdx); - - return i === - 1 ? undefined : messages[messages.length - i - 1]; + if (isReply) { + const msg = findNextNonDeleted(currentlyReplyingId); + currentlyReplyingId = msg?.id ?? null; + return msg; + } else { + const msg = findNextNonDeleted(currentlyEditingId); + currentlyEditingId = msg?.id ?? null; + return msg; + } } -function shouldMention(message) { - const { enabled, userList, shouldPingListed } = Settings.plugins.NoReplyMention; - const shouldPing = !enabled || (shouldPingListed === userList.includes(message.author.id)); - +function shouldMention(message: Message) { switch (settings.store.shouldMention) { - case MentionOptions.NO_REPLY_MENTION_PLUGIN: return shouldPing; - case MentionOptions.DISABLED: return false; - default: return true; + case MentionOptions.NO_REPLY_MENTION_PLUGIN: + if (!Vencord.Plugins.isPluginEnabled(NoReplyMentionPlugin.name)) return true; + return NoReplyMentionPlugin.shouldMention(message, false); + case MentionOptions.DISABLED: + return false; + default: + return true; } } @@ -181,13 +180,16 @@ function shouldMention(message) { function nextReply(isUp: boolean) { const currChannel = ChannelStore.getChannel(SelectedChannelStore.getChannelId()); if (currChannel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, currChannel)) return; + const message = getNextMessage(isUp, true); - if (!message) + if (!message) { return void Dispatcher.dispatch({ type: "DELETE_PENDING_REPLY", channelId: SelectedChannelStore.getChannelId(), }); + } + const channel = ChannelStore.getChannel(message.channel_id); const meId = UserStore.getCurrentUser().id; @@ -199,6 +201,7 @@ function nextReply(isUp: boolean) { showMentionToggle: !channel.isPrivate() && message.author.id !== meId, _isQuickReply: true }); + ComponentDispatch.dispatchToLastSubscribed("TEXTAREA_FOCUS"); jumpIfOffScreen(channel.id, message.id); } @@ -209,11 +212,13 @@ function nextEdit(isUp: boolean) { if (currChannel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, currChannel)) return; const message = getNextMessage(isUp, false); - if (!message) + if (!message) { return Dispatcher.dispatch({ type: "MESSAGE_END_EDIT", channelId: SelectedChannelStore.getChannelId() }); + } + Dispatcher.dispatch({ type: "MESSAGE_START_EDIT", channelId: message.channel_id, @@ -221,5 +226,6 @@ function nextEdit(isUp: boolean) { content: message.content, _isQuickEdit: true }); + jumpIfOffScreen(message.channel_id, message.id); } From b19bb2b7afef5ed621e9a14a110d256ecaba9aa6 Mon Sep 17 00:00:00 2001 From: Vending Machine Date: Sun, 8 Jun 2025 20:10:30 +0200 Subject: [PATCH 4/5] Update README (#3472) --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 61575d4b..52d3c1b6 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,21 @@ # Vencord +![](https://img.shields.io/github/package-json/v/Vendicated/Vencord?style=for-the-badge&logo=github&logoColor=d3869b&label=&color=1d2021&labelColor=282828) [![Codeberg Mirror](https://img.shields.io/static/v1?style=for-the-badge&label=Codeberg%20Mirror&message=codeberg.org/Vee/cord&color=2185D0&logo=data:image/png;base64,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)](https://codeberg.org/Vee/cord) The cutest Discord client mod -| ![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) | -| :--------------------------------------------------------------------------------------------------: | -| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | +![](https://github.com/user-attachments/assets/3fac98c0-c411-4d2a-97a3-13b7da8687a2) ## Features -- Super easy to install (Download Installer, open, click install button, done) -- 100+ plugins built in: [See a list](https://vencord.dev/plugins) - - Some highlights: SpotifyControls, MessageLogger, Experiments, GameActivityToggle, Translate, NoTrack, QuickReply, Free Emotes/Stickers, PermissionsViewer, CustomCommands, ShowHiddenChannels, PronounDB +- Easy to install +- [100+ built in plugins](https://vencord.dev/plugins) - Fairly lightweight despite the many inbuilt plugins - Excellent Browser Support: Run Vencord in your Browser via extension or UserScript -- Works on any Discord branch: Stable, Canary or PTB all work (though for the best experience I recommend stable!) +- Works on any Discord branch: Stable, Canary or PTB all work - Custom CSS and Themes: Inbuilt css editor with support to import any css files (including BetterDiscord themes) -- Privacy friendly, blocks Discord analytics & crash reporting out of the box and has no telemetry +- Privacy friendly: blocks Discord analytics & crash reporting out of the box and has no telemetry - Maintained very actively, broken plugins are usually fixed within 12 hours - Settings sync: Keep your plugins and their settings synchronised between devices / apps (optional) From 18f2b49b6744792c52eb6ffb6f14bde52bbb1622 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 9 Jun 2025 01:56:04 +0200 Subject: [PATCH 5/5] fix loading themes with spaces in their name --- src/main/index.ts | 32 ++++++++++++++++++++------------ src/main/ipcMain.ts | 2 +- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index a001a490..95301ff7 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -16,8 +16,9 @@ * along with this program. If not, see . */ -import { app, protocol } from "electron"; +import { app, net, protocol } from "electron"; import { join } from "path"; +import { pathToFileURL } from "url"; import { initCsp } from "./csp"; import { ensureSafePath } from "./ipcMain"; @@ -27,21 +28,27 @@ import { installExt } from "./utils/extensions"; if (IS_VESKTOP || !IS_VANILLA) { app.whenReady().then(() => { - // Source Maps! Maybe there's a better way but since the renderer is executed - // from a string I don't think any other form of sourcemaps would work - protocol.registerFileProtocol("vencord", ({ url: unsafeUrl }, cb) => { - let url = unsafeUrl.slice("vencord://".length); + protocol.handle("vencord", ({ url: unsafeUrl }) => { + let url = decodeURI(unsafeUrl).slice("vencord://".length).replace(/\?v=\d+$/, ""); + if (url.endsWith("/")) url = url.slice(0, -1); + if (url.startsWith("/themes/")) { const theme = url.slice("/themes/".length); + const safeUrl = ensureSafePath(THEMES_DIR, theme); if (!safeUrl) { - cb({ statusCode: 403 }); - return; + return new Response(null, { + status: 404 + }); } - cb(safeUrl.replace(/\?v=\d+$/, "")); - return; + + return net.fetch(pathToFileURL(safeUrl).toString()); } + + // Source Maps! Maybe there's a better way but since the renderer is executed + // from a string I don't think any other form of sourcemaps would work + switch (url) { case "renderer.js.map": case "vencordDesktopRenderer.js.map": @@ -49,10 +56,11 @@ if (IS_VESKTOP || !IS_VANILLA) { case "vencordDesktopPreload.js.map": case "patcher.js.map": case "vencordDesktopMain.js.map": - cb(join(__dirname, url)); - break; + return net.fetch(pathToFileURL(join(__dirname, url)).toString()); default: - cb({ statusCode: 403 }); + return new Response(null, { + status: 404 + }); } }); diff --git a/src/main/ipcMain.ts b/src/main/ipcMain.ts index 62785867..3979a1bc 100644 --- a/src/main/ipcMain.ts +++ b/src/main/ipcMain.ts @@ -35,7 +35,7 @@ import { makeLinksOpenExternally } from "./utils/externalLinks"; mkdirSync(THEMES_DIR, { recursive: true }); export function ensureSafePath(basePath: string, path: string) { - const normalizedBasePath = normalize(basePath); + const normalizedBasePath = normalize(basePath + "/"); const newPath = join(basePath, path); const normalizedPath = normalize(newPath); return normalizedPath.startsWith(normalizedBasePath) ? normalizedPath : null;