diff --git a/assets/lang/en-US.json b/assets/lang/en-US.json index 6948db9..59fce9a 100644 --- a/assets/lang/en-US.json +++ b/assets/lang/en-US.json @@ -64,6 +64,8 @@ "settings-csp-strict": "Strict", "settings-csp-none": "None", "settings-csp-vanilla": "Vanilla", + "settings-showExperimentalPluginMenu": "Show experimental plugin menu", + "settings-showExperimentalPluginMenu-desc": "Show the experimental plugin menu in settings. This is a temporary setting until the plugin system is fully implemented.", "settings-firstTimeCrash": "We're setting things up for you!", "settings-firstTimeCrash-desc": "Settings are not available on a first-time launch. Please use a button below to restart and settings should be ready after the restart.", "settings-useSystemCssEditor": "Use system CSS editor", @@ -293,6 +295,7 @@ "themes-openThemesFolder": "Open themes folder", "themes-import": "Import", "themes-importUrlPlaceholder": "https://raw.githubusercontent.com/... [.theme.css]", + "themes-installed": "Installed Themes", "games-registeredGames": "Registered Games", "games-refreshList": "Refresh list", "games-add": "Add", diff --git a/package.json b/package.json index 530b752..6e47de8 100644 --- a/package.json +++ b/package.json @@ -43,18 +43,18 @@ "@xmldom/xmldom": "^0.9.10", "app-builder-lib": "^26.8.1", "babel-preset-solid": "^1.9.12", - "electron": "42.3.3", + "electron": "43.1.1", "electron-builder": "26.8.1", "lucide-solid": "^1.16.0", "rolldown": "1.0.3", "rollup-plugin-copy": "^3.5.0", - "solid-js": "^1.9.13", + "solid-js": "^1.9.14", "solid-motionone": "^1.0.4", "typescript": "^6.0.3", "xml-formatter": "^3.7.0" }, "dependencies": { - "adm-zip": "^0.5.17", + "adm-zip": "^0.6.0", "arrpc": "github:Legcord/arrpc#efe7589762470d32b9ba10d529be5acc23cd0e19", "electron-context-menu": "^4.1.2", "electron-is-dev": "^3.0.1", @@ -67,6 +67,6 @@ "optionalDependencies": { "@vencord/venmic": "^6.1.0" }, - "packageManager": "pnpm@11.4.0+sha512.f0febc7e37552ab485494a914241b338e0b3580b93d54ce31f00933015880863129038a1b4ae4e414a0ee63ac35bf21197e990172c4a68256450b5636310968f", + "packageManager": "pnpm@11.10.0", "package-manager-strict": false } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c64720b..1de1012 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: .: dependencies: adm-zip: - specifier: ^0.5.17 - version: 0.5.17 + specifier: ^0.6.0 + version: 0.6.0 arrpc: specifier: github:Legcord/arrpc#efe7589762470d32b9ba10d529be5acc23cd0e19 version: https://codeload.github.com/Legcord/arrpc/tar.gz/efe7589762470d32b9ba10d529be5acc23cd0e19 @@ -44,7 +44,7 @@ importers: version: 2.4.16 '@rolldown-plugin/solid': specifier: ^0.0.4 - version: 0.0.4(csstype@3.2.3)(rolldown@1.0.3)(seroval@1.5.4)(solid-js@1.9.13) + version: 0.0.4(csstype@3.2.3)(rolldown@1.0.3)(seroval@1.5.5)(solid-js@1.9.14) '@rollup/plugin-esm-shim': specifier: ^0.1.8 version: 0.1.8 @@ -71,16 +71,16 @@ importers: version: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1) babel-preset-solid: specifier: ^1.9.12 - version: 1.9.12(@babel/core@8.0.0-rc.6)(solid-js@1.9.13) + version: 1.9.12(@babel/core@8.0.0-rc.6)(solid-js@1.9.14) electron: - specifier: 42.3.3 - version: 42.3.3 + specifier: 43.1.1 + version: 43.1.1 electron-builder: specifier: 26.8.1 version: 26.8.1(electron-builder-squirrel-windows@26.8.1) lucide-solid: specifier: ^1.16.0 - version: 1.16.0(solid-js@1.9.13) + version: 1.16.0(solid-js@1.9.14) rolldown: specifier: 1.0.3 version: 1.0.3 @@ -88,11 +88,11 @@ importers: specifier: ^3.5.0 version: 3.5.0 solid-js: - specifier: ^1.9.13 - version: 1.9.13 + specifier: ^1.9.14 + version: 1.9.14 solid-motionone: specifier: ^1.0.4 - version: 1.0.4(solid-js@1.9.13) + version: 1.0.4(solid-js@1.9.14) typescript: specifier: ^6.0.3 version: 6.0.3 @@ -370,6 +370,10 @@ packages: resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==} engines: {node: '>= 8.9.0'} + '@electron-internal/extract-zip@1.0.4': + resolution: {integrity: sha512-Zr1Vs7E9tpCNhZHDAbFVXc2gEVCG9RqPDjrno5+bdgB6LRAuvgyMHJut4NCVyYwtAieapMzc3fiQ3CSTi75ARg==} + engines: {node: '>=22.12.0'} + '@electron/asar@3.4.1': resolution: {integrity: sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==} engines: {node: '>=10.12.0'} @@ -975,9 +979,6 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@uwu/lune@1.6.2': resolution: {integrity: sha512-0/e1zFGm7pBISyuNvhKMqdg4gEQOpBtf8wdquJaUDqA0enrV55iwMWrrCdJHlfx77K1D8hjnAUGvCXxwFebg5Q==} hasBin: true @@ -1007,9 +1008,9 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - adm-zip@0.5.17: - resolution: {integrity: sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==} - engines: {node: '>=12.0'} + adm-zip@0.6.0: + resolution: {integrity: sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==} + engines: {node: '>=14.0'} agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} @@ -1073,7 +1074,7 @@ packages: engines: {node: '>=8'} arrpc@https://codeload.github.com/Legcord/arrpc/tar.gz/efe7589762470d32b9ba10d529be5acc23cd0e19: - resolution: {gitHosted: true, tarball: https://codeload.github.com/Legcord/arrpc/tar.gz/efe7589762470d32b9ba10d529be5acc23cd0e19} + resolution: {gitHosted: true, integrity: sha512-oxxPz88l9/VDvmgkepTGlhstBkt2kAO4SqxUjRu+pql4mQ2GSagDCjsYv4Lg3KnuazbuqRkl0K5KzOUzdBBJZQ==, tarball: https://codeload.github.com/Legcord/arrpc/tar.gz/efe7589762470d32b9ba10d529be5acc23cd0e19} version: 3.5.0 hasBin: true @@ -1158,9 +1159,6 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -1410,8 +1408,8 @@ packages: resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} engines: {node: '>=8.0.0'} - electron@42.3.3: - resolution: {integrity: sha512-0MwYp9wTb7TrtTalOYqeW+suqd9T/Znstr/nDLKqFGIjHdBZX339guo3mQqTPURRZ/UQmYM4uMpzKpI5wLptfQ==} + electron@43.1.1: + resolution: {integrity: sha512-I5c5vfuVvaXpWx3IZdwvXgxQW44+e7OP1wXGVQkogLeSFSkUZ6sLCcWV05AdEcs65AO5tAIJJwbp7ixw+LdarA==} engines: {node: '>= 22.12.0'} hasBin: true @@ -1494,11 +1492,6 @@ packages: resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==} engines: {node: '>=4'} - extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true - extsprintf@1.4.1: resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} engines: {'0': node >=0.6.0} @@ -1557,6 +1550,10 @@ packages: resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==} engines: {node: '>=14.14'} + fs-extra@11.3.6: + resolution: {integrity: sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==} + engines: {node: '>=14.14'} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -2150,9 +2147,6 @@ packages: resolution: {integrity: sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==} engines: {node: '>=12', npm: '>=6'} - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2342,14 +2336,14 @@ packages: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} - seroval-plugins@1.5.4: - resolution: {integrity: sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw==} + seroval-plugins@1.5.5: + resolution: {integrity: sha512-+BDhqYM6CEn3x09v44dpa9p6974FuUB2dxk+Ctn04k0cO1Zt6QODTXfmEZK0eBaTe/fJBvP4NMGuNJ+R8T+QMg==} engines: {node: '>=10'} peerDependencies: seroval: ^1.0 - seroval@1.5.4: - resolution: {integrity: sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==} + seroval@1.5.5: + resolution: {integrity: sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw==} engines: {node: '>=10'} set-blocking@2.0.0: @@ -2389,8 +2383,8 @@ packages: solid-js@1.6.16: resolution: {integrity: sha512-Ng4CahvLlpGA3BXIMiiMdwhI8WpObZ8gXqm97GCKR4+MpnODs6Pdpco+tmVCY/4ZDFaEKDxz7WRLAAdoXdlY7w==} - solid-js@1.9.13: - resolution: {integrity: sha512-6hJeJMOcEX8ktqjpDoJZEmld3ijvcvWBDtiXBm7f4332SiFN66QeAQI1REQshvyUoISsSeJ4PHDauKYbwao9JQ==} + solid-js@1.9.14: + resolution: {integrity: sha512-sAEXC0Kk0S1EDg+8ysEWJDbYhA3RRoEjwuySUGlKIemeo0I5YZfOyumNjNs9Sv3y2nmhD+0rW66ag2HsMuQiGQ==} solid-motionone@1.0.4: resolution: {integrity: sha512-aqEjgecoO9raDFznu/dEci7ORSmA26Kjj9J4Cn1Gyr0GZuOVdvsNxdxClTL9J40Aq/uYFx4GLwC8n70fMLHiuA==} @@ -2646,10 +2640,6 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yauzl@3.3.1: - resolution: {integrity: sha512-RNPCUkiE/ZgO4w8i9U5yDQVHaFDdnzaFANElRvpJteCspvmv2VqrRb9lvS6odVD+jqI/zDsxAHJVsafpcheVQQ==} - engines: {node: '>=12'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2986,6 +2976,8 @@ snapshots: ajv: 6.15.0 ajv-keywords: 3.5.2(ajv@6.15.0) + '@electron-internal/extract-zip@1.0.4': {} + '@electron/asar@3.4.1': dependencies: commander: 5.1.0 @@ -3071,7 +3063,7 @@ snapshots: dependencies: cross-dirname: 0.1.0 debug: 4.4.3 - fs-extra: 11.3.5 + fs-extra: 11.3.6 minimist: 1.2.8 postject: 1.0.0-alpha.6 transitivePeerDependencies: @@ -3320,7 +3312,7 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.6 optional: true - '@rolldown-plugin/solid@0.0.4(csstype@3.2.3)(rolldown@1.0.3)(seroval@1.5.4)(solid-js@1.9.13)': + '@rolldown-plugin/solid@0.0.4(csstype@3.2.3)(rolldown@1.0.3)(seroval@1.5.5)(solid-js@1.9.14)': dependencies: '@babel/core': 8.0.0-rc.6 '@babel/helper-module-imports': 8.0.0-rc.6 @@ -3333,8 +3325,8 @@ snapshots: html-entities: 2.6.0 parse5: 8.0.1 rolldown: 1.0.3 - seroval: 1.5.4 - solid-js: 1.9.13 + seroval: 1.5.5 + solid-js: 1.9.14 '@rolldown/binding-android-arm64@1.0.3': optional: true @@ -3432,23 +3424,23 @@ snapshots: '@sindresorhus/is@4.6.0': {} - '@solid-primitives/props@3.2.3(solid-js@1.9.13)': + '@solid-primitives/props@3.2.3(solid-js@1.9.14)': dependencies: - '@solid-primitives/utils': 6.4.0(solid-js@1.9.13) - solid-js: 1.9.13 + '@solid-primitives/utils': 6.4.0(solid-js@1.9.14) + solid-js: 1.9.14 - '@solid-primitives/refs@1.1.3(solid-js@1.9.13)': + '@solid-primitives/refs@1.1.3(solid-js@1.9.14)': dependencies: - '@solid-primitives/utils': 6.4.0(solid-js@1.9.13) - solid-js: 1.9.13 + '@solid-primitives/utils': 6.4.0(solid-js@1.9.14) + solid-js: 1.9.14 - '@solid-primitives/transition-group@1.1.2(solid-js@1.9.13)': + '@solid-primitives/transition-group@1.1.2(solid-js@1.9.14)': dependencies: - solid-js: 1.9.13 + solid-js: 1.9.14 - '@solid-primitives/utils@6.4.0(solid-js@1.9.13)': + '@solid-primitives/utils@6.4.0(solid-js@1.9.14)': dependencies: - solid-js: 1.9.13 + solid-js: 1.9.14 '@szmarczak/http-timer@4.0.6': dependencies: @@ -3528,11 +3520,6 @@ snapshots: dependencies: '@types/node': 25.9.1 - '@types/yauzl@2.10.3': - dependencies: - '@types/node': 25.9.1 - optional: true - '@uwu/lune@1.6.2': dependencies: '@babel/core': 7.29.7 @@ -3575,7 +3562,7 @@ snapshots: acorn@8.16.0: {} - adm-zip@0.5.17: {} + adm-zip@0.6.0: {} agent-base@6.0.2: dependencies: @@ -3712,12 +3699,12 @@ snapshots: html-entities: 2.3.3 parse5: 7.3.0 - babel-preset-solid@1.9.12(@babel/core@8.0.0-rc.6)(solid-js@1.9.13): + babel-preset-solid@1.9.12(@babel/core@8.0.0-rc.6)(solid-js@1.9.14): dependencies: '@babel/core': 8.0.0-rc.6 babel-plugin-jsx-dom-expressions: 0.40.7(@babel/core@8.0.0-rc.6) optionalDependencies: - solid-js: 1.9.13 + solid-js: 1.9.14 balanced-match@1.0.2: {} @@ -3757,8 +3744,6 @@ snapshots: node-releases: 2.0.46 update-browserslist-db: 1.2.3(browserslist@4.28.2) - buffer-crc32@0.2.13: {} - buffer-from@1.1.2: {} buffer@5.7.1: @@ -4103,11 +4088,11 @@ snapshots: transitivePeerDependencies: - supports-color - electron@42.3.3: + electron@43.1.1: dependencies: + '@electron-internal/extract-zip': 1.0.4 '@electron/get': 5.0.0 '@types/node': 24.12.4 - extract-zip: 2.0.1 transitivePeerDependencies: - supports-color @@ -4192,16 +4177,6 @@ snapshots: ext-list: 2.2.2 sort-keys-length: 1.0.1 - extract-zip@2.0.1: - dependencies: - debug: 4.4.3 - get-stream: 5.2.0 - yauzl: 3.3.1 - optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color - extsprintf@1.4.1: optional: true @@ -4260,6 +4235,13 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 + fs-extra@11.3.6: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + optional: true + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -4634,9 +4616,9 @@ snapshots: lru-cache@7.18.3: {} - lucide-solid@1.16.0(solid-js@1.9.13): + lucide-solid@1.16.0(solid-js@1.9.14): dependencies: - solid-js: 1.9.13 + solid-js: 1.9.14 magic-string@0.30.21: dependencies: @@ -4819,8 +4801,6 @@ snapshots: pe-library@0.4.1: {} - pend@1.2.0: {} - picocolors@1.1.1: {} picomatch@2.3.2: {} @@ -5037,11 +5017,11 @@ snapshots: type-fest: 0.13.1 optional: true - seroval-plugins@1.5.4(seroval@1.5.4): + seroval-plugins@1.5.5(seroval@1.5.5): dependencies: - seroval: 1.5.4 + seroval: 1.5.5 - seroval@1.5.4: {} + seroval@1.5.5: {} set-blocking@2.0.0: optional: true @@ -5079,21 +5059,21 @@ snapshots: dependencies: csstype: 3.2.3 - solid-js@1.9.13: + solid-js@1.9.14: dependencies: csstype: 3.2.3 - seroval: 1.5.4 - seroval-plugins: 1.5.4(seroval@1.5.4) + seroval: 1.5.5 + seroval-plugins: 1.5.5(seroval@1.5.5) - solid-motionone@1.0.4(solid-js@1.9.13): + solid-motionone@1.0.4(solid-js@1.9.14): dependencies: '@motionone/dom': 10.18.0 '@motionone/utils': 10.18.0 - '@solid-primitives/props': 3.2.3(solid-js@1.9.13) - '@solid-primitives/refs': 1.1.3(solid-js@1.9.13) - '@solid-primitives/transition-group': 1.1.2(solid-js@1.9.13) + '@solid-primitives/props': 3.2.3(solid-js@1.9.14) + '@solid-primitives/refs': 1.1.3(solid-js@1.9.14) + '@solid-primitives/transition-group': 1.1.2(solid-js@1.9.14) csstype: 3.2.3 - solid-js: 1.9.13 + solid-js: 1.9.14 sort-keys-length@1.0.1: dependencies: @@ -5326,11 +5306,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yauzl@3.3.1: - dependencies: - buffer-crc32: 0.2.13 - pend: 1.2.0 - yocto-queue@0.1.0: {} zod@3.25.76: {} diff --git a/src/@types/legcordWindow.d.ts b/src/@types/legcordWindow.d.ts index 424c9e4..9cb9ebd 100644 --- a/src/@types/legcordWindow.d.ts +++ b/src/@types/legcordWindow.d.ts @@ -78,6 +78,7 @@ export interface LegcordWindow { uninstall: (id: string) => void; set: (id: string, state: boolean) => void; getThemes: () => Readonly; + refresh: () => Readonly; openQuickCss: () => void; edit: (id: string) => void; folder: (id: string) => void; diff --git a/src/@types/settings.d.ts b/src/@types/settings.d.ts index bc09c56..46923ae 100644 --- a/src/@types/settings.d.ts +++ b/src/@types/settings.d.ts @@ -77,4 +77,6 @@ export interface Settings { extendedPluginAbilities: boolean; supportBannerDismissed: boolean; pluginStates?: Record; + // Remove below once the plugin system is fully implemented. + showExperimentalPluginMenu: boolean; } diff --git a/src/common/config.ts b/src/common/config.ts index f2791e3..06c5031 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -67,6 +67,7 @@ const defaults: Settings = { extendedPluginAbilities: false, quickCss: true, supportBannerDismissed: false, + showExperimentalPluginMenu: false, pluginStates: {}, }; @@ -80,6 +81,7 @@ const safeMode: Settings = { vaapi: false, additionalArguments: "", extendedPluginAbilities: false, + showExperimentalPluginMenu: false, quickCss: false, }; diff --git a/src/common/themes.ts b/src/common/themes.ts index 68be9db..8a79dce 100644 --- a/src/common/themes.ts +++ b/src/common/themes.ts @@ -112,6 +112,111 @@ function getThemeManifest(themeId: string): ThemeManifest | null { } } +// Performance optimization: Cached theme list with directory watcher +const THEME_LIST_CACHE_TTL = 2000; +let themeListCache: ThemeManifest[] | null = null; +let themeListCacheTime = 0; +let themeWatcher: fs.FSWatcher | null = null; +let themeListRefreshTimeout: NodeJS.Timeout | null = null; + +function importLooseThemeFiles(): void { + try { + if (!fs.existsSync(themesFolder)) return; + const entries = fs.readdirSync(themesFolder); + for (const entry of entries) { + const entryPath = path.join(themesFolder, entry); + const stat = fs.statSync(entryPath); + if (stat.isFile() && (entry.endsWith(".css") || entry.endsWith(".theme.css"))) { + const code = fs.readFileSync(entryPath, "utf8"); + installThemeFromCode(code); + try { + fs.unlinkSync(entryPath); + } catch {} + } + } + } catch (err) { + console.error("[Theme Manager] Failed to import loose theme files:", err); + } +} + +function refreshThemeListCache(): ThemeManifest[] { + try { + if (!fs.existsSync(themesFolder)) { + themeListCache = []; + themeListCacheTime = Date.now(); + return themeListCache; + } + const themes: ThemeManifest[] = []; + const entries = fs.readdirSync(themesFolder); + for (const entry of entries) { + const manifestPath = path.join(themesFolder, entry, "manifest.json"); + if (fs.existsSync(manifestPath)) { + const manifest = getThemeManifest(entry); + if (manifest) { + themes.push({ ...manifest, id: entry }); + } + } + } + themeListCache = themes; + themeListCacheTime = Date.now(); + return themes; + } catch (err) { + console.error("[Theme Manager] Failed to refresh theme list cache:", err); + return themeListCache ?? []; + } +} + +export function getCachedThemeList(): ThemeManifest[] { + const now = Date.now(); + if (themeListCache && now - themeListCacheTime < THEME_LIST_CACHE_TTL) { + return themeListCache; + } + importLooseThemeFiles(); + return refreshThemeListCache(); +} + +export function invalidateThemeListCache(): void { + themeListCache = null; +} + +function debouncedRefreshThemeList(): void { + if (themeListRefreshTimeout) { + clearTimeout(themeListRefreshTimeout); + } + themeListRefreshTimeout = setTimeout(() => { + refreshThemeListCache(); + themeListRefreshTimeout = null; + }, 500); +} + +export function startThemeWatcher(): void { + if (themeWatcher) return; + try { + if (!fs.existsSync(themesFolder)) { + fs.mkdirSync(themesFolder, { recursive: true }); + } + themeWatcher = fs.watch(themesFolder, { recursive: true }, (eventType, filename) => { + if (filename && (filename.endsWith("manifest.json") || eventType === "rename")) { + debouncedRefreshThemeList(); + } + }); + refreshThemeListCache(); + } catch (err) { + console.error("[Theme Manager] Failed to start theme watcher:", err); + } +} + +export function stopThemeWatcher(): void { + if (themeWatcher) { + themeWatcher.close(); + themeWatcher = null; + } + if (themeListRefreshTimeout) { + clearTimeout(themeListRefreshTimeout); + themeListRefreshTimeout = null; + } +} + export function injectThemesMain(browserWindow: BrowserWindow): void { if (process.argv.includes("--safe-mode")) return; if (!fs.existsSync(themesFolder)) { @@ -123,11 +228,13 @@ export function injectThemesMain(browserWindow: BrowserWindow): void { const files = fs.readdirSync(themesFolder); for (const file of files) { const themePath = path.join(themesFolder, file); - if (fs.statSync(themePath).isFile() && file.endsWith(".DS_Store")) { + if (fs.statSync(themePath).isFile() && (file.endsWith(".css") || file.endsWith(".theme.css"))) { console.log(`[Theme Manager] Local theme detected: ${themePath}`); - installTheme(themePath).then(() => { + const code = fs.readFileSync(themePath, "utf8"); + installThemeFromCode(code); + try { fs.unlinkSync(themePath); - }); + } catch {} } else { try { const themeFile = getThemeManifest(file); @@ -168,10 +275,11 @@ export function uninstallTheme(id: string) { fs.rmdirSync(path.join(themesFolder, `${id}-BD`), { recursive: true }); console.log(`Removed ${id} folder`); } + themeManifestCache.delete(id); + invalidateThemeListCache(); } export function setThemeEnabled(id: string, enabled: boolean) { - // Performance optimization: Use cached manifest if available let manifest = getThemeManifest(id); if (!manifest) { manifest = JSON.parse(fs.readFileSync(path.join(themesFolder, id, "/manifest.json"), "utf8")) as ThemeManifest; @@ -196,8 +304,25 @@ export function setThemeEnabled(id: string, enabled: boolean) { manifest.enabled = enabled; fs.writeFileSync(`${themesFolder}/${id}/manifest.json`, JSON.stringify(manifest)); - // Performance optimization: Invalidate cache themeManifestCache.delete(id); + invalidateThemeListCache(); +} + +function installThemeFromCode(code: string, linkOrPath?: string): void { + const manifest = parseBDManifest(code); + const themePath = path.join(themesFolder, `${manifest.name?.replace(" ", "-")}-BD`); + if (!fs.existsSync(themePath)) { + fs.mkdirSync(themePath); + console.log(`Created ${manifest.name} folder`); + } + if (linkOrPath && manifest.updateSrc === undefined) { + manifest.updateSrc = linkOrPath; + } + if (code.includes(".titlebar")) manifest.supportsLegcordTitlebar = true; + else manifest.supportsLegcordTitlebar = false; + fs.writeFileSync(path.join(themePath, "manifest.json"), JSON.stringify(manifest)); + fs.writeFileSync(path.join(themePath, "src.css"), code); + invalidateThemeListCache(); } export async function installTheme(linkOrPath: string) { @@ -209,19 +334,7 @@ export async function installTheme(linkOrPath: string) { } else { code = fs.readFileSync(linkOrPath, "utf8"); } - const manifest = parseBDManifest(code); - const themePath = path.join(themesFolder, `${manifest.name?.replace(" ", "-")}-BD`); - if (!fs.existsSync(themePath)) { - fs.mkdirSync(themePath); - console.log(`Created ${manifest.name} folder`); - } - if (isLinkImport && manifest.updateSrc === undefined) { - manifest.updateSrc = linkOrPath; - } - if (code.includes(".titlebar")) manifest.supportsLegcordTitlebar = true; - else manifest.supportsLegcordTitlebar = false; - fs.writeFileSync(path.join(themePath, "manifest.json"), JSON.stringify(manifest)); - fs.writeFileSync(path.join(themePath, "src.css"), code); + installThemeFromCode(code, isLinkImport ? linkOrPath : undefined); } export function initQuickCss(browserWindow: BrowserWindow) { diff --git a/src/discord/ipc.ts b/src/discord/ipc.ts index de5c1c9..127e36d 100644 --- a/src/discord/ipc.ts +++ b/src/discord/ipc.ts @@ -1,4 +1,4 @@ -import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import type { Game } from "arrpc"; @@ -21,7 +21,16 @@ import { import { getConfig, getConfigLocation, setConfig, setConfigBulk } from "../common/config.js"; import { addDetectable, getDetectables, removeDetectable } from "../common/detectables.js"; import { getLang, getLangName, getRawLang, setLang } from "../common/lang.js"; -import { disableQuickCss, initQuickCss, installTheme, setThemeEnabled, uninstallTheme } from "../common/themes.js"; +import { + disableQuickCss, + getCachedThemeList, + initQuickCss, + installTheme, + invalidateThemeListCache, + setThemeEnabled, + startThemeWatcher, + uninstallTheme, +} from "../common/themes.js"; import { getDisplayVersion, getVersion } from "../common/version.js"; import { openCssEditor } from "../cssEditor/main.js"; import { getAppliedFlags, handleRestart } from "../main.js"; @@ -65,6 +74,7 @@ let ipcRegistered = false; export function registerIpc(passedWindow: BrowserWindow): void { if (ipcRegistered) return; ipcRegistered = true; + startThemeWatcher(); ipcMain.handle("getShelterBundle", () => { return { js: ifExistsRead(path.join(app.getPath("userData"), "shelter.js")), @@ -169,17 +179,11 @@ export function registerIpc(passedWindow: BrowserWindow): void { }); ipcMain.on("getThemes", (event) => { - const themes = []; - const themeFolders = readdirSync(themesPath); - for (const folder of themeFolders) { - if (existsSync(`${themesPath}/${folder}/manifest.json`)) { - const manifest = JSON.parse( - readFileSync(`${themesPath}/${folder}/manifest.json`, "utf8"), - ) as ThemeManifest; - themes.push({ ...manifest, id: folder }); - } - } - event.returnValue = themes; + event.returnValue = getCachedThemeList(); + }); + ipcMain.on("refreshThemesCache", (event) => { + invalidateThemeListCache(); + event.returnValue = getCachedThemeList(); }); ipcMain.on("splashEnd", () => { diff --git a/src/discord/preload/bridge.ts b/src/discord/preload/bridge.ts index 81c492a..09bad96 100644 --- a/src/discord/preload/bridge.ts +++ b/src/discord/preload/bridge.ts @@ -102,6 +102,7 @@ contextBridge.exposeInMainWorld("legcord", { uninstall: (id: string) => ipcRenderer.send("uninstallTheme", id), edit: (id: string) => ipcRenderer.send("editTheme", id), getThemes: () => ipcRenderer.sendSync("getThemes") as ThemeManifest[], + refresh: () => ipcRenderer.sendSync("refreshThemesCache") as ThemeManifest[], openImportPicker: () => ipcRenderer.send("openImportPicker"), set: (id: string, state: boolean) => ipcRenderer.send("setThemeEnabled", id, state), folder: (id: string) => ipcRenderer.send("openThemeFolder", id), diff --git a/src/discord/window.ts b/src/discord/window.ts index e95a9b9..2d8b0d2 100644 --- a/src/discord/window.ts +++ b/src/discord/window.ts @@ -550,7 +550,12 @@ export function createWindow() { const mainWindow = new BrowserWindow(browserWindowOptions); // Restore by position + size directly to match saveWindowState roundtrip. - if (storedBounds.x !== undefined && storedBounds.y !== undefined) { + if ( + storedBounds.x !== undefined && + storedBounds.y !== undefined && + storedBounds.width !== undefined && + storedBounds.height !== undefined + ) { mainWindow.setPosition(storedBounds.x, storedBounds.y); mainWindow.setSize(storedBounds.width, storedBounds.height); } diff --git a/src/shelter/settings/components/ThemesCard.module.css b/src/shelter/settings/components/ThemesCard.module.css index e66ac8d..ba3f19e 100644 --- a/src/shelter/settings/components/ThemesCard.module.css +++ b/src/shelter/settings/components/ThemesCard.module.css @@ -1,45 +1,95 @@ .card { - border: 1px solid var(--background-accent); - border-radius: 4px; - padding: 0.5rem; - margin-top: 0.5rem; - flex-wrap: wrap; + border: 1px solid var(--background-modifier-accent); + border-radius: 8px; + padding: 1rem; + margin-top: 0.75rem; + background: var(--background-secondary); + transition: + box-shadow 0.2s ease, + border-color 0.2s ease; } -.switch { - margin-left: auto; +.card:hover { + border-color: var(--background-modifier-selected); + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); +} + +.topRow { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 1rem; } .info { - align-self: flex-start; + flex: 1; + min-width: 0; } -.eyebrow { - margin-bottom: 0px; - color: var(--text-strong); +.title { + margin: 0; + word-break: break-word; } + +.author { + font-size: 0.85rem; + color: var(--text-muted); + margin-top: 0.15rem; +} + +.description { + margin-top: 0.5rem; + font-size: 0.9rem; + color: var(--text-normal); + line-height: 1.4; +} + +.actions { + display: flex; + gap: 0.25rem; + margin-top: 0.75rem; + padding-top: 0.75rem; + border-top: 1px solid var(--background-modifier-accent); +} + .btn { + display: inline-flex; + align-items: center; + justify-content: center; border: none; - background: none; - color: var(--interactive-text-active); + background: transparent; + width: 32px; + height: 32px; + border-radius: 6px; + cursor: pointer; + color: var(--interactive-normal); + transition: + background 0.15s ease, + color 0.15s ease; +} - transition: opacity 250ms; +.btn:hover { + background: var(--background-modifier-hover); + color: var(--interactive-hover); +} - &:hover { - color: var(--interactive-text-hover); - } +.btnDanger:hover { + background: rgba(218, 55, 60, 0.15); + color: #da373c; } .icon { - height: 20px; - margin-top: 2px; + height: 18px; + width: 18px; + filter: brightness(0) invert(0.7); + pointer-events: none; +} + +.btn:hover .icon { filter: brightness(0) invert(1); } -.mainInfo { - flex-wrap: wrap; - align-items: baseline; - justify-content: center; - gap: 5px; - margin-bottom: -0.5em; - display: flex; + +.btnDanger:hover .icon { + filter: brightness(0) saturate(1) invert(27%) sepia(82%) saturate(3000%) hue-rotate(346deg) brightness(94%) + contrast(90%); } diff --git a/src/shelter/settings/components/ThemesCard.tsx b/src/shelter/settings/components/ThemesCard.tsx index 803bf69..862062b 100644 --- a/src/shelter/settings/components/ThemesCard.tsx +++ b/src/shelter/settings/components/ThemesCard.tsx @@ -1,4 +1,4 @@ -import { createSignal } from "solid-js"; +import { createSignal, Show } from "solid-js"; import type { ThemeManifest } from "../../../@types/themeManifest.js"; import { refreshThemes } from "../settings.js"; import classes from "./ThemesCard.module.css"; @@ -14,7 +14,7 @@ export const ThemesCard = (props: { theme: ThemeManifest }) => { function toggleTheme(state: boolean) { setSwitchState(state); if (props.theme.id) { - window.legcord.themes.set(props.theme.id, switchState()); + window.legcord.themes.set(props.theme.id, state); } refreshThemes(); } @@ -47,35 +47,48 @@ export const ThemesCard = (props: { theme: ThemeManifest }) => { window.legcord.themes.folder(props.theme.id); } } + const showUpdate = !!props.theme.updateSrc; return (
-
-
+
+
{props.theme.name}
-
{store.i18n["themes-by"]}
-
- {props.theme.author} -
-
- +
+ {store.i18n["themes-by"]} {props.theme.author}
-
{props.theme.description}
+ +
+ +
{props.theme.description}
+
+
+ + + + + +
- - - -
); }; diff --git a/src/shelter/settings/index.ts b/src/shelter/settings/index.ts index 90e89fe..9d605ea 100644 --- a/src/shelter/settings/index.ts +++ b/src/shelter/settings/index.ts @@ -25,15 +25,18 @@ const { let settingsCleanups: (() => void)[] = []; function registerSections(): (() => void)[] { - return [ + const array = [ registerSection("divider"), registerSection("header", "Legcord"), registerSection("section", "legcord-settings", "Settings", SettingsPage, { icon: SettingsSidebarIcon }), registerSection("section", "legcord-themes", "Themes", ThemesPage, { icon: ThemesSidebarIcon }), - registerSection("section", "legcord-plugins", "Plugins", PluginsPage, { icon: PluginsSidebarIcon }), registerSection("section", "legcord-keybinds", "Keybinds", KeybindsPage, { icon: KeybindsSidebarIcon }), registerSection("section", "legcord-games", "Games", RegisteredGamesPage, { icon: GamesSidebarIcon }), ]; + if (window.legcord.settings.getConfig().showExperimentalPluginMenu) { + array.push(registerSection("section", "legcord-plugins", "Plugins", PluginsPage, { icon: PluginsSidebarIcon })); + } + return array; } function restartRequired(payload: { event: string; properties: { origin_pane: string } }) { diff --git a/src/shelter/settings/pages/SettingsPage.tsx b/src/shelter/settings/pages/SettingsPage.tsx index 92285ad..9386d75 100644 --- a/src/shelter/settings/pages/SettingsPage.tsx +++ b/src/shelter/settings/pages/SettingsPage.tsx @@ -383,6 +383,13 @@ export function SettingsPage() {
{store.i18n["settings-category-debug"]}
+ setConfig("showExperimentalPluginMenu", e, true)} + > + {store.i18n["settings-showExperimentalPluginMenu"]} + {store.i18n["settings-quickCss"]} -
+
+
+
+
{t["themes-installed"]}
{(theme: ThemeManifest) => } ); diff --git a/src/shelter/settings/pages/ThemesPages.module.css b/src/shelter/settings/pages/ThemesPages.module.css index 01d2c98..e10bc40 100644 --- a/src/shelter/settings/pages/ThemesPages.module.css +++ b/src/shelter/settings/pages/ThemesPages.module.css @@ -1,13 +1,23 @@ -.buttonBox { +.toolbar { display: flex; - justify-content: space-between; - border: 1px solid var(--background-modifier-accent); - padding: 3px; - border-radius: 6px; + gap: 0.5rem; + flex-wrap: wrap; + margin-top: 0.75rem; } + .addBox { display: flex; - justify-content: space-between; - padding: 3px; - margin-top: 1rem; + gap: 0.5rem; + margin-top: 0.5rem; + align-items: center; +} + +.addBox > :first-child { + flex: 1; +} + +.divider { + margin: 1.5rem 0 0.5rem; + border: none; + border-top: 1px solid var(--background-modifier-accent); }