Merge branch 'dev' into feat/usercss
This commit is contained in:
commit
c25e8ac8c1
37 changed files with 598 additions and 77 deletions
3
.github/workflows/test.yml
vendored
3
.github/workflows/test.yml
vendored
|
@ -1,9 +1,6 @@
|
|||
name: test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.6.4",
|
||||
"version": "1.6.5",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -76,7 +76,11 @@ const globNativesPlugin = {
|
|||
if (!await existsAsync(dirPath)) continue;
|
||||
const plugins = await readdir(dirPath);
|
||||
for (const p of plugins) {
|
||||
if (!await existsAsync(join(dirPath, p, "native.ts"))) continue;
|
||||
const nativePath = join(dirPath, p, "native.ts");
|
||||
const indexNativePath = join(dirPath, p, "native/index.ts");
|
||||
|
||||
if (!(await existsAsync(nativePath)) && !(await existsAsync(indexNativePath)))
|
||||
continue;
|
||||
|
||||
const nameParts = p.split(".");
|
||||
const namePartsWithoutTarget = nameParts.length === 1 ? nameParts : nameParts.slice(0, -1);
|
||||
|
|
|
@ -335,15 +335,15 @@ function runTime(token: string) {
|
|||
await (wreq as any).el(sym);
|
||||
delete Object.prototype[sym];
|
||||
|
||||
const validChunksEntryPoints = [] as string[];
|
||||
const validChunks = [] as string[];
|
||||
const invalidChunks = [] as string[];
|
||||
const validChunksEntryPoints = new Set<string>();
|
||||
const validChunks = new Set<string>();
|
||||
const invalidChunks = new Set<string>();
|
||||
|
||||
if (!chunks) throw new Error("Failed to get chunks");
|
||||
|
||||
chunksLoop:
|
||||
for (const entryPoint in chunks) {
|
||||
const chunkIds = chunks[entryPoint];
|
||||
let invalidEntryPoint = false;
|
||||
|
||||
for (const id of chunkIds) {
|
||||
if (!wreq.u(id)) continue;
|
||||
|
@ -353,14 +353,16 @@ function runTime(token: string) {
|
|||
.then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push"));
|
||||
|
||||
if (isWasm) {
|
||||
invalidChunks.push(id);
|
||||
continue chunksLoop;
|
||||
invalidChunks.add(id);
|
||||
invalidEntryPoint = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
validChunks.push(id);
|
||||
validChunks.add(id);
|
||||
}
|
||||
|
||||
validChunksEntryPoints.push(entryPoint);
|
||||
if (!invalidEntryPoint)
|
||||
validChunksEntryPoints.add(entryPoint);
|
||||
}
|
||||
|
||||
for (const entryPoint of validChunksEntryPoints) {
|
||||
|
@ -373,7 +375,7 @@ function runTime(token: string) {
|
|||
const allChunks = Function("return " + (wreq.u.toString().match(/(?<=\()\{.+?\}/s)?.[0] ?? "null"))() as Record<string | number, string[]> | null;
|
||||
if (!allChunks) throw new Error("Failed to get all chunks");
|
||||
const chunksLeft = Object.keys(allChunks).filter(id => {
|
||||
return !(validChunks.includes(id) || invalidChunks.includes(id));
|
||||
return !(validChunks.has(id) || invalidChunks.has(id));
|
||||
});
|
||||
|
||||
for (const id of chunksLeft) {
|
||||
|
|
|
@ -25,7 +25,7 @@ import type { PartialDeep } from "type-fest";
|
|||
|
||||
import { Argument } from "./types";
|
||||
|
||||
const MessageSender = findByPropsLazy("receiveMessage");
|
||||
const MessageCreator = findByPropsLazy("createBotMessage");
|
||||
|
||||
export function generateId() {
|
||||
return `-${SnowflakeUtils.fromTimestamp(Date.now())}`;
|
||||
|
@ -38,9 +38,9 @@ export function generateId() {
|
|||
* @returns {Message}
|
||||
*/
|
||||
export function sendBotMessage(channelId: string, message: PartialDeep<Message>): Message {
|
||||
const botMessage = MessageActions.createBotMessage({ channelId, content: "", embeds: [] });
|
||||
const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] });
|
||||
|
||||
MessageSender.receiveMessage(channelId, mergeDefaults(message, botMessage));
|
||||
MessageActions.receiveMessage(channelId, mergeDefaults(message, botMessage));
|
||||
|
||||
return message as Message;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,21 @@ export interface Settings {
|
|||
frameless: boolean;
|
||||
transparent: boolean;
|
||||
winCtrlQ: boolean;
|
||||
macosTranslucency: boolean;
|
||||
macosVibrancyStyle:
|
||||
| "content"
|
||||
| "fullscreen-ui"
|
||||
| "header"
|
||||
| "hud"
|
||||
| "menu"
|
||||
| "popover"
|
||||
| "selection"
|
||||
| "sidebar"
|
||||
| "titlebar"
|
||||
| "tooltip"
|
||||
| "under-page"
|
||||
| "window"
|
||||
| undefined;
|
||||
macosTranslucency: boolean | undefined;
|
||||
disableMinSize: boolean;
|
||||
winNativeTitleBar: boolean;
|
||||
plugins: {
|
||||
|
@ -80,7 +94,9 @@ const DefaultSettings: Settings = {
|
|||
frameless: false,
|
||||
transparent: false,
|
||||
winCtrlQ: false,
|
||||
macosTranslucency: false,
|
||||
// Replaced by macosVibrancyStyle
|
||||
macosTranslucency: undefined,
|
||||
macosVibrancyStyle: undefined,
|
||||
disableMinSize: false,
|
||||
winNativeTitleBar: false,
|
||||
plugins: {},
|
||||
|
|
|
@ -108,7 +108,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
|
|||
function renderDiff() {
|
||||
return diff?.map(p => {
|
||||
const color = p.added ? "lime" : p.removed ? "red" : "grey";
|
||||
return <div style={{ color, userSelect: "text" }}>{p.value}</div>;
|
||||
return <div style={{ color, userSelect: "text", wordBreak: "break-all", lineBreak: "anywhere" }}>{p.value}</div>;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,15 @@ function VencordSettings() {
|
|||
|
||||
const isWindows = navigator.platform.toLowerCase().startsWith("win");
|
||||
const isMac = navigator.platform.toLowerCase().startsWith("mac");
|
||||
const needsVibrancySettings = IS_DISCORD_DESKTOP && isMac;
|
||||
|
||||
// One-time migration of the old setting to the new one if necessary.
|
||||
React.useEffect(() => {
|
||||
if (settings.macosTranslucency === true && !settings.macosVibrancyStyle) {
|
||||
settings.macosVibrancyStyle = "sidebar";
|
||||
settings.macosTranslucency = undefined;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const Switches: Array<false | {
|
||||
key: KeysOfType<typeof settings, boolean>;
|
||||
|
@ -89,11 +98,6 @@ function VencordSettings() {
|
|||
title: "Disable minimum window size",
|
||||
note: "Requires a full restart"
|
||||
},
|
||||
IS_DISCORD_DESKTOP && isMac && {
|
||||
key: "macosTranslucency",
|
||||
title: "Enable translucent window",
|
||||
note: "Requires a full restart"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
|
@ -152,6 +156,71 @@ function VencordSettings() {
|
|||
</Forms.FormSection>
|
||||
|
||||
|
||||
{needsVibrancySettings && <>
|
||||
<Forms.FormTitle tag="h5">Window vibrancy style (requires restart)</Forms.FormTitle>
|
||||
<Select
|
||||
className={Margins.bottom20}
|
||||
placeholder="Window vibrancy style"
|
||||
options={[
|
||||
// Sorted from most opaque to most transparent
|
||||
{
|
||||
label: "No vibrancy", default: !settings.macosTranslucency, value: undefined
|
||||
},
|
||||
{
|
||||
label: "Under Page (window tinting)",
|
||||
value: "under-page"
|
||||
},
|
||||
{
|
||||
label: "Content",
|
||||
value: "content"
|
||||
},
|
||||
{
|
||||
label: "Window",
|
||||
value: "window"
|
||||
},
|
||||
{
|
||||
label: "Selection",
|
||||
value: "selection"
|
||||
},
|
||||
{
|
||||
label: "Titlebar",
|
||||
value: "titlebar"
|
||||
},
|
||||
{
|
||||
label: "Header",
|
||||
value: "header"
|
||||
},
|
||||
{
|
||||
label: "Sidebar (old value for transparent windows)",
|
||||
value: "sidebar",
|
||||
default: settings.macosTranslucency
|
||||
},
|
||||
{
|
||||
label: "Tooltip",
|
||||
value: "tooltip"
|
||||
},
|
||||
{
|
||||
label: "Menu",
|
||||
value: "menu"
|
||||
},
|
||||
{
|
||||
label: "Popover",
|
||||
value: "popover"
|
||||
},
|
||||
{
|
||||
label: "Fullscreen UI (transparent but slightly muted)",
|
||||
value: "fullscreen-ui"
|
||||
},
|
||||
{
|
||||
label: "HUD (Most transparent)",
|
||||
value: "hud"
|
||||
},
|
||||
]}
|
||||
select={v => settings.macosVibrancyStyle = v}
|
||||
isSelected={v => settings.macosVibrancyStyle === v}
|
||||
serialize={identity} />
|
||||
</>}
|
||||
|
||||
{typeof Notification !== "undefined" && <NotificationSection settings={settings.notifications} />}
|
||||
</SettingsTab>
|
||||
);
|
||||
|
|
|
@ -85,9 +85,15 @@ if (!IS_VANILLA) {
|
|||
options.backgroundColor = "#00000000";
|
||||
}
|
||||
|
||||
if (settings.macosTranslucency && process.platform === "darwin") {
|
||||
const needsVibrancy = process.platform === "darwin" || (settings.macosVibrancyStyle || settings.macosTranslucency);
|
||||
|
||||
if (needsVibrancy) {
|
||||
options.backgroundColor = "#00000000";
|
||||
if (settings.macosTranslucency) {
|
||||
options.vibrancy = "sidebar";
|
||||
} else if (settings.macosVibrancyStyle) {
|
||||
options.vibrancy = settings.macosVibrancyStyle;
|
||||
}
|
||||
}
|
||||
|
||||
process.env.DISCORD_PRELOAD = original;
|
||||
|
|
|
@ -46,6 +46,13 @@ export default definePlugin({
|
|||
match: /(?<=\.activityEmoji,.+?animate:)\i/,
|
||||
replace: "!0"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: ".animatedBannerHoverLayer,onMouseEnter:",
|
||||
replacement: {
|
||||
match: /(?<=guildBanner:\i,animate:)\i/,
|
||||
replace: "!0"
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -124,6 +124,18 @@ export const defaultRules = [
|
|||
"t@*.x.com",
|
||||
"s@*.x.com",
|
||||
"ref_*@*.x.com",
|
||||
"t@*.fixupx.com",
|
||||
"s@*.fixupx.com",
|
||||
"ref_*@*.fixupx.com",
|
||||
"t@*.fxtwitter.com",
|
||||
"s@*.fxtwitter.com",
|
||||
"ref_*@*.fxtwitter.com",
|
||||
"t@*.twittpr.com",
|
||||
"s@*.twittpr.com",
|
||||
"ref_*@*.twittpr.com",
|
||||
"t@*.fixvx.com",
|
||||
"s@*.fixvx.com",
|
||||
"ref_*@*.fixvx.com",
|
||||
"tt_medium",
|
||||
"tt_content",
|
||||
"lr@yandex.*",
|
||||
|
|
|
@ -60,7 +60,7 @@ async function embedDidMount(this: Component<Props>) {
|
|||
|
||||
if (hasTitle) {
|
||||
embed.dearrow.oldTitle = embed.rawTitle;
|
||||
embed.rawTitle = titles[0].title;
|
||||
embed.rawTitle = titles[0].title.replace(/ >(\S)/g, " $1");
|
||||
}
|
||||
|
||||
if (hasThumb) {
|
||||
|
|
|
@ -215,6 +215,9 @@ function initWs(isManual = false) {
|
|||
case "ModuleId":
|
||||
results = Object.keys(search(parsedArgs[0]));
|
||||
break;
|
||||
case "ComponentByCode":
|
||||
results = findAll(filters.componentByCode(...parsedArgs));
|
||||
break;
|
||||
default:
|
||||
return reply("Unknown Find Type " + type);
|
||||
}
|
||||
|
|
|
@ -359,7 +359,7 @@ export default definePlugin({
|
|||
},
|
||||
// Separate patch for allowing using custom app icons
|
||||
{
|
||||
find: "location:\"AppIconHome\"",
|
||||
find: ".FreemiumAppIconIds.DEFAULT&&(",
|
||||
replacement: {
|
||||
match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/,
|
||||
replace: "true"
|
||||
|
@ -787,7 +787,14 @@ export default definePlugin({
|
|||
if (sticker.available !== false && (canUseStickers || sticker.guild_id === guildId))
|
||||
break stickerBypass;
|
||||
|
||||
const link = this.getStickerLink(sticker.id);
|
||||
// [12/12/2023]
|
||||
// Work around an annoying bug where getStickerLink will return StickerType.GIF,
|
||||
// but will give us a normal non animated png for no reason
|
||||
// TODO: Remove this workaround when it's not needed anymore
|
||||
let link = this.getStickerLink(sticker.id);
|
||||
if (sticker.format_type === StickerType.GIF && link.includes(".png")) {
|
||||
link = link.replace(".png", ".gif");
|
||||
}
|
||||
if (sticker.format_type === StickerType.APNG) {
|
||||
this.sendAnimatedSticker(link, sticker.id, channelId);
|
||||
return { cancel: true };
|
||||
|
|
|
@ -14,10 +14,12 @@ export default definePlugin({
|
|||
patches: [
|
||||
{
|
||||
find: "handleImageLoad=",
|
||||
replacement: {
|
||||
match: /(?<=getSrc\(\i\){.+?format:)\i/,
|
||||
replace: "null"
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=getSrc\(\i\){.+?return )\i\.SUPPORTS_WEBP.+?:(?=\i&&\(\i="png"\))/,
|
||||
replace: ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -28,21 +28,22 @@ import style from "./style.css?managed";
|
|||
const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:");
|
||||
|
||||
function makeIcon(showCurrentGame?: boolean) {
|
||||
const controllerIcon = "M3.06 20.4q-1.53 0-2.37-1.065T.06 16.74l1.26-9q.27-1.8 1.605-2.97T6.06 3.6h11.88q1.8 0 3.135 1.17t1.605 2.97l1.26 9q.21 1.53-.63 2.595T20.94 20.4q-.63 0-1.17-.225T18.78 19.5l-2.7-2.7H7.92l-2.7 2.7q-.45.45-.99.675t-1.17.225Zm14.94-7.2q.51 0 .855-.345T19.2 12q0-.51-.345-.855T18 10.8q-.51 0-.855.345T16.8 12q0 .51.345 .855T18 13.2Zm-2.4-3.6q.51 0 .855-.345T16.8 8.4q0-.51-.345-.855T15.6 7.2q-.51 0-.855.345T14.4 8.4q0 .51.345 .855T15.6 9.6ZM6.9 13.2h1.8v-2.1h2.1v-1.8h-2.1v-2.1h-1.8v2.1h-2.1v1.8h2.1v2.1Z";
|
||||
return function () {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path fill="currentColor" mask="url(#gameActivityMask)" d="M3.06 20.4q-1.53 0-2.37-1.065T.06 16.74l1.26-9q.27-1.8 1.605-2.97T6.06 3.6h11.88q1.8 0 3.135 1.17t1.605 2.97l1.26 9q.21 1.53-.63 2.595T20.94 20.4q-.63 0-1.17-.225T18.78 19.5l-2.7-2.7H7.92l-2.7 2.7q-.45.45-.99.675t-1.17.225Zm14.94-7.2q.51 0 .855-.345T19.2 12q0-.51-.345-.855T18 10.8q-.51 0-.855.345T16.8 12q0 .51.345 .855T18 13.2Zm-2.4-3.6q.51 0 .855-.345T16.8 8.4q0-.51-.345-.855T15.6 7.2q-.51 0-.855.345T14.4 8.4q0 .51.345 .855T15.6 9.6ZM6.9 13.2h1.8v-2.1h2.1v-1.8h-2.1v-2.1h-1.8v2.1h-2.1v1.8h2.1v2.1Z" />
|
||||
{!showCurrentGame && <>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24">
|
||||
{showCurrentGame ? (
|
||||
<path fill="currentColor" d={controllerIcon} />
|
||||
) : (
|
||||
<>
|
||||
<mask id="gameActivityMask" >
|
||||
<rect fill="white" x="0" y="0" width="24" height="24" />
|
||||
<path fill="black" d="M23.27 4.54 19.46.73 .73 19.46 4.54 23.27 23.27 4.54Z" />
|
||||
<path fill="black" d="M23.27 4.73 19.27 .73 -.27 20.27 3.73 24.27Z" />
|
||||
</mask>
|
||||
<path fill="var(--status-danger)" d="M23 2.27 21.73 1 1 21.73 2.27 23 23 2.27Z" />
|
||||
</>}
|
||||
<path fill="var(--status-danger)" mask="url(#gameActivityMask)" d={controllerIcon} />
|
||||
<path fill="var(--status-danger)" d="M22.7 2.7a1 1 0 0 0-1.4-1.4l-20 20a1 1 0 1 0 1.4 1.4Z" />
|
||||
</>
|
||||
)}
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -22,7 +22,7 @@ import definePlugin from "@utils/types";
|
|||
export default definePlugin({
|
||||
name: "iLoveSpam",
|
||||
description: "Do not hide messages from 'likely spammers'",
|
||||
authors: [Devs.botato, Devs.Animal],
|
||||
authors: [Devs.botato, Devs.Nyako],
|
||||
patches: [
|
||||
{
|
||||
find: "hasFlag:{writable",
|
||||
|
|
|
@ -72,6 +72,7 @@ export default definePlugin({
|
|||
if (event.detail < 2) return;
|
||||
if (settings.store.requireModifier && !event.ctrlKey && !event.shiftKey) return;
|
||||
if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return;
|
||||
if (msg.deleted === true) return;
|
||||
|
||||
if (isMe) {
|
||||
if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id)) return;
|
||||
|
@ -81,6 +82,9 @@ export default definePlugin({
|
|||
} else {
|
||||
if (!settings.store.enableDoubleClickToReply) return;
|
||||
|
||||
const EPHEMERAL = 64;
|
||||
if (msg.hasFlag(EPHEMERAL)) return;
|
||||
|
||||
FluxDispatcher.dispatch({
|
||||
type: "CREATE_PENDING_REPLY",
|
||||
channel,
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByProps } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
|
||||
const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
guild: {
|
||||
|
@ -63,7 +65,7 @@ export default definePlugin({
|
|||
|
||||
handleMute(guildId: string | null) {
|
||||
if (guildId === "@me" || guildId === "null" || guildId == null) return;
|
||||
findByProps("updateGuildNotificationSettings").updateGuildNotificationSettings(guildId,
|
||||
updateGuildNotificationSettings(guildId,
|
||||
{
|
||||
muted: settings.store.guild,
|
||||
suppress_everyone: settings.store.everyone,
|
||||
|
|
3
src/plugins/notificationVolume/README.md
Normal file
3
src/plugins/notificationVolume/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# NotificationVolume
|
||||
|
||||
Set a separate volume for notifications and in-app sounds (e.g. messages, call sound, mute/unmute) helping your ears stay healthy for many years to come.
|
35
src/plugins/notificationVolume/index.ts
Normal file
35
src/plugins/notificationVolume/index.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 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";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
notificationVolume: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "Notification volume",
|
||||
markers: [0, 25, 50, 75, 100],
|
||||
default: 100,
|
||||
stickToMarkers: false
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "NotificationVolume",
|
||||
description: "Save your ears and set a separate volume for notifications and in-app sounds",
|
||||
authors: [Devs.philipbry],
|
||||
settings,
|
||||
patches: [
|
||||
{
|
||||
find: "_ensureAudio(){",
|
||||
replacement: {
|
||||
match: /onloadeddata=\(\)=>\{.\.volume=/,
|
||||
replace: "$&$self.settings.store.notificationVolume/100*"
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
|
@ -28,7 +28,8 @@ export default definePlugin({
|
|||
start() {
|
||||
fetch("https://raw.githubusercontent.com/adryd325/oneko.js/8fa8a1864aa71cd7a794d58bc139e755e96a236c/oneko.js")
|
||||
.then(x => x.text())
|
||||
.then(s => s.replace("./oneko.gif", "https://raw.githubusercontent.com/adryd325/oneko.js/14bab15a755d0e35cd4ae19c931d96d306f99f42/oneko.gif"))
|
||||
.then(s => s.replace("./oneko.gif", "https://raw.githubusercontent.com/adryd325/oneko.js/14bab15a755d0e35cd4ae19c931d96d306f99f42/oneko.gif")
|
||||
.replace("(isReducedMotion)", "(false)"))
|
||||
.then(eval);
|
||||
},
|
||||
|
||||
|
|
|
@ -55,13 +55,13 @@ const Icons = {
|
|||
};
|
||||
type Platform = keyof typeof Icons;
|
||||
|
||||
const StatusUtils = findByPropsLazy("getStatusColor", "StatusTypes");
|
||||
const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes");
|
||||
|
||||
const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => {
|
||||
const tooltip = platform[0].toUpperCase() + platform.slice(1);
|
||||
const Icon = Icons[platform] ?? Icons.desktop;
|
||||
|
||||
return <Icon color={`var(--${StatusUtils.getStatusColor(status)}`} tooltip={tooltip} small={small} />;
|
||||
return <Icon color={StatusUtils.useStatusFillColor(status)} tooltip={tooltip} small={small} />;
|
||||
};
|
||||
|
||||
const getStatus = (id: string): Record<Platform, string> => PresenceStore.getState()?.clientStatuses?.[id];
|
||||
|
|
|
@ -31,7 +31,7 @@ export default definePlugin({
|
|||
start() {
|
||||
addButton("QuickMention", msg => {
|
||||
const channel = ChannelStore.getChannel(msg.channel_id);
|
||||
if (!PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return null;
|
||||
if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return null;
|
||||
|
||||
return {
|
||||
label: "Quick Mention",
|
||||
|
|
|
@ -18,27 +18,28 @@
|
|||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { React } from "@webpack/common";
|
||||
|
||||
let ERROR_CODES: any;
|
||||
const CODES_URL =
|
||||
"https://raw.githubusercontent.com/facebook/react/17.0.2/scripts/error-codes/codes.json";
|
||||
|
||||
export default definePlugin({
|
||||
name: "ReactErrorDecoder",
|
||||
description: 'Replaces "Minifed React Error" with the actual error.',
|
||||
authors: [Devs.Cyn],
|
||||
authors: [Devs.Cyn, Devs.maisymoe],
|
||||
patches: [
|
||||
{
|
||||
find: '"https://reactjs.org/docs/error-decoder.html?invariant="',
|
||||
replacement: {
|
||||
match: /(function .\(.\)){(for\(var .="https:\/\/reactjs\.org\/docs\/error-decoder\.html\?invariant="\+.,.=1;.<arguments\.length;.\+\+\).\+="&args\[\]="\+encodeURIComponent\(arguments\[.\]\);return"Minified React error #"\+.\+"; visit "\+.\+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.")}/,
|
||||
replace: (_, func, original) =>
|
||||
`${func}{var decoded=Vencord.Plugins.plugins.ReactErrorDecoder.decodeError.apply(null, arguments);if(decoded)return decoded;${original}}`,
|
||||
`${func}{var decoded=$self.decodeError.apply(null, arguments);if(decoded)return decoded;${original}}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async start() {
|
||||
const CODES_URL = `https://raw.githubusercontent.com/facebook/react/v${React.version}/scripts/error-codes/codes.json`;
|
||||
|
||||
ERROR_CODES = await fetch(CODES_URL)
|
||||
.then(res => res.json())
|
||||
.catch(e => console.error("[ReactErrorDecoder] Failed to fetch React error codes\n", e));
|
||||
|
|
|
@ -42,6 +42,13 @@ export default definePlugin({
|
|||
match: /codeBlock:\{react\((\i),(\i),(\i)\)\{/,
|
||||
replace: "$&return $self.renderHighlighter($1,$2,$3);"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: ".PREVIEW_NUM_LINES",
|
||||
replacement: {
|
||||
match: /(?<=function \i\((\i)\)\{)(?=let\{text:\i,language:)/,
|
||||
replace: "return $self.renderHighlighter({lang:$1.language,content:$1.text});"
|
||||
}
|
||||
}
|
||||
],
|
||||
start: async () => {
|
||||
|
|
|
@ -77,7 +77,7 @@ export default definePlugin({
|
|||
},
|
||||
// Do not check for unreads when selecting the render level if the channel is hidden
|
||||
{
|
||||
match: /(?=!\(0,\i\.getHasImportantUnread\)\(this\.record\))/,
|
||||
match: /(?<=&&)(?=!\i\.\i\.hasUnread\(this\.record\.id\))/,
|
||||
replace: "$self.isHiddenChannel(this.record)||"
|
||||
},
|
||||
// Make channels we dont have access to be the same level as normal ones
|
||||
|
@ -334,12 +334,12 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Remove the divider and the open chat button for the HiddenChannelLockScreen
|
||||
match: /"more-options-popout"\)\),(?<=let{channel:(\i).+?inCall:(\i).+?)/,
|
||||
match: /"more-options-popout"\)\),(?<=channel:(\i).+?inCall:(\i).+?)/,
|
||||
replace: (m, channel, inCall) => `${m}${inCall}||!$self.isHiddenChannel(${channel},true)&&`
|
||||
},
|
||||
{
|
||||
// Remove invite users button for the HiddenChannelLockScreen
|
||||
match: /"popup".{0,100}?if\((?<=let{channel:(\i).+?inCall:(\i).+?)/,
|
||||
match: /"popup".{0,100}?if\((?<=channel:(\i).+?inCall:(\i).+?)/,
|
||||
replace: (m, channel, inCall) => `${m}(${inCall}||!$self.isHiddenChannel(${channel},true))&&`
|
||||
},
|
||||
]
|
||||
|
|
|
@ -55,13 +55,19 @@ export default definePlugin({
|
|||
replace: "return [$self.renderPlayer(),$1]"
|
||||
}
|
||||
},
|
||||
// Adds POST and a Marker to the SpotifyAPI (so we can easily find it)
|
||||
{
|
||||
find: ".PLAYER_DEVICES",
|
||||
replacement: {
|
||||
replacement: [{
|
||||
// Adds POST and a Marker to the SpotifyAPI (so we can easily find it)
|
||||
match: /get:(\i)\.bind\(null,(\i\.\i)\.get\)/,
|
||||
replace: "post:$1.bind(null,$2.post),$&"
|
||||
}
|
||||
},
|
||||
{
|
||||
// Spotify Connect API returns status 202 instead of 204 when skipping tracks.
|
||||
// Discord rejects 202 which causes the request to send twice. This patch prevents this.
|
||||
match: /202===\i\.status/,
|
||||
replace: "false",
|
||||
}]
|
||||
},
|
||||
// Discord doesn't give you the repeat kind, only a boolean
|
||||
{
|
||||
|
|
|
@ -46,10 +46,10 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: ".hasAvailableBurstCurrency)",
|
||||
find: ".trackEmojiSearchEmpty,200",
|
||||
replacement: {
|
||||
match: /(?<=\.useBurstReactionsExperiment.{0,20})useState\(!1\)(?=.+?(\i===\i\.EmojiIntention.REACTION))/,
|
||||
replace: "useState($self.settings.store.superReactByDefault && $1)"
|
||||
match: /(\.trackEmojiSearchEmpty,200(?=.+?isBurstReaction:(\i).+?(\i===\i\.EmojiIntention.REACTION)).+?\[\2,\i\]=\i\.useState\().+?\)/,
|
||||
replace: (_, rest, isBurstReactionVariable, isReactionIntention) => `${rest}$self.settings.store.superReactByDefault&&${isReactionIntention})`
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -21,7 +21,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
|||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findExportedComponentLazy, findStoreLazy } from "@webpack";
|
||||
import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||
import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||
|
||||
import { buildSeveralUsers } from "../typingTweaks";
|
||||
|
||||
|
@ -47,7 +47,7 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
|
|||
return oldKeys.length === currentKeys.length && currentKeys.every(key => old[key] != null);
|
||||
}
|
||||
);
|
||||
|
||||
const currentChannelId: string = useStateFromStores([SelectedChannelStore], () => SelectedChannelStore.getChannelId());
|
||||
const guildId = ChannelStore.getChannel(channelId).guild_id;
|
||||
|
||||
if (!settings.store.includeMutedChannels) {
|
||||
|
@ -55,6 +55,10 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
|
|||
if (isChannelMuted) return null;
|
||||
}
|
||||
|
||||
if (!settings.store.includeCurrentChannel) {
|
||||
if (currentChannelId === channelId) return null;
|
||||
}
|
||||
|
||||
const myId = UserStore.getCurrentUser()?.id;
|
||||
|
||||
const typingUsersArray = Object.keys(typingUsers).filter(id => id !== myId && !(RelationshipStore.isBlocked(id) && !settings.store.includeBlockedUsers));
|
||||
|
@ -101,6 +105,11 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
|
|||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
includeCurrentChannel: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to show the typing indicator for the currently selected channel",
|
||||
default: true
|
||||
},
|
||||
includeMutedChannels: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether to show the typing indicator for muted channels.",
|
||||
|
|
|
@ -20,9 +20,11 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { saveFile } from "@utils/web";
|
||||
import { findByProps } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Clipboard, ComponentDispatch } from "@webpack/common";
|
||||
|
||||
const ctxMenuCallbacks = findByPropsLazy("contextMenuCallbackNative");
|
||||
|
||||
async function fetchImage(url: string) {
|
||||
const res = await fetch(url);
|
||||
if (res.status !== 200) return;
|
||||
|
@ -55,7 +57,6 @@ export default definePlugin({
|
|||
|
||||
start() {
|
||||
if (settings.store.addBack) {
|
||||
const ctxMenuCallbacks = findByProps("contextMenuCallbackNative");
|
||||
window.removeEventListener("contextmenu", ctxMenuCallbacks.contextMenuCallbackWeb);
|
||||
window.addEventListener("contextmenu", ctxMenuCallbacks.contextMenuCallbackNative);
|
||||
this.changedListeners = true;
|
||||
|
@ -64,7 +65,6 @@ export default definePlugin({
|
|||
|
||||
stop() {
|
||||
if (this.changedListeners) {
|
||||
const ctxMenuCallbacks = findByProps("contextMenuCallbackNative");
|
||||
window.removeEventListener("contextmenu", ctxMenuCallbacks.contextMenuCallbackNative);
|
||||
window.addEventListener("contextmenu", ctxMenuCallbacks.contextMenuCallbackWeb);
|
||||
}
|
||||
|
|
15
src/plugins/xsOverlay.desktop/README.md
Normal file
15
src/plugins/xsOverlay.desktop/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# XSOverlay Notifier
|
||||
|
||||
Sends Discord messages to [XSOverlay](https://store.steampowered.com/app/1173510/XSOverlay/) for easier viewing while using VR.
|
||||
|
||||
## Preview
|
||||
|
||||
![](https://github.com/Vendicated/Vencord/assets/24845294/205d2055-bb4a-44e4-b7e3-265391bccd40)
|
||||
|
||||
![](https://github.com/Vendicated/Vencord/assets/24845294/f15eff61-2d52-4620-bcab-808ecb1606d2)
|
||||
|
||||
## Usage
|
||||
- Enable this plugin
|
||||
- Set plugin settings as desired
|
||||
- Open XSOverlay
|
||||
- get ping spammed
|
288
src/plugins/xsOverlay.desktop/index.ts
Normal file
288
src/plugins/xsOverlay.desktop/index.ts
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType, PluginNative } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { ChannelStore, GuildStore, UserStore } from "@webpack/common";
|
||||
import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general";
|
||||
|
||||
const enum ChannelTypes {
|
||||
DM = 1,
|
||||
GROUP_DM = 3
|
||||
}
|
||||
|
||||
interface Message {
|
||||
guild_id: string,
|
||||
attachments: MessageAttachment[],
|
||||
author: User,
|
||||
channel_id: string,
|
||||
components: any[],
|
||||
content: string,
|
||||
edited_timestamp: string,
|
||||
embeds: Embed[],
|
||||
sticker_items?: Sticker[],
|
||||
flags: number,
|
||||
id: string,
|
||||
member: GuildMember,
|
||||
mention_everyone: boolean,
|
||||
mention_roles: string[],
|
||||
mentions: Mention[],
|
||||
nonce: string,
|
||||
pinned: false,
|
||||
referenced_message: any,
|
||||
timestamp: string,
|
||||
tts: boolean,
|
||||
type: number;
|
||||
}
|
||||
|
||||
interface Mention {
|
||||
avatar: string,
|
||||
avatar_decoration_data: any,
|
||||
discriminator: string,
|
||||
global_name: string,
|
||||
id: string,
|
||||
public_flags: number,
|
||||
username: string;
|
||||
}
|
||||
|
||||
interface Sticker {
|
||||
t: "Sticker";
|
||||
description: string;
|
||||
format_type: number;
|
||||
guild_id: string;
|
||||
id: string;
|
||||
name: string;
|
||||
tags: string;
|
||||
type: number;
|
||||
}
|
||||
|
||||
interface Call {
|
||||
channel_id: string,
|
||||
guild_id: string,
|
||||
message_id: string,
|
||||
region: string,
|
||||
ringing: string[];
|
||||
}
|
||||
|
||||
const MuteStore = findByPropsLazy("isSuppressEveryoneEnabled");
|
||||
const XSLog = new Logger("XSOverlay");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
ignoreBots: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Ignore messages from bots",
|
||||
default: false
|
||||
},
|
||||
pingColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "User mention color",
|
||||
default: "#7289da"
|
||||
},
|
||||
channelPingColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Channel mention color",
|
||||
default: "#8a2be2"
|
||||
},
|
||||
soundPath: {
|
||||
type: OptionType.STRING,
|
||||
description: "Notification sound (default/warning/error)",
|
||||
default: "default"
|
||||
},
|
||||
timeout: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Notif duration (secs)",
|
||||
default: 1.0,
|
||||
},
|
||||
opacity: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "Notif opacity",
|
||||
default: 1,
|
||||
markers: makeRange(0, 1, 0.1)
|
||||
},
|
||||
volume: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "Volume",
|
||||
default: 0.2,
|
||||
markers: makeRange(0, 1, 0.1)
|
||||
},
|
||||
});
|
||||
|
||||
const Native = VencordNative.pluginHelpers.XsOverlay as PluginNative<typeof import("./native")>;
|
||||
|
||||
export default definePlugin({
|
||||
name: "XSOverlay",
|
||||
description: "Forwards discord notifications to XSOverlay, for easy viewing in VR",
|
||||
authors: [Devs.Nyako],
|
||||
tags: ["vr", "notify"],
|
||||
settings,
|
||||
flux: {
|
||||
CALL_UPDATE({ call }: { call: Call; }) {
|
||||
if (call?.ringing?.includes(UserStore.getCurrentUser().id)) {
|
||||
const channel = ChannelStore.getChannel(call.channel_id);
|
||||
sendOtherNotif("Incoming call", `${channel.name} is calling you...`);
|
||||
}
|
||||
},
|
||||
MESSAGE_CREATE({ message, optimistic }: { message: Message; optimistic: boolean; }) {
|
||||
// Apparently without this try/catch, discord's socket connection dies if any part of this errors
|
||||
try {
|
||||
if (optimistic) return;
|
||||
const channel = ChannelStore.getChannel(message.channel_id);
|
||||
if (!shouldNotify(message, channel)) return;
|
||||
|
||||
const pingColor = settings.store.pingColor.replaceAll("#", "").trim();
|
||||
const channelPingColor = settings.store.channelPingColor.replaceAll("#", "").trim();
|
||||
let finalMsg = message.content;
|
||||
let titleString = "";
|
||||
|
||||
if (channel.guild_id) {
|
||||
const guild = GuildStore.getGuild(channel.guild_id);
|
||||
titleString = `${message.author.username} (${guild.name}, #${channel.name})`;
|
||||
}
|
||||
|
||||
|
||||
switch (channel.type) {
|
||||
case ChannelTypes.DM:
|
||||
titleString = message.author.username.trim();
|
||||
break;
|
||||
case ChannelTypes.GROUP_DM:
|
||||
const channelName = channel.name.trim() ?? channel.rawRecipients.map(e => e.username).join(", ");
|
||||
titleString = `${message.author.username} (${channelName})`;
|
||||
break;
|
||||
}
|
||||
|
||||
if (message.referenced_message) {
|
||||
titleString += " (reply)";
|
||||
}
|
||||
|
||||
if (message.embeds.length > 0) {
|
||||
finalMsg += " [embed] ";
|
||||
if (message.content === "") {
|
||||
finalMsg = "sent message embed(s)";
|
||||
}
|
||||
}
|
||||
|
||||
if (message.sticker_items) {
|
||||
finalMsg += " [sticker] ";
|
||||
if (message.content === "") {
|
||||
finalMsg = "sent a sticker";
|
||||
}
|
||||
}
|
||||
|
||||
const images = message.attachments.filter(e =>
|
||||
typeof e?.content_type === "string"
|
||||
&& e?.content_type.startsWith("image")
|
||||
);
|
||||
|
||||
|
||||
images.forEach(img => {
|
||||
finalMsg += ` [image: ${img.filename}] `;
|
||||
});
|
||||
|
||||
message.attachments.filter(a => a && !a.content_type?.startsWith("image")).forEach(a => {
|
||||
finalMsg += ` [attachment: ${a.filename}] `;
|
||||
});
|
||||
|
||||
// make mentions readable
|
||||
if (message.mentions.length > 0) {
|
||||
finalMsg = finalMsg.replace(/<@!?(\d{17,20})>/g, (_, id) => `<color=#${pingColor}><b>@${UserStore.getUser(id)?.username || "unknown-user"}</color></b>`);
|
||||
}
|
||||
|
||||
if (message.mention_roles.length > 0) {
|
||||
for (const roleId of message.mention_roles) {
|
||||
const role = GuildStore.getGuild(channel.guild_id).roles[roleId];
|
||||
if (!role) continue;
|
||||
const roleColor = role.colorString ?? `#${pingColor}`;
|
||||
finalMsg = finalMsg.replace(`<@&${roleId}>`, `<b><color=${roleColor}>@${role.name}</color></b>`);
|
||||
}
|
||||
}
|
||||
|
||||
// make emotes and channel mentions readable
|
||||
const emoteMatches = finalMsg.match(new RegExp("(<a?:\\w+:\\d+>)", "g"));
|
||||
const channelMatches = finalMsg.match(new RegExp("<(#\\d+)>", "g"));
|
||||
|
||||
if (emoteMatches) {
|
||||
for (const eMatch of emoteMatches) {
|
||||
finalMsg = finalMsg.replace(new RegExp(`${eMatch}`, "g"), `:${eMatch.split(":")[1]}:`);
|
||||
}
|
||||
}
|
||||
|
||||
if (channelMatches) {
|
||||
for (const cMatch of channelMatches) {
|
||||
let channelId = cMatch.split("<#")[1];
|
||||
channelId = channelId.substring(0, channelId.length - 1);
|
||||
finalMsg = finalMsg.replace(new RegExp(`${cMatch}`, "g"), `<b><color=#${channelPingColor}>#${ChannelStore.getChannel(channelId).name}</color></b>`);
|
||||
}
|
||||
}
|
||||
|
||||
sendMsgNotif(titleString, finalMsg, message);
|
||||
} catch (err) {
|
||||
XSLog.error(`Failed to catch MESSAGE_CREATE: ${err}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function sendMsgNotif(titleString: string, content: string, message: Message) {
|
||||
fetch(`https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`).then(response => response.arrayBuffer()).then(result => {
|
||||
const msgData = {
|
||||
messageType: 1,
|
||||
index: 0,
|
||||
timeout: settings.store.timeout,
|
||||
height: calculateHeight(cleanMessage(content)),
|
||||
opacity: settings.store.opacity,
|
||||
volume: settings.store.volume,
|
||||
audioPath: settings.store.soundPath,
|
||||
title: titleString,
|
||||
content: content,
|
||||
useBase64Icon: true,
|
||||
icon: result,
|
||||
sourceApp: "Vencord"
|
||||
};
|
||||
Native.sendToOverlay(msgData);
|
||||
});
|
||||
}
|
||||
|
||||
function sendOtherNotif(content: string, titleString: string) {
|
||||
const msgData = {
|
||||
messageType: 1,
|
||||
index: 0,
|
||||
timeout: settings.store.timeout,
|
||||
height: calculateHeight(cleanMessage(content)),
|
||||
opacity: settings.store.opacity,
|
||||
volume: settings.store.volume,
|
||||
audioPath: settings.store.soundPath,
|
||||
title: titleString,
|
||||
content: content,
|
||||
useBase64Icon: false,
|
||||
icon: null,
|
||||
sourceApp: "Vencord"
|
||||
};
|
||||
Native.sendToOverlay(msgData);
|
||||
}
|
||||
|
||||
function shouldNotify(message: Message, channel: Channel) {
|
||||
const currentUser = UserStore.getCurrentUser();
|
||||
if (message.author.id === currentUser.id) return false;
|
||||
if (message.author.bot && settings.store.ignoreBots) return false;
|
||||
if (MuteStore.allowAllMessages(channel) || message.mention_everyone && !MuteStore.isSuppressEveryoneEnabled(message.guild_id)) return true;
|
||||
|
||||
return message.mentions.some(m => m.id === currentUser.id);
|
||||
}
|
||||
|
||||
function calculateHeight(content: string) {
|
||||
if (content.length <= 100) return 100;
|
||||
if (content.length <= 200) return 150;
|
||||
if (content.length <= 300) return 200;
|
||||
return 250;
|
||||
}
|
||||
|
||||
function cleanMessage(content: string) {
|
||||
return content.replace(new RegExp("<[^>]*>", "g"), "");
|
||||
}
|
16
src/plugins/xsOverlay.desktop/native.ts
Normal file
16
src/plugins/xsOverlay.desktop/native.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { createSocket, Socket } from "dgram";
|
||||
|
||||
let xsoSocket: Socket;
|
||||
|
||||
export function sendToOverlay(_, data: any) {
|
||||
data.icon = Buffer.from(data.icon).toString("base64");
|
||||
const json = JSON.stringify(data);
|
||||
xsoSocket ??= createSocket("udp4");
|
||||
xsoSocket.send(json, 42069, "127.0.0.1");
|
||||
}
|
|
@ -78,8 +78,8 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
name: "Samu",
|
||||
id: 702973430449832038n,
|
||||
},
|
||||
Animal: {
|
||||
name: "Animal",
|
||||
Nyako: {
|
||||
name: "nyako",
|
||||
id: 118437263754395652n
|
||||
},
|
||||
MaiKokain: {
|
||||
|
@ -387,10 +387,18 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
name: "ant0n",
|
||||
id: 145224646868860928n
|
||||
},
|
||||
philipbry: {
|
||||
name: "philipbry",
|
||||
id: 554994003318276106n
|
||||
},
|
||||
Korbo: {
|
||||
name: "Korbo",
|
||||
id: 455856406420258827n
|
||||
},
|
||||
maisymoe: {
|
||||
name: "maisy",
|
||||
id: 257109471589957632n,
|
||||
},
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
// iife so #__PURE__ works correctly
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { findByProps, findByPropsLazy } from "@webpack";
|
||||
import { findByPropsLazy, findExportedComponentLazy } from "@webpack";
|
||||
import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react";
|
||||
|
||||
import { LazyComponent } from "./react";
|
||||
|
@ -118,7 +118,7 @@ export type ImageModal = ComponentType<{
|
|||
shouldHideMediaOptions?: boolean;
|
||||
}>;
|
||||
|
||||
export const ImageModal = LazyComponent(() => findByProps("ImageModal").ImageModal as ImageModal);
|
||||
export const ImageModal = findExportedComponentLazy("ImageModal") as ImageModal;
|
||||
|
||||
export const ModalRoot = LazyComponent(() => Modals.ModalRoot);
|
||||
export const ModalHeader = LazyComponent(() => Modals.ModalHeader);
|
||||
|
|
Loading…
Reference in a new issue