diff --git a/src/plugins/customFolderIcons/index.tsx b/src/plugins/customFolderIcons/index.tsx
new file mode 100644
index 000000000..f2d51501a
--- /dev/null
+++ b/src/plugins/customFolderIcons/index.tsx
@@ -0,0 +1,170 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import "./style.css";
+
+import { DataStore } from "@api/index";
+import { Devs } from "@utils/constants";
+import { closeModal, ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
+import definePlugin from "@utils/types";
+import { Button, Menu, TextInput } from "@webpack/common";
+const DATA_STORE_NAME = "CFI_DATA";
+enum ICON_TYPE {
+ PNG,
+ SVG
+}
+interface folderIcon{
+ type: ICON_TYPE,
+ url: string
+}
+interface config {
+ [key: string]: folderIcon
+}
+let d: config;
+export default definePlugin({
+ start: async ()=>{
+ d = await DataStore.get(DATA_STORE_NAME) || {} as config;
+ },
+ name: "_customFolderIcons",
+ description: "customize folder icons with any png",
+ authors: [
+ Devs.sadan
+ ],
+ patches: [
+ {
+ find: ".expandedFolderIconWrapper",
+ replacement: {
+ match: /(return.{0,80}expandedFolderIconWrapper.*,)\(0,..jsxs\)\(.*]}\)/,
+ replace: "$1$self.test(e)"
+ }
+ }
+ ],
+ contextMenus: {
+ "guild-context": (c, a) => {
+ if(!("folderId" in a)) return;
+ c.push(makeContextItem(a.folderId));
+ }
+ },
+ commands: [
+ {
+ name: "test",
+ description: "test command for some wack shit",
+ execute: async () => {
+ console.log("asd");
+ }
+ }
+ ],
+ test(e){
+ console.log(e);
+ if(d && e.folderNode.id in d){
+ switch(d[e.folderNode.id].type){
+ case ICON_TYPE.PNG:
+ return (
+
+ );
+ case ICON_TYPE.SVG:
+ return null;
+ }
+ }
+ // TODO: when using the default set the color properly
+ return(
+
+ );
+ }
+});
+const setFolderUrl = async (id: string, url: string) => {
+ console.log(id, url);
+ DataStore.get(DATA_STORE_NAME).then(v => {
+ if(v){
+ v[id] = {
+ type: ICON_TYPE.PNG,
+ url: url
+ };
+ DataStore.set(DATA_STORE_NAME, v).then(() => { d = v; }).catch(e => {
+ handleUpdateError(e);
+ });
+ }else{
+ v = {};
+ v[id] = {
+ type: ICON_TYPE.PNG,
+ url: url
+ };
+ DataStore.set(DATA_STORE_NAME, v).then(() => { d = v; }).catch(e => {
+ handleUpdateError(e);
+ });
+ }
+ }
+ )
+ .catch(e => {
+ handleUpdateError(e);
+ });
+};
+function ImageModal(a: { folderId: string }){
+ let v = "";
+ return(
+ <>
+
+ {
+ v = val;
+ }}
+ placeholder="https://example.com/image.png"
+ >
+
+
+ >
+ );
+}
+function makeContextItem(id: string) {
+ return (
+ {
+ console.log("menu clicked");
+ openModalLazy(async () => {
+ return props => (
+
+
+
+ Set a New Icon.
+
+
+
+
+
+
+ );
+ },
+ {
+ modalKey: "custom-folder-icon"
+ });
+ }}/>
+ );
+}
+function handleUpdateError(e: any) {
+ throw e;
+}
diff --git a/src/plugins/customFolderIcons/style.css b/src/plugins/customFolderIcons/style.css
new file mode 100644
index 000000000..26400f314
--- /dev/null
+++ b/src/plugins/customFolderIcons/style.css
@@ -0,0 +1,7 @@
+.customFolderDefaultIcon{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: var(--custom-folder-item-guild-icon-size);
+ height: var(--custom-folder-item-guild-icon-size);
+}
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 4e3422526..dcb19979a 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -518,6 +518,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "verticalsync",
id: 328165170536775680n
},
+ sadan: {
+ name: "sadan",
+ id: 521819891141967883n
+ },
} satisfies Record);
// iife so #__PURE__ works correctly