Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
thororen 2026-08-18 23:02:48 -04:00
commit 65bcda4826
No known key found for this signature in database
43 changed files with 397 additions and 281 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "equicord", "name": "equicord",
"private": "true", "private": "true",
"version": "1.15.1.0", "version": "1.15.2.0",
"description": "The other cutest Discord client mod", "description": "The other cutest Discord client mod",
"homepage": "https://github.com/Equicord/Equicord#readme", "homepage": "https://github.com/Equicord/Equicord#readme",
"bugs": { "bugs": {

View file

@ -1,7 +1,26 @@
import type { ComponentType, CSSProperties, ForwardRefRenderFunction, MouseEvent, PropsWithChildren, ReactNode, UIEvent } from "react"; import type { ComponentType, CSSProperties, ForwardRefRenderFunction, MouseEvent, PropsWithChildren, ReactNode, UIEvent } from "react";
import { LiteralUnion } from "type-fest";
type RC<C> = ComponentType<PropsWithChildren<C & Record<string, any>>>; type RC<C> = ComponentType<PropsWithChildren<C & Record<string, any>>>;
type Accessory =
| {
type: "icon";
icon: ComponentType<any>;
color?: string;
size?: LiteralUnion<"refresh_sm", string>;
}
| {
type: "emoji";
emojiId?: string;
src?: string;
animated?: boolean;
}
| {
type: "image";
src: string;
};
export interface Menu { export interface Menu {
Menu: RC<{ Menu: RC<{
navId: string; navId: string;
@ -20,6 +39,7 @@ export interface Menu {
label: ReactNode; label: ReactNode;
action?(e: MouseEvent): void; action?(e: MouseEvent): void;
icon?: ComponentType<any>; icon?: ComponentType<any>;
leadingAccessory?: Accessory;
color?: string; color?: string;
render?: ComponentType<any>; render?: ComponentType<any>;

View file

@ -1,7 +1,7 @@
{ {
"name": "@equicord/types", "name": "@equicord/types",
"private": false, "private": false,
"version": "1.14.15", "version": "1.15.1",
"description": "", "description": "",
"types": "index.d.ts", "types": "index.d.ts",
"scripts": { "scripts": {
@ -19,7 +19,7 @@
"dependencies": { "dependencies": {
"@types/lodash": "4.17.19", "@types/lodash": "4.17.19",
"@types/node": "^26.0.1", "@types/node": "^26.0.1",
"@vencord/discord-types": "^1.0.1", "@vencord/discord-types": "^1.0.2",
"highlight.js": "11.11.1", "highlight.js": "11.11.1",
"moment": "^2.22.2", "moment": "^2.22.2",
"ts-pattern": "^5.6.0", "ts-pattern": "^5.6.0",

210
pnpm-lock.yaml generated
View file

@ -65,7 +65,7 @@ importers:
version: 4.2.0 version: 4.2.0
'@stylistic/eslint-plugin': '@stylistic/eslint-plugin':
specifier: ^5.10.0 specifier: ^5.10.0
version: 5.10.0(eslint@10.6.0) version: 5.10.0(eslint@10.6.0(supports-color@10.2.2))
'@types/chrome': '@types/chrome':
specifier: ^0.2.0 specifier: ^0.2.0
version: 0.2.0 version: 0.2.0
@ -95,22 +95,22 @@ importers:
version: 0.28.1 version: 0.28.1
eslint: eslint:
specifier: 10.6.0 specifier: 10.6.0
version: 10.6.0 version: 10.6.0(supports-color@10.2.2)
eslint-import-resolver-alias: eslint-import-resolver-alias:
specifier: ^1.1.2 specifier: ^1.1.2
version: 1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0)) version: 1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2)))
eslint-plugin-react: eslint-plugin-react:
specifier: ^7.37.5 specifier: ^7.37.5
version: 7.37.5(eslint@10.6.0) version: 7.37.5(eslint@10.6.0(supports-color@10.2.2))
eslint-plugin-simple-header: eslint-plugin-simple-header:
specifier: ^1.2.1 specifier: ^1.2.1
version: 1.2.2(eslint@10.6.0) version: 1.2.2(eslint@10.6.0(supports-color@10.2.2))
eslint-plugin-simple-import-sort: eslint-plugin-simple-import-sort:
specifier: ^13.0.0 specifier: ^13.0.0
version: 13.0.0(eslint@10.6.0) version: 13.0.0(eslint@10.6.0(supports-color@10.2.2))
eslint-plugin-unused-imports: eslint-plugin-unused-imports:
specifier: ^4.4.1 specifier: ^4.4.1
version: 4.4.1(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0) version: 4.4.1(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2))
highlight.js: highlight.js:
specifier: 11.11.1 specifier: 11.11.1
version: 11.11.1 version: 11.11.1
@ -134,10 +134,10 @@ importers:
version: 41.2.1 version: 41.2.1
stylelint: stylelint:
specifier: ^17.14.0 specifier: ^17.14.0
version: 17.14.0(typescript@6.0.3) version: 17.14.0(supports-color@10.2.2)(typescript@6.0.3)
stylelint-config-standard: stylelint-config-standard:
specifier: ^40.0.0 specifier: ^40.0.0
version: 40.0.0(stylelint@17.14.0(typescript@6.0.3)) version: 40.0.0(stylelint@17.14.0(supports-color@10.2.2)(typescript@6.0.3))
svgo: svgo:
specifier: ^4.0.1 specifier: ^4.0.1
version: 4.0.1 version: 4.0.1
@ -158,7 +158,7 @@ importers:
version: 6.0.3 version: 6.0.3
typescript-eslint: typescript-eslint:
specifier: ^8.62.1 specifier: ^8.62.1
version: 8.62.1(eslint@10.6.0)(typescript@6.0.3) version: 8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
typescript-transform-paths: typescript-transform-paths:
specifier: ^3.5.6 specifier: ^3.5.6
version: 3.5.6(typescript@6.0.3) version: 3.5.6(typescript@6.0.3)
@ -193,8 +193,8 @@ importers:
specifier: 19.0.4 specifier: 19.0.4
version: 19.0.4(@types/react@19.0.12) version: 19.0.4(@types/react@19.0.12)
'@vencord/discord-types': '@vencord/discord-types':
specifier: ^1.0.1 specifier: ^1.0.2
version: 1.0.1(@types/react@19.0.12) version: 1.0.2(@types/react@19.0.12)
highlight.js: highlight.js:
specifier: 11.11.1 specifier: 11.11.1
version: 11.11.1 version: 11.11.1
@ -710,10 +710,10 @@ packages:
'@vap/shiki@0.10.5': '@vap/shiki@0.10.5':
resolution: {integrity: sha512-5BHVGvQT8qonbLSASon5aQFQ18OZU4FxSl9tLSj6oJ0sap3KdMbYcfGq25M9zFZR1g1dJN7fgjmZXBIS5omIdw==} resolution: {integrity: sha512-5BHVGvQT8qonbLSASon5aQFQ18OZU4FxSl9tLSj6oJ0sap3KdMbYcfGq25M9zFZR1g1dJN7fgjmZXBIS5omIdw==}
'@vencord/discord-types@1.0.1': '@vencord/discord-types@1.0.2':
resolution: {integrity: sha512-uSQMDUTo9hMFgOMykNtTMTxScSKVVUHHFCevktO3bMyaLh1s3iDMfIvUvqU1fBkmFwvMbmEYsUrztsVKKHvang==} resolution: {integrity: sha512-nq3YZebbv085/FsFcKhoVBCs9U9QuAEPy1noL9x9Bb5B29TdhQqr/hJCcApSWjY8J299+tLi30LEe0YGo3yn0Q==}
peerDependencies: peerDependencies:
'@types/react': ^19.0.10 '@types/react': 19.1.0
acorn-jsx@5.3.2: acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
@ -1133,8 +1133,8 @@ packages:
peerDependencies: peerDependencies:
eslint-plugin-import: '>=1.4.0' eslint-plugin-import: '>=1.4.0'
eslint-import-resolver-node@0.3.9: eslint-import-resolver-node@0.3.10:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==}
eslint-module-utils@2.13.0: eslint-module-utils@2.13.0:
resolution: {integrity: sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==} resolution: {integrity: sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==}
@ -1815,6 +1815,10 @@ packages:
engines: {node: '>=10.5.0'} engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead deprecated: Use your platform's native DOMException instead
node-exports-info@1.6.2:
resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==}
engines: {node: '>= 0.4'}
normalize-path@3.0.0: normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -1995,11 +1999,6 @@ packages:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'} engines: {node: '>=4'}
resolve@1.22.11:
resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
engines: {node: '>= 0.4'}
hasBin: true
resolve@1.22.8: resolve@1.22.8:
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
hasBin: true hasBin: true
@ -2008,6 +2007,11 @@ packages:
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
hasBin: true hasBin: true
resolve@2.0.0-next.7:
resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==}
engines: {node: '>= 0.4'}
hasBin: true
reusify@1.1.0: reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'} engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@ -2259,10 +2263,6 @@ packages:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
type-fest@4.41.0:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'}
type-fest@5.7.0: type-fest@5.7.0:
resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==}
engines: {node: '>=20'} engines: {node: '>=20'}
@ -2572,17 +2572,17 @@ snapshots:
'@esbuild/win32-x64@0.28.1': '@esbuild/win32-x64@0.28.1':
optional: true optional: true
'@eslint-community/eslint-utils@4.9.1(eslint@10.6.0)': '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0(supports-color@10.2.2))':
dependencies: dependencies:
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {} '@eslint-community/regexpp@4.12.2': {}
'@eslint/config-array@0.23.5': '@eslint/config-array@0.23.5(supports-color@10.2.2)':
dependencies: dependencies:
'@eslint/object-schema': 3.0.5 '@eslint/object-schema': 3.0.5
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
minimatch: 10.2.5 minimatch: 10.2.5
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -2687,11 +2687,11 @@ snapshots:
'@streamparser/json@0.0.22': {} '@streamparser/json@0.0.22': {}
'@stylistic/eslint-plugin@5.10.0(eslint@10.6.0)': '@stylistic/eslint-plugin@5.10.0(eslint@10.6.0(supports-color@10.2.2))':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(supports-color@10.2.2))
'@typescript-eslint/types': 8.58.2 '@typescript-eslint/types': 8.58.2
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
espree: 10.4.0 espree: 10.4.0
estraverse: 5.3.0 estraverse: 5.3.0
@ -2762,15 +2762,15 @@ snapshots:
dependencies: dependencies:
'@types/node': 26.0.1 '@types/node': 26.0.1
'@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0)(typescript@6.0.3)': '@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.62.1(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/parser': 8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/scope-manager': 8.62.1 '@typescript-eslint/scope-manager': 8.62.1
'@typescript-eslint/type-utils': 8.62.1(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/type-utils': 8.62.1(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3)
'@typescript-eslint/utils': 8.62.1(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/utils': 8.62.1(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.62.1 '@typescript-eslint/visitor-keys': 8.62.1
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
ignore: 7.0.5 ignore: 7.0.5
natural-compare: 1.4.0 natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@6.0.3) ts-api-utils: 2.5.0(typescript@6.0.3)
@ -2778,14 +2778,14 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3)': '@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 8.62.1 '@typescript-eslint/scope-manager': 8.62.1
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.62.1
'@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.62.1 '@typescript-eslint/visitor-keys': 8.62.1
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -2794,7 +2794,7 @@ snapshots:
dependencies: dependencies:
'@typescript-eslint/tsconfig-utils': 8.62.1(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@6.0.3)
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.62.1
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -2808,13 +2808,13 @@ snapshots:
dependencies: dependencies:
typescript: 6.0.3 typescript: 6.0.3
'@typescript-eslint/type-utils@8.62.1(eslint@10.6.0)(typescript@6.0.3)': '@typescript-eslint/type-utils@8.62.1(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3)':
dependencies: dependencies:
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.62.1
'@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3)
'@typescript-eslint/utils': 8.62.1(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/utils': 8.62.1(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3)
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
ts-api-utils: 2.5.0(typescript@6.0.3) ts-api-utils: 2.5.0(typescript@6.0.3)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
@ -2830,7 +2830,7 @@ snapshots:
'@typescript-eslint/tsconfig-utils': 8.62.1(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@6.0.3)
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.62.1
'@typescript-eslint/visitor-keys': 8.62.1 '@typescript-eslint/visitor-keys': 8.62.1
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
minimatch: 10.2.5 minimatch: 10.2.5
semver: 7.8.5 semver: 7.8.5
tinyglobby: 0.2.17 tinyglobby: 0.2.17
@ -2839,13 +2839,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@8.62.1(eslint@10.6.0)(typescript@6.0.3)': '@typescript-eslint/utils@8.62.1(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(supports-color@10.2.2))
'@typescript-eslint/scope-manager': 8.62.1 '@typescript-eslint/scope-manager': 8.62.1
'@typescript-eslint/types': 8.62.1 '@typescript-eslint/types': 8.62.1
'@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3)
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -2865,11 +2865,11 @@ snapshots:
vscode-oniguruma: 1.7.0 vscode-oniguruma: 1.7.0
vscode-textmate: 5.2.0 vscode-textmate: 5.2.0
'@vencord/discord-types@1.0.1(@types/react@19.0.12)': '@vencord/discord-types@1.0.2(@types/react@19.0.12)':
dependencies: dependencies:
'@types/react': 19.0.12 '@types/react': 19.0.12
moment: 2.30.1 moment: 2.22.2
type-fest: 4.41.0 type-fest: 5.7.0
acorn-jsx@5.3.2(acorn@8.15.0): acorn-jsx@5.3.2(acorn@8.15.0):
dependencies: dependencies:
@ -3188,9 +3188,11 @@ snapshots:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
debug@4.4.3: debug@4.4.3(supports-color@10.2.2):
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
optionalDependencies:
supports-color: 10.2.2
deep-is@0.1.4: {} deep-is@0.1.4: {}
@ -3524,29 +3526,29 @@ snapshots:
escape-string-regexp@4.0.0: {} escape-string-regexp@4.0.0: {}
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0)): eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2))):
dependencies: dependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2))
eslint-import-resolver-node@0.3.9: eslint-import-resolver-node@0.3.10:
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
is-core-module: 2.16.2 is-core-module: 2.16.2
resolve: 1.22.11 resolve: 2.0.0-next.7
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-module-utils@2.13.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@10.6.0): eslint-module-utils@2.13.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.6.0(supports-color@10.2.2)):
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 8.62.1(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/parser': 8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.10
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0): eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2)):
dependencies: dependencies:
'@rtsao/scc': 1.1.0 '@rtsao/scc': 1.1.0
array-includes: 3.1.9 array-includes: 3.1.9
@ -3555,9 +3557,9 @@ snapshots:
array.prototype.flatmap: 1.3.3 array.prototype.flatmap: 1.3.3
debug: 3.2.7 debug: 3.2.7
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.10
eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@10.6.0) eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.6.0(supports-color@10.2.2))
hasown: 2.0.4 hasown: 2.0.4
is-core-module: 2.16.2 is-core-module: 2.16.2
is-glob: 4.0.3 is-glob: 4.0.3
@ -3569,13 +3571,13 @@ snapshots:
string.prototype.trimend: 1.0.10 string.prototype.trimend: 1.0.10
tsconfig-paths: 3.15.0 tsconfig-paths: 3.15.0
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 8.62.1(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/parser': 8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
transitivePeerDependencies: transitivePeerDependencies:
- eslint-import-resolver-typescript - eslint-import-resolver-typescript
- eslint-import-resolver-webpack - eslint-import-resolver-webpack
- supports-color - supports-color
eslint-plugin-react@7.37.5(eslint@10.6.0): eslint-plugin-react@7.37.5(eslint@10.6.0(supports-color@10.2.2)):
dependencies: dependencies:
array-includes: 3.1.8 array-includes: 3.1.8
array.prototype.findlast: 1.2.5 array.prototype.findlast: 1.2.5
@ -3583,7 +3585,7 @@ snapshots:
array.prototype.tosorted: 1.1.4 array.prototype.tosorted: 1.1.4
doctrine: 2.1.0 doctrine: 2.1.0
es-iterator-helpers: 1.2.1 es-iterator-helpers: 1.2.1
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
estraverse: 5.3.0 estraverse: 5.3.0
hasown: 2.0.2 hasown: 2.0.2
jsx-ast-utils: 3.3.5 jsx-ast-utils: 3.3.5
@ -3597,19 +3599,19 @@ snapshots:
string.prototype.matchall: 4.0.12 string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0 string.prototype.repeat: 1.0.0
eslint-plugin-simple-header@1.2.2(eslint@10.6.0): eslint-plugin-simple-header@1.2.2(eslint@10.6.0(supports-color@10.2.2)):
dependencies: dependencies:
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
eslint-plugin-simple-import-sort@13.0.0(eslint@10.6.0): eslint-plugin-simple-import-sort@13.0.0(eslint@10.6.0(supports-color@10.2.2)):
dependencies: dependencies:
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0): eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2)):
dependencies: dependencies:
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
optionalDependencies: optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3)
eslint-scope@9.1.2: eslint-scope@9.1.2:
dependencies: dependencies:
@ -3624,11 +3626,11 @@ snapshots:
eslint-visitor-keys@5.0.1: {} eslint-visitor-keys@5.0.1: {}
eslint@10.6.0: eslint@10.6.0(supports-color@10.2.2):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(supports-color@10.2.2))
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.23.5 '@eslint/config-array': 0.23.5(supports-color@10.2.2)
'@eslint/config-helpers': 0.6.0 '@eslint/config-helpers': 0.6.0
'@eslint/core': 1.2.1 '@eslint/core': 1.2.1
'@eslint/plugin-kit': 0.7.2 '@eslint/plugin-kit': 0.7.2
@ -3638,7 +3640,7 @@ snapshots:
'@types/estree': 1.0.9 '@types/estree': 1.0.9
ajv: 6.15.0 ajv: 6.15.0
cross-spawn: 7.0.6 cross-spawn: 7.0.6
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
escape-string-regexp: 4.0.0 escape-string-regexp: 4.0.0
eslint-scope: 9.1.2 eslint-scope: 9.1.2
eslint-visitor-keys: 5.0.1 eslint-visitor-keys: 5.0.1
@ -4242,6 +4244,13 @@ snapshots:
node-domexception@1.0.0: node-domexception@1.0.0:
optional: true optional: true
node-exports-info@1.6.2:
dependencies:
array.prototype.flatmap: 1.3.3
es-errors: 1.3.0
object.entries: 1.1.9
semver: 6.3.1
normalize-path@3.0.0: {} normalize-path@3.0.0: {}
nth-check@2.1.1: nth-check@2.1.1:
@ -4437,12 +4446,6 @@ snapshots:
resolve-from@4.0.0: {} resolve-from@4.0.0: {}
resolve@1.22.11:
dependencies:
is-core-module: 2.16.2
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
resolve@1.22.8: resolve@1.22.8:
dependencies: dependencies:
is-core-module: 2.16.1 is-core-module: 2.16.1
@ -4455,6 +4458,15 @@ snapshots:
path-parse: 1.0.7 path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0 supports-preserve-symlinks-flag: 1.0.0
resolve@2.0.0-next.7:
dependencies:
es-errors: 1.3.0
is-core-module: 2.16.2
node-exports-info: 1.6.2
object-keys: 1.1.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
reusify@1.1.0: {} reusify@1.1.0: {}
run-parallel@1.2.0: run-parallel@1.2.0:
@ -4669,16 +4681,16 @@ snapshots:
strip-bom@3.0.0: {} strip-bom@3.0.0: {}
stylelint-config-recommended@18.0.0(stylelint@17.14.0(typescript@6.0.3)): stylelint-config-recommended@18.0.0(stylelint@17.14.0(supports-color@10.2.2)(typescript@6.0.3)):
dependencies: dependencies:
stylelint: 17.14.0(typescript@6.0.3) stylelint: 17.14.0(supports-color@10.2.2)(typescript@6.0.3)
stylelint-config-standard@40.0.0(stylelint@17.14.0(typescript@6.0.3)): stylelint-config-standard@40.0.0(stylelint@17.14.0(supports-color@10.2.2)(typescript@6.0.3)):
dependencies: dependencies:
stylelint: 17.14.0(typescript@6.0.3) stylelint: 17.14.0(supports-color@10.2.2)(typescript@6.0.3)
stylelint-config-recommended: 18.0.0(stylelint@17.14.0(typescript@6.0.3)) stylelint-config-recommended: 18.0.0(stylelint@17.14.0(supports-color@10.2.2)(typescript@6.0.3))
stylelint@17.14.0(typescript@6.0.3): stylelint@17.14.0(supports-color@10.2.2)(typescript@6.0.3):
dependencies: dependencies:
'@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
@ -4691,7 +4703,7 @@ snapshots:
cosmiconfig: 9.0.2(typescript@6.0.3) cosmiconfig: 9.0.2(typescript@6.0.3)
css-functions-list: 3.3.3 css-functions-list: 3.3.3
css-tree: 3.2.1 css-tree: 3.2.1
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
fast-glob: 3.3.3 fast-glob: 3.3.3
fastest-levenshtein: 1.0.16 fastest-levenshtein: 1.0.16
file-entry-cache: 11.1.5 file-entry-cache: 11.1.5
@ -4806,8 +4818,6 @@ snapshots:
dependencies: dependencies:
prelude-ls: 1.2.1 prelude-ls: 1.2.1
type-fest@4.41.0: {}
type-fest@5.7.0: type-fest@5.7.0:
dependencies: dependencies:
tagged-tag: 1.0.0 tagged-tag: 1.0.0
@ -4856,13 +4866,13 @@ snapshots:
typed-query-selector@2.12.2: {} typed-query-selector@2.12.2: {}
typescript-eslint@8.62.1(eslint@10.6.0)(typescript@6.0.3): typescript-eslint@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3):
dependencies: dependencies:
'@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0)(typescript@6.0.3))(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3)
'@typescript-eslint/parser': 8.62.1(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/parser': 8.62.1(eslint@10.6.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3)
'@typescript-eslint/utils': 8.62.1(eslint@10.6.0)(typescript@6.0.3) '@typescript-eslint/utils': 8.62.1(eslint@10.6.0(supports-color@10.2.2))(typescript@6.0.3)
eslint: 10.6.0 eslint: 10.6.0(supports-color@10.2.2)
typescript: 6.0.3 typescript: 6.0.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color

View file

@ -18,3 +18,5 @@ verifyDepsBeforeRun: prompt
minimumReleaseAge: 7 minimumReleaseAge: 7
minimumReleaseAgeExclude: minimumReleaseAgeExclude:
- "@song-spotlight/api" - "@song-spotlight/api"
- "@vencord/*"
- "@equicord/*"

View file

@ -80,7 +80,8 @@ async function syncSettings() {
body: "We've noticed you have cloud integrations enabled in another client! Due to limitations, you will " + body: "We've noticed you have cloud integrations enabled in another client! Due to limitations, you will " +
"need to re-authenticate to continue using them. Click here to go to the settings page to do so!", "need to re-authenticate to continue using them. Click here to go to the settings page to do so!",
color: "var(--yellow-360)", color: "var(--yellow-360)",
onClick: () => SettingsRouter.openUserSettings("equicord_cloud_panel") onClick: () => SettingsRouter.openUserSettings("equicord_cloud_panel"),
noPersist: true
}); });
return; return;
} }

View file

@ -32,3 +32,11 @@
flex-shrink: 0; flex-shrink: 0;
padding-top: 2px; padding-top: 2px;
} }
.vc-form-switch-border {
margin-top: 20px;
}
.vc-form-switch-description {
color: var(--text-subtle);
}

View file

@ -9,7 +9,7 @@ import "./FormSwitch.css";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import type { PropsWithChildren, ReactNode } from "react"; import type { PropsWithChildren, ReactNode } from "react";
import { Flex } from "./Flex"; import { Divider } from "./Divider";
import { Span } from "./Span"; import { Span } from "./Span";
import { Switch } from "./Switch"; import { Switch } from "./Switch";
@ -26,22 +26,24 @@ export interface FormSwitchProps {
export function FormSwitch({ onChange, title, value, description, disabled, className, hideBorder }: FormSwitchProps) { export function FormSwitch({ onChange, title, value, description, disabled, className, hideBorder }: FormSwitchProps) {
return ( return (
<div className={classes("vc-form-switch-container", hideBorder && "vc-form-switch-no-border")}> <label className="vc-form-switch-wrapper">
<label className={classes("vc-form-switch", className, disabled && "vc-form-switch-disabled")}> <div className={classes("vc-form-switch", className, disabled && "vc-form-switch-disabled")}>
<div className="vc-form-switch-text"> <div className={"vc-form-switch-text"}>
<Flex flexDirection="column" gap="4px"> <Span size="md" weight="medium">{title}</Span>
<Span size="md" weight="medium" color="text-strong">{title}</Span> {description && <Span size="sm" weight="normal" className="vc-form-switch-description">{description}</Span>}
{description && <Span size="sm" color="text-subtle">{description}</Span>}
</Flex>
</div> </div>
<div className="vc-form-switch-control">
<Switch checked={value} onChange={onChange} disabled={disabled} /> <Switch checked={value} onChange={onChange} disabled={disabled} />
</div> </div>
{!hideBorder && <Divider className="vc-form-switch-border" />}
</label> </label>
</div>
); );
} }
// #region Old compatibility
export function FormSwitchCompat({ note, children, ...restProps }: PropsWithChildren<any>) { export function FormSwitchCompat({ note, children, ...restProps }: PropsWithChildren<any>) {
return <FormSwitch title={children ?? ""} description={note} {...restProps} />; return <FormSwitch title={children ?? ""} description={note} {...restProps} />;
} }
// #endregion

View file

@ -1252,3 +1252,33 @@ export function StarOutlined(props: IconProps) {
</Icon> </Icon>
); );
} }
export function SearchIcon(props: IconProps) {
return (
<Icon
{...props}
viewBox="0 0 24 24"
>
<path
fill={props.fill || "currentColor"}
fillRule="evenodd"
d="M15.62 17.03a9 9 0 1 1 1.41-1.41l4.68 4.67a1 1 0 0 1-1.42 1.42l-4.67-4.68ZM17 10a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z"
clipRule="evenodd"
/>
</Icon>
);
}
export function TopRightArrow(props: IconProps) {
return (
<Icon
{...props}
viewBox="0 0 24 24"
>
<path
fill={props.fill || "currentColor"}
d="M8 5a1 1 0 0 0 0 2h7.59L5.29 17.3a1 1 0 1 0 1.42 1.4L17 8.42V16a1 1 0 1 0 2 0V6a1 1 0 0 0-1-1H8Z"
/>
</Icon>
);
}

View file

@ -39,3 +39,7 @@
margin-left: auto; margin-left: auto;
} }
} }
.vc-plugin-ui-elements-description {
color: var(--text-subtle);
}

View file

@ -19,7 +19,7 @@ import { Margins } from "@utils/margins";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { IconComponent } from "@utils/types"; import { IconComponent } from "@utils/types";
import { RenderModalProps } from "@vencord/discord-types"; import { RenderModalProps } from "@vencord/discord-types";
import { Clickable, Modal,openModal } from "@webpack/common"; import { Clickable, Modal, openModal } from "@webpack/common";
const cl = classNameFactory("vc-plugin-ui-elements-"); const cl = classNameFactory("vc-plugin-ui-elements-");
@ -75,7 +75,7 @@ function Section(props: {
return ( return (
<section> <section>
<BaseText tag="h3" size="lg" weight="semibold">{title}</BaseText> <BaseText tag="h3" size="lg" weight="semibold">{title}</BaseText>
<Paragraph size="sm" className={classes(Margins.top8, Margins.bottom20)}>{description}</Paragraph> <Paragraph size="sm" className={classes(Margins.top8, Margins.bottom20, cl("description"))}>{description}</Paragraph>
<div className={cl("switches")}> <div className={cl("switches")}>
{switches.length === 0 && ( {switches.length === 0 && (

View file

@ -26,7 +26,7 @@
color: var(--text-strong); color: var(--text-strong);
} }
.vc-plugins-setting-inline .vc-plugins-setting-description { .vc-plugins-setting-description {
color: var(--text-subtle); color: var(--text-subtle);
} }

View file

@ -89,3 +89,8 @@
.vc-cloud-button-icon { .vc-cloud-button-icon {
height: 1.25em; height: 1.25em;
} }
.vc-settings-switches {
display: grid;
gap: 8px;
}

View file

@ -91,6 +91,7 @@ function Updater() {
Control how Equicord keeps itself up to date. You can choose to update automatically in the background or be notified when new updates are available. Control how Equicord keeps itself up to date. You can choose to update automatically in the background or be notified when new updates are available.
</Paragraph> </Paragraph>
<div className="vc-settings-switches">
<FormSwitch <FormSwitch
title="Automatically update" title="Automatically update"
description="When enabled, Equicord will automatically download and install updates in the background without asking for confirmation. You'll need to restart Discord to apply the changes." description="When enabled, Equicord will automatically download and install updates in the background without asking for confirmation. You'll need to restart Discord to apply the changes."
@ -99,13 +100,14 @@ function Updater() {
hideBorder hideBorder
/> />
<FormSwitch <FormSwitch
value={settings.autoUpdateNotification}
onChange={(v: boolean) => settings.autoUpdateNotification = v}
title="Get notified when an automatic update completes" title="Get notified when an automatic update completes"
description="Receive a notification when Equicord finishes downloading an update in the background, so you know when to restart Discord." description="Receive a notification when Equicord finishes downloading an update in the background, so you know when to restart Discord."
value={settings.autoUpdateNotification}
onChange={(v: boolean) => settings.autoUpdateNotification = v}
disabled={!settings.autoUpdate} disabled={!settings.autoUpdate}
hideBorder hideBorder
/> />
</div>
<Divider className={Margins.top20} /> <Divider className={Margins.top20} />

View file

@ -7,7 +7,6 @@
import "./VencordTab.css"; import "./VencordTab.css";
import { openNotificationLogModal } from "@api/Notifications/notificationLog"; import { openNotificationLogModal } from "@api/Notifications/notificationLog";
import { plugins } from "@api/PluginManager";
import { useSettings } from "@api/Settings"; import { useSettings } from "@api/Settings";
import { Button } from "@components/Button"; import { Button } from "@components/Button";
import { Divider } from "@components/Divider"; import { Divider } from "@components/Divider";
@ -20,6 +19,7 @@ import { openContributorModal, openPluginModal, SettingsTab, wrapTab } from "@co
import { QuickAction, QuickActionCard } from "@components/settings/QuickAction"; import { QuickAction, QuickActionCard } from "@components/settings/QuickAction";
import { SpecialCard } from "@components/settings/SpecialCard"; import { SpecialCard } from "@components/settings/SpecialCard";
import BadgeAPI from "@plugins/_api/badges"; import BadgeAPI from "@plugins/_api/badges";
import SettingsPlugin from "@plugins/_core/settings";
import { gitRemote } from "@shared/vencordUserAgent"; import { gitRemote } from "@shared/vencordUserAgent";
import { DONOR_ROLE_ID, GUILD_ID, IS_WINDOWS, VC_DONOR_ROLE_ID, VC_GUILD_ID } from "@utils/constants"; import { DONOR_ROLE_ID, GUILD_ID, IS_WINDOWS, VC_DONOR_ROLE_ID, VC_GUILD_ID } from "@utils/constants";
import { classNameFactory } from "@utils/css"; import { classNameFactory } from "@utils/css";
@ -55,13 +55,7 @@ function Switches() {
{ {
key: "useQuickCss", key: "useQuickCss",
title: "Enable Custom CSS", title: "Enable Custom CSS",
description: "Load custom CSS from the QuickCSS editor. This allows you to customize Discord's appearance with your own styles.", description: "Apply your configured QuickCSS"
},
!IS_WEB && {
key: "enableReactDevtools",
title: "Enable React Developer Tools",
description: "Enable the React Developer Tools extension for debugging Discord's React components. Useful for plugin development.",
restartRequired: true,
}, },
(!IS_WEB && !IS_DISCORD_DESKTOP || !IS_WINDOWS) && { (!IS_WEB && !IS_DISCORD_DESKTOP || !IS_WINDOWS) && {
key: "mainWindowFrameless", key: "mainWindowFrameless",
@ -95,8 +89,8 @@ function Switches() {
IS_DISCORD_DESKTOP && { IS_DISCORD_DESKTOP && {
key: "disableMinSize", key: "disableMinSize",
title: "Disable Minimum Window Size", title: "Disable Minimum Window Size",
description: "Allow the Discord window to be resized smaller than its default minimum size. Useful for tiling window managers or small screens.", description: "Allows you to resize the window to any size, even smaller than Discord's minimum size",
restartRequired: true, restartRequired: true
}, },
!IS_WEB && IS_WINDOWS && { !IS_WEB && IS_WINDOWS && {
key: "winCtrlQ", key: "winCtrlQ",
@ -104,6 +98,12 @@ function Switches() {
description: "Add Ctrl+Q as a keyboard shortcut to close Discord. This provides an alternative to Alt+F4 for quickly closing the application.", description: "Add Ctrl+Q as a keyboard shortcut to close Discord. This provides an alternative to Alt+F4 for quickly closing the application.",
restartRequired: true, restartRequired: true,
}, },
!IS_WEB && {
key: "enableReactDevtools",
title: "Enable React Developer Tools",
description: "Mainly useful for plugin developers. Ignore this if you don't know what it is",
restartRequired: true
},
] satisfies Array<false | { ] satisfies Array<false | {
key: KeysOfType<typeof settings, boolean>; key: KeysOfType<typeof settings, boolean>;
title: string; title: string;
@ -136,6 +136,7 @@ function Switches() {
) )
} }
value={settings[key]} value={settings[key]}
hideBorder
onChange={v => { onChange={v => {
settings[key] = v; settings[key] = v;
@ -149,7 +150,6 @@ function Switches() {
}); });
} }
}} }}
hideBorder
/> />
); );
}); });
@ -258,20 +258,19 @@ function EquicordSettings() {
<Divider className={Margins.top20} /> <Divider className={Margins.top20} />
<Heading className={Margins.top20}>Client Settings</Heading> <section className={Margins.top16}>
<Paragraph className={Margins.bottom16}> <Heading tag="h5">Settings</Heading>
Configure how Equicord behaves and integrates with Discord. These settings affect the Discord client's appearance and behavior. <Paragraph className={Margins.bottom20} style={{ color: "var(--text-muted)" }}>
Hint: You can change the position of this settings section in the{" "}
<a onClick={() => openPluginModal(SettingsPlugin)}>
settings of the Settings plugin
</a>!
</Paragraph> </Paragraph>
<Notice.Info className={Margins.bottom20} style={{ width: "100%" }}>
You can customize where this settings section appears in Discord's settings menu by configuring the{" "} <div className="vc-settings-switches">
<a <Switches />
role="button" </div>
onClick={() => openPluginModal(plugins.Settings)} </section>
style={{ cursor: "pointer", color: "var(--text-link)" }}
>
Settings Plugin
</a>.
</Notice.Info>
<Switches /> <Switches />

View file

@ -14,7 +14,7 @@ import pLimit from "p-limit";
function getWebpackChunkMap() { function getWebpackChunkMap() {
const sym = Symbol(); const sym = Symbol();
let chunksMap: unknown; let chunksMap: unknown = null;
Object.defineProperty(Object.prototype, sym, { Object.defineProperty(Object.prototype, sym, {
get() { get() {
@ -30,9 +30,38 @@ function getWebpackChunkMap() {
return chunksMap as Record<PropertyKey, string> | null; return chunksMap as Record<PropertyKey, string> | null;
} }
let chunksAlreadyLoaded = false;
export async function loadLazyChunks() { export async function loadLazyChunks() {
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader"); const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
if (chunksAlreadyLoaded) {
LazyChunkLoaderLogger.log("Lazy chunks have already been loaded");
return;
}
const queue = pLimit(50); const queue = pLimit(50);
const workerAssetCache = new Map<string, Promise<boolean>>();
const WORKER_ASSET_REGEX = /importScripts\(|self\.postMessage/;
async function isWorkerAsset(url: string, useQueue: boolean = true): Promise<boolean> {
if (workerAssetCache.has(url)) {
return workerAssetCache.get(url)!;
}
const doFetch = () => {
return fetch(url)
.then(r => r.text())
.then(t => WORKER_ASSET_REGEX.test(t));
};
const res = useQueue
? queue(doFetch)
: doFetch();
workerAssetCache.set(url, res);
return res;
}
try { try {
LazyChunkLoaderLogger.log("Loading all chunks..."); LazyChunkLoaderLogger.log("Loading all chunks...");
@ -41,12 +70,12 @@ export async function loadLazyChunks() {
const invalidChunks = new Set<PropertyKey>(); const invalidChunks = new Set<PropertyKey>();
const deferredRequires = new Set<PropertyKey>(); const deferredRequires = new Set<PropertyKey>();
const { promise: chunksSearchingDone, resolve: chunksSearchingResolve } = Promise.withResolvers<void>(); const { promise: chunkSearchingDone, resolve: chunkSearchingDoneResolve } = Promise.withResolvers<void>();
// True if resolved, false otherwise // True if searching promise for that chunk is resolved, false otherwise
const chunksSearchPromises = [] as Array<() => boolean>; let chunkSearchResolvedGetters = [] as Array<() => boolean>;
// This regex loads all language packs which makes webpack finds testing extremely slow, so for now, we prioritize using the one which doesnt include those // This regex loads all language packs which makes webpack finds testing extremely slow, so for now, we prioritize using the one which doesn't include those
const CompleteLazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?((?:\i\.e\("?[^)]+?"?\),?)+?)(?:\]\))?)\.then\(\i(?:\.\i)?\.bind\(\i,"?([^)]+?)"?(?:,[^)]+?)?\)\)/g); const CompleteLazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?((?:\i\.e\("?[^)]+?"?\),?)+?)(?:\]\))?)\.then\(\i(?:\.\i)?\.bind\(\i,"?([^)]+?)"?(?:,[^)]+?)?\)\)/g);
const PartialLazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?((?:\i\.e\("?[^)]+?"?\),?)+?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g); const PartialLazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?((?:\i\.e\("?[^)]+?"?\),?)+?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g);
@ -56,7 +85,7 @@ export async function loadLazyChunks() {
// Workaround to avoid loading the CSS debugging chunk which turns the app pink // Workaround to avoid loading the CSS debugging chunk which turns the app pink
// const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&")); // const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&"));
// Disabled for now since this causes lots of chunks concatenated into the same module get marked as invalid, and thus not loaded. // Disabled for now since this causes lots of chunks concatenated into the same module to get marked as invalid, and thus not loaded.
const hasCssDebuggingLoad = foundCssDebuggingLoad = false; const hasCssDebuggingLoad = foundCssDebuggingLoad = false;
const lazyChunks = factoryCode.matchAll(hasCssDebuggingLoad ? CompleteLazyChunkRegex : PartialLazyChunkRegex); const lazyChunks = factoryCode.matchAll(hasCssDebuggingLoad ? CompleteLazyChunkRegex : PartialLazyChunkRegex);
@ -93,13 +122,7 @@ export async function loadLazyChunks() {
if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue;
const isWorkerAsset = await queue(() => if (await isWorkerAsset(wreq.p + wreq.u(id))) {
fetch(wreq.p + wreq.u(id))
.then(r => r.text())
.then(t => /importScripts\(|self\.postMessage/.test(t))
);
if (isWorkerAsset) {
invalidChunks.add(id); invalidChunks.add(id);
invalidChunkGroup = true; invalidChunkGroup = true;
continue; continue;
@ -140,35 +163,27 @@ export async function loadLazyChunks() {
} }
} }
// setImmediate to only check if all chunks were loaded after this function resolves
// We check if all chunks were loaded every time a factory is loaded // Filter out resolved chunk search promises. If the array length is 0 that means all the pending searches and loadings are done
// If we are still looking for chunks in the other factories, the array will have that factory's chunk search promise not resolved // and our regex has finished scanning all modules. Once this happens, the artificial "natural" loading of chunks has been completed
// But, if all chunk search promises are resolved, this means we found every lazy chunk loaded by Discord code and manually loaded them // and we can continue with the rest of the code.
// setImmediate here is needed so the filtering also includes the promises created from searching the modules loaded by the current invokation.
// Otherwise, it could resolve before the async code being put at the end of the event loop has a chance to run and add the promise to the array.
// The async code mentioned is the invokation of the current function from the factoryListener.
setTimeout(() => { setTimeout(() => {
let allResolved = true; chunkSearchResolvedGetters = chunkSearchResolvedGetters.filter(isResolvedGetter => !isResolvedGetter());
if (chunkSearchResolvedGetters.length === 0) {
for (let i = 0; i < chunksSearchPromises.length; i++) { chunkSearchingDoneResolve();
const isResolved = chunksSearchPromises[i]();
if (isResolved) {
// Remove finished promises to avoid having to iterate through a huge array everytime
chunksSearchPromises.splice(i--, 1);
} else {
allResolved = false;
} }
}
if (allResolved) chunksSearchingResolve();
}, 0); }, 0);
} }
function factoryListener(factory: AnyModuleFactory | ModuleFactory) { function factoryListener(factory: AnyModuleFactory | ModuleFactory) {
let isResolved = false; let isResolved = false;
searchAndLoadLazyChunks(String(factory)) searchAndLoadLazyChunks(String(factory))
.then(() => isResolved = true) .finally(() => isResolved = true);
.catch(() => isResolved = true);
chunksSearchPromises.push(() => isResolved); chunkSearchResolvedGetters.push(() => isResolved);
} }
Webpack.factoryListeners.add(factoryListener); Webpack.factoryListeners.add(factoryListener);
@ -176,7 +191,7 @@ export async function loadLazyChunks() {
factoryListener(wreq.m[moduleId]); factoryListener(wreq.m[moduleId]);
} }
await chunksSearchingDone; await chunkSearchingDone;
Webpack.factoryListeners.delete(factoryListener); Webpack.factoryListeners.delete(factoryListener);
// Require deferred entry points // Require deferred entry points
@ -198,17 +213,16 @@ export async function loadLazyChunks() {
}); });
await Promise.all(chunksLeft.map(async id => queue(async () => { await Promise.all(chunksLeft.map(async id => queue(async () => {
const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) // We will deadlock if we use the queue inside a queue func
.then(r => r.text()) const isWorkerFile = await isWorkerAsset(wreq.p + wreq.u(id), false);
.then(t => /importScripts\(|self\.postMessage/.test(t));
// Loads the chunk. Currently this only happens with the language packs which are loaded differently if (!isWorkerFile) {
if (!isWorkerAsset) {
await wreq.e(id); await wreq.e(id);
} }
}))); })));
LazyChunkLoaderLogger.log("Finished loading all chunks!"); LazyChunkLoaderLogger.log("Finished loading all chunks!");
chunksAlreadyLoaded = true;
} catch (e) { } catch (e) {
LazyChunkLoaderLogger.log("A fatal error occurred:", e); LazyChunkLoaderLogger.log("A fatal error occurred:", e);
} }

View file

@ -19,7 +19,7 @@ async function runReporter() {
try { try {
ReporterLogger.log("Starting test..."); ReporterLogger.log("Starting test...");
const { promise: loadLazyChunksDone, resolve: loadLazyChunksResolve } = Promise.withResolvers<void>(); const { promise: loadLazyChunksDone, resolve: loadLazyChunksDoneResolve } = Promise.withResolvers<void>();
// The main patch for starting the reporter chunk loading // The main patch for starting the reporter chunk loading
addPatch({ addPatch({
@ -30,11 +30,10 @@ async function runReporter() {
} }
}, "Equicord Reporter"); }, "Equicord Reporter");
// initReporter is called in the patched entry point of Discord
// @ts-expect-error // @ts-expect-error
Vencord.Webpack._initReporter = function () { Vencord.Webpack._initReporter = function () {
// initReporter is called in the patched entry point of Discord loadLazyChunks().then(loadLazyChunksDoneResolve);
// setImmediate to only start searching for lazy chunks after Discord initialized the app
setTimeout(() => loadLazyChunks().then(loadLazyChunksResolve), 0);
}; };
await loadLazyChunksDone; await loadLazyChunksDone;

View file

@ -84,18 +84,21 @@ function CopyContextMenu({ name, type, path }: { type: string; name: string; pat
label={`Copy ${type} Name`} label={`Copy ${type} Name`}
action={() => copyWithToast(name)} action={() => copyWithToast(name)}
icon={CopyIcon} icon={CopyIcon}
leadingAccessory={{ type: "icon", icon: CopyIcon }}
/> />
<Menu.MenuItem <Menu.MenuItem
id="vc-spotify-copy-link" id="vc-spotify-copy-link"
label={`Copy ${type} Link`} label={`Copy ${type} Link`}
action={() => copyWithToast("https://open.spotify.com" + path)} action={() => copyWithToast("https://open.spotify.com" + path)}
icon={LinkIcon} icon={LinkIcon}
leadingAccessory={{ type: "icon", icon: LinkIcon }}
/> />
<Menu.MenuItem <Menu.MenuItem
id="vc-spotify-open" id="vc-spotify-open"
label={`Open ${type} in Spotify`} label={`Open ${type} in Spotify`}
action={() => SpotifyStore.openExternal(path)} action={() => SpotifyStore.openExternal(path)}
icon={OpenExternalIcon} icon={OpenExternalIcon}
leadingAccessory={{ type: "icon", icon: OpenExternalIcon }}
/> />
</Menu.Menu> </Menu.Menu>
); );
@ -224,6 +227,7 @@ function AlbumContextMenu({ track }: { track: Track; }) {
label="Open Album" label="Open Album"
action={() => SpotifyStore.openExternal(`/album/${track.album.id}`)} action={() => SpotifyStore.openExternal(`/album/${track.album.id}`)}
icon={OpenExternalIcon} icon={OpenExternalIcon}
leadingAccessory={{ type: "icon", icon: OpenExternalIcon }}
/> />
<Menu.MenuItem <Menu.MenuItem
key="view-cover" key="view-cover"
@ -232,6 +236,7 @@ function AlbumContextMenu({ track }: { track: Track; }) {
// trolley // trolley
action={() => openImageModal(track.album.image)} action={() => openImageModal(track.album.image)}
icon={ImageIcon} icon={ImageIcon}
leadingAccessory={{ type: "icon", icon: ImageIcon }}
/> />
<Menu.MenuControlItem <Menu.MenuControlItem
id="spotify-volume" id="spotify-volume"

View file

@ -20,6 +20,7 @@ import "./fixDiscordBadgePadding.css";
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { CopyIcon, LinkIcon } from "@components/Icons";
import { openContributorModal } from "@components/settings/tabs"; import { openContributorModal } from "@components/settings/tabs";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/discord"; import { copyWithToast } from "@utils/discord";
@ -114,6 +115,7 @@ export function BadgeContextMenu({ badge }: { badge: Omit<ProfileBadge, "id"> &
id="vc-badge-copy-name" id="vc-badge-copy-name"
label="Copy Badge Name" label="Copy Badge Name"
action={() => copyWithToast(badge.description!)} action={() => copyWithToast(badge.description!)}
leadingAccessory={{ type: "icon", icon: CopyIcon }}
/> />
)} )}
{badge.iconSrc && ( {badge.iconSrc && (
@ -121,6 +123,7 @@ export function BadgeContextMenu({ badge }: { badge: Omit<ProfileBadge, "id"> &
id="vc-badge-copy-link" id="vc-badge-copy-link"
label="Copy Badge Image Link" label="Copy Badge Image Link"
action={() => copyWithToast(badge.iconSrc!)} action={() => copyWithToast(badge.iconSrc!)}
leadingAccessory={{ type: "icon", icon: LinkIcon }}
/> />
)} )}
</Menu.Menu> </Menu.Menu>

View file

@ -102,6 +102,7 @@ export function buildExtraRoleContextMenuItems(role: Role, guild: Guild, popoutR
GuildSettingsActions.selectRole(role.id); GuildSettingsActions.selectRole(role.id);
}} }}
icon={PencilIcon} icon={PencilIcon}
leadingAccessory={{ type: "icon", icon: PencilIcon }}
/> />
), ),
role.colorString && ( role.colorString && (
@ -111,6 +112,7 @@ export function buildExtraRoleContextMenuItems(role: Role, guild: Guild, popoutR
label="Copy Role Color" label="Copy Role Color"
action={() => copyToClipboard(role.colorString!)} action={() => copyToClipboard(role.colorString!)}
icon={AppearanceIcon} icon={AppearanceIcon}
leadingAccessory={{ type: "icon", icon: AppearanceIcon }}
/> />
) )
].filter(isTruthy); ].filter(isTruthy);
@ -129,6 +131,7 @@ export function buildExtraRoleContextMenuItems(role: Role, guild: Guild, popoutR
}); });
}} }}
icon={ImageIcon} icon={ImageIcon}
leadingAccessory={{ type: "icon", icon: ImageIcon }}
/> />
), ),
popoutRef && ( popoutRef && (
@ -159,6 +162,7 @@ export function buildExtraRoleContextMenuItems(role: Role, guild: Guild, popoutR
{...popoutProps} {...popoutProps}
> >
<div className={MenuItemClasses.label}>View Role Members</div> <div className={MenuItemClasses.label}>View Role Members</div>
{/* FIXME: update to new icon style */}
<div className={MenuItemClasses.iconContainer}> <div className={MenuItemClasses.iconContainer}>
<RoleMembersIcon /> <RoleMembersIcon />
</div> </div>
@ -197,6 +201,7 @@ export function openRoleContextMenu(event: React.MouseEvent<HTMLElement>, { guil
id="vc-better-role-context-copy-role-id" id="vc-better-role-context-copy-role-id"
label={getIntlMessage("COPY_ID_ROLE")} label={getIntlMessage("COPY_ID_ROLE")}
icon={CopyIdIcon} icon={CopyIdIcon}
leadingAccessory={{ type: "icon", icon: CopyIdIcon }}
action={() => copyToClipboard(role.id)} action={() => copyToClipboard(role.id)}
/> />
</Menu.Menu> </Menu.Menu>

View file

@ -70,6 +70,7 @@ export const addViewStreamContext: NavContextMenuPatchCallback = (children, { us
label="View Stream Preview" label="View Stream Preview"
id="view-stream-preview" id="view-stream-preview"
icon={ScreenshareIcon} icon={ScreenshareIcon}
leadingAccessory={{ type: "icon", icon: ScreenshareIcon }}
action={() => stream && handleViewPreview(stream)} action={() => stream && handleViewPreview(stream)}
disabled={!stream} disabled={!stream}
/> />

View file

@ -5,6 +5,7 @@
*/ */
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { CopyIcon } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/discord"; import { copyWithToast } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
@ -69,6 +70,7 @@ export default definePlugin({
"Success! Copied emoji markdown." "Success! Copied emoji markdown."
); );
}} }}
leadingAccessory={{ type: "icon", icon: CopyIcon }}
/> />
); );
}, },

View file

@ -18,6 +18,7 @@
import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { isPluginEnabled } from "@api/PluginManager"; import { isPluginEnabled } from "@api/PluginManager";
import { LinkIcon, TopRightArrow } from "@components/Icons";
import ExpressionClonerPlugin from "@plugins/expressionCloner"; import ExpressionClonerPlugin from "@plugins/expressionCloner";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/discord"; import { copyWithToast } from "@utils/discord";
@ -43,14 +44,17 @@ function buildMenuItem(sticker: PartialSticker, addBottomSeparator: boolean) {
<Menu.MenuItem <Menu.MenuItem
id="vc-copy-sticker-link" id="vc-copy-sticker-link"
key="vc-copy-sticker-link" key="vc-copy-sticker-link"
label="Copy Link" label="Copy Sticker Link"
leadingAccessory={{ type: "icon", icon: LinkIcon }}
action={() => copyWithToast(getUrl(sticker), "Link copied!")} action={() => copyWithToast(getUrl(sticker), "Link copied!")}
/> />
<Menu.MenuItem <Menu.MenuItem
id="vc-open-sticker-link" id="vc-open-sticker-link"
key="vc-open-sticker-link" key="vc-open-sticker-link"
label="Open Link" label="Open Sticker Link"
leadingAccessory={{ type: "icon", icon: LinkIcon }}
trailingIndicator={{ type: "icon", icon: TopRightArrow }}
action={() => VencordNative.native.openExternal(getUrl(sticker))} action={() => VencordNative.native.openExternal(getUrl(sticker))}
/> />
</Menu.MenuGroup> </Menu.MenuGroup>

View file

@ -39,6 +39,7 @@ const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: U
label="Copy User URL" label="Copy User URL"
action={() => copyToClipboard(`<https://discord.com/users/${user.id}>`)} action={() => copyToClipboard(`<https://discord.com/users/${user.id}>`)}
icon={LinkIcon} icon={LinkIcon}
leadingAccessory={{ type: "icon", icon: LinkIcon }}
/> />
); );
}; };

View file

@ -23,6 +23,7 @@ export default function DecorationContextMenu({ decoration }: { decoration: Deco
id={cl("decoration-context-menu-copy-hash")} id={cl("decoration-context-menu-copy-hash")}
label="Copy Decoration Hash" label="Copy Decoration Hash"
icon={CopyIcon} icon={CopyIcon}
leadingAccessory={{ type: "icon", icon: CopyIcon }}
action={() => copyToClipboard(decoration.hash)} action={() => copyToClipboard(decoration.hash)}
/> />
{decoration.authorId === UserStore.getCurrentUser().id && {decoration.authorId === UserStore.getCurrentUser().id &&
@ -31,6 +32,7 @@ export default function DecorationContextMenu({ decoration }: { decoration: Deco
label="Delete Decoration" label="Delete Decoration"
color="danger" color="danger"
icon={DeleteIcon} icon={DeleteIcon}
leadingAccessory={{ type: "icon", icon: DeleteIcon }}
action={() => openModal(props => ( action={() => openModal(props => (
<ConfirmModal <ConfirmModal
{...props} {...props}

View file

@ -22,6 +22,7 @@ import { BaseText } from "@components/BaseText";
import { CheckedTextInput } from "@components/CheckedTextInput"; import { CheckedTextInput } from "@components/CheckedTextInput";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { Heading } from "@components/Heading"; import { Heading } from "@components/Heading";
import { PlusIcon } from "@components/Icons";
import { Paragraph } from "@components/Paragraph"; import { Paragraph } from "@components/Paragraph";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getGuildAcronym, hasGuildFeature } from "@utils/discord"; import { getGuildAcronym, hasGuildFeature } from "@utils/discord";
@ -329,6 +330,7 @@ function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Om
id="emote-cloner" id="emote-cloner"
key="emote-cloner" key="emote-cloner"
label={`Clone ${type}`} label={`Clone ${type}`}
leadingAccessory={{ type: "icon", icon: PlusIcon }}
action={() => action={() =>
openModalLazy(async () => { openModalLazy(async () => {
const res = await fetchData(); const res = await fetchData();

View file

@ -15,6 +15,7 @@ import { isPluginEnabled } from "@api/PluginManager";
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles"; import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { DeleteIcon, EyeIcon } from "@components/Icons";
import { Devs, EQUIBOT_USER_ID, EquicordDevs, SUPPORT_CHANNEL_ID, VC_SUPPORT_CATEGORY_ID, VENBOT_USER_ID } from "@utils/constants"; import { Devs, EQUIBOT_USER_ID, EquicordDevs, SUPPORT_CHANNEL_ID, VC_SUPPORT_CATEGORY_ID, VENBOT_USER_ID } from "@utils/constants";
import { getIntlMessage } from "@utils/discord"; import { getIntlMessage } from "@utils/discord";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
@ -120,6 +121,7 @@ const patchMessageContextMenu: NavContextMenuPatchCallback = (
id={TOGGLE_DELETE_STYLE_ID} id={TOGGLE_DELETE_STYLE_ID}
key={TOGGLE_DELETE_STYLE_ID} key={TOGGLE_DELETE_STYLE_ID}
label="Toggle Deleted Highlight" label="Toggle Deleted Highlight"
leadingAccessory={{ type: "icon", icon: EyeIcon }}
action={() => domElement.classList.toggle("messagelogger-deleted")} action={() => domElement.classList.toggle("messagelogger-deleted")}
/>, />,
); );
@ -161,6 +163,7 @@ const patchMessageContextMenu: NavContextMenuPatchCallback = (
id={REMOVE_HISTORY_ID} id={REMOVE_HISTORY_ID}
key={REMOVE_HISTORY_ID} key={REMOVE_HISTORY_ID}
label={label} label={label}
leadingAccessory={{ type: "icon", icon: DeleteIcon }}
color="danger" color="danger"
action={() => { action={() => {
clearMessageHistory(message); clearMessageHistory(message);

View file

@ -104,6 +104,7 @@ const makeContextMenuPatch: (shouldAddIcon: boolean) => NavContextMenuPatchCallb
label="Apply NewGuildSettings" label="Apply NewGuildSettings"
id="vc-newguildsettings-apply" id="vc-newguildsettings-apply"
icon={shouldAddIcon ? CogWheel : void 0} icon={shouldAddIcon ? CogWheel : void 0}
leadingAccessory={shouldAddIcon ? { type: "icon", icon: CogWheel } : void 0}
action={() => applyDefaultSettings(guild.id)} action={() => applyDefaultSettings(guild.id)}
/> />
); );
@ -117,6 +118,7 @@ const makeContextMenuPatch: (shouldAddIcon: boolean) => NavContextMenuPatchCallb
label="Apply NewGuildSettings to Folder" label="Apply NewGuildSettings to Folder"
id="vc-newguildsettings-apply-folder" id="vc-newguildsettings-apply-folder"
icon={shouldAddIcon ? CogWheel : void 0} icon={shouldAddIcon ? CogWheel : void 0}
leadingAccessory={shouldAddIcon ? { type: "icon", icon: CogWheel } : void 0}
action={async () => { action={async () => {
for (const guildId of folder.guildIds) { for (const guildId of folder.guildIds) {
applyDefaultSettings(guildId); applyDefaultSettings(guildId);

View file

@ -230,6 +230,7 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str
id={cl("copy-role-id")} id={cl("copy-role-id")}
label={getIntlMessage("COPY_ID_ROLE")} label={getIntlMessage("COPY_ID_ROLE")}
icon={CopyIdIcon} icon={CopyIdIcon}
leadingAccessory={{ type: "icon", icon: CopyIdIcon }}
action={() => copyToClipboard(roleId)} action={() => copyToClipboard(roleId)}
/> />
@ -240,6 +241,7 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str
id={cl("view-as-role")} id={cl("view-as-role")}
label={getIntlMessage("VIEW_AS_ROLE")} label={getIntlMessage("VIEW_AS_ROLE")}
icon={ViewAsRoleIcon} icon={ViewAsRoleIcon}
leadingAccessory={{ type: "icon", icon: ViewAsRoleIcon }}
action={() => { action={() => {
onClose(); onClose();
FluxDispatcher.dispatch({ FluxDispatcher.dispatch({
@ -269,6 +271,7 @@ function UserContextMenu({ userId }: { userId: string; }) {
<Menu.MenuItem <Menu.MenuItem
id={cl("copy-user-id")} id={cl("copy-user-id")}
label={getIntlMessage("COPY_ID_USER")} label={getIntlMessage("COPY_ID_USER")}
leadingAccessory={{ type: "icon", icon: CopyIdIcon }}
action={() => { action={() => {
copyToClipboard(userId); copyToClipboard(userId);
}} }}

View file

@ -59,13 +59,14 @@ export const settings = definePluginSettings({
}, },
}); });
function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) { function MenuItem(guildId: string, { id, type, withIcon }: { id?: string, type?: MenuItemParentType, withIcon?: boolean; }) {
if (type === MenuItemParentType.User && !GuildMemberStore.isMember(guildId, id!)) return null; if (type === MenuItemParentType.User && !GuildMemberStore.isMember(guildId, id!)) return null;
return ( return (
<Menu.MenuItem <Menu.MenuItem
id="perm-viewer-permissions" id="perm-viewer-permissions"
label="Permissions" label="View Permissions"
leadingAccessory={withIcon ? { type: "icon", icon: SafetyIcon } : undefined}
action={() => { action={() => {
const guild = GuildStore.getGuild(guildId); const guild = GuildStore.getGuild(guildId);
@ -125,7 +126,7 @@ function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) {
); );
} }
function makeContextMenuPatch(childId: string | string[], type?: MenuItemParentType): NavContextMenuPatchCallback { function makeContextMenuPatch(childId: string | string[], type?: MenuItemParentType, withIcon = false): NavContextMenuPatchCallback {
return (children, props) => { return (children, props) => {
if ( if (
!props || !props ||
@ -139,9 +140,9 @@ function makeContextMenuPatch(childId: string | string[], type?: MenuItemParentT
const group = findGroupChildrenByChildId(childId, children); const group = findGroupChildrenByChildId(childId, children);
const item = match(type) const item = match(type)
.with(MenuItemParentType.User, () => MenuItem(props.guildId, props.user.id, type)) .with(MenuItemParentType.User, () => MenuItem(props.guildId, { id: props.user.id, type, withIcon }))
.with(MenuItemParentType.Channel, () => MenuItem(props.guild.id, props.channel.id, type)) .with(MenuItemParentType.Channel, () => MenuItem(props.guild.id, { id: props.channel.id, type, withIcon }))
.with(MenuItemParentType.Guild, () => MenuItem(props.guild.id)) .with(MenuItemParentType.Guild, () => MenuItem(props.guild.id, { withIcon }))
.otherwise(() => null); .otherwise(() => null);
if (item == null) return; if (item == null) return;
@ -214,6 +215,6 @@ export default definePlugin({
"user-context": makeContextMenuPatch("roles", MenuItemParentType.User), "user-context": makeContextMenuPatch("roles", MenuItemParentType.User),
"channel-context": makeContextMenuPatch(["mute-channel", "unmute-channel"], MenuItemParentType.Channel), "channel-context": makeContextMenuPatch(["mute-channel", "unmute-channel"], MenuItemParentType.Channel),
"guild-context": makeContextMenuPatch("privacy", MenuItemParentType.Guild), "guild-context": makeContextMenuPatch("privacy", MenuItemParentType.Guild),
"guild-header-popout": makeContextMenuPatch("privacy", MenuItemParentType.Guild) "guild-header-popout": makeContextMenuPatch("privacy", MenuItemParentType.Guild, true)
} }
}); });

View file

@ -18,6 +18,7 @@
import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons"; import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";
import { generateId, sendBotMessage } from "@api/Commands"; import { generateId, sendBotMessage } from "@api/Commands";
import { EyeIcon } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { IconComponent, StartAt } from "@utils/types"; import definePlugin, { IconComponent, StartAt } from "@utils/types";
import { CloudUpload, MessageAttachment } from "@vencord/discord-types"; import { CloudUpload, MessageAttachment } from "@vencord/discord-types";
@ -73,17 +74,12 @@ const getAttachments = async (channelId: string) =>
const PreviewIcon: IconComponent = ({ height = 20, width = 20, className }) => { const PreviewIcon: IconComponent = ({ height = 20, width = 20, className }) => {
return ( return (
<svg <EyeIcon
fill="currentColor"
fillRule="evenodd"
width={width} width={width}
height={height} height={height}
className={className} className={className}
viewBox="0 0 24 24"
style={{ scale: "1.096", translate: "0 -1px" }} style={{ scale: "1.096", translate: "0 -1px" }}
> />
<path d="M22.89 11.7c.07.2.07.4 0 .6C22.27 13.9 19.1 21 12 21c-7.11 0-10.27-7.11-10.89-8.7a.83.83 0 0 1 0-.6C1.73 10.1 4.9 3 12 3c7.11 0 10.27 7.11 10.89 8.7Zm-4.5-3.62A15.11 15.11 0 0 1 20.85 12c-.38.88-1.18 2.47-2.46 3.92C16.87 17.62 14.8 19 12 19c-2.8 0-4.87-1.38-6.39-3.08A15.11 15.11 0 0 1 3.15 12c.38-.88 1.18-2.47 2.46-3.92C7.13 6.38 9.2 5 12 5c2.8 0 4.87 1.38 6.39 3.08ZM15.56 11.77c.2-.1.44.02.44.23a4 4 0 1 1-4-4c.21 0 .33.25.23.44a2.5 2.5 0 0 0 3.32 3.32Z" />
</svg>
); );
}; };

View file

@ -6,7 +6,7 @@
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { definePluginSettings, migratePluginSettings } from "@api/Settings"; import { definePluginSettings, migratePluginSettings } from "@api/Settings";
import { Flex } from "@components/Flex"; import { SearchIcon } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { Menu } from "@webpack/common"; import { Menu } from "@webpack/common";
@ -89,6 +89,7 @@ function makeSearchItem(src: string) {
label="Search Text" label="Search Text"
key="search-text" key="search-text"
id="vc-search-text" id="vc-search-text"
leadingAccessory={{ type: "icon", icon: SearchIcon }}
> >
{Object.keys(Engines).map(engine => { {Object.keys(Engines).map(engine => {
const key = "vc-search-content-" + engine; const key = "vc-search-content-" + engine;
@ -96,20 +97,8 @@ function makeSearchItem(src: string) {
<Menu.MenuItem <Menu.MenuItem
key={key} key={key}
id={key} id={key}
label={ label={engine}
<Flex gap="0.5em" alignItems="center"> leadingAccessory={{ type: "image", src: `https://icons.duckduckgo.com/ip3/${new URL(Engines[engine]).hostname}.ico` }}
<img
style={{
borderRadius: "50%"
}}
aria-hidden="true"
height={16}
width={16}
src={`https://icons.duckduckgo.com/ip3/${new URL(Engines[engine]).hostname}.ico`}
/>
{engine}
</Flex>
}
action={() => search(src, Engines[engine])} action={() => search(src, Engines[engine])}
/> />
); );

View file

@ -17,8 +17,7 @@
*/ */
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { Flex } from "@components/Flex"; import { OpenExternalIcon, SearchIcon } from "@components/Icons";
import { OpenExternalIcon } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { Menu } from "@webpack/common"; import { Menu } from "@webpack/common";
@ -43,6 +42,7 @@ function makeSearchItem(src: string) {
label="Search Image" label="Search Image"
key="search-image" key="search-image"
id="search-image" id="search-image"
leadingAccessory={{ type: "icon", icon: SearchIcon }}
> >
{Object.keys(Engines).map((engine, i) => { {Object.keys(Engines).map((engine, i) => {
const key = "search-image-" + engine; const key = "search-image-" + engine;
@ -50,20 +50,8 @@ function makeSearchItem(src: string) {
<Menu.MenuItem <Menu.MenuItem
key={key} key={key}
id={key} id={key}
label={ label={engine}
<Flex alignItems="center" gap="0.5em"> leadingAccessory={{ type: "image", src: `https://icons.duckduckgo.com/ip3/${new URL(Engines[engine]).host}.ico` }}
<img
style={{
borderRadius: "50%",
}}
aria-hidden="true"
height={16}
width={16}
src={`https://icons.duckduckgo.com/ip3/${new URL(Engines[engine]).host}.ico`}
/>
{engine}
</Flex>
}
action={() => search(src, Engines[engine])} action={() => search(src, Engines[engine])}
/> />
); );
@ -71,12 +59,8 @@ function makeSearchItem(src: string) {
<Menu.MenuItem <Menu.MenuItem
key="search-image-all" key="search-image-all"
id="search-image-all" id="search-image-all"
label={ label="All"
<Flex alignItems="center" gap="0.5em"> leadingAccessory={{ type: "icon", icon: OpenExternalIcon }}
<OpenExternalIcon height={16} width={16} />
All
</Flex>
}
action={() => Object.values(Engines).forEach(e => search(src, e))} action={() => Object.values(Engines).forEach(e => search(src, e))}
/> />
</Menu.MenuItem> </Menu.MenuItem>

View file

@ -49,6 +49,7 @@ const guildPopoutPatch: NavContextMenuPatchCallback = (children, { guild }: { gu
label="View Reviews" label="View Reviews"
id="vc-rdb-server-reviews" id="vc-rdb-server-reviews"
icon={OpenExternalIcon} icon={OpenExternalIcon}
leadingAccessory={{ type: "icon", icon: OpenExternalIcon }}
action={() => openReviewsModal(guild.id, guild.name, ReviewType.Server)} action={() => openReviewsModal(guild.id, guild.name, ReviewType.Server)}
/> />
); );
@ -61,6 +62,7 @@ const userContextPatch: NavContextMenuPatchCallback = (children, { user }: { use
label="View Reviews" label="View Reviews"
id="vc-rdb-user-reviews" id="vc-rdb-user-reviews"
icon={OpenExternalIcon} icon={OpenExternalIcon}
leadingAccessory={{ type: "icon", icon: OpenExternalIcon }}
action={() => openReviewsModal(user.id, user.username, ReviewType.User)} action={() => openReviewsModal(user.id, user.username, ReviewType.User)}
/> />
); );

View file

@ -6,6 +6,7 @@
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { InfoIcon } from "@components/Icons";
import { Devs, EquicordDevs } from "@utils/constants"; import { Devs, EquicordDevs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { Guild } from "@vencord/discord-types"; import { Guild } from "@vencord/discord-types";
@ -13,13 +14,14 @@ import { Menu } from "@webpack/common";
import { openGuildInfoModal } from "./GuildInfoModal"; import { openGuildInfoModal } from "./GuildInfoModal";
const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { const makePatch: (showIcon: boolean) => NavContextMenuPatchCallback = showIcon => (children, { guild }: { guild: Guild; }) => {
const group = findGroupChildrenByChildId("privacy", children); const group = findGroupChildrenByChildId("privacy", children);
group?.push( group?.push(
<Menu.MenuItem <Menu.MenuItem
id="vc-server-info" id="vc-server-info"
label="Server Info" label="Server Info"
leadingAccessory={showIcon ? { type: "icon", icon: InfoIcon } : undefined}
action={() => openGuildInfoModal(guild)} action={() => openGuildInfoModal(guild)}
/> />
); );
@ -56,8 +58,7 @@ export default definePlugin({
searchTerms: ["guild", "info", "ServerProfile"], searchTerms: ["guild", "info", "ServerProfile"],
isModified: true, isModified: true,
contextMenus: { contextMenus: {
"guild-context": Patch, "guild-context": makePatch(false),
"guild-header-popout": Patch "guild-header-popout": makePatch(true)
}, }
settings
}); });

View file

@ -41,6 +41,7 @@ const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }: { m
id="vc-trans" id="vc-trans"
label="Translate" label="Translate"
icon={TranslateIcon} icon={TranslateIcon}
leadingAccessory={{ type: "icon", icon: TranslateIcon }}
action={async () => { action={async () => {
const trans = await translate("received", content); const trans = await translate("received", content);
handleTranslate(message.id, trans); handleTranslate(message.id, trans);

View file

@ -48,6 +48,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (
label={isEmbedSuppressed ? "Unsuppress Embeds" : "Suppress Embeds"} label={isEmbedSuppressed ? "Unsuppress Embeds" : "Suppress Embeds"}
color={isEmbedSuppressed ? undefined : "danger"} color={isEmbedSuppressed ? undefined : "danger"}
icon={isEmbedSuppressed ? ImageVisible : ImageInvisible} icon={isEmbedSuppressed ? ImageVisible : ImageInvisible}
leadingAccessory={{ type: "icon", icon: isEmbedSuppressed ? ImageVisible : ImageInvisible }}
action={() => action={() =>
RestAPI.patch({ RestAPI.patch({
url: Constants.Endpoints.MESSAGE(channel.id, messageId), url: Constants.Endpoints.MESSAGE(channel.id, messageId),

View file

@ -118,13 +118,14 @@ export type VoiceChannelIndicatorProps = {
userId: string; userId: string;
isMessageIndicator?: boolean; isMessageIndicator?: boolean;
isProfile?: boolean; isProfile?: boolean;
isMessage?: boolean;
isActionButton?: boolean; isActionButton?: boolean;
shouldHighlight?: boolean; shouldHighlight?: boolean;
}; };
const clickTimers = new Map<string, any>(); const clickTimers = new Map<string, any>();
export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId, isProfile, isActionButton, shouldHighlight }: VoiceChannelIndicatorProps) => { export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId, isProfile, isMessage, isActionButton, shouldHighlight }: VoiceChannelIndicatorProps) => {
const channelId = useStateFromStores([VoiceStateStore], () => VoiceStateStore.getVoiceStateForUser(userId)?.channelId); const channelId = useStateFromStores([VoiceStateStore], () => VoiceStateStore.getVoiceStateForUser(userId)?.channelId);
const { isMuted, isDeaf } = useStateFromStores([VoiceStateStore], () => { const { isMuted, isDeaf } = useStateFromStores([VoiceStateStore], () => {
@ -194,7 +195,7 @@ export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId, isProfile, is
isActionButton && shouldHighlight && ActionButtonClasses.highlight, isActionButton && shouldHighlight && ActionButtonClasses.highlight,
cl(isProfile && "profile-speaker") cl(isProfile && "profile-speaker")
)} )}
size={isActionButton ? 20 : 16} size={(isActionButton || isMessage) ? 20 : 16}
/> />
)} )}
</Tooltip> </Tooltip>

View file

@ -14,8 +14,9 @@
} }
.vc-uvs-profile-speaker { .vc-uvs-profile-speaker {
width: var(--custom-nickname-icon-size);
height: var(--custom-nickname-icon-size); height: var(--custom-nickname-icon-size);
margin-right: 4px;
transform: translateY(1.3px);
} }
.vc-uvs-tooltip-container { .vc-uvs-tooltip-container {

View file

@ -43,7 +43,7 @@ interface GroupDMContextProps {
const settings = definePluginSettings({ const settings = definePluginSettings({
format: { format: {
type: OptionType.SELECT, type: OptionType.SELECT,
description: "Choose the image format to use for non animated images. Animated images will always use .gif", description: "Choose the image format to use for non-animated images. Animated images will always use .webp",
options: [ options: [
{ {
label: "webp", label: "webp",
@ -85,7 +85,7 @@ function openImage({ url, width, height, event }: OpenImageProps) {
const format = url.startsWith("/") const format = url.startsWith("/")
? "png" ? "png"
: u.searchParams.get("animated") === "true" : u.searchParams.get("animated") === "true"
? "gif" ? "webp"
: settings.store.format; : settings.store.format;
u.searchParams.set("size", settings.store.imgSize); u.searchParams.set("size", settings.store.imgSize);
@ -116,6 +116,7 @@ const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: U
label="View Avatar" label="View Avatar"
action={() => openAvatar(IconUtils.getUserAvatarURL(user, true))} action={() => openAvatar(IconUtils.getUserAvatarURL(user, true))}
icon={ImageIcon} icon={ImageIcon}
leadingAccessory={{ type: "icon", icon: ImageIcon }}
/> />
{memberAvatar && ( {memberAvatar && (
<Menu.MenuItem <Menu.MenuItem
@ -128,6 +129,7 @@ const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: U
canAnimate: true canAnimate: true
}))} }))}
icon={ImageIcon} icon={ImageIcon}
leadingAccessory={{ type: "icon", icon: ImageIcon }}
/> />
)} )}
{avatarDecoration && ( {avatarDecoration && (
@ -140,6 +142,7 @@ const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: U
canAnimate: true canAnimate: true
})!)} })!)}
icon={ImageIcon} icon={ImageIcon}
leadingAccessory={{ type: "icon", icon: ImageIcon }}
/> />
)} )}
</Menu.MenuGroup> </Menu.MenuGroup>
@ -166,6 +169,7 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
})!) })!)
} }
icon={ImageIcon} icon={ImageIcon}
leadingAccessory={{ type: "icon", icon: ImageIcon }}
/> />
) : null} ) : null}
{banner ? ( {banner ? (
@ -176,6 +180,7 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
openBanner(IconUtils.getGuildBannerURL(guild, true)!) openBanner(IconUtils.getGuildBannerURL(guild, true)!)
} }
icon={ImageIcon} icon={ImageIcon}
leadingAccessory={{ type: "icon", icon: ImageIcon }}
/> />
) : null} ) : null}
</Menu.MenuGroup> </Menu.MenuGroup>
@ -194,6 +199,7 @@ const GroupDMContext: NavContextMenuPatchCallback = (children, { channel }: Grou
openAvatar(IconUtils.getChannelIconURL(channel)!) openAvatar(IconUtils.getChannelIconURL(channel)!)
} }
icon={ImageIcon} icon={ImageIcon}
leadingAccessory={{ type: "icon", icon: ImageIcon }}
/> />
</Menu.MenuGroup> </Menu.MenuGroup>
)); ));

View file

@ -149,6 +149,7 @@ function MakeContextCallback(name: "Guild" | "Role" | "User" | "Channel" | "Mess
label="View Raw" label="View Raw"
action={action} action={action}
icon={CopyRawIcon} icon={CopyRawIcon}
leadingAccessory={{ type: "icon", icon: CopyRawIcon }}
/> />
); );
}; };
@ -167,6 +168,7 @@ const devContextCallback: NavContextMenuPatchCallback = (children, { id }: { id:
label="View Raw" label="View Raw"
action={() => openViewRawModal(JSON.stringify(role, null, 4), "Role")} action={() => openViewRawModal(JSON.stringify(role, null, 4), "Role")}
icon={CopyRawIcon} icon={CopyRawIcon}
leadingAccessory={{ type: "icon", icon: CopyRawIcon }}
/> />
); );
}; };

View file

@ -1,16 +1,18 @@
:root { :root {
/* opt out of auto-dark-mode plugins, which break discord css, such as force-dark, dark-reader etc */ /* opt out of auto-dark-mode plugins, which break discord css, such as force-dark, dark-reader etc */
color-scheme: only light; color-scheme: only light;
/* force title bar to be same as WCO bar height, regardless of zoom, device pixel ratio or OS */
/* is only set when WCO is defined, otherwise sets to 0 */
--custom-app-top-bar-height: env(titlebar-area-height, 0px) !important;
} }
/* position trailing titlebar elements alongside WCO */ @media all and not (display-mode: browser) {
[class*="bar_"] [class*="trailing_"] { :root {
/* force title bar to be same as WCO bar height, regardless of zoom, device pixel ratio or OS, is only set when WCO is defined, otherwise sets to 0 */
--custom-app-top-bar-height: env(titlebar-area-height, 0px) !important;
}
/* position trailing titlebar elements alongside WCO */
[class*="bar_"] [class*="trailing_"] {
padding-right: calc(100vw - env(titlebar-area-width, 100%) - env(titlebar-area-x, 0px)); padding-right: calc(100vw - env(titlebar-area-width, 100%) - env(titlebar-area-x, 0px));
}
} }
/* force hide trailing icons when standalone so there's no ugly pixel overflow */ /* force hide trailing icons when standalone so there's no ugly pixel overflow */