Compare commits
38 commits
7907bf7120
...
06ceb4b82c
Author | SHA1 | Date | |
---|---|---|---|
06ceb4b82c | |||
4a77c885dd | |||
7d696f6b71 | |||
bf758167d6 | |||
a0e1e46765 | |||
5578cef24d | |||
|
2bfeef88ca | ||
|
7ca4ea3d13 | ||
|
f8dfe217b1 | ||
|
f22d0e14a4 | ||
|
ac1b1d44f5 | ||
|
13993f3f69 | ||
|
a0308e03af | ||
|
cd61f4e744 | ||
|
5cf22113cf | ||
|
ea2772476d | ||
|
99458da3be | ||
|
c4f6f151e6 | ||
|
8558b1a589 | ||
|
76df29fba2 | ||
|
25ceff5ec2 | ||
|
e0d66ff071 | ||
|
66a75747f8 | ||
|
211569f7f5 | ||
|
af1edc88bf | ||
|
7ef536c6c6 | ||
|
69dc4fd594 | ||
|
1fe7912ec1 | ||
|
0cb84cee83 | ||
|
49c9fa1c8e | ||
|
fd1aba7bab | ||
|
3b295e1f6f | ||
|
152d4fdbb3 | ||
|
ce0740b885 | ||
|
64c3dd1c16 | ||
|
5f7a2c59c6 | ||
|
cd3a998c4b | ||
|
2270b88a98 |
46 changed files with 1253 additions and 212 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.10.6",
|
||||
"version": "1.10.7",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
@ -39,8 +39,10 @@
|
|||
"@sapphi-red/web-noise-suppressor": "0.3.5",
|
||||
"@vap/core": "0.0.12",
|
||||
"@vap/shiki": "0.10.5",
|
||||
"dompurify": "^3.1.7",
|
||||
"fflate": "^0.8.2",
|
||||
"gifenc": "github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3",
|
||||
"katex": "^0.16.11",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"virtual-merge": "^1.0.1"
|
||||
|
@ -49,6 +51,8 @@
|
|||
"@stylistic/eslint-plugin": "^2.6.1",
|
||||
"@types/chrome": "^0.0.269",
|
||||
"@types/diff": "^5.2.1",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/lodash": "^4.17.7",
|
||||
"@types/node": "^22.0.3",
|
||||
"@types/react": "^18.3.3",
|
||||
|
@ -107,6 +111,6 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"pnpm": ">=9"
|
||||
"pnpm": "*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,18 @@ importers:
|
|||
'@vap/shiki':
|
||||
specifier: 0.10.5
|
||||
version: 0.10.5
|
||||
dompurify:
|
||||
specifier: ^3.1.7
|
||||
version: 3.1.7
|
||||
fflate:
|
||||
specifier: ^0.8.2
|
||||
version: 0.8.2
|
||||
gifenc:
|
||||
specifier: github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3
|
||||
version: https://codeload.github.com/mattdesl/gifenc/tar.gz/64842fca317b112a8590f8fef2bf3825da8f6fe3
|
||||
katex:
|
||||
specifier: ^0.16.11
|
||||
version: 0.16.11
|
||||
monaco-editor:
|
||||
specifier: ^0.50.0
|
||||
version: 0.50.0
|
||||
|
@ -53,6 +59,12 @@ importers:
|
|||
'@types/diff':
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1
|
||||
'@types/dompurify':
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
'@types/katex':
|
||||
specifier: ^0.16.7
|
||||
version: 0.16.7
|
||||
'@types/lodash':
|
||||
specifier: ^4.17.7
|
||||
version: 4.17.7
|
||||
|
@ -622,6 +634,9 @@ packages:
|
|||
'@types/diff@5.2.1':
|
||||
resolution: {integrity: sha512-uxpcuwWJGhe2AR1g8hD9F5OYGCqjqWnBUQFD8gMZsDbv8oPHzxJF6iMO6n8Tk0AdzlxoaaoQhOYlIg/PukVU8g==}
|
||||
|
||||
'@types/dompurify@3.0.5':
|
||||
resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==}
|
||||
|
||||
'@types/eslint@9.6.0':
|
||||
resolution: {integrity: sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==}
|
||||
|
||||
|
@ -649,6 +664,9 @@ packages:
|
|||
'@types/jsonfile@6.1.4':
|
||||
resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
|
||||
|
||||
'@types/katex@0.16.7':
|
||||
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
|
||||
|
||||
'@types/lodash@4.14.194':
|
||||
resolution: {integrity: sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==}
|
||||
|
||||
|
@ -688,6 +706,9 @@ packages:
|
|||
'@types/scheduler@0.16.3':
|
||||
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||
|
||||
|
@ -995,6 +1016,10 @@ packages:
|
|||
commander@2.20.3:
|
||||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||
|
||||
commander@8.3.0:
|
||||
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
component-emitter@1.3.0:
|
||||
resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==}
|
||||
|
||||
|
@ -1127,6 +1152,9 @@ packages:
|
|||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
dompurify@3.1.7:
|
||||
resolution: {integrity: sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==}
|
||||
|
||||
dot-case@3.0.4:
|
||||
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
|
||||
|
||||
|
@ -1945,6 +1973,10 @@ packages:
|
|||
jszip@2.7.0:
|
||||
resolution: {integrity: sha512-JIsRKRVC3gTRo2vM4Wy9WBC3TRcfnIZU8k65Phi3izkvPH975FowRYtKGT6PxevA0XnJ/yO8b0QwV0ydVyQwfw==}
|
||||
|
||||
katex@0.16.11:
|
||||
resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==}
|
||||
hasBin: true
|
||||
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
|
@ -3052,6 +3084,10 @@ snapshots:
|
|||
|
||||
'@types/diff@5.2.1': {}
|
||||
|
||||
'@types/dompurify@3.0.5':
|
||||
dependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
|
||||
'@types/eslint@9.6.0':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
|
@ -3080,6 +3116,8 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/node': 18.16.3
|
||||
|
||||
'@types/katex@0.16.7': {}
|
||||
|
||||
'@types/lodash@4.14.194': {}
|
||||
|
||||
'@types/lodash@4.17.7': {}
|
||||
|
@ -3124,6 +3162,8 @@ snapshots:
|
|||
|
||||
'@types/scheduler@0.16.3': {}
|
||||
|
||||
'@types/trusted-types@2.0.7': {}
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 22.0.3
|
||||
|
@ -3494,6 +3534,8 @@ snapshots:
|
|||
|
||||
commander@2.20.3: {}
|
||||
|
||||
commander@8.3.0: {}
|
||||
|
||||
component-emitter@1.3.0: {}
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
@ -3612,6 +3654,8 @@ snapshots:
|
|||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
dompurify@3.1.7: {}
|
||||
|
||||
dot-case@3.0.4:
|
||||
dependencies:
|
||||
no-case: 3.0.4
|
||||
|
@ -4503,6 +4547,10 @@ snapshots:
|
|||
dependencies:
|
||||
pako: 1.0.11
|
||||
|
||||
katex@0.16.11:
|
||||
dependencies:
|
||||
commander: 8.3.0
|
||||
|
||||
keyv@4.5.4:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
|
|
|
@ -225,7 +225,7 @@ page.on("console", async e => {
|
|||
plugin,
|
||||
type,
|
||||
id,
|
||||
match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"),
|
||||
match: regex.replace(/\(\?:\[A-Za-z_\$\]\[\\w\$\]\*\)/g, "\\i"),
|
||||
error: await maybeGetError(e.args()[3])
|
||||
});
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import "./iconStyles.css";
|
||||
|
||||
import { getIntlMessage, getTheme, Theme } from "@utils/discord";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
|
@ -122,8 +122,8 @@ export function InfoIcon(props: IconProps) {
|
|||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
transform="translate(2 2)"
|
||||
d="M9,7 L11,7 L11,5 L9,5 L9,7 Z M10,18 C5.59,18 2,14.41 2,10 C2,5.59 5.59,2 10,2 C14.41,2 18,5.59 18,10 C18,14.41 14.41,18 10,18 L10,18 Z M10,4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16,4.4771525 0,10 C-1.33226763e-15,12.6521649 1.0535684,15.195704 2.92893219,17.0710678 C4.80429597,18.9464316 7.3478351,20 10,20 C12.6521649,20 15.195704,18.9464316 17.0710678,17.0710678 C18.9464316,15.195704 20,12.6521649 20,10 C20,7.3478351 18.9464316,4.80429597 17.0710678,2.92893219 C15.195704,1.0535684 12.6521649,2.22044605e-16 10,0 L10,4.4408921e-16 Z M9,15 L11,15 L11,9 L9,9 L9,15 L9,15 Z"
|
||||
fill-rule="evenodd"
|
||||
d="M23 12a11 11 0 1 1-22 0 11 11 0 0 1 22 0Zm-9.5-4.75a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Zm-.77 3.96a1 1 0 1 0-1.96-.42l-1.04 4.86a2.77 2.77 0 0 0 4.31 2.83l.24-.17a1 1 0 1 0-1.16-1.62l-.24.17a.77.77 0 0 1-1.2-.79l1.05-4.86Z" clip-rule="evenodd"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
|
@ -211,9 +211,10 @@ export function CogWheel(props: IconProps) {
|
|||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
fill="currentColor"
|
||||
d="M19.738 10H22V14H19.739C19.498 14.931 19.1 15.798 18.565 16.564L20 18L18 20L16.565 18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069 19.498 8.203 19.099 7.436 18.564L6 20L4 18L5.436 16.564C4.901 15.799 4.502 14.932 4.262 14H2V10H4.262C4.502 9.068 4.9 8.202 5.436 7.436L4 6L6 4L7.436 5.436C8.202 4.9 9.068 4.502 10 4.262V2H14V4.261C14.932 4.502 15.797 4.9 16.565 5.435L18 3.999L20 5.999L18.564 7.436C19.099 8.202 19.498 9.069 19.738 10ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z"
|
||||
fill-rule="evenodd"
|
||||
d="M10.56 1.1c-.46.05-.7.53-.64.98.18 1.16-.19 2.2-.98 2.53-.8.33-1.79-.15-2.49-1.1-.27-.36-.78-.52-1.14-.24-.77.59-1.45 1.27-2.04 2.04-.28.36-.12.87.24 1.14.96.7 1.43 1.7 1.1 2.49-.33.8-1.37 1.16-2.53.98-.45-.07-.93.18-.99.64a11.1 11.1 0 0 0 0 2.88c.06.46.54.7.99.64 1.16-.18 2.2.19 2.53.98.33.8-.14 1.79-1.1 2.49-.36.27-.52.78-.24 1.14.59.77 1.27 1.45 2.04 2.04.36.28.87.12 1.14-.24.7-.95 1.7-1.43 2.49-1.1.8.33 1.16 1.37.98 2.53-.07.45.18.93.64.99a11.1 11.1 0 0 0 2.88 0c.46-.06.7-.54.64-.99-.18-1.16.19-2.2.98-2.53.8-.33 1.79.14 2.49 1.1.27.36.78.52 1.14.24.77-.59 1.45-1.27 2.04-2.04.28-.36.12-.87-.24-1.14-.96-.7-1.43-1.7-1.1-2.49.33-.8 1.37-1.16 2.53-.98.45.07.93-.18.99-.64a11.1 11.1 0 0 0 0-2.88c-.06-.46-.54-.7-.99-.64-1.16.18-2.2-.19-2.53-.98-.33-.8.14-1.79 1.1-2.49.36-.27.52-.78.24-1.14a11.07 11.07 0 0 0-2.04-2.04c-.36-.28-.87-.12-1.14.24-.7.96-1.7 1.43-2.49 1.1-.8-.33-1.16-1.37-.98-2.53.07-.45-.18-.93-.64-.99a11.1 11.1 0 0 0-2.88 0ZM16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
|
@ -406,23 +407,30 @@ export function PencilIcon(props: IconProps) {
|
|||
);
|
||||
}
|
||||
|
||||
const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
|
||||
const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
|
||||
const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
|
||||
const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
|
||||
|
||||
export function GithubIcon(props: ImageProps) {
|
||||
const src = getTheme() === Theme.Light
|
||||
? GithubIconLight
|
||||
: GithubIconDark;
|
||||
|
||||
return <img {...props} src={src} />;
|
||||
export function GithubIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
viewBox="-3 -3 30 30"
|
||||
>
|
||||
<path
|
||||
fill={props.fill || "currentColor"}
|
||||
d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.438 9.8 8.205 11.385.6.11.82-.26.82-.577v-2.17c-3.338.726-4.042-1.61-4.042-1.61-.546-1.387-1.333-1.757-1.333-1.757-1.09-.745.083-.73.083-.73 1.205.084 1.84 1.237 1.84 1.237 1.07 1.835 2.807 1.305 3.492.998.108-.775.42-1.305.763-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.467-2.38 1.235-3.22-.123-.303-.535-1.523.117-3.176 0 0 1.008-.322 3.3 1.23.957-.266 1.98-.398 3-.403 1.02.005 2.043.137 3 .403 2.29-1.552 3.297-1.23 3.297-1.23.653 1.653.24 2.873.118 3.176.77.84 1.233 1.91 1.233 3.22 0 4.61-2.803 5.625-5.475 5.92.43.37.823 1.102.823 2.222v3.293c0 .32.218.694.825.577C20.565 21.797 24 17.298 24 12c0-6.63-5.37-12-12-12z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function WebsiteIcon(props: ImageProps) {
|
||||
const src = getTheme() === Theme.Light
|
||||
? WebsiteIconLight
|
||||
: WebsiteIconDark;
|
||||
|
||||
return <img {...props} src={src} />;
|
||||
export function WebsiteIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill={props.fill || "currentColor"}
|
||||
d="M12 2C6.486 2 2 6.486 2 12s4.486 10 10 10 10-4.486 10-10S17.514 2 12 2zM4 12c0-.899.156-1.762.431-2.569L6 11l2 2v2l2 2 1 1v1.931C7.061 19.436 4 16.072 4 12zm14.33 4.873C17.677 16.347 16.687 16 16 16v-1a2 2 0 0 0-2-2h-4v-3a2 2 0 0 0 2-2V7h1a2 2 0 0 0 2-2v-.411C17.928 5.778 20 8.65 20 12a7.947 7.947 0 0 1-1.67 4.873z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,16 +6,19 @@
|
|||
|
||||
import "./LinkIconButton.css";
|
||||
|
||||
import { getTheme, Theme } from "@utils/discord";
|
||||
import { MaskedLink, Tooltip } from "@webpack/common";
|
||||
|
||||
import { GithubIcon, WebsiteIcon } from "..";
|
||||
|
||||
export function GithubLinkIcon() {
|
||||
return <GithubIcon aria-hidden className={"vc-settings-modal-link-icon"} />;
|
||||
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF";
|
||||
return <GithubIcon aria-hidden fill={theme} className={"vc-settings-modal-link-icon"} />;
|
||||
}
|
||||
|
||||
export function WebsiteLinkIcon() {
|
||||
return <WebsiteIcon aria-hidden className={"vc-settings-modal-link-icon"} />;
|
||||
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF";
|
||||
return <WebsiteIcon aria-hidden fill={theme} className={"vc-settings-modal-link-icon"} />;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -247,7 +247,7 @@ function FullPatchInput({ setFind, setParsedFind, setMatch, setReplacement }: Fu
|
|||
}
|
||||
|
||||
try {
|
||||
const parsed = (0, eval)(`(${fullPatch})`) as Patch;
|
||||
const parsed = (0, eval)(`([${fullPatch}][0])`) as Patch;
|
||||
|
||||
if (!parsed.find) throw new Error("No 'find' field");
|
||||
if (!parsed.replacement) throw new Error("No 'replacement' field");
|
||||
|
|
|
@ -27,7 +27,12 @@ export async function loadLazyChunks() {
|
|||
|
||||
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g);
|
||||
|
||||
let foundCssDebuggingLoad = false;
|
||||
|
||||
async function searchAndLoadLazyChunks(factoryCode: string) {
|
||||
// Workaround to avoid loading the CSS debugging chunk which turns the app pink
|
||||
const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&"));
|
||||
|
||||
const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
|
||||
const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>();
|
||||
|
||||
|
@ -43,6 +48,16 @@ export async function loadLazyChunks() {
|
|||
let invalidChunkGroup = false;
|
||||
|
||||
for (const id of chunkIds) {
|
||||
if (hasCssDebuggingLoad) {
|
||||
if (chunkIds.length > 1) {
|
||||
throw new Error("Found multiple chunks in factory that loads the CSS debugging chunk");
|
||||
}
|
||||
|
||||
invalidChunks.add(id);
|
||||
invalidChunkGroup = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue;
|
||||
|
||||
const isWorkerAsset = await fetch(wreq.p + wreq.u(id))
|
||||
|
|
|
@ -59,15 +59,7 @@ export default definePlugin({
|
|||
replace: "$&return;"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: ".installedLogHooks)",
|
||||
replacement: {
|
||||
// if getDebugLogging() returns false, the hooks don't get installed.
|
||||
match: "getDebugLogging(){",
|
||||
replace: "getDebugLogging(){return false;"
|
||||
}
|
||||
},
|
||||
}
|
||||
],
|
||||
|
||||
startAt: StartAt.Init,
|
||||
|
|
|
@ -65,7 +65,7 @@ export default definePlugin({
|
|||
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
|
||||
},
|
||||
{
|
||||
match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,60}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
|
||||
match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
|
||||
replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})`
|
||||
}
|
||||
]
|
||||
|
|
|
@ -28,10 +28,18 @@ export default definePlugin({
|
|||
patches: [
|
||||
{
|
||||
find: 'action:"EXPAND_ROLES"',
|
||||
replacement: {
|
||||
match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/,
|
||||
replace: (_, rest, setExpandedRoles) => `${rest}!0)`
|
||||
}
|
||||
replacement: [
|
||||
{
|
||||
match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/,
|
||||
replace: (_, rest, setExpandedRoles) => `${rest}!0)`
|
||||
},
|
||||
{
|
||||
// Fix not calculating non-expanded roles because the above patch makes the default "expanded",
|
||||
// which makes the collapse button never show up and calculation never occur
|
||||
match: /(?<=useLayoutEffect\(\(\)=>{if\()\i/,
|
||||
replace: isExpanded => "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
43
src/plugins/badge.ts
Normal file
43
src/plugins/badge.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
/* eslint-disable header/header */
|
||||
import { BadgePosition, ProfileBadge } from "@api/Badges";
|
||||
import { Badges } from "@api/index";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { UserStore } from "@webpack/common";
|
||||
|
||||
const SHIGGY_BADGE = "https://cdn.discordapp.com/emojis/1101838344146665502.gif?size=240&quality=lossless";
|
||||
const BLOBFOXBOX_BADGE = "https://cdn.discordapp.com/emojis/1036216552736952350.webp?size=240&quality=lossless";
|
||||
|
||||
const ShiggyBadge: ProfileBadge = {
|
||||
description: "true shiggy fan",
|
||||
image: SHIGGY_BADGE,
|
||||
position: BadgePosition.START,
|
||||
props: {
|
||||
style: { transform: "scale(0.9)" }
|
||||
},
|
||||
shouldShow: ({ user }) => user.id === UserStore.getCurrentUser().id,
|
||||
link: "https://ryanccn.dev/"
|
||||
};
|
||||
const BlobfoxBoxBadge: ProfileBadge = {
|
||||
description: "blobfox",
|
||||
image: BLOBFOXBOX_BADGE,
|
||||
position: BadgePosition.START,
|
||||
props: {
|
||||
style: { transform: "scale(0.9)" }
|
||||
},
|
||||
shouldShow: ({ user }) => user.id === UserStore.getCurrentUser().id,
|
||||
link: "https://ryanccn.dev/"
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "Ryan's Extra Badges",
|
||||
description: "shiggy",
|
||||
authors: [Devs.RyanCaoDev],
|
||||
dependencies: ["BadgeAPI"],
|
||||
|
||||
|
||||
start() {
|
||||
Badges.addBadge(ShiggyBadge);
|
||||
Badges.addBadge(BlobfoxBoxBadge);
|
||||
},
|
||||
});
|
|
@ -275,16 +275,16 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
|
||||
try {
|
||||
return child => {
|
||||
if (isBetterFolders) {
|
||||
return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
|
||||
try {
|
||||
return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
|
||||
|
|
63
src/plugins/bottom/components/Indicator.tsx
Normal file
63
src/plugins/bottom/components/Indicator.tsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This plugin was modified from code licensed under the following license:
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021-present Sebastian Law
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Tooltip } from "@webpack/common";
|
||||
|
||||
export default function Indicator({ layers, bottom }: { layers: number; bottom: boolean; }) {
|
||||
return (
|
||||
<Tooltip color="black" position="top" text={layers <= 1 ? "🥺" : `Decoded from ${layers} nested bottom messages`}>
|
||||
{({ onMouseLeave, onMouseEnter }) => (
|
||||
<span
|
||||
className={`power-bottom-indicator ${findByPropsLazy("edited").edited}`}
|
||||
style={{ color: "var(--text-muted)" }}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
{bottom ? "(bottom)" : "(original)"}
|
||||
</span>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
73
src/plugins/bottom/encoding.ts
Normal file
73
src/plugins/bottom/encoding.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file was adapted from https://github.com/bottom-software-foundation/bottom-js
|
||||
* Which is, hopefully, licensed under MIT.
|
||||
*/
|
||||
|
||||
const CHARACTER_VALUES: [number, string][] = [
|
||||
[200, "🫂"],
|
||||
[50, "💖"],
|
||||
[10, "✨"],
|
||||
[5, "🥺"],
|
||||
[1, ","],
|
||||
[0, "❤️"],
|
||||
];
|
||||
const SECTION_SEPERATOR = "👉👈";
|
||||
const FINAL_TERMINATOR = new RegExp(`(${SECTION_SEPERATOR})?$`);
|
||||
|
||||
function encodeChar(charValue: number): string {
|
||||
if (charValue === 0) return "";
|
||||
const [val, currentCase]: [number, string] =
|
||||
CHARACTER_VALUES.find(([val]) => charValue >= val) || CHARACTER_VALUES[-1];
|
||||
return `${currentCase}${encodeChar(charValue - val)}`;
|
||||
}
|
||||
|
||||
export function encode(value: string): string {
|
||||
return Array.from(new TextEncoder().encode(value))
|
||||
.map((v: number) => encodeChar(v) + SECTION_SEPERATOR)
|
||||
.join("");
|
||||
}
|
||||
|
||||
export function decode(value: string): string {
|
||||
return new TextDecoder().decode(Uint8Array.from(
|
||||
value
|
||||
.trim()
|
||||
.replace(FINAL_TERMINATOR, "")
|
||||
.split(SECTION_SEPERATOR)
|
||||
.map(letters => {
|
||||
return Array.from(letters)
|
||||
.map(character => {
|
||||
const [value, emoji]: [number, string] = CHARACTER_VALUES.find(
|
||||
([_, em]) => em === character
|
||||
) || [-1, ""];
|
||||
if (!emoji) {
|
||||
throw new TypeError(`Invalid bottom text: '${character}'`);
|
||||
}
|
||||
return value;
|
||||
})
|
||||
.reduce((p, c) => p + c);
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
export default {
|
||||
encode: encode,
|
||||
decode: decode
|
||||
};
|
158
src/plugins/bottom/handler.ts
Normal file
158
src/plugins/bottom/handler.ts
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This plugin was modified from code licensed under the following license:
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021-present Sebastian Law
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { FluxDispatcher, MessageStore } from "@webpack/common";
|
||||
import type { Message } from "discord-types/general";
|
||||
|
||||
import Bottom from "./encoding";
|
||||
|
||||
class BottomHandler {
|
||||
|
||||
cache: Record<string, Record<string, { originalContent: string; top?: boolean; layers?: number; }>>;
|
||||
re: RegExp;
|
||||
|
||||
constructor() {
|
||||
this.cache = {};
|
||||
this.re = /((?:((?:\uD83E\uDEC2)?(?:💖)*(?:✨)*(?:🥺)*(?:,)*(❤️)?)(?:👉👈|\u200b))+)/gm;
|
||||
}
|
||||
|
||||
isTranslated(message) {
|
||||
if (
|
||||
!this.cache[message.channel_id] ||
|
||||
!this.cache[message.channel_id][message.id]
|
||||
) { return false; }
|
||||
|
||||
return this.cache[message.channel_id][message.id].originalContent !== message.content;
|
||||
}
|
||||
|
||||
translate(text: string, notNested: boolean) {
|
||||
var original = text;
|
||||
var translated = text;
|
||||
var layers = 0;
|
||||
while (original.match(this.re)) {
|
||||
translated = original.replace(this.re, (str, p1, offset, s) => Bottom.decode(p1) || p1);
|
||||
|
||||
// the regex can sometimes pick up invalid bottom in which case we want to return to avoid an infinite loop
|
||||
if (translated === original || notNested) break;
|
||||
else {
|
||||
original = translated;
|
||||
layers++;
|
||||
}
|
||||
}
|
||||
return {
|
||||
translated: translated,
|
||||
layers: layers,
|
||||
};
|
||||
}
|
||||
|
||||
translateMessage(message: Message, decodeLayers: boolean) {
|
||||
if (!message.content || message.content.length === 0) {
|
||||
return "";
|
||||
}
|
||||
// Build cache if it doesn't exist
|
||||
if (!this.cache[message.channel_id]) {
|
||||
this.cache[message.channel_id] = {};
|
||||
}
|
||||
if (!this.cache[message.channel_id][message.id]) {
|
||||
this.cache[message.channel_id][message.id] = {
|
||||
originalContent: message.content,
|
||||
};
|
||||
}
|
||||
|
||||
const cached = this.cache[message.channel_id][message.id];
|
||||
|
||||
if (this.isTranslated(message)) {
|
||||
// if we're reverting back to original, just set the content back to original
|
||||
message.content = cached.originalContent;
|
||||
this.updateMessage(message);
|
||||
} else {
|
||||
// the message hasn't been edited, let's try to decode it
|
||||
const { translated, layers } = this.translate(message.content, !decodeLayers);
|
||||
if (translated === message.content) {
|
||||
// we don't want to do anything if there is no bottom
|
||||
// since the translation fails, mark this message to not show the indicator
|
||||
cached.top = true;
|
||||
throw new Error("No Bottom detected 🥺");
|
||||
} else {
|
||||
// let the indicator show how many layers of decoding we did
|
||||
cached.layers = layers;
|
||||
message.content = translated;
|
||||
this.updateMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateMessage(message: Message) {
|
||||
console.log({
|
||||
bottomTranslation: true,
|
||||
type: "MESSAGE_UPDATE",
|
||||
message,
|
||||
});
|
||||
FluxDispatcher.dispatch({
|
||||
bottomTranslation: true,
|
||||
type: "MESSAGE_UPDATE",
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
for (const channelID in this.cache) {
|
||||
for (const messageID in this.cache[channelID]) {
|
||||
this.removeMessage(channelID, messageID);
|
||||
}
|
||||
}
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
removeMessage(channelID: string, messageID: string, reset = true) {
|
||||
const message = MessageStore.getMessage(channelID, messageID);
|
||||
if (reset) {
|
||||
message.content = this.cache[channelID][messageID].originalContent;
|
||||
this.updateMessage(message);
|
||||
}
|
||||
delete this.cache[channelID][messageID];
|
||||
}
|
||||
}
|
||||
|
||||
export default BottomHandler;
|
262
src/plugins/bottom/index.tsx
Normal file
262
src/plugins/bottom/index.tsx
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This plugin was modified from code licensed under the following license:
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021-present Sebastian Law
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { findOption, RequiredMessageOption } from "@api/Commands";
|
||||
import { addAccessory, removeAccessory } from "@api/MessageAccessories";
|
||||
import { addPreSendListener, removePreSendListener } from "@api/MessageEvents";
|
||||
import { addButton, removeButton } from "@api/MessagePopover";
|
||||
import { definePluginSettings } from "@api/settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { ChannelStore, Toasts } from "@webpack/common";
|
||||
|
||||
import Indicator from "./components/Indicator";
|
||||
import Bottom from "./encoding";
|
||||
import BottomHandler from "./handler";
|
||||
|
||||
const Handler = new BottomHandler();
|
||||
|
||||
const settings = definePluginSettings({
|
||||
"decode-layers": {
|
||||
description: "Decode Layers",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
},
|
||||
"auto-encode-send": {
|
||||
description: "Automatically encode outgoing messages",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
},
|
||||
"encode-send-type": {
|
||||
description: "Automatic Encode Behavior",
|
||||
type: OptionType.SELECT,
|
||||
options:
|
||||
[
|
||||
{
|
||||
label: "All",
|
||||
default: true,
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: "Inline (Greedy)",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "Inline (Parsed)",
|
||||
value: 2,
|
||||
}
|
||||
],
|
||||
},
|
||||
"inline-bottom-prefix": {
|
||||
description: "Inline bottom prefix",
|
||||
type: OptionType.STRING,
|
||||
default: "👉",
|
||||
},
|
||||
"inline-bottom-suffix": {
|
||||
description: "Inline bottom suffix",
|
||||
type: OptionType.STRING,
|
||||
default: "👈",
|
||||
},
|
||||
});
|
||||
|
||||
const escapeRegex: (string: string) => string = string => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
|
||||
function count(string: string, subString: string): number {
|
||||
var n = 0;
|
||||
var pos = 0;
|
||||
const step = subString.length;
|
||||
|
||||
while (true) {
|
||||
pos = string.indexOf(subString, pos);
|
||||
if (pos >= 0) {
|
||||
n++;
|
||||
pos += step;
|
||||
} else break;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
function inlineEncode(p: string, s: string, text: string): string {
|
||||
var np = count(text, p);
|
||||
var ns = count(text, s);
|
||||
|
||||
if (np === 0 || ns === 0) return text;
|
||||
|
||||
var pl = p.length;
|
||||
var sl = s.length;
|
||||
const result: string[] = [];
|
||||
let idx = 0;
|
||||
|
||||
while (true) {
|
||||
var startIndex = text.indexOf(p, idx);
|
||||
|
||||
if (startIndex < 0) {
|
||||
result.push(text.slice(idx));
|
||||
break;
|
||||
}
|
||||
|
||||
var endIndex = text.indexOf(s, startIndex + pl);
|
||||
|
||||
if (endIndex < 0) {
|
||||
result.push(text.slice(idx));
|
||||
break;
|
||||
}
|
||||
|
||||
result.push(text.slice(idx, startIndex));
|
||||
startIndex += pl;
|
||||
result.push(Bottom.encode(text.slice(startIndex, endIndex)));
|
||||
endIndex += sl;
|
||||
idx = endIndex;
|
||||
}
|
||||
|
||||
return result.join("");
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "Bottom",
|
||||
description: "The Vencord plugin for bottom 🥺",
|
||||
authors: [
|
||||
{
|
||||
id: 1038096782963507210n,
|
||||
name: "skyevg",
|
||||
},
|
||||
],
|
||||
dependencies: ["MessagePopoverAPI", "CommandsAPI", "MessageEventsAPI", "MessageAccessoriesAPI"],
|
||||
|
||||
settings,
|
||||
|
||||
start() {
|
||||
addButton("bottom", msg => {
|
||||
return {
|
||||
label: "Translate Bottom",
|
||||
icon: () => (
|
||||
<svg x="0" y="0" aria-hidden="false" width="22" height="22" viewBox="0 0 36 36" fill="currentColor" className="icon">
|
||||
<circle fill="#FFCC4D" cx="18" cy="18" r="18" />
|
||||
<path fill="#65471B" d="M20.996 27c-.103 0-.206-.016-.309-.049-1.76-.571-3.615-.571-5.375 0-.524.169-1.089-.117-1.26-.642-.171-.525.117-1.089.643-1.26 2.162-.702 4.447-.702 6.609 0 .525.171.813.735.643 1.26-.137.421-.529.691-.951.691z" />
|
||||
<path fill="#FFF" d="M30.335 12.068c-.903 2.745-3.485 4.715-6.494 4.715-.144 0-.289-.005-.435-.014-1.477-.093-2.842-.655-3.95-1.584.036.495.076.997.136 1.54.152 1.388.884 2.482 2.116 3.163.82.454 1.8.688 2.813.752 1.734.109 3.57-.28 4.873-.909 1.377-.665 2.272-1.862 2.456-3.285.183-1.415-.354-2.924-1.515-4.378z" />
|
||||
<path fill="#65471B" d="M21.351 7.583c-1.297.55-1.947 2.301-1.977 5.289l.039.068c.897 1.319 2.373 2.224 4.088 2.332.114.007.228.011.341.011 2.634 0 4.849-1.937 5.253-4.524-.115-.105-.221-.212-.343-.316-3.715-3.17-6.467-3.257-7.401-2.86z" />
|
||||
<path fill="#F4900C" d="M23.841 16.783c3.009 0 5.591-1.97 6.494-4.715-.354-.443-.771-.88-1.241-1.309-.404 2.587-2.619 4.524-5.253 4.524-.113 0-.227-.004-.341-.011-1.715-.108-3.191-1.013-4.088-2.332l-.039-.068c-.007.701.021 1.473.083 2.313 1.108.929 2.473 1.491 3.95 1.584.146.01.291.014.435.014z" />
|
||||
<circle fill="#FFF" cx="21.413" cy="10.705" r="1.107" />
|
||||
<path fill="#FFF" d="M12.159 16.783c-3.009 0-5.591-1.97-6.494-4.715-1.161 1.454-1.697 2.963-1.515 4.377.185 1.423 1.079 2.621 2.456 3.285 1.303.629 3.138 1.018 4.873.909 1.013-.064 1.993-.297 2.813-.752 1.231-.681 1.963-1.775 2.116-3.163.06-.542.1-1.042.136-1.536-1.103.923-2.47 1.487-3.95 1.58-.146.011-.291.015-.435.015z" />
|
||||
<path fill="#65471B" d="M12.159 15.283c.113 0 .227-.004.341-.011 1.715-.108 3.191-1.013 4.088-2.332l.039-.068c-.031-2.988-.68-4.739-1.977-5.289-.934-.397-3.687-.31-7.401 2.859-.122.104-.227.211-.343.316.404 2.588 2.619 4.525 5.253 4.525z" />
|
||||
<path fill="#F4900C" d="M16.626 12.872l-.039.068c-.897 1.319-2.373 2.224-4.088 2.332-.114.007-.228.011-.341.011-2.634 0-4.849-1.937-5.253-4.524-.47.429-.887.866-1.241 1.309.903 2.745 3.485 4.715 6.494 4.715.144 0 .289-.005.435-.014 1.48-.093 2.847-.657 3.95-1.58.062-.841.091-1.614.083-2.317z" />
|
||||
<path fill="#FFF" d="M9.781 11.81c.61-.038 1.074-.564 1.035-1.174-.038-.61-.564-1.074-1.174-1.036-.61.038-1.074.564-1.036 1.174.039.61.565 1.074 1.175 1.036z" />
|
||||
</svg>
|
||||
),
|
||||
message: msg,
|
||||
channel: ChannelStore.getChannel(msg.channel_id),
|
||||
onClick: async () => {
|
||||
try {
|
||||
Handler.translateMessage(msg, settings.store["decode-layers"]);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
Toasts.show(
|
||||
{
|
||||
id: Toasts.genId(),
|
||||
message: e.message,
|
||||
type: Toasts.Type.MESSAGE
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
addAccessory("bottom", props => {
|
||||
try {
|
||||
if (!Handler.cache[props.message.channel_id][props.message.id].top) {
|
||||
try {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Indicator layers={Handler.cache[props.message.channel_id][props.message.id].layers ?? 0} bottom={!Handler.isTranslated(props.message)} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return null;
|
||||
});
|
||||
|
||||
this.preSend = addPreSendListener((_, msg) => {
|
||||
if (settings.store["auto-encode-send"]) {
|
||||
const sendType = settings.store["encode-send-type"];
|
||||
var { content } = msg;
|
||||
|
||||
switch (sendType) {
|
||||
case 0: // all
|
||||
content = Bottom.encode(content);
|
||||
break;
|
||||
case 1: // inline greedy
|
||||
var prefix = escapeRegex(settings.store["inline-bottom-prefix"]);
|
||||
var suffix = escapeRegex(settings.store["inline-bottom-suffix"]);
|
||||
var reg = new RegExp(`${prefix}(.+)${suffix}`, "gm");
|
||||
content = content.replace(reg, (str, p1, o, s) => Bottom.encode(p1));
|
||||
break;
|
||||
case 2: // inline parsed
|
||||
var prefix = settings.store["inline-bottom-prefix"];
|
||||
var suffix = settings.store["inline-bottom-prefix"];
|
||||
content = inlineEncode(prefix, suffix, content);
|
||||
break;
|
||||
}
|
||||
msg.content = content;
|
||||
}
|
||||
});
|
||||
},
|
||||
stop() {
|
||||
removeButton("bottom");
|
||||
removeAccessory("bottom");
|
||||
removePreSendListener(this.preSend);
|
||||
},
|
||||
|
||||
commands: [
|
||||
{
|
||||
name: "bottom",
|
||||
description: "Translate and send text as bottom 🥺",
|
||||
options: [RequiredMessageOption],
|
||||
execute: opts => ({
|
||||
content: Bottom.encode(findOption(opts, "message", "")),
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
|
@ -110,7 +110,7 @@ const settings = definePluginSettings({
|
|||
|
||||
export default definePlugin({
|
||||
name: "ClientTheme",
|
||||
authors: [Devs.F53, Devs.Nuckyz],
|
||||
authors: [Devs.Nuckyz],
|
||||
description: "Recreation of the old client theme experiment. Add a color to your Discord client theme",
|
||||
settings,
|
||||
|
||||
|
|
|
@ -147,5 +147,5 @@ export default definePlugin({
|
|||
replace: "$self.NoopLogger()"
|
||||
}
|
||||
}
|
||||
],
|
||||
]
|
||||
});
|
||||
|
|
|
@ -93,7 +93,7 @@ export const useAuthorizationStore = proxyLazy(() => zustandCreate(
|
|||
} as AuthorizationState),
|
||||
{
|
||||
name: "decor-auth",
|
||||
getStorage: () => indexedDBStorage,
|
||||
storage: indexedDBStorage,
|
||||
partialize: state => ({ tokens: state.tokens }),
|
||||
onRehydrateStorage: () => state => state?.init()
|
||||
}
|
||||
|
|
|
@ -95,24 +95,39 @@ export const useUsersDecorationsStore = proxyLazy(() => zustandCreate((set: any,
|
|||
} as UsersDecorationsState)));
|
||||
|
||||
export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | null | undefined {
|
||||
const [decorAvatarDecoration, setDecorAvatarDecoration] = useState<string | null>(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null);
|
||||
try {
|
||||
const [decorAvatarDecoration, setDecorAvatarDecoration] = useState<string | null>(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null);
|
||||
|
||||
useEffect(() => {
|
||||
const destructor = useUsersDecorationsStore.subscribe(
|
||||
state => {
|
||||
if (!user) return;
|
||||
const newDecorAvatarDecoration = state.getAsset(user.id);
|
||||
if (!newDecorAvatarDecoration) return;
|
||||
if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration);
|
||||
}
|
||||
);
|
||||
useEffect(() => {
|
||||
const destructor = (() => {
|
||||
try {
|
||||
return useUsersDecorationsStore.subscribe(
|
||||
state => {
|
||||
if (!user) return;
|
||||
const newDecorAvatarDecoration = state.getAsset(user.id);
|
||||
if (!newDecorAvatarDecoration) return;
|
||||
if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration);
|
||||
}
|
||||
);
|
||||
} catch {
|
||||
return () => { };
|
||||
}
|
||||
})();
|
||||
|
||||
if (user) {
|
||||
const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState();
|
||||
fetchUserDecorAvatarDecoration(user.id);
|
||||
}
|
||||
return destructor;
|
||||
}, []);
|
||||
try {
|
||||
if (user) {
|
||||
const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState();
|
||||
fetchUserDecorAvatarDecoration(user.id);
|
||||
}
|
||||
} catch { }
|
||||
|
||||
return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null;
|
||||
return destructor;
|
||||
}, []);
|
||||
|
||||
return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { openInviteModal } from "@utils/discord";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { classes, copyWithToast } from "@utils/misc";
|
||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { Queue } from "@utils/Queue";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
@ -49,6 +50,8 @@ interface SectionHeaderProps {
|
|||
section: Section;
|
||||
}
|
||||
|
||||
const fetchAuthorsQueue = new Queue();
|
||||
|
||||
function SectionHeader({ section }: SectionHeaderProps) {
|
||||
const hasSubtitle = typeof section.subtitle !== "undefined";
|
||||
const hasAuthorIds = typeof section.authorIds !== "undefined";
|
||||
|
@ -56,17 +59,18 @@ function SectionHeader({ section }: SectionHeaderProps) {
|
|||
const [authors, setAuthors] = useState<User[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
fetchAuthorsQueue.push(async () => {
|
||||
if (!section.authorIds) return;
|
||||
|
||||
for (const authorId of section.authorIds) {
|
||||
const author = UserStore.getUser(authorId) ?? await UserUtils.getUser(authorId);
|
||||
const author = UserStore.getUser(authorId) ?? await UserUtils.getUser(authorId).catch(() => null);
|
||||
if (author == null) continue;
|
||||
|
||||
setAuthors(authors => [...authors, author]);
|
||||
}
|
||||
})();
|
||||
});
|
||||
}, [section.authorIds]);
|
||||
|
||||
|
||||
return <div>
|
||||
<Flex>
|
||||
<Forms.FormTitle style={{ flexGrow: 1 }}>{section.title}</Forms.FormTitle>
|
||||
|
|
|
@ -20,7 +20,7 @@ import { AvatarDecorationModalPreview } from "../components";
|
|||
const FileUpload = findComponentByCodeLazy("fileUploadInput,");
|
||||
|
||||
const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE=3]="POSITIVE', {
|
||||
HelpMessageTypes: filters.byProps("POSITIVE", "WARNING"),
|
||||
HelpMessageTypes: filters.byProps("POSITIVE", "WARNING", "INFO"),
|
||||
HelpMessage: filters.byCode(".iconDiv")
|
||||
});
|
||||
|
||||
|
@ -119,8 +119,8 @@ function CreateDecorationModal(props: ModalProps) {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Forms.FormText type="description" className={Margins.bottom16}>
|
||||
<br />You can receive updates on your decoration's review by joining <Link
|
||||
<HelpMessage messageType={HelpMessageTypes.INFO} className={Margins.bottom8}>
|
||||
To receive updates on your decoration's review, join <Link
|
||||
href={`https://discord.gg/${INVITE_KEY}`}
|
||||
onClick={async e => {
|
||||
e.preventDefault();
|
||||
|
@ -138,8 +138,8 @@ function CreateDecorationModal(props: ModalProps) {
|
|||
}}
|
||||
>
|
||||
Decor's Discord server
|
||||
</Link>.
|
||||
</Forms.FormText>
|
||||
</Link> and allow direct messages.
|
||||
</HelpMessage>
|
||||
</ErrorBoundary>
|
||||
</ModalContent>
|
||||
<ModalFooter className={cl("modal-footer")}>
|
||||
|
|
|
@ -310,7 +310,8 @@ function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Om
|
|||
}
|
||||
|
||||
function isGifUrl(url: string) {
|
||||
return new URL(url).pathname.endsWith(".gif");
|
||||
const u = new URL(url);
|
||||
return u.pathname.endsWith(".gif") || u.searchParams.get("animated") === "true";
|
||||
}
|
||||
|
||||
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
|
||||
|
|
|
@ -20,11 +20,11 @@ import { addPreEditListener, addPreSendListener, removePreEditListener, removePr
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
|
||||
import { getCurrentGuild } from "@utils/discord";
|
||||
import { getCurrentGuild, getEmojiURL } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType, Patch } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
|
||||
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||
import type { Emoji } from "@webpack/types";
|
||||
import type { Message } from "discord-types/general";
|
||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||
|
@ -920,7 +920,7 @@ export default definePlugin({
|
|||
|
||||
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
|
||||
|
||||
const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize }));
|
||||
const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize));
|
||||
url.searchParams.set("size", s.emojiSize.toString());
|
||||
url.searchParams.set("name", emoji.name);
|
||||
|
||||
|
@ -953,7 +953,7 @@ export default definePlugin({
|
|||
|
||||
hasBypass = true;
|
||||
|
||||
const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize }));
|
||||
const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize));
|
||||
url.searchParams.set("size", s.emojiSize.toString());
|
||||
url.searchParams.set("name", emoji.name);
|
||||
|
||||
|
|
63
src/plugins/katex/index.tsx
Normal file
63
src/plugins/katex/index.tsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
import "./katex.css";
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { ReporterTestable } from "@utils/types";
|
||||
import { React, useMemo } from "@webpack/common";
|
||||
import katex from "katex";
|
||||
import domPurify from "dompurify";
|
||||
|
||||
export interface HighlighterProps {
|
||||
lang?: string;
|
||||
content: string;
|
||||
isPreview: boolean;
|
||||
tempSettings?: Record<string, any>;
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "KaTeX",
|
||||
description: "TeX typesetting in discord",
|
||||
authors: [Devs.skyevg],
|
||||
reporterTestable: ReporterTestable.Patches,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "codeBlock:{react(",
|
||||
replacement: {
|
||||
match: /codeBlock:\{react\((\i),(\i),(\i)\)\{/,
|
||||
replace: "$&if($1.lang == 'latex') return $self.createBlock($1,$2,$3);"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "inlineCode:{react:(",
|
||||
replacement: {
|
||||
match: /inlineCode:\{react:\((\i),(\i),(\i)\)=>/,
|
||||
replace: "$&($1.content.startsWith('$$') && $1.content.endsWith('$$'))?$self.createInline($1,$2,$3):"
|
||||
}
|
||||
}
|
||||
],
|
||||
start: async () => {
|
||||
},
|
||||
stop: () => {
|
||||
},
|
||||
|
||||
createBlock: (props: HighlighterProps) => (
|
||||
<Latex displayMode formula={props.content} />
|
||||
),
|
||||
createInline: (props: HighlighterProps) => (
|
||||
<Latex formula={props.content.substring(1, props.content.length - 1)} />
|
||||
),
|
||||
});
|
||||
|
||||
function Latex({ formula, displayMode }: { formula: string, displayMode?: boolean; }) {
|
||||
const result = useMemo(() => {
|
||||
const html = katex.renderToString(formula, {
|
||||
displayMode,
|
||||
throwOnError: false
|
||||
});
|
||||
return domPurify.sanitize(html);
|
||||
}, [formula, displayMode]);
|
||||
|
||||
return displayMode
|
||||
? <div className="tex" dangerouslySetInnerHTML={{ __html: result }} />
|
||||
: <span className="tex" dangerouslySetInnerHTML={{ __html: result }} />;
|
||||
}
|
6
src/plugins/katex/katex.css
Normal file
6
src/plugins/katex/katex.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
@import url("https://unpkg.com/katex@0.16.9/dist/katex.min.css");
|
||||
|
||||
.katex-display {
|
||||
width: min-content;
|
||||
margin: 1em;
|
||||
}
|
|
@ -28,7 +28,7 @@ import { getIntlMessage } from "@utils/discord";
|
|||
import { Logger } from "@utils/Logger";
|
||||
import { classes } from "@utils/misc";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
|
||||
|
@ -43,7 +43,6 @@ interface MLMessage extends Message {
|
|||
}
|
||||
|
||||
const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage");
|
||||
const getMessage = findByCodeLazy('replace(/^\\n+|\\n+$/g,"")');
|
||||
|
||||
function addDeleteStyle() {
|
||||
if (Settings.plugins.MessageLogger.deleteStyle === "text") {
|
||||
|
@ -312,35 +311,33 @@ export default definePlugin({
|
|||
);
|
||||
},
|
||||
|
||||
Messages: {
|
||||
// DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}")
|
||||
// TODO: find a better way to generate intl messages
|
||||
DELETED_MESSAGE_COUNT: () => ({
|
||||
ast: [[
|
||||
6,
|
||||
"count",
|
||||
{
|
||||
"=0": ["No deleted messages"],
|
||||
one: [
|
||||
[
|
||||
1,
|
||||
"count"
|
||||
],
|
||||
" deleted message"
|
||||
// DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}")
|
||||
// TODO: Find a better way to generate intl messages
|
||||
DELETED_MESSAGE_COUNT: () => ({
|
||||
ast: [[
|
||||
6,
|
||||
"count",
|
||||
{
|
||||
"=0": ["No deleted messages"],
|
||||
one: [
|
||||
[
|
||||
1,
|
||||
"count"
|
||||
],
|
||||
other: [
|
||||
[
|
||||
1,
|
||||
"count"
|
||||
],
|
||||
" deleted messages"
|
||||
]
|
||||
},
|
||||
0,
|
||||
"cardinal"
|
||||
]]
|
||||
})
|
||||
},
|
||||
" deleted message"
|
||||
],
|
||||
other: [
|
||||
[
|
||||
1,
|
||||
"count"
|
||||
],
|
||||
" deleted messages"
|
||||
]
|
||||
},
|
||||
0,
|
||||
"cardinal"
|
||||
]]
|
||||
}),
|
||||
|
||||
patches: [
|
||||
{
|
||||
|
@ -531,7 +528,7 @@ export default definePlugin({
|
|||
},
|
||||
{
|
||||
match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\?.*?:/,
|
||||
replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.Messages.DELETED_MESSAGE_COUNT:',
|
||||
replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.DELETED_MESSAGE_COUNT:',
|
||||
},
|
||||
],
|
||||
predicate: () => Settings.plugins.MessageLogger.collapseDeleted
|
||||
|
|
|
@ -28,8 +28,8 @@ import { Message } from "discord-types/general";
|
|||
const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked");
|
||||
|
||||
interface MessageDeleteProps {
|
||||
// i18n message i18n.t["+FcYMz"] if deleted, with args
|
||||
collapsedReason: () => any
|
||||
// Internal intl message for BLOCKED_MESSAGE_COUNT
|
||||
collapsedReason: () => any;
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
|
|
|
@ -20,7 +20,7 @@ const settings = definePluginSettings({
|
|||
export default definePlugin({
|
||||
name: "NoMosaic",
|
||||
authors: [Devs.AutumnVN],
|
||||
description: "Removes Discord new image mosaic",
|
||||
description: "Removes Discord image mosaic",
|
||||
tags: ["image", "mosaic", "media"],
|
||||
|
||||
settings,
|
||||
|
@ -29,8 +29,8 @@ export default definePlugin({
|
|||
{
|
||||
find: '=>"IMAGE"===',
|
||||
replacement: {
|
||||
match: /=>"IMAGE"===\i\|\|"VIDEO"===\i;/,
|
||||
replace: "=>false;"
|
||||
match: /=>"IMAGE"===\i\|\|"VIDEO"===\i(?:\|\|("VISUAL_PLACEHOLDER"===\i))?;/,
|
||||
replace: (_, visualPlaceholderPred) => visualPlaceholderPred != null ? `=>${visualPlaceholderPred};` : "=>false;"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -87,7 +87,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "trackAnnouncementMessageLinkClicked({",
|
||||
replacement: {
|
||||
match: /function (\i\(\i,\i\)\{)(?=.{0,100}trusted:)/,
|
||||
match: /function (\i\(\i,\i\)\{)(?=.{0,150}trusted:)/,
|
||||
replace: "async function $1 if(await $self.handleLink(...arguments)) return;"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -30,10 +30,10 @@ export default definePlugin({
|
|||
{
|
||||
find: ".removeMosaicItemHoverButton),",
|
||||
replacement: {
|
||||
match: /\.nonMediaMosaicItem\]:!(\i).{0,50}?children:\[\S,(\S)/,
|
||||
replace: "$&,$1&&$2&&$self.renderPiPButton(),"
|
||||
},
|
||||
},
|
||||
match: /\.nonMediaMosaicItem\]:.{0,40}children:\[(?<=showDownload:(\i).+?isVisualMediaType:(\i).+?)/,
|
||||
replace: "$&$1&&$2&&$self.renderPiPButton(),"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
renderPiPButton: ErrorBoundary.wrap(() => {
|
||||
|
|
|
@ -50,6 +50,8 @@ async function runMigrations() {
|
|||
|
||||
export async function syncAndRunChecks() {
|
||||
await runMigrations();
|
||||
if (UserStore.getCurrentUser() == null) return;
|
||||
|
||||
const [oldGuilds, oldGroups, oldFriends] = await DataStore.getMany([
|
||||
guildsKey(),
|
||||
groupsKey(),
|
||||
|
|
|
@ -20,6 +20,7 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { ChannelStore, GuildMemberStore, GuildStore } from "@webpack/common";
|
||||
|
@ -51,6 +52,12 @@ const settings = definePluginSettings({
|
|||
description: "Show role colors in the reactors list",
|
||||
restartNeeded: true
|
||||
},
|
||||
pollResults: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Show role colors in the poll results",
|
||||
restartNeeded: true
|
||||
},
|
||||
colorChatMessages: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
|
@ -62,14 +69,15 @@ const settings = definePluginSettings({
|
|||
description: "Intensity of message coloring.",
|
||||
markers: makeRange(0, 100, 10),
|
||||
default: 30
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default definePlugin({
|
||||
name: "RoleColorEverywhere",
|
||||
authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN, Devs.Kyuuhachi],
|
||||
authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN, Devs.Kyuuhachi, Devs.jamesbt365],
|
||||
description: "Adds the top role color anywhere possible",
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
// Chat Mentions
|
||||
{
|
||||
|
@ -77,82 +85,131 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
match: /onContextMenu:\i,color:\i,\.\.\.\i(?=,children:)(?<=user:(\i),channel:(\i).{0,500}?)/,
|
||||
replace: "$&,color:$self.getUserColor($1?.id,{channelId:$2?.id})"
|
||||
replace: "$&,color:$self.getColorInt($1?.id,$2?.id)"
|
||||
}
|
||||
],
|
||||
predicate: () => settings.store.chatMentions,
|
||||
predicate: () => settings.store.chatMentions
|
||||
},
|
||||
// Slate
|
||||
{
|
||||
find: ".userTooltip,children",
|
||||
replacement: [
|
||||
{
|
||||
match: /let\{id:(\i),guildId:(\i)[^}]*\}.*?\.\i,{(?=children)/,
|
||||
replace: "$&color:$self.getUserColor($1,{guildId:$2}),"
|
||||
match: /let\{id:(\i),guildId:\i,channelId:(\i)[^}]*\}.*?\.\i,{(?=children)/,
|
||||
replace: "$&color:$self.getColorInt($1,$2),"
|
||||
}
|
||||
],
|
||||
predicate: () => settings.store.chatMentions,
|
||||
predicate: () => settings.store.chatMentions
|
||||
},
|
||||
// Member List Role Headers
|
||||
{
|
||||
find: 'tutorialId:"whos-online',
|
||||
replacement: [
|
||||
{
|
||||
match: /null,\i," — ",\i\]/,
|
||||
replace: "null,$self.roleGroupColor(arguments[0])]"
|
||||
replace: "null,$self.RoleGroupColor(arguments[0])]"
|
||||
},
|
||||
],
|
||||
predicate: () => settings.store.memberList,
|
||||
predicate: () => settings.store.memberList
|
||||
},
|
||||
{
|
||||
find: "#{intl::THREAD_BROWSER_PRIVATE}",
|
||||
replacement: [
|
||||
{
|
||||
match: /children:\[\i," — ",\i\]/,
|
||||
replace: "children:[$self.roleGroupColor(arguments[0])]"
|
||||
replace: "children:[$self.RoleGroupColor(arguments[0])]"
|
||||
},
|
||||
],
|
||||
predicate: () => settings.store.memberList,
|
||||
predicate: () => settings.store.memberList
|
||||
},
|
||||
// Voice Users
|
||||
{
|
||||
find: "renderPrioritySpeaker",
|
||||
find: "renderPrioritySpeaker(){",
|
||||
replacement: [
|
||||
{
|
||||
match: /renderName\(\){.+?usernameSpeaking\]:.+?(?=children)/,
|
||||
replace: "$&...$self.getVoiceProps(this.props),"
|
||||
replace: "$&style:$self.getColorStyle(this?.props?.user?.id,this?.props?.guildId),"
|
||||
}
|
||||
],
|
||||
predicate: () => settings.store.voiceUsers,
|
||||
predicate: () => settings.store.voiceUsers
|
||||
},
|
||||
// Reaction List
|
||||
{
|
||||
find: ".reactorDefault",
|
||||
replacement: {
|
||||
match: /,onContextMenu:e=>.{0,15}\((\i),(\i),(\i)\).{0,250}tag:"strong"/,
|
||||
replace: "$&,style:{color:$self.getColor($2?.id,$1)}"
|
||||
match: /,onContextMenu:\i=>.{0,15}\((\i),(\i),(\i)\).{0,250}tag:"strong"/,
|
||||
replace: "$&,style:$self.getColorStyle($2?.id,$1?.channel?.id)"
|
||||
},
|
||||
predicate: () => settings.store.reactorsList,
|
||||
},
|
||||
// Poll Results
|
||||
{
|
||||
find: ",reactionVoteCounts",
|
||||
replacement: {
|
||||
match: /\.nickname,(?=children:)/,
|
||||
replace: "$&style:$self.getColorStyle(arguments[0]?.user?.id,arguments[0]?.channel?.id),"
|
||||
},
|
||||
predicate: () => settings.store.pollResults
|
||||
},
|
||||
// Messages
|
||||
{
|
||||
find: "#{intl::MESSAGE_EDITED}",
|
||||
replacement: {
|
||||
match: /(?<=isUnsupported\]:(\i)\.isUnsupported\}\),)(?=children:\[)/,
|
||||
replace: "style:{color:$self.useMessageColor($1)},"
|
||||
replace: "style:$self.useMessageColorsStyle($1),"
|
||||
},
|
||||
predicate: () => settings.store.colorChatMessages,
|
||||
},
|
||||
predicate: () => settings.store.colorChatMessages
|
||||
}
|
||||
],
|
||||
settings,
|
||||
|
||||
getColor(userId: string, { channelId, guildId }: { channelId?: string; guildId?: string; }) {
|
||||
if (!(guildId ??= ChannelStore.getChannel(channelId!)?.guild_id)) return null;
|
||||
return GuildMemberStore.getMember(guildId, userId)?.colorString ?? null;
|
||||
getColorString(userId: string, channelOrGuildId: string) {
|
||||
try {
|
||||
const guildId = ChannelStore.getChannel(channelOrGuildId)?.guild_id ?? GuildStore.getGuild(channelOrGuildId)?.id;
|
||||
if (guildId == null) return null;
|
||||
|
||||
return GuildMemberStore.getMember(guildId, userId)?.colorString ?? null;
|
||||
} catch (e) {
|
||||
new Logger("RoleColorEverywhere").error("Failed to get color string", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
getUserColor(userId: string, ids: { channelId?: string; guildId?: string; }) {
|
||||
const colorString = this.getColor(userId, ids);
|
||||
getColorInt(userId: string, channelOrGuildId: string) {
|
||||
const colorString = this.getColorString(userId, channelOrGuildId);
|
||||
return colorString && parseInt(colorString.slice(1), 16);
|
||||
},
|
||||
|
||||
roleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => {
|
||||
getColorStyle(userId: string, channelOrGuildId: string) {
|
||||
const colorString = this.getColorString(userId, channelOrGuildId);
|
||||
|
||||
return colorString && {
|
||||
color: colorString
|
||||
};
|
||||
},
|
||||
|
||||
useMessageColorsStyle(message: any) {
|
||||
try {
|
||||
const { messageSaturation } = settings.use(["messageSaturation"]);
|
||||
const author = useMessageAuthor(message);
|
||||
|
||||
if (author.colorString != null && messageSaturation !== 0) {
|
||||
const value = `color-mix(in oklab, ${author.colorString} ${messageSaturation}%, var({DEFAULT}))`;
|
||||
|
||||
return {
|
||||
color: value.replace("{DEFAULT}", "--text-normal"),
|
||||
"--header-primary": value.replace("{DEFAULT}", "--header-primary"),
|
||||
"--text-muted": value.replace("{DEFAULT}", "--text-muted")
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
new Logger("RoleColorEverywhere").error("Failed to get message color", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
RoleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => {
|
||||
const role = GuildStore.getRole(guildId, id);
|
||||
|
||||
return (
|
||||
|
@ -164,25 +221,5 @@ export default definePlugin({
|
|||
{title ?? label} — {count}
|
||||
</span>
|
||||
);
|
||||
}, { noop: true }),
|
||||
|
||||
getVoiceProps({ user: { id: userId }, guildId }: { user: { id: string; }; guildId: string; }) {
|
||||
return {
|
||||
style: {
|
||||
color: this.getColor(userId, { guildId })
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
useMessageColor(message: any) {
|
||||
try {
|
||||
const { messageSaturation } = settings.use(["messageSaturation"]);
|
||||
const author = useMessageAuthor(message);
|
||||
if (author.colorString !== undefined && messageSaturation !== 0)
|
||||
return `color-mix(in oklab, ${author.colorString} ${messageSaturation}%, var(--text-normal))`;
|
||||
} catch (e) {
|
||||
console.error("[RCE] failed to get message color", e);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
}, { noop: true })
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
* Copyright (c) 2024 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
|
||||
|
@ -18,9 +18,9 @@
|
|||
|
||||
import { IShikiTheme } from "@vap/shiki";
|
||||
|
||||
export const SHIKI_REPO = "shikijs/shiki";
|
||||
export const SHIKI_REPO_COMMIT = "0b28ad8ccfbf2615f2d9d38ea8255416b8ac3043";
|
||||
export const shikiRepoTheme = (name: string) => `https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/shiki/themes/${name}.json`;
|
||||
export const SHIKI_REPO = "shikijs/textmate-grammars-themes";
|
||||
export const SHIKI_REPO_COMMIT = "2d87559c7601a928b9f7e0f0dda243d2fb6d4499";
|
||||
export const shikiRepoTheme = (name: string) => `https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/tm-themes/themes/${name}.json`;
|
||||
|
||||
export const themes = {
|
||||
// Default
|
||||
|
@ -30,33 +30,59 @@ export const themes = {
|
|||
MaterialCandy: "https://raw.githubusercontent.com/millsp/material-candy/master/material-candy.json",
|
||||
|
||||
// More from Shiki repo
|
||||
Andromeeda: shikiRepoTheme("andromeeda"),
|
||||
AuroraX: shikiRepoTheme("aurora-x"),
|
||||
AyuDark: shikiRepoTheme("ayu-dark"),
|
||||
CatppuccinLatte: shikiRepoTheme("catppuccin-latte"),
|
||||
CatppuccinFrappe: shikiRepoTheme("catppuccin-frappe"),
|
||||
CatppuccinMacchiato: shikiRepoTheme("catppuccin-macchiato"),
|
||||
CatppuccinMocha: shikiRepoTheme("catppuccin-mocha"),
|
||||
DraculaSoft: shikiRepoTheme("dracula-soft"),
|
||||
Dracula: shikiRepoTheme("dracula"),
|
||||
EverforestDark: shikiRepoTheme("everforest-dark"),
|
||||
EverforestLight: shikiRepoTheme("everforest-light"),
|
||||
GithubDarkDefault: shikiRepoTheme("github-dark-default"),
|
||||
GithubDarkDimmed: shikiRepoTheme("github-dark-dimmed"),
|
||||
GithubDarkHighContrast: shikiRepoTheme("github-dark-high-contrast"),
|
||||
GithubDark: shikiRepoTheme("github-dark"),
|
||||
GithubLightDefault: shikiRepoTheme("github-light-default"),
|
||||
GithubLightHighContrast: shikiRepoTheme("github-light-high-contrast"),
|
||||
GithubLight: shikiRepoTheme("github-light"),
|
||||
Houston: shikiRepoTheme("houston"),
|
||||
KanagawaDragon: shikiRepoTheme("kanagawa-dragon"),
|
||||
KanagawaLotus: shikiRepoTheme("kanagawa-lotus"),
|
||||
KanagawaWave: shikiRepoTheme("kanagawa-wave"),
|
||||
LaserWave: shikiRepoTheme("laserwave"),
|
||||
LightPlus: shikiRepoTheme("light-plus"),
|
||||
MaterialDarker: shikiRepoTheme("material-darker"),
|
||||
MaterialDefault: shikiRepoTheme("material-default"),
|
||||
MaterialLighter: shikiRepoTheme("material-lighter"),
|
||||
MaterialOcean: shikiRepoTheme("material-ocean"),
|
||||
MaterialPalenight: shikiRepoTheme("material-palenight"),
|
||||
MaterialDarker: shikiRepoTheme("material-theme-darker"),
|
||||
MaterialDefault: shikiRepoTheme("material-theme"),
|
||||
MaterialLighter: shikiRepoTheme("material-theme-lighter"),
|
||||
MaterialOcean: shikiRepoTheme("material-theme-ocean"),
|
||||
MaterialPalenight: shikiRepoTheme("material-theme-palenight"),
|
||||
MinDark: shikiRepoTheme("min-dark"),
|
||||
MinLight: shikiRepoTheme("min-light"),
|
||||
Monokai: shikiRepoTheme("monokai"),
|
||||
NightOwl: shikiRepoTheme("night-owl"),
|
||||
Nord: shikiRepoTheme("nord"),
|
||||
OneDarkPro: shikiRepoTheme("one-dark-pro"),
|
||||
OneLight: shikiRepoTheme("one-light"),
|
||||
Plastic: shikiRepoTheme("plastic"),
|
||||
Poimandres: shikiRepoTheme("poimandres"),
|
||||
Red: shikiRepoTheme("red"),
|
||||
RosePineDawn: shikiRepoTheme("rose-pine-dawn"),
|
||||
RosePineMoon: shikiRepoTheme("rose-pine-moon"),
|
||||
RosePine: shikiRepoTheme("rose-pine"),
|
||||
SlackDark: shikiRepoTheme("slack-dark"),
|
||||
SlackOchin: shikiRepoTheme("slack-ochin"),
|
||||
SnazzyLight: shikiRepoTheme("snazzy-light"),
|
||||
SolarizedDark: shikiRepoTheme("solarized-dark"),
|
||||
SolarizedLight: shikiRepoTheme("solarized-light"),
|
||||
Synthwave84: shikiRepoTheme("synthwave-84"),
|
||||
TokyoNight: shikiRepoTheme("tokyo-night"),
|
||||
Vesper: shikiRepoTheme("vesper"),
|
||||
VitesseBlack: shikiRepoTheme("vitesse-black"),
|
||||
VitesseDark: shikiRepoTheme("vitesse-dark"),
|
||||
VitesseLight: shikiRepoTheme("vitesse-light"),
|
||||
CssVariables: shikiRepoTheme("css-variables"),
|
||||
};
|
||||
|
||||
export const themeCache = new Map<string, IShikiTheme>();
|
||||
|
|
|
@ -103,7 +103,7 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Do not show confirmation to join a voice channel when already connected to another if clicking on a hidden voice channel
|
||||
match: /(?<=getBlockedUsersForVoiceChannel\((\i)\.id\);return\()/,
|
||||
match: /(?<=getIgnoredUsersForVoiceChannel\((\i)\.id\);return\()/,
|
||||
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`
|
||||
},
|
||||
{
|
||||
|
|
|
@ -107,7 +107,7 @@ export default definePlugin({
|
|||
predicate: () => settings.store.disableDisallowedDiscoveryFilters,
|
||||
all: true,
|
||||
replacement: {
|
||||
match: /\i\.\i\.get\(\{url:\i\.\i\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0\}\)/g,
|
||||
match: /\i\.\i\.get\(\{url:\i\.\i\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0,rejectWithError:!1\}\)/g,
|
||||
replace: "Promise.resolve({ body: { valid: true } })"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import { Devs } from "@utils/constants";
|
|||
import { getIntlMessage } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
|
||||
import { ChannelStore, GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||
import { GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||
|
||||
import { buildSeveralUsers } from "../typingTweaks";
|
||||
|
||||
|
@ -44,7 +44,7 @@ function getDisplayName(guildId: string, userId: string) {
|
|||
return GuildMemberStore.getNick(guildId, userId) ?? (user as any).globalName ?? user.username;
|
||||
}
|
||||
|
||||
function TypingIndicator({ channelId }: { channelId: string; }) {
|
||||
function TypingIndicator({ channelId, guildId }: { channelId: string; guildId: string; }) {
|
||||
const typingUsers: Record<string, number> = useStateFromStores(
|
||||
[TypingStore],
|
||||
() => ({ ...TypingStore.getTypingUsers(channelId) as Record<string, number> }),
|
||||
|
@ -57,7 +57,6 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
|
|||
}
|
||||
);
|
||||
const currentChannelId: string = useStateFromStores([SelectedChannelStore], () => SelectedChannelStore.getChannelId());
|
||||
const guildId = ChannelStore.getChannel(channelId).guild_id;
|
||||
|
||||
if (!settings.store.includeMutedChannels) {
|
||||
const isChannelMuted = UserGuildSettingsStore.isChannelMuted(guildId, channelId);
|
||||
|
@ -165,7 +164,7 @@ export default definePlugin({
|
|||
find: "UNREAD_IMPORTANT:",
|
||||
replacement: {
|
||||
match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
|
||||
replace: "$&,$self.TypingIndicator($1.id)"
|
||||
replace: "$&,$self.TypingIndicator($1.id,$1.getGuildId())"
|
||||
}
|
||||
},
|
||||
// Theads
|
||||
|
@ -174,14 +173,14 @@ export default definePlugin({
|
|||
find: "M11 9H4C2.89543 9 2 8.10457 2 7V1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1V7C0 9.20914 1.79086 11 4 11H11C11.5523 11 12 10.5523 12 10C12 9.44771 11.5523 9 11 9Z",
|
||||
replacement: {
|
||||
match: /mentionsCount:\i.+?null(?<=channel:(\i).+?)/,
|
||||
replace: "$&,$self.TypingIndicator($1.id)"
|
||||
replace: "$&,$self.TypingIndicator($1.id,$1.getGuildId())"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
TypingIndicator: (channelId: string) => (
|
||||
TypingIndicator: (channelId: string, guildId: string) => (
|
||||
<ErrorBoundary noop>
|
||||
<TypingIndicator channelId={channelId} />
|
||||
<TypingIndicator channelId={channelId} guildId={guildId} />
|
||||
</ErrorBoundary>
|
||||
),
|
||||
});
|
||||
|
|
|
@ -129,14 +129,22 @@ export default definePlugin({
|
|||
buildSeveralUsers,
|
||||
|
||||
mutateChildren(props: any, users: User[], children: any) {
|
||||
if (!Array.isArray(children)) return children;
|
||||
try {
|
||||
if (!Array.isArray(children)) {
|
||||
return children;
|
||||
}
|
||||
|
||||
let element = 0;
|
||||
let element = 0;
|
||||
|
||||
return children.map(c =>
|
||||
c.type === "strong"
|
||||
? <TypingUser {...props} user={users[element++]} />
|
||||
: c
|
||||
);
|
||||
return children.map(c =>
|
||||
c.type === "strong" || (typeof c !== "string" && !React.isValidElement(c))
|
||||
? <TypingUser {...props} user={users[element++]} />
|
||||
: c
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
});
|
||||
|
|
46
src/plugins/useAltSearch.ts
Normal file
46
src/plugins/useAltSearch.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "UseAlternativeSearch",
|
||||
description: "Use alternative search engine in right click menu",
|
||||
authors: [
|
||||
{
|
||||
id: 1038096782963507210n,
|
||||
name: "skyevg",
|
||||
},
|
||||
],
|
||||
patches: [
|
||||
{
|
||||
find: "https://www.google.com/search?q=",
|
||||
replacement: {
|
||||
match: /"https:\/\/www.google.com\/search\?q=".concat\(encodeURIComponent\(e\)\)/,
|
||||
replace: "Vencord.Settings.plugins.UseAlternativeSearch.source.replace(\"!QUERY!\", encodeURIComponent(e))"
|
||||
}
|
||||
}
|
||||
],
|
||||
options: {
|
||||
source: {
|
||||
description: "Search engine's url (use !QUERY! as replacement for the search term)",
|
||||
type: OptionType.STRING,
|
||||
default: "https://duckduckgo.com/?q=!QUERY!",
|
||||
}
|
||||
}
|
||||
});
|
143
src/plugins/uwuifier.ts
Normal file
143
src/plugins/uwuifier.ts
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { findOption, RequiredMessageOption } from "@api/Commands";
|
||||
import { addPreEditListener, addPreSendListener, MessageObject, removePreEditListener, removePreSendListener } from "@api/MessageEvents";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
const endings = [
|
||||
"rawr x3",
|
||||
"OwO",
|
||||
"UwU",
|
||||
"o.O",
|
||||
"-.-",
|
||||
">w<",
|
||||
"(⑅˘꒳˘)",
|
||||
"(ꈍᴗꈍ)",
|
||||
"(˘ω˘)",
|
||||
"(U ᵕ U❁)",
|
||||
"σωσ",
|
||||
"òωó",
|
||||
"(///ˬ///✿)",
|
||||
"(U ﹏ U)",
|
||||
"( ͡o ω ͡o )",
|
||||
"ʘwʘ",
|
||||
":3",
|
||||
":3", // important enough to have twice
|
||||
"XD",
|
||||
"nyaa~~",
|
||||
"mya",
|
||||
">_<",
|
||||
"😳",
|
||||
"🥺",
|
||||
"😳😳😳",
|
||||
"rawr",
|
||||
"^^",
|
||||
"^^;;",
|
||||
"(ˆ ﻌ ˆ)♡",
|
||||
"^•ﻌ•^",
|
||||
"/(^•ω•^)",
|
||||
"(✿oωo)"
|
||||
];
|
||||
|
||||
const replacements = [
|
||||
["small", "smol"],
|
||||
["cute", "kawaii~"],
|
||||
["fluff", "floof"],
|
||||
["love", "luv"],
|
||||
["stupid", "baka"],
|
||||
["what", "nani"],
|
||||
["meow", "nya~"],
|
||||
["hello", "hewwo"],
|
||||
];
|
||||
|
||||
const settings = definePluginSettings({
|
||||
uwuEveryMessage: {
|
||||
description: "Make every single message uwuified",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
restartNeeded: false
|
||||
}
|
||||
});
|
||||
|
||||
function selectRandomElement(arr) {
|
||||
// generate a random index based on the length of the array
|
||||
const randomIndex = Math.floor(Math.random() * arr.length);
|
||||
|
||||
// return the element at the randomly generated index
|
||||
return arr[randomIndex];
|
||||
}
|
||||
|
||||
|
||||
function uwuify(message: string): string {
|
||||
message = message.toLowerCase();
|
||||
// words
|
||||
for (const pair of replacements) {
|
||||
message = message.replaceAll(pair[0], pair[1]);
|
||||
}
|
||||
message = message
|
||||
.replaceAll(/([ \t\n])n/g, "$1ny") // nyaify
|
||||
.replaceAll(/[lr]/g, "w") // [lr] > w
|
||||
.replaceAll(/([ \t\n])([a-z])/g, (_, p1, p2) => Math.random() < .5 ? `${p1}${p2}-${p2}` : `${p1}${p2}`) // stutter
|
||||
.replaceAll(/([^.,!][.,!])([ \t\n])/g, (_, p1, p2) => `${p1} ${selectRandomElement(endings)}${p2}`); // endings
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// actual command declaration
|
||||
export default definePlugin({
|
||||
name: "UwUifier",
|
||||
description: "Simply uwuify commands",
|
||||
authors: [Devs.echo, Devs.skyevg, Devs.PandaNinjas],
|
||||
dependencies: ["CommandsAPI", "MessageEventsAPI"],
|
||||
settings,
|
||||
|
||||
commands: [
|
||||
{
|
||||
name: "uwuify",
|
||||
description: "uwuifies your messages",
|
||||
options: [RequiredMessageOption],
|
||||
|
||||
execute: opts => ({
|
||||
content: uwuify(findOption(opts, "message", "")),
|
||||
}),
|
||||
},
|
||||
],
|
||||
|
||||
onSend(msg: MessageObject) {
|
||||
// Only run when it's enabled
|
||||
if (settings.store.uwuEveryMessage) {
|
||||
msg.content = uwuify(msg.content);
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
this.preSend = addPreSendListener((_, msg) => this.onSend(msg));
|
||||
this.preEdit = addPreEditListener((_cid, _mid, msg) =>
|
||||
this.onSend(msg)
|
||||
);
|
||||
},
|
||||
|
||||
stop() {
|
||||
removePreSendListener(this.preSend);
|
||||
removePreEditListener(this.preEdit);
|
||||
},
|
||||
});
|
|
@ -209,10 +209,11 @@ export default definePlugin({
|
|||
},
|
||||
// Group DMs top small & large icon
|
||||
{
|
||||
find: /\.recipients\.length>=2(?!<isMultiUserDM.{0,50})/,
|
||||
find: '["aria-hidden"],"aria-label":',
|
||||
replacement: {
|
||||
match: /null==\i\.icon\?.+?src:(\(0,\i\.\i\).+?\))(?=[,}])/,
|
||||
replace: (m, iconUrl) => `${m},onClick:()=>$self.openAvatar(${iconUrl})`
|
||||
// We have to check that icon is not an unread GDM in the server bar
|
||||
replace: (m, iconUrl) => `${m},onClick:()=>arguments[0]?.size!=="SIZE_48"&&$self.openAvatar(${iconUrl})`
|
||||
}
|
||||
},
|
||||
// User DMs top small icon
|
||||
|
|
|
@ -155,6 +155,7 @@ export default definePlugin({
|
|||
"guild-context": MakeContextCallback("Guild"),
|
||||
"channel-context": MakeContextCallback("Channel"),
|
||||
"thread-context": MakeContextCallback("Channel"),
|
||||
"gdm-context": MakeContextCallback("Channel"),
|
||||
"user-context": MakeContextCallback("User")
|
||||
},
|
||||
|
||||
|
|
|
@ -267,10 +267,6 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
name: "arHSM",
|
||||
id: 841509053422632990n
|
||||
},
|
||||
F53: {
|
||||
name: "Cassie (Code)",
|
||||
id: 280411966126948353n
|
||||
},
|
||||
AutumnVN: {
|
||||
name: "AutumnVN",
|
||||
id: 393694671383166998n
|
||||
|
@ -520,8 +516,8 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
id: 721717126523781240n,
|
||||
},
|
||||
nyx: {
|
||||
name: "verticalsync",
|
||||
id: 328165170536775680n
|
||||
name: "verticalsync.",
|
||||
id: 1207087393929171095n
|
||||
},
|
||||
nekohaxx: {
|
||||
name: "nekohaxx",
|
||||
|
@ -575,10 +571,14 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
name: "RamziAH",
|
||||
id: 1279957227612147747n,
|
||||
},
|
||||
SomeAspy: {
|
||||
SomeAspy: {
|
||||
name: "SomeAspy",
|
||||
id: 516750892372852754n,
|
||||
},
|
||||
jamesbt365: {
|
||||
name: "jamesbt365",
|
||||
id: 158567567487795200n,
|
||||
},
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
// iife so #__PURE__ works correctly
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import "./discord.css";
|
||||
|
||||
import { MessageObject } from "@api/MessageEvents";
|
||||
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||
import { Channel, Guild, Message, User } from "discord-types/general";
|
||||
import { Except } from "type-fest";
|
||||
|
||||
|
@ -212,3 +212,14 @@ export async function fetchUserProfile(id: string, options?: FetchUserProfileOpt
|
|||
export function getUniqueUsername(user: User) {
|
||||
return user.discriminator === "0" ? user.username : user.tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL for an emoji. This function always returns a gif URL for animated emojis, instead of webp
|
||||
* @param id The emoji id
|
||||
* @param animated Whether the emoji is animated
|
||||
* @param size The size for the emoji
|
||||
*/
|
||||
export function getEmojiURL(id: string, animated: boolean, size: number) {
|
||||
const url = IconUtils.getEmojiURL({ id, animated, size });
|
||||
return animated ? url.replace(".webp", ".gif") : url;
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ import { Patch, PatchReplacement, ReplaceFn } from "./types";
|
|||
|
||||
export function canonicalizeMatch<T extends RegExp | string>(match: T): T {
|
||||
let partialCanon = typeof match === "string" ? match : match.source;
|
||||
partialCanon = partialCanon.replaceAll(/#{intl::([A-Za-z_$][\w$]*)}/g, (_, key) => {
|
||||
const hashed = runtimeHashMessageKey(key);
|
||||
partialCanon = partialCanon.replaceAll(/#{intl::([\w$+/]*)(?:::(\w+))?}/g, (_, key, modifier) => {
|
||||
const hashed = modifier === "raw" ? key : runtimeHashMessageKey(key);
|
||||
|
||||
const isString = typeof match === "string";
|
||||
const hasSpecialChars = !Number.isNaN(Number(hashed[0])) || hashed.includes("+") || hashed.includes("/");
|
||||
|
@ -40,7 +40,7 @@ export function canonicalizeMatch<T extends RegExp | string>(match: T): T {
|
|||
return partialCanon as T;
|
||||
}
|
||||
|
||||
const canonSource = partialCanon.replaceAll(String.raw`\i`, String.raw`(?:[A-Za-z_$][\w$]*)`);
|
||||
const canonSource = partialCanon.replaceAll("\\i", String.raw`(?:[A-Za-z_$][\w$]*)`);
|
||||
return new RegExp(canonSource, match.flags) as T;
|
||||
}
|
||||
|
||||
|
|
|
@ -163,9 +163,13 @@ waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
|
|||
|
||||
export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint");
|
||||
|
||||
export const zustandCreate = findByCodeLazy("will be removed in v4");
|
||||
export const { zustandCreate } = mapMangledModuleLazy(["useSyncExternalStoreWithSelector:", "Object.assign"], {
|
||||
zustandCreate: filters.byCode(/=>(\i)\?\i\(\1/)
|
||||
});
|
||||
|
||||
export const zustandPersist = findByCodeLazy("[zustand persist middleware]");
|
||||
export const { zustandPersist } = mapMangledModuleLazy(".onRehydrateStorage)?", {
|
||||
zustandPersist: filters.byCode(/(\(\i,\i\))=>.+?\i\1/)
|
||||
});
|
||||
|
||||
export const MessageActions = findByPropsLazy("editMessage", "sendMessage");
|
||||
export const MessageCache = findByPropsLazy("clearCache", "_channelMessages");
|
||||
|
@ -181,7 +185,7 @@ export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModuleLa
|
|||
toggleExpressionPicker: filters.byCode(/getState\(\)\.activeView===\i\?\i\(\):\i\(/),
|
||||
setExpressionPickerView: filters.byCode(/setState\({activeView:\i,lastActiveView:/),
|
||||
setSearchQuery: filters.byCode("searchQuery:"),
|
||||
useExpressionPickerStore: filters.byCode("Object.is")
|
||||
useExpressionPickerStore: filters.byCode(/\(\i,\i=\i\)=>/)
|
||||
});
|
||||
|
||||
export const PopoutActions: t.PopoutActions = mapMangledModuleLazy('type:"POPOUT_WINDOW_OPEN"', {
|
||||
|
|
Loading…
Reference in a new issue