├── .github └── workflows │ └── build.yml ├── .gitignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── assets ├── banner.svg └── neptune-screenshot.png ├── flake.lock ├── flake.nix ├── injector ├── index.js ├── package.json ├── preload.js └── redux-devtools │ ├── 079db4a1c8da8ec06700.woff2 │ ├── 56f3f8ac2e0a51c02e1c.woff2 │ ├── background.bundle.js │ ├── background.bundle.js.LICENSE.txt │ ├── c60b44947671d757833d.woff2 │ ├── content.bundle.js │ ├── devpanel.bundle.js │ ├── devpanel.bundle.js.LICENSE.txt │ ├── devpanel.html │ ├── devtools.bundle.js │ ├── devtools.html │ ├── e46177b21b27cd6643c5.woff2 │ ├── ef865b56e54f6a46f73f.woff2 │ ├── img │ ├── loading.svg │ └── logo │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 38x38.png │ │ ├── 48x48.png │ │ ├── error.png │ │ ├── gray.png │ │ └── scalable.png │ ├── manifest.json │ ├── options.bundle.js │ ├── options.bundle.js.LICENSE.txt │ ├── options.html │ ├── page.bundle.js │ ├── pagewrap.bundle.js │ ├── remote.bundle.js │ ├── remote.bundle.js.LICENSE.txt │ ├── remote.html │ ├── window.bundle.js │ ├── window.bundle.js.LICENSE.txt │ └── window.html ├── package.json ├── pnpm-lock.yaml ├── rollup.config.js ├── src ├── api │ ├── hookContextMenu.js │ ├── intercept.js │ ├── observe.js │ ├── plugins.js │ ├── registerRoute.js │ ├── registerTab.js │ ├── showModal.js │ ├── themes.js │ └── utils.js ├── handleExfiltrations.js ├── index.js ├── styles.js ├── ui │ ├── components.js │ ├── pluginsTab.js │ ├── settings.js │ └── themesTab.js └── windowObject.js └── types ├── LICENSE ├── api ├── hookContextMenu.d.ts ├── intercept.d.ts ├── observe.d.ts ├── plugins.d.ts ├── registerRoute.d.ts ├── registerTab.d.ts ├── showModal.d.ts └── utils.d.ts ├── index.d.ts ├── package.json ├── pnpm-lock.yaml ├── tidal └── index.d.ts ├── tsconfig.base.json └── ui └── components.d.ts /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and push 2 | on: 3 | push: 4 | branches: [master] 5 | paths: 6 | - "src/**" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/checkout@v3 16 | with: 17 | repository: "uwu/neptune-builds" 18 | path: "builds" 19 | token: ${{ secrets.LINK_TOKEN }} 20 | - uses: actions/setup-node@v3 21 | with: 22 | node-version: 20 23 | 24 | - name: Install dependencies 25 | run: | 26 | npm i -g pnpm 27 | pnpm i 28 | 29 | - name: Build 30 | run: npm run build 31 | 32 | - name: Push builds 33 | run: | 34 | rm $GITHUB_WORKSPACE/builds/* || true 35 | cp -r dist/* $GITHUB_WORKSPACE/builds || true 36 | cd $GITHUB_WORKSPACE/builds 37 | git config --local user.email "actions@github.com" 38 | git config --local user.name "GitHub Actions" 39 | git add . 40 | git commit -m "Build $GITHUB_SHA" || exit 0 41 | git push 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/macos,node 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,node 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | ### macOS Patch ### 34 | # iCloud generated files 35 | *.icloud 36 | 37 | ### Node ### 38 | # Logs 39 | logs 40 | *.log 41 | npm-debug.log* 42 | yarn-debug.log* 43 | yarn-error.log* 44 | lerna-debug.log* 45 | .pnpm-debug.log* 46 | 47 | # Diagnostic reports (https://nodejs.org/api/report.html) 48 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 49 | 50 | # Runtime data 51 | pids 52 | *.pid 53 | *.seed 54 | *.pid.lock 55 | 56 | # Directory for instrumented libs generated by jscoverage/JSCover 57 | lib-cov 58 | 59 | # Coverage directory used by tools like istanbul 60 | coverage 61 | *.lcov 62 | 63 | # nyc test coverage 64 | .nyc_output 65 | 66 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 67 | .grunt 68 | 69 | # Bower dependency directory (https://bower.io/) 70 | bower_components 71 | 72 | # node-waf configuration 73 | .lock-wscript 74 | 75 | # Compiled binary addons (https://nodejs.org/api/addons.html) 76 | build/Release 77 | 78 | # Dependency directories 79 | node_modules/ 80 | jspm_packages/ 81 | 82 | # Snowpack dependency directory (https://snowpack.dev/) 83 | web_modules/ 84 | 85 | # TypeScript cache 86 | *.tsbuildinfo 87 | 88 | # Optional npm cache directory 89 | .npm 90 | 91 | # Optional eslint cache 92 | .eslintcache 93 | 94 | # Optional stylelint cache 95 | .stylelintcache 96 | 97 | # Microbundle cache 98 | .rpt2_cache/ 99 | .rts2_cache_cjs/ 100 | .rts2_cache_es/ 101 | .rts2_cache_umd/ 102 | 103 | # Optional REPL history 104 | .node_repl_history 105 | 106 | # Output of 'npm pack' 107 | *.tgz 108 | 109 | # Yarn Integrity file 110 | .yarn-integrity 111 | 112 | # dotenv environment variable files 113 | .env 114 | .env.development.local 115 | .env.test.local 116 | .env.production.local 117 | .env.local 118 | 119 | # parcel-bundler cache (https://parceljs.org/) 120 | .cache 121 | .parcel-cache 122 | 123 | # Next.js build output 124 | .next 125 | out 126 | 127 | # Nuxt.js build / generate output 128 | .nuxt 129 | dist 130 | 131 | # Gatsby files 132 | .cache/ 133 | # Comment in the public line in if your project uses Gatsby and not Next.js 134 | # https://nextjs.org/blog/next-9-1#public-directory-support 135 | # public 136 | 137 | # vuepress build output 138 | .vuepress/dist 139 | 140 | # vuepress v2.x temp and cache directory 141 | .temp 142 | 143 | # Docusaurus cache and generated files 144 | .docusaurus 145 | 146 | # Serverless directories 147 | .serverless/ 148 | 149 | # FuseBox cache 150 | .fusebox/ 151 | 152 | # DynamoDB Local files 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | .tern-port 157 | 158 | # Stores VSCode versions used for testing VSCode extensions 159 | .vscode-test 160 | 161 | # yarn v2 162 | .yarn/cache 163 | .yarn/unplugged 164 | .yarn/build-state.yml 165 | .yarn/install-state.gz 166 | .pnp.* 167 | 168 | ### Node Patch ### 169 | # Serverless Webpack directories 170 | .webpack/ 171 | 172 | # Optional stylelint cache 173 | 174 | # SvelteKit build / generate output 175 | .svelte-kit 176 | unreleased_code.js -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": false, 7 | "quoteProps": "as-needed", 8 | "jsxSingleQuote": false, 9 | "trailingComma": "all", 10 | "bracketSpacing": true, 11 | "bracketSameLine": true, 12 | "arrowParens": "always", 13 | "proseWrap": "always" 14 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Microsoft Public License (Ms-PL) 2 | 3 | This license governs use of the accompanying software. If you use the 4 | software, you accept this license. If you do not accept the license, 5 | do not use the software. 6 | 7 | 1. Definitions 8 | 9 | The terms "reproduce," "reproduction," "derivative works," and 10 | "distribution" have the same meaning here as under U.S. copyright 11 | law. 12 | 13 | A "contribution" is the original software, or any additions or 14 | changes to the software. 15 | 16 | A "contributor" is any person that distributes its contribution 17 | under this license. 18 | 19 | "Licensed patents" are a contributor's patent claims that read 20 | directly on its contribution. 21 | 22 | 2. Grant of Rights 23 | 24 | (A) Copyright Grant- Subject to the terms of this license, 25 | including the license conditions and limitations in section 3, 26 | each contributor grants you a non-exclusive, worldwide, 27 | royalty-free copyright license to reproduce its contribution, 28 | prepare derivative works of its contribution, and distribute its 29 | contribution or any derivative works that you create. 30 | 31 | (B) Patent Grant- Subject to the terms of this license, including 32 | the license conditions and limitations in section 3, each 33 | contributor grants you a non-exclusive, worldwide, royalty-free 34 | license under its licensed patents to make, have made, use, sell, 35 | offer for sale, import, and/or otherwise dispose of its 36 | contribution in the software or derivative works of the 37 | contribution in the software. 38 | 39 | 3. Conditions and Limitations 40 | 41 | (A) No Trademark License- This license does not grant you rights 42 | to use any contributors' name, logo, or trademarks. 43 | 44 | (B) If you bring a patent claim against any contributor over 45 | patents that you claim are infringed by the software, your patent 46 | license from such contributor to the software ends automatically. 47 | 48 | (C) If you distribute any portion of the software, you must retain 49 | all copyright, patent, trademark, and attribution notices that are 50 | present in the software. 51 | 52 | (D) If you distribute any portion of the software in source code 53 | form, you may do so only under this license by including a 54 | complete copy of this license with your distribution. If you 55 | distribute any portion of the software in compiled or object code 56 | form, you may only do so under a license that complies with this 57 | license. 58 | 59 | (E) The software is licensed "as-is." You bear the risk of using 60 | it. The contributors give no express warranties, guarantees, or 61 | conditions. You may have additional consumer rights under your 62 | local laws which this license cannot change. To the extent 63 | permitted under your local laws, the contributors exclude the 64 | implied warranties of merchantability, fitness for a particular 65 | purpose and non-infringement. 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![neptune](https://github.com/uwu/neptune/raw/master/assets/banner.svg) 2 | 3 | ## what is neptune? 4 | neptune was an experimental client mod for TIDAL that provides a simple plugin and theme system. 5 | 6 | i have no interest in maintaining software that nobody wants to contribute to. if you want to make your own thing, i highly prefer that you commit to it. 7 | 8 | ## screenshot(s) 9 | ![a screenshot of the neptune settings tab](https://github.com/uwu/neptune/raw/master/assets/neptune-screenshot.png) 10 | 11 | ## how can i install neptune? 12 | you can download the neptune installer [here](https://github.com/uwu/neptune-installer/releases). 13 | 14 | ### NixOS 15 | 16 | > [!WARNING] 17 | > TIDAL-HIFI right now is colliding with neptune when trying to login 18 | > 19 | > create a nix-shell with tidal-hifi and login once, after that you can use the neptune package 20 | 21 | you install this package as an overlay 22 | 23 | add as an input in your flakes: 24 | ```nix 25 | inputs = { 26 | neptune = { 27 | url = "github.com:uwu/neptune"; 28 | inputs.nixpkgs.follows = "nixpkgs"; 29 | } 30 | }; 31 | ``` 32 | 33 | configure your package system to use this overlay: 34 | ```nix 35 | nixpkgs.overlays = [ inputs.neptune.overlays.default ]; 36 | ``` 37 | 38 | and then just add neptune as a package: 39 | ```nix 40 | enivronment.systemPackages = [ pkgs.neptune ]; 41 | ``` 42 | 43 | After that you can find TIDAL-HIFI as program in your system 44 | 45 | ## developing plugins for neptune 46 | neptune exfiltrates every single action one can do in TIDAL into an easily accessible API found on `window.neptune.actions`. 47 | 48 | TIDAL is built on [Redux](https://redux.js.org) and neptune's actions are simply exfiltrated Redux actions, which are explained in [this document](https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow#actions) on Redux's website. 49 | 50 | neptune includes full type definitions for all of TIDAL's actions. 51 | 52 | To get the global Redux store's state, you can use `window.neptune.store.getState()`. The return value of `getState()` will change as a direct result of actions. 53 | 54 | To intercept and subscribe to actions, you can use `window.neptune.intercept("category/ACTION_NAME", ([payload]) => {})`, with the first argument being the name (or an array of names) of the action(s) to subscribe to, and the second argument being a function that gets called upon that action being ran. If you return `true` the action will automatically be cancelled. 55 | 56 | A template for making neptune plugins is available [here](https://github.com/uwu/neptune-template). 57 | -------------------------------------------------------------------------------- /assets/neptune-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/assets/neptune-screenshot.png -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1733376361, 6 | "narHash": "sha256-aLJxoTDDSqB+/3orsulE6/qdlX6MzDLIITLZqdgMpqo=", 7 | "owner": "nixos", 8 | "repo": "nixpkgs", 9 | "rev": "929116e316068c7318c54eb4d827f7d9756d5e9c", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "nixos", 14 | "ref": "nixpkgs-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 4 | }; 5 | 6 | outputs = inputs: 7 | let 8 | neptuneOverlay = final: prev: 9 | let 10 | neptune-src = prev.fetchzip { 11 | url = "https://github.com/uwu/neptune/archive/548f93b.zip"; 12 | sha256 = "sha256-oI/bRjL6zjsaA8p8QTeJEB5k+SXkJqSJ/hEAltDenok="; 13 | }; 14 | in 15 | { 16 | # Use the already existing package tidal-hifi and inject neptune in it 17 | tidal-hifi = prev.tidal-hifi.overrideAttrs (old: { 18 | 19 | # Patch neptune into tidal-hifi 20 | # Needing to override the full thing to get everything from the install phase 21 | installPhase = '' 22 | runHook preInstall 23 | 24 | mkdir -p "$out/bin" 25 | cp -R "opt" "$out" 26 | cp -R "usr/share" "$out/share" 27 | chmod -R g-w "$out" 28 | 29 | cp -r ${neptune-src}/injector/ $out/opt/tidal-hifi/resources/app/ 30 | mv $out/opt/tidal-hifi/resources/app.asar $out/opt/tidal-hifi/resources/original.asar 31 | 32 | runHook postInstall 33 | 34 | ''; 35 | }); 36 | 37 | # declare a new package named neptune that uses the new tidal-hifi 38 | neptune = final.tidal-hifi; 39 | }; 40 | 41 | system = "x86_64-linux"; # This setting is based on my system, change if needed 42 | 43 | # The Module Configuration 44 | # Disclaimer: There is no module configuration yet, because I dont have a solution myself, to get access to the InnoDB 45 | 46 | in { 47 | # Overlay used by other flakes 48 | overlays.default = neptuneOverlay; 49 | 50 | # Testing the overlay/package 51 | devShells."${system}".default = let 52 | pkgs = import inputs.nixpkgs { 53 | inherit system; 54 | overlays = [ neptuneOverlay ]; 55 | }; 56 | in pkgs.mkShell { 57 | packages = [ pkgs.neptune ]; 58 | shellHook = '' ${pkgs.neptune}/bin/tidal-hifi ''; # Activating the package/overlay 59 | }; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /injector/index.js: -------------------------------------------------------------------------------- 1 | const electron = require("electron"); 2 | const path = require("path"); 3 | const Module = require("module"); 4 | const fs = require("fs"); 5 | const https = require("https"); 6 | 7 | const logger = new Proxy(console, { 8 | get: (target, key) => 9 | function (...args) { 10 | return target[key].apply(console, ["[neptune]", ...args]); 11 | }, 12 | }); 13 | 14 | logger.log("Loading..."); 15 | 16 | // #region Bundle 17 | const remoteUrl = 18 | process.env.NEPTUNE_BUNDLE_URL || 19 | "https://raw.githubusercontent.com/uwu/neptune-builds/master/neptune.js"; 20 | const localBundle = process.env.NEPTUNE_DIST_PATH; 21 | 22 | let fetchPromise; // only fetch once 23 | 24 | if (!localBundle) 25 | fetchPromise = new Promise((resolve, reject) => { 26 | const req = https.get(remoteUrl); 27 | 28 | req.on("response", (res) => { 29 | const chunks = []; 30 | 31 | res.on("data", (chunk) => chunks.push(chunk)); 32 | res.on("end", () => { 33 | let data = Buffer.concat(chunks).toString("utf-8"); 34 | 35 | if (!data.includes("//# sourceMappingURL=")) 36 | data += `\n//# sourceMappingURL=${remoteUrl + ".map"}`; 37 | 38 | resolve(data); 39 | }); 40 | }); 41 | 42 | req.on("error", reject); 43 | 44 | req.end(); 45 | }); 46 | 47 | const getNeptuneBundle = () => 48 | !localBundle 49 | ? fetchPromise 50 | : Promise.resolve( 51 | fs.readFileSync(path.join(localBundle, "neptune.js"), "utf8") + 52 | `\n//# sourceMappingURL=file:////${path.join( 53 | localBundle, 54 | "neptune.js.map" 55 | )}` 56 | ); 57 | // #endregion 58 | 59 | // #region IPC 60 | electron.ipcMain.on("NEPTUNE_ORIGINAL_PRELOAD", (event) => { 61 | event.returnValue = event.sender.originalPreload; 62 | }); 63 | 64 | electron.ipcMain.handle("NEPTUNE_BUNDLE_FETCH", getNeptuneBundle); 65 | // #endregion 66 | 67 | // #region Redux Devtools 68 | electron.app.whenReady().then(() => { 69 | electron.session.defaultSession.loadExtension( 70 | path.join(process.resourcesPath, "app", "redux-devtools") 71 | ); 72 | }); 73 | // #endregion 74 | 75 | // #region CSP bypass 76 | electron.app.whenReady().then(() => { 77 | electron.protocol.handle("https", async (req) => { 78 | const url = new URL(req.url); 79 | if (url.pathname === "/" || url.pathname == "/index.html") { 80 | console.log(req.url); 81 | const res = await electron.net.fetch(req, { bypassCustomProtocolHandlers: true }) 82 | let body = await res.text(); 83 | body = body.replace( 84 | //g, ` -------------------------------------------------------------------------------- /injector/redux-devtools/devtools.bundle.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";var e={};function t(e){chrome.devtools.panels.create("Redux","img/logo/scalable.png",e,(function(){}))}e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),(()=>{var t;e.g.importScripts&&(t=e.g.location+"");var r=e.g.document;if(!t&&r&&(r.currentScript&&(t=r.currentScript.src),!t)){var n=r.getElementsByTagName("script");n.length&&(t=n[n.length-1].src)}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),e.p=t})(),e.p,chrome.runtime.getBackgroundPage?chrome.runtime.getBackgroundPage((e=>{t(e?"window.html":"devpanel.html")})):t("devpanel.html")})(); -------------------------------------------------------------------------------- /injector/redux-devtools/devtools.html: -------------------------------------------------------------------------------- 1 | Redux DevTools
-------------------------------------------------------------------------------- /injector/redux-devtools/e46177b21b27cd6643c5.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/injector/redux-devtools/e46177b21b27cd6643c5.woff2 -------------------------------------------------------------------------------- /injector/redux-devtools/ef865b56e54f6a46f73f.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/injector/redux-devtools/ef865b56e54f6a46f73f.woff2 -------------------------------------------------------------------------------- /injector/redux-devtools/img/loading.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /injector/redux-devtools/img/logo/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/injector/redux-devtools/img/logo/128x128.png -------------------------------------------------------------------------------- /injector/redux-devtools/img/logo/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/injector/redux-devtools/img/logo/16x16.png -------------------------------------------------------------------------------- /injector/redux-devtools/img/logo/38x38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/injector/redux-devtools/img/logo/38x38.png -------------------------------------------------------------------------------- /injector/redux-devtools/img/logo/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/injector/redux-devtools/img/logo/48x48.png -------------------------------------------------------------------------------- /injector/redux-devtools/img/logo/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/injector/redux-devtools/img/logo/error.png -------------------------------------------------------------------------------- /injector/redux-devtools/img/logo/gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/injector/redux-devtools/img/logo/gray.png -------------------------------------------------------------------------------- /injector/redux-devtools/img/logo/scalable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwu/neptune/724cd20dcf734d34661e9453f696be128508e7e1/injector/redux-devtools/img/logo/scalable.png -------------------------------------------------------------------------------- /injector/redux-devtools/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3.0.19", 3 | "name": "Redux DevTools", 4 | "description": "Redux DevTools for debugging application's state changes.", 5 | "homepage_url": "https://github.com/reduxjs/redux-devtools", 6 | "manifest_version": 2, 7 | "page_action": { 8 | "default_icon": "img/logo/gray.png", 9 | "default_title": "Redux DevTools", 10 | "default_popup": "window.html#popup" 11 | }, 12 | "commands": { 13 | "devtools-left": { 14 | "description": "DevTools window to left" 15 | }, 16 | "devtools-right": { 17 | "description": "DevTools window to right" 18 | }, 19 | "devtools-bottom": { 20 | "description": "DevTools window to bottom" 21 | }, 22 | "devtools-remote": { 23 | "description": "Remote DevTools" 24 | }, 25 | "_execute_page_action": { 26 | "suggested_key": { 27 | "default": "Ctrl+Shift+E" 28 | } 29 | } 30 | }, 31 | "icons": { 32 | "16": "img/logo/16x16.png", 33 | "48": "img/logo/48x48.png", 34 | "128": "img/logo/128x128.png" 35 | }, 36 | "options_ui": { 37 | "page": "options.html", 38 | "chrome_style": true 39 | }, 40 | "background": { 41 | "scripts": ["background.bundle.js"], 42 | "persistent": false 43 | }, 44 | "content_scripts": [ 45 | { 46 | "matches": [""], 47 | "exclude_globs": ["https://www.google*"], 48 | "js": ["content.bundle.js", "pagewrap.bundle.js"], 49 | "run_at": "document_start", 50 | "all_frames": true 51 | } 52 | ], 53 | "devtools_page": "devtools.html", 54 | "web_accessible_resources": ["page.bundle.js"], 55 | "externally_connectable": { 56 | "ids": ["*"] 57 | }, 58 | "permissions": [ 59 | "notifications", 60 | "contextMenus", 61 | "storage", 62 | "file:///*", 63 | "http://*/*", 64 | "https://*/*" 65 | ], 66 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;", 67 | "update_url": "https://clients2.google.com/service/update2/crx", 68 | "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB" 69 | } 70 | -------------------------------------------------------------------------------- /injector/redux-devtools/options.bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * @license React 3 | * react-dom.production.min.js 4 | * 5 | * Copyright (c) Facebook, Inc. and its affiliates. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE file in the root directory of this source tree. 9 | */ 10 | 11 | /** 12 | * @license React 13 | * react.production.min.js 14 | * 15 | * Copyright (c) Facebook, Inc. and its affiliates. 16 | * 17 | * This source code is licensed under the MIT license found in the 18 | * LICENSE file in the root directory of this source tree. 19 | */ 20 | 21 | /** 22 | * @license React 23 | * scheduler.production.min.js 24 | * 25 | * Copyright (c) Facebook, Inc. and its affiliates. 26 | * 27 | * This source code is licensed under the MIT license found in the 28 | * LICENSE file in the root directory of this source tree. 29 | */ 30 | -------------------------------------------------------------------------------- /injector/redux-devtools/options.html: -------------------------------------------------------------------------------- 1 | Redux DevTools Options
-------------------------------------------------------------------------------- /injector/redux-devtools/page.bundle.js: -------------------------------------------------------------------------------- 1 | (()=>{var t={40472:t=>{var e=function(t){"use strict";if("function"!=typeof t)return[];var e=t.toString().replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,""),r=e.slice(e.indexOf("(")+1,e.indexOf(")")).match(/([^\s,]+)/g);return null===r?[]:r};void 0!==t.exports&&(t.exports=e),"undefined"!=typeof window&&(window.GetParams=e)},63822:(t,e,r)=>{t.exports=r(14199)},83617:(t,e,r)=>{r(39327);var n=r(90229),o="undefined"!=typeof WeakMap?WeakMap:function(){var t=[],e=[];return{set:function(r,n){t.push(r),e.push(n)},get:function(r){for(var n=0;n{var n=r(83617);e.stringify=function(t,e,r,o){if(arguments.length<4)try{return 1===arguments.length?JSON.stringify(t):JSON.stringify.apply(JSON,arguments)}catch(t){}var i=o||!1;"boolean"==typeof i&&(i={date:i,function:i,regex:i,undefined:i,error:i,symbol:i,map:i,set:i,nan:i,infinity:i});var a=n.decycle(t,i,e);return 1===arguments.length?JSON.stringify(a):JSON.stringify(a,Array.isArray(e)?e:null,r)},e.parse=function(t,e){var r,o=/"\$jsan"/.test(t);return r=1===arguments.length?JSON.parse(t):JSON.parse(t,e),o&&(r=n.retrocycle(r)),r}},39327:t=>{t.exports=function(t,e){if("$"!==e)for(var r=function(t){for(var e,r=/(?:\.(\w+))|(?:\[(\d+)\])|(?:\["((?:[^\\"]|\\.)*)"\])/g,n=[];e=r.exec(t);)n.push(e[1]||e[2]||e[3]);return n}(e),n=0;n{var n=r(39327),o=r(14199);e.getRegexFlags=function(t){var e="";return t.ignoreCase&&(e+="i"),t.global&&(e+="g"),t.multiline&&(e+="m"),e},e.stringifyFunction=function(t,e){if("function"==typeof e)return e(t);var r=t.toString(),n=r.match(/^[^{]*{|^[^=]*=>/),o=n?n[0]:" ",i="}"===r[r.length-1]?"}":"";return o.replace(/\r\n|\n/g," ").replace(/\s+/g," ")+" /* ... */ "+i},e.restore=function(t,e){var r=t[0],i=t.slice(1);switch(r){case"$":return n(e,t);case"r":var a=i.indexOf(","),s=i.slice(0,a),c=i.slice(a+1);return RegExp(c,s);case"d":return new Date(+i);case"f":var u=function(){throw new Error("can't run jsan parsed function")};return u.toString=function(){return i},u;case"u":return;case"e":var f=new Error(i);return f.stack="Stack is unavailable for jsan parsed errors",f;case"s":return Symbol(i);case"g":return Symbol.for(i);case"m":return new Map(o.parse(i));case"l":return new Set(o.parse(i));case"n":return NaN;case"i":return 1/0;case"y":return-1/0;default:return console.warn("unknown type",t),t}}},35839:(t,e,r)=>{var n=r(80751)(r(73401),"DataView");t.exports=n},61538:(t,e,r)=>{var n=r(59219),o=r(95937),i=r(44054),a=r(99991),s=r(62753);function c(t){var e=-1,r=null==t?0:t.length;for(this.clear();++e{var n=r(53647),o=r(40073),i=r(97903),a=r(43832),s=r(87074);function c(t){var e=-1,r=null==t?0:t.length;for(this.clear();++e{var n=r(80751)(r(73401),"Map");t.exports=n},2767:(t,e,r)=>{var n=r(53070),o=r(83638),i=r(38444),a=r(55877),s=r(58990);function c(t){var e=-1,r=null==t?0:t.length;for(this.clear();++e{var n=r(80751)(r(73401),"Promise");t.exports=n},353:(t,e,r)=>{var n=r(80751)(r(73401),"Set");t.exports=n},25561:(t,e,r)=>{var n=r(2767),o=r(16),i=r(64832);function a(t){var e=-1,r=null==t?0:t.length;for(this.__data__=new n;++e{var n=r(624),o=r(79882),i=r(86639),a=r(73887),s=r(2603),c=r(57853);function u(t){var e=this.__data__=new n(t);this.size=e.size}u.prototype.clear=o,u.prototype.delete=i,u.prototype.get=a,u.prototype.has=s,u.prototype.set=c,t.exports=u},66293:(t,e,r)=>{var n=r(73401).Symbol;t.exports=n},39069:(t,e,r)=>{var n=r(73401).Uint8Array;t.exports=n},53180:(t,e,r)=>{var n=r(80751)(r(73401),"WeakMap");t.exports=n},20267:t=>{t.exports=function(t,e,r){switch(r.length){case 0:return t.call(e);case 1:return t.call(e,r[0]);case 2:return t.call(e,r[0],r[1]);case 3:return t.call(e,r[0],r[1],r[2])}return t.apply(e,r)}},51177:t=>{t.exports=function(t,e){for(var r=-1,n=null==t?0:t.length,o=0,i=[];++r{var n=r(85036);t.exports=function(t,e){return!(null==t||!t.length)&&n(t,e,0)>-1}},60510:t=>{t.exports=function(t,e,r){for(var n=-1,o=null==t?0:t.length;++n{var n=r(85606),o=r(43735),i=r(2428),a=r(7757),s=r(30911),c=r(56868),u=Object.prototype.hasOwnProperty;t.exports=function(t,e){var r=i(t),f=!r&&o(t),p=!r&&!f&&a(t),l=!r&&!f&&!p&&c(t),d=r||f||p||l,y=d?n(t.length,String):[],v=y.length;for(var h in t)!e&&!u.call(t,h)||d&&("length"==h||p&&("offset"==h||"parent"==h)||l&&("buffer"==h||"byteLength"==h||"byteOffset"==h)||s(h,v))||y.push(h);return y}},67631:t=>{t.exports=function(t,e){for(var r=-1,n=null==t?0:t.length,o=Array(n);++r{t.exports=function(t,e){for(var r=-1,n=e.length,o=t.length;++r{t.exports=function(t,e){for(var r=-1,n=null==t?0:t.length;++r{var n=r(3284);t.exports=function(t,e){for(var r=t.length;r--;)if(n(t[r][0],e))return r;return-1}},80897:(t,e,r)=>{var n=r(57965);t.exports=function(t,e,r){"__proto__"==e&&n?n(t,e,{configurable:!0,enumerable:!0,value:r,writable:!0}):t[e]=r}},88131:(t,e,r)=>{var n=r(25561),o=r(34598),i=r(60510),a=r(67631),s=r(52715),c=r(8529);t.exports=function(t,e,r,u){var f=-1,p=o,l=!0,d=t.length,y=[],v=e.length;if(!d)return y;r&&(e=a(e,s(r))),u?(p=i,l=!1):e.length>=200&&(p=c,l=!1,e=new n(e));t:for(;++f{t.exports=function(t,e,r,n){for(var o=t.length,i=r+(n?1:-1);n?i--:++i{var n=r(96581),o=r(49912);t.exports=function t(e,r,i,a,s){var c=-1,u=e.length;for(i||(i=o),s||(s=[]);++c0&&i(f)?r>1?t(f,r-1,i,a,s):n(s,f):a||(s[s.length]=f)}return s}},51431:(t,e,r)=>{var n=r(4257)();t.exports=n},89399:(t,e,r)=>{var n=r(51431),o=r(58834);t.exports=function(t,e){return t&&n(t,e,o)}},87856:(t,e,r)=>{var n=r(96322),o=r(28091);t.exports=function(t,e){for(var r=0,i=(e=n(e,t)).length;null!=t&&r{var n=r(96581),o=r(2428);t.exports=function(t,e,r){var i=e(t);return o(t)?i:n(i,r(t))}},57398:(t,e,r)=>{var n=r(66293),o=r(46945),i=r(51584),a=n?n.toStringTag:void 0;t.exports=function(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":a&&a in Object(t)?o(t):i(t)}},86752:t=>{t.exports=function(t,e){return null!=t&&e in Object(t)}},85036:(t,e,r)=>{var n=r(83663),o=r(18826),i=r(31154);t.exports=function(t,e,r){return e==e?i(t,e,r):n(t,o,r)}},75227:(t,e,r)=>{var n=r(57398),o=r(89109);t.exports=function(t){return o(t)&&"[object Arguments]"==n(t)}},33892:(t,e,r)=>{var n=r(86502),o=r(89109);t.exports=function t(e,r,i,a,s){return e===r||(null==e||null==r||!o(e)&&!o(r)?e!=e&&r!=r:n(e,r,i,a,t,s))}},86502:(t,e,r)=>{var n=r(20014),o=r(1979),i=r(75473),a=r(7287),s=r(65064),c=r(2428),u=r(7757),f=r(56868),p="[object Arguments]",l="[object Array]",d="[object Object]",y=Object.prototype.hasOwnProperty;t.exports=function(t,e,r,v,h,m){var g=c(t),_=c(e),b=g?l:s(t),S=_?l:s(e),O=(b=b==p?d:b)==d,x=(S=S==p?d:S)==d,T=b==S;if(T&&u(t)){if(!u(e))return!1;g=!0,O=!1}if(T&&!O)return m||(m=new n),g||f(t)?o(t,e,r,v,h,m):i(t,e,b,r,v,h,m);if(!(1&r)){var E=O&&y.call(t,"__wrapped__"),I=x&&y.call(e,"__wrapped__");if(E||I){var A=E?t.value():t,w=I?e.value():e;return m||(m=new n),h(A,w,r,v,m)}}return!!T&&(m||(m=new n),a(t,e,r,v,h,m))}},46166:(t,e,r)=>{var n=r(20014),o=r(33892);t.exports=function(t,e,r,i){var a=r.length,s=a,c=!i;if(null==t)return!s;for(t=Object(t);a--;){var u=r[a];if(c&&u[2]?u[1]!==t[u[0]]:!(u[0]in t))return!1}for(;++a{t.exports=function(t){return t!=t}},99578:(t,e,r)=>{var n=r(7419),o=r(43283),i=r(6627),a=r(19235),s=/^\[object .+?Constructor\]$/,c=Function.prototype,u=Object.prototype,f=c.toString,p=u.hasOwnProperty,l=RegExp("^"+f.call(p).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=function(t){return!(!i(t)||o(t))&&(n(t)?l:s).test(a(t))}},89126:(t,e,r)=>{var n=r(57398),o=r(6705),i=r(89109),a={};a["[object Float32Array]"]=a["[object Float64Array]"]=a["[object Int8Array]"]=a["[object Int16Array]"]=a["[object Int32Array]"]=a["[object Uint8Array]"]=a["[object Uint8ClampedArray]"]=a["[object Uint16Array]"]=a["[object Uint32Array]"]=!0,a["[object Arguments]"]=a["[object Array]"]=a["[object ArrayBuffer]"]=a["[object Boolean]"]=a["[object DataView]"]=a["[object Date]"]=a["[object Error]"]=a["[object Function]"]=a["[object Map]"]=a["[object Number]"]=a["[object Object]"]=a["[object RegExp]"]=a["[object Set]"]=a["[object String]"]=a["[object WeakMap]"]=!1,t.exports=function(t){return i(t)&&o(t.length)&&!!a[n(t)]}},61757:(t,e,r)=>{var n=r(97549),o=r(728),i=r(98958),a=r(2428),s=r(91363);t.exports=function(t){return"function"==typeof t?t:null==t?i:"object"==typeof t?a(t)?o(t[0],t[1]):n(t):s(t)}},790:(t,e,r)=>{var n=r(92403),o=r(39339),i=Object.prototype.hasOwnProperty;t.exports=function(t){if(!n(t))return o(t);var e=[];for(var r in Object(t))i.call(t,r)&&"constructor"!=r&&e.push(r);return e}},97549:(t,e,r)=>{var n=r(46166),o=r(7378),i=r(49513);t.exports=function(t){var e=o(t);return 1==e.length&&e[0][2]?i(e[0][0],e[0][1]):function(r){return r===t||n(r,t,e)}}},728:(t,e,r)=>{var n=r(33892),o=r(2423),i=r(64400),a=r(44781),s=r(92801),c=r(49513),u=r(28091);t.exports=function(t,e){return a(t)&&s(e)?c(u(t),e):function(r){var a=o(r,t);return void 0===a&&a===e?i(r,t):n(e,a,3)}}},81515:t=>{t.exports=function(t){return function(e){return null==e?void 0:e[t]}}},21834:(t,e,r)=>{var n=r(87856);t.exports=function(t){return function(e){return n(e,t)}}},17063:(t,e,r)=>{var n=r(98958),o=r(58544),i=r(11863);t.exports=function(t,e){return i(o(t,e,n),t+"")}},43182:(t,e,r)=>{var n=r(75269),o=r(57965),i=r(98958),a=o?function(t,e){return o(t,"toString",{configurable:!0,enumerable:!1,value:n(e),writable:!0})}:i;t.exports=a},85606:t=>{t.exports=function(t,e){for(var r=-1,n=Array(t);++r{var n=r(66293),o=r(67631),i=r(2428),a=r(42848),s=n?n.prototype:void 0,c=s?s.toString:void 0;t.exports=function t(e){if("string"==typeof e)return e;if(i(e))return o(e,t)+"";if(a(e))return c?c.call(e):"";var r=e+"";return"0"==r&&1/e==-Infinity?"-0":r}},33897:(t,e,r)=>{var n=r(15012),o=/^\s+/;t.exports=function(t){return t?t.slice(0,n(t)+1).replace(o,""):t}},52715:t=>{t.exports=function(t){return function(e){return t(e)}}},18296:(t,e,r)=>{var n=r(25561),o=r(34598),i=r(60510),a=r(8529),s=r(33295),c=r(33005);t.exports=function(t,e,r){var u=-1,f=o,p=t.length,l=!0,d=[],y=d;if(r)l=!1,f=i;else if(p>=200){var v=e?null:s(t);if(v)return c(v);l=!1,f=a,y=new n}else y=e?[]:d;t:for(;++u{t.exports=function(t,e){return t.has(e)}},96322:(t,e,r)=>{var n=r(2428),o=r(44781),i=r(61596),a=r(44091);t.exports=function(t,e){return n(t)?t:o(t,e)?[t]:i(a(t))}},54640:(t,e,r)=>{var n=r(73401)["__core-js_shared__"];t.exports=n},4257:t=>{t.exports=function(t){return function(e,r,n){for(var o=-1,i=Object(e),a=n(e),s=a.length;s--;){var c=a[t?s:++o];if(!1===r(i[c],c,i))break}return e}}},33295:(t,e,r)=>{var n=r(353),o=r(91530),i=r(33005),a=n&&1/i(new n([,-0]))[1]==1/0?function(t){return new n(t)}:o;t.exports=a},57965:(t,e,r)=>{var n=r(80751),o=function(){try{var t=n(Object,"defineProperty");return t({},"",{}),t}catch(t){}}();t.exports=o},1979:(t,e,r)=>{var n=r(25561),o=r(93531),i=r(8529);t.exports=function(t,e,r,a,s,c){var u=1&r,f=t.length,p=e.length;if(f!=p&&!(u&&p>f))return!1;var l=c.get(t),d=c.get(e);if(l&&d)return l==e&&d==t;var y=-1,v=!0,h=2&r?new n:void 0;for(c.set(t,e),c.set(e,t);++y{var n=r(66293),o=r(39069),i=r(3284),a=r(1979),s=r(98368),c=r(33005),u=n?n.prototype:void 0,f=u?u.valueOf:void 0;t.exports=function(t,e,r,n,u,p,l){switch(r){case"[object DataView]":if(t.byteLength!=e.byteLength||t.byteOffset!=e.byteOffset)return!1;t=t.buffer,e=e.buffer;case"[object ArrayBuffer]":return!(t.byteLength!=e.byteLength||!p(new o(t),new o(e)));case"[object Boolean]":case"[object Date]":case"[object Number]":return i(+t,+e);case"[object Error]":return t.name==e.name&&t.message==e.message;case"[object RegExp]":case"[object String]":return t==e+"";case"[object Map]":var d=s;case"[object Set]":var y=1&n;if(d||(d=c),t.size!=e.size&&!y)return!1;var v=l.get(t);if(v)return v==e;n|=2,l.set(t,e);var h=a(d(t),d(e),n,u,p,l);return l.delete(t),h;case"[object Symbol]":if(f)return f.call(t)==f.call(e)}return!1}},7287:(t,e,r)=>{var n=r(90393),o=Object.prototype.hasOwnProperty;t.exports=function(t,e,r,i,a,s){var c=1&r,u=n(t),f=u.length;if(f!=n(e).length&&!c)return!1;for(var p=f;p--;){var l=u[p];if(!(c?l in e:o.call(e,l)))return!1}var d=s.get(t),y=s.get(e);if(d&&y)return d==e&&y==t;var v=!0;s.set(t,e),s.set(e,t);for(var h=c;++p{var n="object"==typeof r.g&&r.g&&r.g.Object===Object&&r.g;t.exports=n},90393:(t,e,r)=>{var n=r(14755),o=r(69128),i=r(58834);t.exports=function(t){return n(t,i,o)}},61499:(t,e,r)=>{var n=r(1889);t.exports=function(t,e){var r=t.__data__;return n(e)?r["string"==typeof e?"string":"hash"]:r.map}},7378:(t,e,r)=>{var n=r(92801),o=r(58834);t.exports=function(t){for(var e=o(t),r=e.length;r--;){var i=e[r],a=t[i];e[r]=[i,a,n(a)]}return e}},80751:(t,e,r)=>{var n=r(99578),o=r(38027);t.exports=function(t,e){var r=o(t,e);return n(r)?r:void 0}},8187:(t,e,r)=>{var n=r(73518)(Object.getPrototypeOf,Object);t.exports=n},46945:(t,e,r)=>{var n=r(66293),o=Object.prototype,i=o.hasOwnProperty,a=o.toString,s=n?n.toStringTag:void 0;t.exports=function(t){var e=i.call(t,s),r=t[s];try{t[s]=void 0;var n=!0}catch(t){}var o=a.call(t);return n&&(e?t[s]=r:delete t[s]),o}},69128:(t,e,r)=>{var n=r(51177),o=r(35615),i=Object.prototype.propertyIsEnumerable,a=Object.getOwnPropertySymbols,s=a?function(t){return null==t?[]:(t=Object(t),n(a(t),(function(e){return i.call(t,e)})))}:o;t.exports=s},65064:(t,e,r)=>{var n=r(35839),o=r(17973),i=r(80712),a=r(353),s=r(53180),c=r(57398),u=r(19235),f="[object Map]",p="[object Promise]",l="[object Set]",d="[object WeakMap]",y="[object DataView]",v=u(n),h=u(o),m=u(i),g=u(a),_=u(s),b=c;(n&&b(new n(new ArrayBuffer(1)))!=y||o&&b(new o)!=f||i&&b(i.resolve())!=p||a&&b(new a)!=l||s&&b(new s)!=d)&&(b=function(t){var e=c(t),r="[object Object]"==e?t.constructor:void 0,n=r?u(r):"";if(n)switch(n){case v:return y;case h:return f;case m:return p;case g:return l;case _:return d}return e}),t.exports=b},38027:t=>{t.exports=function(t,e){return null==t?void 0:t[e]}},60706:(t,e,r)=>{var n=r(96322),o=r(43735),i=r(2428),a=r(30911),s=r(6705),c=r(28091);t.exports=function(t,e,r){for(var u=-1,f=(e=n(e,t)).length,p=!1;++u{var n=r(24556);t.exports=function(){this.__data__=n?n(null):{},this.size=0}},95937:t=>{t.exports=function(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}},44054:(t,e,r)=>{var n=r(24556),o=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;if(n){var r=e[t];return"__lodash_hash_undefined__"===r?void 0:r}return o.call(e,t)?e[t]:void 0}},99991:(t,e,r)=>{var n=r(24556),o=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;return n?void 0!==e[t]:o.call(e,t)}},62753:(t,e,r)=>{var n=r(24556);t.exports=function(t,e){var r=this.__data__;return this.size+=this.has(t)?0:1,r[t]=n&&void 0===e?"__lodash_hash_undefined__":e,this}},49912:(t,e,r)=>{var n=r(66293),o=r(43735),i=r(2428),a=n?n.isConcatSpreadable:void 0;t.exports=function(t){return i(t)||o(t)||!!(a&&t&&t[a])}},30911:t=>{var e=/^(?:0|[1-9]\d*)$/;t.exports=function(t,r){var n=typeof t;return!!(r=null==r?9007199254740991:r)&&("number"==n||"symbol"!=n&&e.test(t))&&t>-1&&t%1==0&&t{var n=r(2428),o=r(42848),i=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,a=/^\w*$/;t.exports=function(t,e){if(n(t))return!1;var r=typeof t;return!("number"!=r&&"symbol"!=r&&"boolean"!=r&&null!=t&&!o(t))||a.test(t)||!i.test(t)||null!=e&&t in Object(e)}},1889:t=>{t.exports=function(t){var e=typeof t;return"string"==e||"number"==e||"symbol"==e||"boolean"==e?"__proto__"!==t:null===t}},43283:(t,e,r)=>{var n,o=r(54640),i=(n=/[^.]+$/.exec(o&&o.keys&&o.keys.IE_PROTO||""))?"Symbol(src)_1."+n:"";t.exports=function(t){return!!i&&i in t}},92403:t=>{var e=Object.prototype;t.exports=function(t){var r=t&&t.constructor;return t===("function"==typeof r&&r.prototype||e)}},92801:(t,e,r)=>{var n=r(6627);t.exports=function(t){return t==t&&!n(t)}},53647:t=>{t.exports=function(){this.__data__=[],this.size=0}},40073:(t,e,r)=>{var n=r(15869),o=Array.prototype.splice;t.exports=function(t){var e=this.__data__,r=n(e,t);return!(r<0||(r==e.length-1?e.pop():o.call(e,r,1),--this.size,0))}},97903:(t,e,r)=>{var n=r(15869);t.exports=function(t){var e=this.__data__,r=n(e,t);return r<0?void 0:e[r][1]}},43832:(t,e,r)=>{var n=r(15869);t.exports=function(t){return n(this.__data__,t)>-1}},87074:(t,e,r)=>{var n=r(15869);t.exports=function(t,e){var r=this.__data__,o=n(r,t);return o<0?(++this.size,r.push([t,e])):r[o][1]=e,this}},53070:(t,e,r)=>{var n=r(61538),o=r(624),i=r(17973);t.exports=function(){this.size=0,this.__data__={hash:new n,map:new(i||o),string:new n}}},83638:(t,e,r)=>{var n=r(61499);t.exports=function(t){var e=n(this,t).delete(t);return this.size-=e?1:0,e}},38444:(t,e,r)=>{var n=r(61499);t.exports=function(t){return n(this,t).get(t)}},55877:(t,e,r)=>{var n=r(61499);t.exports=function(t){return n(this,t).has(t)}},58990:(t,e,r)=>{var n=r(61499);t.exports=function(t,e){var r=n(this,t),o=r.size;return r.set(t,e),this.size+=r.size==o?0:1,this}},98368:t=>{t.exports=function(t){var e=-1,r=Array(t.size);return t.forEach((function(t,n){r[++e]=[n,t]})),r}},49513:t=>{t.exports=function(t,e){return function(r){return null!=r&&r[t]===e&&(void 0!==e||t in Object(r))}}},15646:(t,e,r)=>{var n=r(74153);t.exports=function(t){var e=n(t,(function(t){return 500===r.size&&r.clear(),t})),r=e.cache;return e}},24556:(t,e,r)=>{var n=r(80751)(Object,"create");t.exports=n},39339:(t,e,r)=>{var n=r(73518)(Object.keys,Object);t.exports=n},20126:(t,e,r)=>{t=r.nmd(t);var n=r(40151),o=e&&!e.nodeType&&e,i=o&&t&&!t.nodeType&&t,a=i&&i.exports===o&&n.process,s=function(){try{return i&&i.require&&i.require("util").types||a&&a.binding&&a.binding("util")}catch(t){}}();t.exports=s},51584:t=>{var e=Object.prototype.toString;t.exports=function(t){return e.call(t)}},73518:t=>{t.exports=function(t,e){return function(r){return t(e(r))}}},58544:(t,e,r)=>{var n=r(20267),o=Math.max;t.exports=function(t,e,r){return e=o(void 0===e?t.length-1:e,0),function(){for(var i=arguments,a=-1,s=o(i.length-e,0),c=Array(s);++a{var n=r(40151),o="object"==typeof self&&self&&self.Object===Object&&self,i=n||o||Function("return this")();t.exports=i},16:t=>{t.exports=function(t){return this.__data__.set(t,"__lodash_hash_undefined__"),this}},64832:t=>{t.exports=function(t){return this.__data__.has(t)}},33005:t=>{t.exports=function(t){var e=-1,r=Array(t.size);return t.forEach((function(t){r[++e]=t})),r}},11863:(t,e,r)=>{var n=r(43182),o=r(29426)(n);t.exports=o},29426:t=>{var e=800,r=16,n=Date.now;t.exports=function(t){var o=0,i=0;return function(){var a=n(),s=r-(a-i);if(i=a,s>0){if(++o>=e)return arguments[0]}else o=0;return t.apply(void 0,arguments)}}},79882:(t,e,r)=>{var n=r(624);t.exports=function(){this.__data__=new n,this.size=0}},86639:t=>{t.exports=function(t){var e=this.__data__,r=e.delete(t);return this.size=e.size,r}},73887:t=>{t.exports=function(t){return this.__data__.get(t)}},2603:t=>{t.exports=function(t){return this.__data__.has(t)}},57853:(t,e,r)=>{var n=r(624),o=r(17973),i=r(2767);t.exports=function(t,e){var r=this.__data__;if(r instanceof n){var a=r.__data__;if(!o||a.length<199)return a.push([t,e]),this.size=++r.size,this;r=this.__data__=new i(a)}return r.set(t,e),this.size=r.size,this}},31154:t=>{t.exports=function(t,e,r){for(var n=r-1,o=t.length;++n{var n=r(15646),o=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,i=/\\(\\)?/g,a=n((function(t){var e=[];return 46===t.charCodeAt(0)&&e.push(""),t.replace(o,(function(t,r,n,o){e.push(n?o.replace(i,"$1"):r||t)})),e}));t.exports=a},28091:(t,e,r)=>{var n=r(42848);t.exports=function(t){if("string"==typeof t||n(t))return t;var e=t+"";return"0"==e&&1/t==-Infinity?"-0":e}},19235:t=>{var e=Function.prototype.toString;t.exports=function(t){if(null!=t){try{return e.call(t)}catch(t){}try{return t+""}catch(t){}}return""}},15012:t=>{var e=/\s/;t.exports=function(t){for(var r=t.length;r--&&e.test(t.charAt(r)););return r}},75269:t=>{t.exports=function(t){return function(){return t}}},89678:(t,e,r)=>{var n=r(6627),o=r(85365),i=r(67948),a=Math.max,s=Math.min;t.exports=function(t,e,r){var c,u,f,p,l,d,y=0,v=!1,h=!1,m=!0;if("function"!=typeof t)throw new TypeError("Expected a function");function g(e){var r=c,n=u;return c=u=void 0,y=e,p=t.apply(n,r)}function _(t){var r=t-d;return void 0===d||r>=e||r<0||h&&t-y>=f}function b(){var t=o();if(_(t))return S(t);l=setTimeout(b,function(t){var r=e-(t-d);return h?s(r,f-(t-y)):r}(t))}function S(t){return l=void 0,m&&c?g(t):(c=u=void 0,p)}function O(){var t=o(),r=_(t);if(c=arguments,u=this,d=t,r){if(void 0===l)return function(t){return y=t,l=setTimeout(b,e),v?g(t):p}(d);if(h)return clearTimeout(l),l=setTimeout(b,e),g(d)}return void 0===l&&(l=setTimeout(b,e)),p}return e=i(e)||0,n(r)&&(v=!!r.leading,f=(h="maxWait"in r)?a(i(r.maxWait)||0,e):f,m="trailing"in r?!!r.trailing:m),O.cancel=function(){void 0!==l&&clearTimeout(l),y=0,c=d=u=l=void 0},O.flush=function(){return void 0===l?p:S(o())},O}},43485:(t,e,r)=>{var n=r(88131),o=r(44140),i=r(17063),a=r(21392),s=i((function(t,e){return a(t)?n(t,o(e,1,a,!0)):[]}));t.exports=s},3284:t=>{t.exports=function(t,e){return t===e||t!=t&&e!=e}},2423:(t,e,r)=>{var n=r(87856);t.exports=function(t,e,r){var o=null==t?void 0:n(t,e);return void 0===o?r:o}},64400:(t,e,r)=>{var n=r(86752),o=r(60706);t.exports=function(t,e){return null!=t&&o(t,e,n)}},98958:t=>{t.exports=function(t){return t}},43735:(t,e,r)=>{var n=r(75227),o=r(89109),i=Object.prototype,a=i.hasOwnProperty,s=i.propertyIsEnumerable,c=n(function(){return arguments}())?n:function(t){return o(t)&&a.call(t,"callee")&&!s.call(t,"callee")};t.exports=c},2428:t=>{var e=Array.isArray;t.exports=e},71701:(t,e,r)=>{var n=r(7419),o=r(6705);t.exports=function(t){return null!=t&&o(t.length)&&!n(t)}},21392:(t,e,r)=>{var n=r(71701),o=r(89109);t.exports=function(t){return o(t)&&n(t)}},7757:(t,e,r)=>{t=r.nmd(t);var n=r(73401),o=r(88553),i=e&&!e.nodeType&&e,a=i&&t&&!t.nodeType&&t,s=a&&a.exports===i?n.Buffer:void 0,c=(s?s.isBuffer:void 0)||o;t.exports=c},7419:(t,e,r)=>{var n=r(57398),o=r(6627);t.exports=function(t){if(!o(t))return!1;var e=n(t);return"[object Function]"==e||"[object GeneratorFunction]"==e||"[object AsyncFunction]"==e||"[object Proxy]"==e}},6705:t=>{t.exports=function(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=9007199254740991}},6627:t=>{t.exports=function(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}},89109:t=>{t.exports=function(t){return null!=t&&"object"==typeof t}},67066:(t,e,r)=>{var n=r(57398),o=r(8187),i=r(89109),a=Function.prototype,s=Object.prototype,c=a.toString,u=s.hasOwnProperty,f=c.call(Object);t.exports=function(t){if(!i(t)||"[object Object]"!=n(t))return!1;var e=o(t);if(null===e)return!0;var r=u.call(e,"constructor")&&e.constructor;return"function"==typeof r&&r instanceof r&&c.call(r)==f}},42848:(t,e,r)=>{var n=r(57398),o=r(89109);t.exports=function(t){return"symbol"==typeof t||o(t)&&"[object Symbol]"==n(t)}},56868:(t,e,r)=>{var n=r(89126),o=r(52715),i=r(20126),a=i&&i.isTypedArray,s=a?o(a):n;t.exports=s},58834:(t,e,r)=>{var n=r(47189),o=r(790),i=r(71701);t.exports=function(t){return i(t)?n(t):o(t)}},2903:(t,e,r)=>{var n=r(80897),o=r(89399),i=r(61757);t.exports=function(t,e){var r={};return e=i(e,3),o(t,(function(t,o,i){n(r,o,e(t,o,i))})),r}},74153:(t,e,r)=>{var n=r(2767),o="Expected a function";function i(t,e){if("function"!=typeof t||null!=e&&"function"!=typeof e)throw new TypeError(o);var r=function(){var n=arguments,o=e?e.apply(this,n):n[0],i=r.cache;if(i.has(o))return i.get(o);var a=t.apply(this,n);return r.cache=i.set(o,a)||i,a};return r.cache=new(i.Cache||n),r}i.Cache=n,t.exports=i},91530:t=>{t.exports=function(){}},85365:(t,e,r)=>{var n=r(73401);t.exports=function(){return n.Date.now()}},91363:(t,e,r)=>{var n=r(81515),o=r(21834),i=r(44781),a=r(28091);t.exports=function(t){return i(t)?n(a(t)):o(t)}},35615:t=>{t.exports=function(){return[]}},88553:t=>{t.exports=function(){return!1}},23763:(t,e,r)=>{var n=r(89678),o=r(6627);t.exports=function(t,e,r){var i=!0,a=!0;if("function"!=typeof t)throw new TypeError("Expected a function");return o(r)&&(i="leading"in r?!!r.leading:i,a="trailing"in r?!!r.trailing:a),n(t,e,{leading:i,maxWait:e,trailing:a})}},67948:(t,e,r)=>{var n=r(33897),o=r(6627),i=r(42848),a=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,c=/^0o[0-7]+$/i,u=parseInt;t.exports=function(t){if("number"==typeof t)return t;if(i(t))return NaN;if(o(t)){var e="function"==typeof t.valueOf?t.valueOf():t;t=o(e)?e+"":e}if("string"!=typeof t)return 0===t?t:+t;t=n(t);var r=s.test(t);return r||c.test(t)?u(t.slice(2),r?2:8):a.test(t)?NaN:+t}},44091:(t,e,r)=>{var n=r(17185);t.exports=function(t){return null==t?"":n(t)}},54740:(t,e,r)=>{var n=r(44140),o=r(17063),i=r(18296),a=r(21392),s=o((function(t){return i(n(t,1,a,!0))}));t.exports=s}},e={};function r(n){var o=e[n];if(void 0!==o)return o.exports;var i=e[n]={id:n,loaded:!1,exports:{}};return t[n](i,i.exports,r),i.loaded=!0,i.exports}r.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return r.d(e,{a:e}),e},r.d=(t,e)=>{for(var n in e)r.o(e,n)&&!r.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r.nmd=t=>(t.paths=[],t.children||(t.children=[]),t),(()=>{"use strict";var t=r(2903),e=r.n(t);function n(t){return Array.isArray(t)}function o(t){const e=t.actionsDenylist??t.actionsBlacklist,r=t.actionsAllowlist??t.actionsWhitelist;if(e||r)return{allowlist:n(r)?r.join("|"):r,denylist:n(e)?e.join("|"):e}}var i=r(40472),a=r.n(i),s=r(63822);function c(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",r=[];return Object.keys(t).forEach((n=>{const o=t[n];"function"==typeof o?r.push({name:e+(n||o.name||"anonymous"),func:o,args:a()(o)}):"object"==typeof o&&(r=r.concat(c(o,e+n+".")))})),r}function u(t){return Array.isArray(t)?t:c(t)}const f=t=>new Function("return "+t)();var p=r(23763),l=r.n(p);"function"==typeof Symbol&&Symbol.observable;var d=function(){return Math.random().toString(36).substring(7).split("").join(".")};d(),d();var y=r(43485),v=r.n(y),h=r(54740),m=r.n(h),g=r(67066),_=r.n(g);const b={PERFORM_ACTION:"PERFORM_ACTION",RESET:"RESET",ROLLBACK:"ROLLBACK",COMMIT:"COMMIT",SWEEP:"SWEEP",TOGGLE_ACTION:"TOGGLE_ACTION",SET_ACTIONS_ACTIVE:"SET_ACTIONS_ACTIVE",JUMP_TO_STATE:"JUMP_TO_STATE",JUMP_TO_ACTION:"JUMP_TO_ACTION",REORDER_ACTION:"REORDER_ACTION",IMPORT_STATE:"IMPORT_STATE",LOCK_CHANGES:"LOCK_CHANGES",PAUSE_RECORDING:"PAUSE_RECORDING"},S="object"==typeof window&&(void 0!==window.chrome||void 0!==window.process&&"renderer"===window.process.type),O=S||"undefined"!=typeof process&&process.release&&"node"===process.release.name,x={performAction(t,e,r,n){if(!_()(t))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===t.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');let o;if(e){let i=0;if("function"==typeof e)o=e(t);else{const t=Error();let e;if(Error.captureStackTrace&&O?(r&&Error.stackTraceLimitr)&&null!=o){const t=o.split("\n");r&&t.length>r&&(o=t.slice(0,r+i+(t[0].startsWith("Error")?1:0)).join("\n"))}}}return{type:b.PERFORM_ACTION,action:t,timestamp:Date.now(),stack:o}},reset:()=>({type:b.RESET,timestamp:Date.now()}),rollback:()=>({type:b.ROLLBACK,timestamp:Date.now()}),commit:()=>({type:b.COMMIT,timestamp:Date.now()}),sweep:()=>({type:b.SWEEP}),toggleAction:t=>({type:b.TOGGLE_ACTION,id:t}),setActionsActive(t,e){let r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return{type:b.SET_ACTIONS_ACTIVE,start:t,end:e,active:r}},reorderAction:(t,e)=>({type:b.REORDER_ACTION,actionId:t,beforeActionId:e}),jumpToState:t=>({type:b.JUMP_TO_STATE,index:t}),jumpToAction:t=>({type:b.JUMP_TO_ACTION,actionId:t}),importState:(t,e)=>({type:b.IMPORT_STATE,nextLiftedState:t,noRecompute:e}),lockChanges:t=>({type:b.LOCK_CHANGES,status:t}),pauseRecording:t=>({type:b.PAUSE_RECORDING,status:t})},T={type:"@@INIT"};function E(t,e,r,n){return n?function(t,e,r){let n,o=r;try{o=t(r,e)}catch(t){n=t.toString(),S?setTimeout((()=>{throw t})):console.error(t)}return{state:o,error:n}}(t,e,r):{state:t(r,e)}}function I(t,e,r,n,o,i,a,s){if(!t||-1===e||e>=t.length&&t.length===i.length)return t;const c=t.slice(0,e);for(let t=e;t-1?f:s&&f&&f.error?{state:p,error:"Interrupted by an error up the chain"}:E(r,u,p,s),c.push(l)}return c}function A(t,e,r,n){return x.performAction(t,e,r,n)}var w=r(98958),j=r.n(w);function R(t){const e=window.location.href.match(new RegExp(`[?&]${t}=([^&#]+)\\b`));return e&&e.length>0?e[1]:null}function N(t,r,n){return function(){for(var t=arguments.length,e=new Array(t),r=0;r0&&void 0!==arguments[0]?arguments[0]:()=>null,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("number"==typeof e.maxAge&&e.maxAge<2)throw new Error("DevTools.instrument({ maxAge }) option, if specified, may not be less than 2.");return r=>(n,o)=>{function i(r){if("function"!=typeof r){if(r&&"function"==typeof r.default)throw new Error('Expected the reducer to be a function. Instead got an object with a "default" field. Did you pass a module instead of the default export? Try passing require(...).default instead.');throw new Error("Expected the reducer to be a function.")}return function(t,e,r,n){const o={monitorState:r(void 0,{}),nextActionId:1,actionsById:{0:A(T)},stagedActionIds:[0],skippedActionIds:[],committedState:e,currentStateIndex:0,computedStates:[],isLocked:!0===n.shouldStartLocked,isPaused:!1===n.shouldRecordChanges};return(i,a)=>{let{monitorState:s,actionsById:c,nextActionId:u,stagedActionIds:f,skippedActionIds:p,committedState:l,currentStateIndex:d,computedStates:y,isLocked:h,isPaused:g}=i||o;function _(t){let e=t,r=f.slice(1,e+1);for(let t=0;t-1===r.indexOf(t))),f=[0,...f.slice(e+1)],l=y[e].state,y=y.slice(e),d=d>e?d-e:0}function S(e){let o;return e?(o=y[d],s=r(s,a)):o=E(t,a.action,y[d].state,!1),n.pauseActionType&&1!==u?(e&&(d===f.length-1&&d++,f=[...f,u],u++),{monitorState:s,actionsById:{...c,[u-1]:A({type:n.pauseActionType})},nextActionId:u,stagedActionIds:f,skippedActionIds:p,committedState:l,currentStateIndex:d,computedStates:[...y.slice(0,f.length-1),o],isLocked:h,isPaused:!0}):{monitorState:s,actionsById:{0:A(T)},nextActionId:1,stagedActionIds:[0],skippedActionIds:[],committedState:o.state,currentStateIndex:0,computedStates:[o],isLocked:h,isPaused:!0}}i||(c={...c});let O=0,x=n.maxAge;if("function"==typeof x&&(x=x(a,i)),/^@@redux\/(INIT|REPLACE)/.test(a.type))!1===n.shouldHotReload&&(c={0:A(T)},u=1,f=[0],p=[],l=0===y.length?e:y[d].state,d=0,y=[]),O=0,x&&f.length>x&&(y=I(y,O,t,l,c,f,p,n.shouldCatchErrors),_(f.length-x),O=1/0);else switch(a.type){case b.PERFORM_ACTION:{if(h)return i||o;if(g)return S();x&&f.length>=x&&_(f.length-x+1),d===f.length-1&&d++;const t=u++;c[t]=a,f=[...f,t],O=f.length-1;break}case b.RESET:c={0:A(T)},u=1,f=[0],p=[],l=e,d=0,y=[];break;case b.COMMIT:c={0:A(T)},u=1,f=[0],p=[],l=y[d].state,d=0,y=[];break;case b.ROLLBACK:c={0:A(T)},u=1,f=[0],p=[],d=0,y=[];break;case b.TOGGLE_ACTION:{const{id:t}=a,e=p.indexOf(t);p=-1===e?[t,...p]:p.filter((e=>e!==t)),O=f.indexOf(t);break}case b.SET_ACTIONS_ACTIVE:{const{start:t,end:e,active:r}=a,n=[];for(let r=t;rf[t-1]?t:1}const o=e-n;o>0?(f=[...f.slice(0,n),t,...f.slice(n,e),...f.slice(e+1)],O=n):o<0&&(f=[...f.slice(0,e),...f.slice(e+1,n),t,...f.slice(n)],O=e);break}case b.IMPORT_STATE:w=a.nextLiftedState,Array.isArray(w)?(c={0:A(T)},u=1,f=[0],p=[],d=a.nextLiftedState.length,y=[],l=a.preloadedState,O=0,a.nextLiftedState.forEach((t=>{c[u]=A(t,n.trace||n.shouldIncludeCallstack),f.push(u),u++}))):(({monitorState:s,actionsById:c,nextActionId:u,stagedActionIds:f,skippedActionIds:p,committedState:l,currentStateIndex:d,computedStates:y}=a.nextLiftedState),a.noRecompute&&(O=1/0));break;case b.LOCK_CHANGES:h=a.status,O=1/0;break;case b.PAUSE_RECORDING:if(g=a.status,g)return S(!0);c={0:A(T)},u=1,f=[0],p=[],l=y[d].state,d=0,y=[];break;default:O=1/0}var w;return y=I(y,O,t,l,c,f,p,n.shouldCatchErrors),s=r(s,a),{monitorState:s,actionsById:c,nextActionId:u,stagedActionIds:f,skippedActionIds:p,committedState:l,currentStateIndex:d,computedStates:y,isLocked:h,isPaused:g}}}(r,o,t,e)}const a=r(i(n));if(a.liftedStore)throw new Error("DevTools instrumentation should not be applied more than once. Check your store configuration.");return function(t,e,r){let n;const o=r.trace||r.shouldIncludeCallstack,i=r.traceLimit||10;function a(){const e=function(t){const{computedStates:e,currentStateIndex:r}=t,{state:n}=e[r];return n}(t.getState());return void 0!==e&&(n=e),n}const s="function"==typeof Symbol&&Symbol.observable||"@@observable";return s in t||console.warn("Symbol.observable as defined by Redux and Redux DevTools do not match. This could cause your app to behave differently if the DevTools are not loaded. Consider polyfilling Symbol.observable before Redux is imported or avoid polyfilling Symbol.observable altogether."),{liftedStore:t,dispatch:function e(r){return t.dispatch(A(r,o,i,e)),r},subscribe:t.subscribe,getState:a,replaceReducer(r){t.replaceReducer(e(r))},[s]:()=>({subscribe(e){if("object"!=typeof e)throw new TypeError("Expected the observer to be an object.");function r(){e.next&&e.next(a())}return r(),{unsubscribe:t.subscribe(r)}},[s](){return this}})}}(a,i,e)}}(r,{maxAge:n.maxAge,trace:n.trace,traceLimit:n.traceLimit,shouldCatchErrors:n.shouldCatchErrors||window.shouldCatchErrors,shouldHotReload:n.shouldHotReload,shouldRecordChanges:n.shouldRecordChanges,shouldStartLocked:n.shouldStartLocked,pauseActionType:n.pauseActionType||"@@PAUSED"}),function(t){let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:j(),n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:j();return t?o=>(i,a)=>{const s=`redux-dev-session-${t}`;let c;try{const t=localStorage.getItem(s);t&&(u=JSON.parse(t),c={...u,actionsById:e()(u.actionsById,(t=>({...t,action:n(t.action)}))),committedState:r(u.committedState),computedStates:u.computedStates.map((t=>({...t,state:r(t.state)})))}||a,o(i,a))}catch(t){console.warn("Could not read debug session from localStorage:",t);try{localStorage.removeItem(s)}finally{c=void 0}}var u;const f=o(i,c);return{...f,dispatch(t){f.dispatch(t);try{localStorage.setItem(s,JSON.stringify(f.getState()))}catch(t){console.warn("Could not write debug session to localStorage:",t)}return t}}}:t=>function(){return t(...arguments)}}(R("debug_session")))(t)}const C=t=>!(t||window.devToolsOptions&&window.devToolsOptions.filter&&"DO_NOT_FILTER"!==window.devToolsOptions.filter);function L(t,e){if(C(e)||"string"!=typeof t&&"function"!=typeof t.type.match)return!1;const{allowlist:r,denylist:n}=e||window.devToolsOptions||{},o=t.type||t;return r&&!o.match(r)||n&&o.match(n)}function P(t,r){return r?e()(t,((t,e)=>({...t,action:r(t.action,e)}))):t}function k(t,e){return e?t.map(((t,r)=>({...t,state:e(t.state,r)}))):t}function D(t,e,r,n,o){if(o||!C(e)){const i=[],a=[],s=n&&{},{actionsById:c}=t,{computedStates:u}=t;return t.stagedActionIds.forEach(((t,f)=>{const p=c[t];if(!p)return;const l=p.action,d=u[f],y=d.state;if(f){if(o&&!o(y,l))return;if(L(l,e))return}i.push(t),a.push(r?{...d,state:r(y,f)}:d),n&&(s[t]={...p,action:n(l,t)})})),{...t,actionsById:s||c,stagedActionIds:i,computedStates:a}}return r||n?{...t,actionsById:P(t.actionsById,n),computedStates:k(t.computedStates,r)}:t}let M;const z=t=>""!==t?t.split("\n").filter(Boolean).join("|"):null,$=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:M;return!t||t.inject||!t.urls||location.href.match(z(t.urls))};function B(t,e,r){return(e=function(t){var e=function(t,e){if("object"!=typeof t||null===t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var n=r.call(t,"string");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(t)}(t);return"symbol"==typeof e?e:String(e)}(e))in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}class U{constructor(t){var e=this;B(this,"reducer",(function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=arguments.length>1?arguments[1]:void 0;return e.active?(e.lastAction=r.type,"LOCK_CHANGES"===r.type?window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__=r.status:"PAUSE_RECORDING"===r.type?e.paused=r.status:e.isHotReloaded()&&setTimeout(e.update,0),t):t})),B(this,"start",(t=>{this.active=!0,t||this.update()})),B(this,"stop",(()=>{this.active=!1,clearTimeout(this.waitingTimeout)})),B(this,"isHotReloaded",(()=>this.lastAction&&/^@@redux\/(INIT|REPLACE)/.test(this.lastAction))),B(this,"isMonitorAction",(()=>this.lastAction&&"PERFORM_ACTION"!==this.lastAction)),B(this,"isTimeTraveling",(()=>"JUMP_TO_STATE"===this.lastAction||"JUMP_TO_ACTION"===this.lastAction)),B(this,"isPaused",(()=>!(!this.paused||"BLOCKED"!==this.lastAction&&(window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__||(this.lastAction="BLOCKED"),1)))),B(this,"isLocked",(()=>!(!window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__||"BLOCKED"!==this.lastAction&&(this.lastAction="BLOCKED",1)))),this.update=t}}let J,F=0;const G=function(t){let e=1;return function(t){if(t)return e=1,0;let r=Math.pow(2,e-1);return e<5&&(e+=1),5e3*r}}();function X(t){var e;window.devToolsOptions&&!window.devToolsOptions.shouldCatchErrors||t.timeStamp-Fs.parse(t,q(r.immutable,r.refs,r.replacer,r.reviver).reviver):function(t){return!!t.immutable}(r)&&(n=t=>s.parse(t,r.reviver)));const o=n(t);let i="payload"in o&&o.preloadedState?n(o.preloadedState):void 0;return{nextLiftedState:"payload"in o?n(o.payload):o,preloadedState:i}}function Q(t){var e;e={source:"@devtools-page",type:"OPEN",position:t||"right"},window.postMessage(e,"*")}let Z=0;function tt(t){return t||++Z}const et={},rt="@devtools-page";function nt(t,e){return e&&e.window===e?"[WINDOW]":e}let ot;function it(t,e){const r=void 0===e?function(t){try{return JSON.stringify(t)}catch(e){return s.stringify(t,nt,void 0,{circular:"[CIRCULAR]",date:!0})}}(t):s.stringify(t,e.replacer,void 0,e.options);return!ot&&r&&r.length>16777216&&(console.warn("Application state or actions payloads are too large making Redux DevTools serialization slow and consuming a lot of memory. See https://github.com/reduxjs/redux-devtools-extension/blob/master/docs/Troubleshooting.md#excessive-use-of-memory-and-cpu on how to configure it."),ot=!0),r}function at(t){const e=t.serialize;if(e){if(!0===e)return{options:!0};if(e.immutable){const t=q(e.immutable,e.refs,e.replacer,e.reviver);return{replacer:t.replacer,reviver:t.reviver,options:"object"==typeof e.options?{...t.options,...e.options}:t.options}}return e.replacer||e.reviver?{replacer:e.replacer,reviver:e.reviver,options:e.options||!0}:{options:e.options}}}function st(t){window.postMessage(t,"*")}function ct(t,e,r){let n=Date.now(),o=function(t,e){if(!t.trace)return;if("function"==typeof t.trace)return t.trace();let r,n,o=0;const i=t.traceLimit,a=Error();if(Error.captureStackTrace?(Error.stackTraceLimiti){const t=r.split("\n");t.length>i&&(r=t.slice(0,i+o+("Error"===t[0]?1:0)).join("\n"))}return r}(e,r);return"string"==typeof t?{action:{type:t},timestamp:n,stack:o}:t.type?t.action?o?{stack:o,...t}:t:{action:t,timestamp:n,stack:o}:{action:{type:"update"},timestamp:n,stack:o}}function ut(t,e,r){if("ACTION"===t.type)st({...t,action:it(t.action,r),payload:it(t.payload,e)});else if("STATE"===t.type){const{actionsById:n,computedStates:o,committedState:i,...a}=t.payload;st({...t,payload:a,actionsById:it(n,r),computedStates:it(o,e),committedState:void 0!==i})}else if("PARTIAL_STATE"===t.type){const{actionsById:n,computedStates:o,committedState:i,...a}=t.payload;st({...t,payload:a,actionsById:it(n,r),computedStates:it(o,e),committedState:void 0!==i})}else"EXPORT"===t.type?st({...t,payload:it(t.payload,r),committedState:void 0!==t.committedState?it(t.committedState,e):t.committedState}):st(t)}function ft(t,e,r,n,o){let i=t;"object"!=typeof r&&(r={},t&&(i=ct(t,r,ft))),ut(t?{type:"ACTION",action:i,payload:e,maxAge:r.maxAge,source:rt,name:r.name||o,instanceId:r.instanceId||n||1}:{type:"STATE",action:i,payload:e,maxAge:r.maxAge,source:rt,name:r.name||o,instanceId:r.instanceId||n||1},r.serialize,r.serialize)}function pt(t){if(!t||t.source!==window)return;const e=t.data;e&&"@devtools-extension"===e.source&&Object.keys(et).forEach((t=>{if(e.id&&t!==e.id)return;const r=et[t];"function"==typeof r?r(e):r.forEach((t=>{t(e)}))}))}function lt(t,e){et[e]=t,window.addEventListener("message",pt,!1)}const dt="@devtools-page";let yt,vt={};function ht(t,e){console.warn(`${t} parameter is deprecated, use ${e} instead: https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md`)}function mt(t){let e;"object"!=typeof t&&(t={}),window.devToolsOptions||(window.devToolsOptions={});let r,n,i=!1,a=1;const s=tt(t.instanceId),c=o(t),p=at(t),d=at(t);let{stateSanitizer:y,actionSanitizer:v,predicate:h,latency:m=500}=t;t.actionsWhitelist&&ht("actionsWhiteList","actionsAllowlist"),t.actionsBlacklist&&ht("actionsBlacklist","actionsDenylist");const g=l()(((t,r)=>{b.cancel();const n=t||e.liftedStore.getState();a=n.nextActionId,ut({type:"STATE",payload:D(n,c,y,v,h),source:dt,instanceId:s,libConfig:r},p,d)}),m),_=new U(g),b=l()((()=>{const t=e.liftedStore.getState(),r=t.nextActionId,n=r-1,o=t.actionsById[n];if(a===n){a=r;const e=o.action,n=t.computedStates;if(L(e,c)||h&&!h(n[n.length-1].state,e))return;const i=t.computedStates[t.computedStates.length-1].state;return void ut({type:"ACTION",payload:y?y(i,r-1):i,source:dt,instanceId:s,action:v?v(t.actionsById[r-1].action,r-1):t.actionsById[r-1],maxAge:T(),nextActionId:r},p,d)}const i=function(t,e,r,n,o,i){const a=e.stagedActionIds;if(t<=a[1])return e;const s=a.indexOf(t);if(-1===s)return e;const c=i||!C(r),u=c?[0]:a,f=e.actionsById,p=e.computedStates,l={},d=[];let y,v,h;for(let t=c?1:s;t{n.push(r[t].action)})),ut({type:"EXPORT",payload:n,committedState:t.committedState,source:dt,instanceId:s},p,d)}();case"UPDATE":return void g();case"START":return _.start(!0),!n&&t.actionCreators&&(n=u(t.actionCreators)),g(void 0,{name:t.name||document.title,actionCreators:JSON.stringify(n),features:t.features,serialize:!!t.serialize,type:"redux"}),void(yt&&(ut({type:"GET_REPORT",payload:yt,source:dt,instanceId:s},p,d),yt=null));case"STOP":_.stop(),b.cancel(),g.cancel(),r.failed||ut({type:"STOP",payload:void 0,source:dt,instanceId:s},p,d)}}const x=[],T=(e,n)=>{let o=t&&t.maxAge||window.devToolsOptions.maxAge||50;if(!e||C(c)||!e.action)return o;if((!r||r=o){const t=n.stagedActionIds;let e=1;for(;r>o&&-1===x.indexOf(t[e]);)r--,e++;x.shift()}return r};function E(){lt(O,s),V((()=>{i=!0;const t=e.liftedStore.getState();return t.computedStates[t.currentStateIndex].error&&g(t),!0})),ut({type:"INIT_INSTANCE",payload:void 0,source:dt,instanceId:s},p,d),e.subscribe(I),void 0===yt&&(yt=R("remotedev_report"),yt&&Q())}function I(){if(!_.active)return;if(!i&&!_.isMonitorAction())return void b();if(_.isPaused()||_.isLocked()||_.isTimeTraveling())return;const t=e.liftedStore.getState();i&&!t.computedStates[t.currentStateIndex].error&&(i=!1),g(t)}return r=>(n,o)=>$(window.devToolsOptions)?(e=vt[s]=N(r,_.reducer,{...t,maxAge:T})(n,o),function(){try{return window.self!==window.top}catch(t){return!0}}()?setTimeout(E,3e3):E(),e):r(n,o)}window.__REDUX_DEVTOOLS_EXTENSION__=mt,window.__REDUX_DEVTOOLS_EXTENSION__.open=Q,window.__REDUX_DEVTOOLS_EXTENSION__.notifyErrors=V,window.__REDUX_DEVTOOLS_EXTENSION__.send=ft,window.__REDUX_DEVTOOLS_EXTENSION__.listen=lt,window.__REDUX_DEVTOOLS_EXTENSION__.connect=function(t){const e=t||{},r=tt(e.instanceId);e.instanceId||(e.instanceId=r),e.name||(e.name=document.title&&1===r?document.title:`Instance ${r}`),e.serialize&&(e.serialize=at(e));const n=e.actionCreators||{},i=e.latency,a=e.predicate,s=o(e),c=e.autoPause;let f=c,p=[],d=[];et[r]=[t=>{if(c&&("START"===t.type?f=!1:"STOP"===t.type&&(f=!0)),"DISPATCH"===t.type){const e=t.payload;"PAUSE_RECORDING"===e.type&&(f=e.status,ut({type:"LIFTED",liftedState:{isPaused:f},instanceId:r,source:rt}))}}];const y=l()((()=>{ft(p,d,e),p=[],d=[]}),i),v=(t,r)=>{if(f||L(t,s)||a&&!a(r,t))return;let n=t;const o=e.stateSanitizer?e.stateSanitizer(r):r;if(t&&(e.getActionType?(n=e.getActionType(t),"object"!=typeof n&&(n={action:{type:n},timestamp:Date.now()})):e.actionSanitizer&&(n=e.actionSanitizer(t)),n=ct(n,e,v),i))return p.push(n),d.push(o),void y();ft(n,o,e)};return window.addEventListener("message",pt,!1),st({type:"INIT_INSTANCE",instanceId:r,source:rt}),{init:(t,o)=>{const i={type:"INIT",payload:it(t,e.serialize),instanceId:r,source:rt};o&&Array.isArray(o)?(i.action=it(o),i.name=e.name):(o&&(i.liftedState=o,o.isPaused&&(f=!0)),i.libConfig={actionCreators:JSON.stringify(u(n)),name:e.name||document.title,features:e.features,serialize:!!e.serialize,type:e.type}),st(i)},subscribe:t=>{if(!t)return;const n=((t,e)=>r=>{"IMPORT"===r.type?t({type:"DISPATCH",payload:{type:"IMPORT_STATE",...Y(r.state,e)}}):t(r)})(t,e),o=et[r];return o.push(n),function(){const t=o.indexOf(n);o.splice(t,1)}},unsubscribe:()=>{delete et[r]},send:v,error:t=>{st({type:"ERROR",payload:t,instanceId:r,source:rt})}}},window.__REDUX_DEVTOOLS_EXTENSION__.disconnect=function(){window.removeEventListener("message",pt),st({type:"DISCONNECT",source:rt})};const gt=t=>e=>(r,n)=>{const o=e(r,n);return vt[t]&&(vt[t].initialDispatch=o.dispatch),{...o,dispatch:function(){return!window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__&&o.dispatch(...arguments)}}},_t=t=>function(){for(var e=arguments.length,r=new Array(e),n=0;ne(t)),mt({...t,instanceId:e})(...arguments))}};window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__=function(){for(var t=arguments.length,e=new Array(t),r=0;r 12 | * @license MIT 13 | */ 14 | 15 | /*! 16 | * es6-template 17 | * 18 | * Copyright (c) 2015 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk) 19 | * Released under the MIT license. 20 | */ 21 | 22 | /*! 23 | * es6-template-regex 24 | * Copyright (c) 2014-2015, Jon Schlinkert, 2014 Lo-Dash 2.4.1 25 | * Licensed under the MIT License. 26 | * 27 | * Used to match ES template delimiters 28 | * See: http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components 29 | */ 30 | 31 | /*! 32 | * get-value 33 | * 34 | * Copyright (c) 2014-2015, Jon Schlinkert. 35 | * Licensed under the MIT License. 36 | */ 37 | 38 | /*! 39 | * is-extendable 40 | * 41 | * Copyright (c) 2015, Jon Schlinkert. 42 | * Licensed under the MIT License. 43 | */ 44 | 45 | /*! 46 | * is-plain-object 47 | * 48 | * Copyright (c) 2014-2017, Jon Schlinkert. 49 | * Released under the MIT License. 50 | */ 51 | 52 | /*! 53 | * isobject 54 | * 55 | * Copyright (c) 2014-2017, Jon Schlinkert. 56 | * Released under the MIT License. 57 | */ 58 | 59 | /*! 60 | * shallow-clone 61 | * 62 | * Copyright (c) 2015-present, Jon Schlinkert. 63 | * Released under the MIT License. 64 | */ 65 | 66 | /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ 67 | 68 | /** 69 | * @license React 70 | * react-dom.production.min.js 71 | * 72 | * Copyright (c) Facebook, Inc. and its affiliates. 73 | * 74 | * This source code is licensed under the MIT license found in the 75 | * LICENSE file in the root directory of this source tree. 76 | */ 77 | 78 | /** 79 | * @license React 80 | * react-is.production.min.js 81 | * 82 | * Copyright (c) Facebook, Inc. and its affiliates. 83 | * 84 | * This source code is licensed under the MIT license found in the 85 | * LICENSE file in the root directory of this source tree. 86 | */ 87 | 88 | /** 89 | * @license React 90 | * react.production.min.js 91 | * 92 | * Copyright (c) Facebook, Inc. and its affiliates. 93 | * 94 | * This source code is licensed under the MIT license found in the 95 | * LICENSE file in the root directory of this source tree. 96 | */ 97 | 98 | /** 99 | * @license React 100 | * scheduler.production.min.js 101 | * 102 | * Copyright (c) Facebook, Inc. and its affiliates. 103 | * 104 | * This source code is licensed under the MIT license found in the 105 | * LICENSE file in the root directory of this source tree. 106 | */ 107 | 108 | /** 109 | * @license React 110 | * use-sync-external-store-shim.production.min.js 111 | * 112 | * Copyright (c) Facebook, Inc. and its affiliates. 113 | * 114 | * This source code is licensed under the MIT license found in the 115 | * LICENSE file in the root directory of this source tree. 116 | */ 117 | 118 | /** 119 | * @license React 120 | * use-sync-external-store-shim/with-selector.production.min.js 121 | * 122 | * Copyright (c) Facebook, Inc. and its affiliates. 123 | * 124 | * This source code is licensed under the MIT license found in the 125 | * LICENSE file in the root directory of this source tree. 126 | */ 127 | 128 | /** @license React v16.13.1 129 | * react-is.production.min.js 130 | * 131 | * Copyright (c) Facebook, Inc. and its affiliates. 132 | * 133 | * This source code is licensed under the MIT license found in the 134 | * LICENSE file in the root directory of this source tree. 135 | */ 136 | 137 | /** @license URI.js v4.4.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */ 138 | -------------------------------------------------------------------------------- /injector/redux-devtools/remote.html: -------------------------------------------------------------------------------- 1 | RemoteDev
-------------------------------------------------------------------------------- /injector/redux-devtools/window.bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | localForage -- Offline Storage, Improved 3 | Version 1.10.0 4 | https://localforage.github.io/localForage 5 | (c) 2013-2017 Mozilla, Apache License 2.0 6 | */ 7 | 8 | /*! 9 | * The buffer module from node.js, for the browser. 10 | * 11 | * @author Feross Aboukhadijeh 12 | * @license MIT 13 | */ 14 | 15 | /*! 16 | * es6-template 17 | * 18 | * Copyright (c) 2015 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk) 19 | * Released under the MIT license. 20 | */ 21 | 22 | /*! 23 | * es6-template-regex 24 | * Copyright (c) 2014-2015, Jon Schlinkert, 2014 Lo-Dash 2.4.1 25 | * Licensed under the MIT License. 26 | * 27 | * Used to match ES template delimiters 28 | * See: http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components 29 | */ 30 | 31 | /*! 32 | * get-value 33 | * 34 | * Copyright (c) 2014-2015, Jon Schlinkert. 35 | * Licensed under the MIT License. 36 | */ 37 | 38 | /*! 39 | * is-extendable 40 | * 41 | * Copyright (c) 2015, Jon Schlinkert. 42 | * Licensed under the MIT License. 43 | */ 44 | 45 | /*! 46 | * is-plain-object 47 | * 48 | * Copyright (c) 2014-2017, Jon Schlinkert. 49 | * Released under the MIT License. 50 | */ 51 | 52 | /*! 53 | * isobject 54 | * 55 | * Copyright (c) 2014-2017, Jon Schlinkert. 56 | * Released under the MIT License. 57 | */ 58 | 59 | /*! 60 | * shallow-clone 61 | * 62 | * Copyright (c) 2015-present, Jon Schlinkert. 63 | * Released under the MIT License. 64 | */ 65 | 66 | /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ 67 | 68 | /** 69 | * @license React 70 | * react-dom.production.min.js 71 | * 72 | * Copyright (c) Facebook, Inc. and its affiliates. 73 | * 74 | * This source code is licensed under the MIT license found in the 75 | * LICENSE file in the root directory of this source tree. 76 | */ 77 | 78 | /** 79 | * @license React 80 | * react-is.production.min.js 81 | * 82 | * Copyright (c) Facebook, Inc. and its affiliates. 83 | * 84 | * This source code is licensed under the MIT license found in the 85 | * LICENSE file in the root directory of this source tree. 86 | */ 87 | 88 | /** 89 | * @license React 90 | * react.production.min.js 91 | * 92 | * Copyright (c) Facebook, Inc. and its affiliates. 93 | * 94 | * This source code is licensed under the MIT license found in the 95 | * LICENSE file in the root directory of this source tree. 96 | */ 97 | 98 | /** 99 | * @license React 100 | * scheduler.production.min.js 101 | * 102 | * Copyright (c) Facebook, Inc. and its affiliates. 103 | * 104 | * This source code is licensed under the MIT license found in the 105 | * LICENSE file in the root directory of this source tree. 106 | */ 107 | 108 | /** 109 | * @license React 110 | * use-sync-external-store-shim.production.min.js 111 | * 112 | * Copyright (c) Facebook, Inc. and its affiliates. 113 | * 114 | * This source code is licensed under the MIT license found in the 115 | * LICENSE file in the root directory of this source tree. 116 | */ 117 | 118 | /** 119 | * @license React 120 | * use-sync-external-store-shim/with-selector.production.min.js 121 | * 122 | * Copyright (c) Facebook, Inc. and its affiliates. 123 | * 124 | * This source code is licensed under the MIT license found in the 125 | * LICENSE file in the root directory of this source tree. 126 | */ 127 | 128 | /** @license React v16.13.1 129 | * react-is.production.min.js 130 | * 131 | * Copyright (c) Facebook, Inc. and its affiliates. 132 | * 133 | * This source code is licensed under the MIT license found in the 134 | * LICENSE file in the root directory of this source tree. 135 | */ 136 | 137 | /** @license URI.js v4.4.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */ 138 | -------------------------------------------------------------------------------- /injector/redux-devtools/window.html: -------------------------------------------------------------------------------- 1 | Redux DevTools
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neptune", 3 | "version": "1.0.1", 4 | "description": "A client modification for the Tidal music streaming app.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "build": "rollup --config rollup.config.js", 8 | "format": "prettier -wc ./src", 9 | "watch": "rollup --config rollup.config.js -w", 10 | "run": "npm run build && set NEPTUNE_DIST_PATH=%cd%\\dist&& %LOCALAPPDATA%\\TIDAL\\TIDAL.exe" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "MS-Pl", 15 | "dependencies": { 16 | "@cumjar/websmack": "^1.2.0", 17 | "@uwu/quartz": "^1.5.1", 18 | "idb-keyval": "^6.2.1", 19 | "quartz-plugin-url-import": "^1.0.1", 20 | "spitroast": "^2.1.6", 21 | "voby": "^0.57.3" 22 | }, 23 | "type": "module", 24 | "devDependencies": { 25 | "@rollup/plugin-node-resolve": "^15.1.0", 26 | "@rollup/plugin-terser": "^0.4.3", 27 | "prettier": "^3.0.0", 28 | "rollup": "^3.26.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@cumjar/websmack': 12 | specifier: ^1.2.0 13 | version: 1.2.0 14 | '@uwu/quartz': 15 | specifier: ^1.5.1 16 | version: 1.5.1 17 | idb-keyval: 18 | specifier: ^6.2.1 19 | version: 6.2.1 20 | quartz-plugin-url-import: 21 | specifier: ^1.0.1 22 | version: 1.0.1 23 | spitroast: 24 | specifier: ^2.1.6 25 | version: 2.1.6 26 | voby: 27 | specifier: ^0.57.3 28 | version: 0.57.4 29 | devDependencies: 30 | '@rollup/plugin-node-resolve': 31 | specifier: ^15.1.0 32 | version: 15.3.1(rollup@3.29.5) 33 | '@rollup/plugin-terser': 34 | specifier: ^0.4.3 35 | version: 0.4.4(rollup@3.29.5) 36 | prettier: 37 | specifier: ^3.0.0 38 | version: 3.5.3 39 | rollup: 40 | specifier: ^3.26.1 41 | version: 3.29.5 42 | 43 | packages: 44 | 45 | '@cumjar/websmack@1.2.0': 46 | resolution: {integrity: sha512-CtNQ3/EdeEAEx8bAPi3eKNCC41P88u40ja69TwpVerMZ93l0Kk7NpfvqY+YW50AvBN90FdmfLCRsJYzPIxQ90A==} 47 | 48 | '@jridgewell/gen-mapping@0.3.8': 49 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 50 | engines: {node: '>=6.0.0'} 51 | 52 | '@jridgewell/resolve-uri@3.1.2': 53 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 54 | engines: {node: '>=6.0.0'} 55 | 56 | '@jridgewell/set-array@1.2.1': 57 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 58 | engines: {node: '>=6.0.0'} 59 | 60 | '@jridgewell/source-map@0.3.6': 61 | resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} 62 | 63 | '@jridgewell/sourcemap-codec@1.5.0': 64 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 65 | 66 | '@jridgewell/trace-mapping@0.3.25': 67 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 68 | 69 | '@rollup/plugin-node-resolve@15.3.1': 70 | resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} 71 | engines: {node: '>=14.0.0'} 72 | peerDependencies: 73 | rollup: ^2.78.0||^3.0.0||^4.0.0 74 | peerDependenciesMeta: 75 | rollup: 76 | optional: true 77 | 78 | '@rollup/plugin-terser@0.4.4': 79 | resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} 80 | engines: {node: '>=14.0.0'} 81 | peerDependencies: 82 | rollup: ^2.0.0||^3.0.0||^4.0.0 83 | peerDependenciesMeta: 84 | rollup: 85 | optional: true 86 | 87 | '@rollup/pluginutils@5.1.4': 88 | resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} 89 | engines: {node: '>=14.0.0'} 90 | peerDependencies: 91 | rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 92 | peerDependenciesMeta: 93 | rollup: 94 | optional: true 95 | 96 | '@types/estree@1.0.6': 97 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 98 | 99 | '@types/resolve@1.20.2': 100 | resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} 101 | 102 | '@uwu/quartz@1.5.1': 103 | resolution: {integrity: sha512-rtlCwvfpEKaxt66AMMDPYq0Ep0HGwdzffxL7LnfRoO//OaDSYiWIXW07Bv+T6O7JpqhZuohQumKzG0hiY3IuSQ==} 104 | 105 | acorn@8.14.0: 106 | resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} 107 | engines: {node: '>=0.4.0'} 108 | hasBin: true 109 | 110 | buffer-from@1.1.2: 111 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 112 | 113 | commander@2.20.3: 114 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} 115 | 116 | deepmerge@4.3.1: 117 | resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} 118 | engines: {node: '>=0.10.0'} 119 | 120 | es-module-lexer@1.6.0: 121 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} 122 | 123 | estree-walker@2.0.2: 124 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 125 | 126 | fsevents@2.3.3: 127 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 128 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 129 | os: [darwin] 130 | 131 | function-bind@1.1.2: 132 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 133 | 134 | hasown@2.0.2: 135 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 136 | engines: {node: '>= 0.4'} 137 | 138 | htm@3.1.1: 139 | resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==} 140 | 141 | idb-keyval@6.2.1: 142 | resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} 143 | 144 | is-core-module@2.16.1: 145 | resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} 146 | engines: {node: '>= 0.4'} 147 | 148 | is-module@1.0.0: 149 | resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} 150 | 151 | oby@15.1.2: 152 | resolution: {integrity: sha512-6QD9iEoPzV+pMDdcg3RtFWhgDX8pS5hZouVHvgXGDy3Q9RxFfnI3CYv9i62keeuX+qk6iN2z5E9FD3q3OckZ6A==} 153 | 154 | path-parse@1.0.7: 155 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 156 | 157 | picomatch@4.0.2: 158 | resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} 159 | engines: {node: '>=12'} 160 | 161 | prettier@3.5.3: 162 | resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} 163 | engines: {node: '>=14'} 164 | hasBin: true 165 | 166 | quartz-plugin-url-import@1.0.1: 167 | resolution: {integrity: sha512-5vbl4DnZcnj8cBiNaTYElUW0E/Fok9JQQOrMWhv/V4lJPMol7yhpEzQbp4oBjY9KGMPaXW8AmCS4aJe+NhqNHQ==} 168 | 169 | randombytes@2.1.0: 170 | resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} 171 | 172 | resolve@1.22.10: 173 | resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} 174 | engines: {node: '>= 0.4'} 175 | hasBin: true 176 | 177 | rollup@3.29.5: 178 | resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} 179 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 180 | hasBin: true 181 | 182 | safe-buffer@5.2.1: 183 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 184 | 185 | serialize-javascript@6.0.2: 186 | resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} 187 | 188 | smob@1.5.0: 189 | resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} 190 | 191 | source-map-support@0.5.21: 192 | resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} 193 | 194 | source-map@0.6.1: 195 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 196 | engines: {node: '>=0.10.0'} 197 | 198 | spitroast@2.1.6: 199 | resolution: {integrity: sha512-xil2XeVh8WQoBlG8Lm8jT6ifqWGmSgTGq2clRcq2roycb0kg1h4fv5ydoiBId9kIQp4Y12PK/cjVzD8xpWXZvg==} 200 | 201 | supports-preserve-symlinks-flag@1.0.0: 202 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 203 | engines: {node: '>= 0.4'} 204 | 205 | terser@5.39.0: 206 | resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==} 207 | engines: {node: '>=10'} 208 | hasBin: true 209 | 210 | voby@0.57.4: 211 | resolution: {integrity: sha512-qbN5YJl4h/SP89CK5rIE+IZKvio1/UV51rYpSGGw5ifAhOF47Dh3OHHxC9FxfuDFFunSUrs8wrU5NcFu9bp92Q==} 212 | 213 | snapshots: 214 | 215 | '@cumjar/websmack@1.2.0': {} 216 | 217 | '@jridgewell/gen-mapping@0.3.8': 218 | dependencies: 219 | '@jridgewell/set-array': 1.2.1 220 | '@jridgewell/sourcemap-codec': 1.5.0 221 | '@jridgewell/trace-mapping': 0.3.25 222 | 223 | '@jridgewell/resolve-uri@3.1.2': {} 224 | 225 | '@jridgewell/set-array@1.2.1': {} 226 | 227 | '@jridgewell/source-map@0.3.6': 228 | dependencies: 229 | '@jridgewell/gen-mapping': 0.3.8 230 | '@jridgewell/trace-mapping': 0.3.25 231 | 232 | '@jridgewell/sourcemap-codec@1.5.0': {} 233 | 234 | '@jridgewell/trace-mapping@0.3.25': 235 | dependencies: 236 | '@jridgewell/resolve-uri': 3.1.2 237 | '@jridgewell/sourcemap-codec': 1.5.0 238 | 239 | '@rollup/plugin-node-resolve@15.3.1(rollup@3.29.5)': 240 | dependencies: 241 | '@rollup/pluginutils': 5.1.4(rollup@3.29.5) 242 | '@types/resolve': 1.20.2 243 | deepmerge: 4.3.1 244 | is-module: 1.0.0 245 | resolve: 1.22.10 246 | optionalDependencies: 247 | rollup: 3.29.5 248 | 249 | '@rollup/plugin-terser@0.4.4(rollup@3.29.5)': 250 | dependencies: 251 | serialize-javascript: 6.0.2 252 | smob: 1.5.0 253 | terser: 5.39.0 254 | optionalDependencies: 255 | rollup: 3.29.5 256 | 257 | '@rollup/pluginutils@5.1.4(rollup@3.29.5)': 258 | dependencies: 259 | '@types/estree': 1.0.6 260 | estree-walker: 2.0.2 261 | picomatch: 4.0.2 262 | optionalDependencies: 263 | rollup: 3.29.5 264 | 265 | '@types/estree@1.0.6': {} 266 | 267 | '@types/resolve@1.20.2': {} 268 | 269 | '@uwu/quartz@1.5.1': 270 | dependencies: 271 | es-module-lexer: 1.6.0 272 | 273 | acorn@8.14.0: {} 274 | 275 | buffer-from@1.1.2: {} 276 | 277 | commander@2.20.3: {} 278 | 279 | deepmerge@4.3.1: {} 280 | 281 | es-module-lexer@1.6.0: {} 282 | 283 | estree-walker@2.0.2: {} 284 | 285 | fsevents@2.3.3: 286 | optional: true 287 | 288 | function-bind@1.1.2: {} 289 | 290 | hasown@2.0.2: 291 | dependencies: 292 | function-bind: 1.1.2 293 | 294 | htm@3.1.1: {} 295 | 296 | idb-keyval@6.2.1: {} 297 | 298 | is-core-module@2.16.1: 299 | dependencies: 300 | hasown: 2.0.2 301 | 302 | is-module@1.0.0: {} 303 | 304 | oby@15.1.2: {} 305 | 306 | path-parse@1.0.7: {} 307 | 308 | picomatch@4.0.2: {} 309 | 310 | prettier@3.5.3: {} 311 | 312 | quartz-plugin-url-import@1.0.1: {} 313 | 314 | randombytes@2.1.0: 315 | dependencies: 316 | safe-buffer: 5.2.1 317 | 318 | resolve@1.22.10: 319 | dependencies: 320 | is-core-module: 2.16.1 321 | path-parse: 1.0.7 322 | supports-preserve-symlinks-flag: 1.0.0 323 | 324 | rollup@3.29.5: 325 | optionalDependencies: 326 | fsevents: 2.3.3 327 | 328 | safe-buffer@5.2.1: {} 329 | 330 | serialize-javascript@6.0.2: 331 | dependencies: 332 | randombytes: 2.1.0 333 | 334 | smob@1.5.0: {} 335 | 336 | source-map-support@0.5.21: 337 | dependencies: 338 | buffer-from: 1.1.2 339 | source-map: 0.6.1 340 | 341 | source-map@0.6.1: {} 342 | 343 | spitroast@2.1.6: {} 344 | 345 | supports-preserve-symlinks-flag@1.0.0: {} 346 | 347 | terser@5.39.0: 348 | dependencies: 349 | '@jridgewell/source-map': 0.3.6 350 | acorn: 8.14.0 351 | commander: 2.20.3 352 | source-map-support: 0.5.21 353 | 354 | voby@0.57.4: 355 | dependencies: 356 | htm: 3.1.1 357 | oby: 15.1.2 358 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import terser from "@rollup/plugin-terser"; 2 | import resolve from "@rollup/plugin-node-resolve"; 3 | 4 | export default { 5 | input: "src/index.js", 6 | output: { 7 | file: "dist/neptune.js", 8 | format: "iife", 9 | sourcemap: true, 10 | }, 11 | plugins: [ 12 | resolve({ browser: true }), 13 | terser(), 14 | ], 15 | }; -------------------------------------------------------------------------------- /src/api/hookContextMenu.js: -------------------------------------------------------------------------------- 1 | import intercept from "./intercept.js"; 2 | import { observe } from "./observe.js"; 3 | 4 | export default function hookContextMenu(menuType, name, handler) { 5 | return intercept("contextMenu/OPEN", ([payload]) => { 6 | // TODO: Change this to a switch statement. 7 | 8 | if (payload?.type != menuType) return; 9 | 10 | const unob = observe(`[data-type="contextmenu-item"]`, (elem) => { 11 | unob.now(); 12 | 13 | const contextMenuItem = elem.cloneNode(true); 14 | const contextMenuLabel = contextMenuItem.querySelector(`[class^="_actionTextInner_"]`); 15 | contextMenuLabel.innerText = name; 16 | 17 | const parentClasses = contextMenuLabel.parentElement.classList; 18 | 19 | contextMenuItem.innerHTML = ""; 20 | 21 | const contextMenuWrapper = document.createElement("div"); 22 | contextMenuWrapper.setAttribute("tabindex", "0"); 23 | contextMenuWrapper.classList.add(...parentClasses); 24 | contextMenuWrapper.appendChild(contextMenuLabel); 25 | 26 | contextMenuItem.addEventListener("keyup", (event) => { 27 | if (event.keyCode != 13) return; 28 | 29 | event.target.click(); 30 | }); 31 | 32 | contextMenuItem.appendChild(contextMenuWrapper); 33 | 34 | contextMenuItem.addEventListener("click", (event) => { 35 | handler(event); 36 | }); 37 | 38 | elem.parentElement.appendChild(contextMenuItem); 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /src/api/intercept.js: -------------------------------------------------------------------------------- 1 | export const interceptors = {}; 2 | 3 | export default function intercept(types, cb, once = false) { 4 | if (typeof types == "string") types = [types]; 5 | 6 | const uninterceptors = []; 7 | const unintercept = () => uninterceptors.forEach((u) => u()); 8 | 9 | for (let type of types) { 10 | if (!interceptors[type]) interceptors[type] = []; 11 | 12 | const handleIntercept = once 13 | ? (...args) => { 14 | unintercept(); 15 | 16 | return cb(...args); 17 | } 18 | : cb; 19 | interceptors[type].push(handleIntercept); 20 | 21 | uninterceptors.push(() => 22 | interceptors[type].splice(interceptors[type].indexOf(handleIntercept), 1), 23 | ); 24 | } 25 | 26 | return unintercept; 27 | } 28 | -------------------------------------------------------------------------------- /src/api/observe.js: -------------------------------------------------------------------------------- 1 | // code source: https://github.com/KaiHax/kaihax/blob/master/src/patcher.ts 2 | 3 | const observations = new Set(); 4 | const observer = new MutationObserver((records) => { 5 | // de-dupe to be sure 6 | const changedElems = new Set(); 7 | 8 | for (const record of records) { 9 | changedElems.add(record.target); 10 | 11 | for (const e of record.removedNodes) 12 | if (e instanceof HTMLElement || e instanceof SVGElement) changedElems.add(e); 13 | } 14 | 15 | for (const elem of changedElems) 16 | for (const obs of observations) { 17 | if (elem.matches(obs[0])) obs[1](elem); 18 | 19 | elem 20 | .querySelectorAll(obs[0]) 21 | .forEach( 22 | (e) => !obs[2] && (e instanceof HTMLElement || e instanceof SVGElement) && obs[1](e), 23 | ); 24 | } 25 | }); 26 | 27 | const startObserving = () => 28 | observer.observe(document.body, { 29 | subtree: true, 30 | childList: true, 31 | attributes: true, 32 | }); 33 | 34 | const stopObserving = () => observer.disconnect(); 35 | 36 | export function observe(sel, cb) { 37 | if (observations.size === 0) startObserving(); 38 | const entry = [sel, cb, false]; 39 | observations.add(entry); 40 | 41 | const unobs = () => { 42 | observations.delete(entry); 43 | if (observations.size === 0) stopObserving(); 44 | }; 45 | 46 | unobs.now = () => { 47 | entry[2] = true; 48 | unobs(); 49 | }; 50 | 51 | return unobs; 52 | } 53 | export function unobserve() { 54 | observations.clear(); 55 | stopObserving(); 56 | } 57 | -------------------------------------------------------------------------------- /src/api/plugins.js: -------------------------------------------------------------------------------- 1 | import { store } from "voby"; 2 | import { createPersistentObject, neptuneIdbStore, parseManifest } from "./utils.js"; 3 | import { del } from "idb-keyval"; 4 | import quartz from "@uwu/quartz"; 5 | import urlImport from "quartz-plugin-url-import"; 6 | import { actions } from "../handleExfiltrations.js"; 7 | import intercept from "./intercept.js"; 8 | 9 | export const [pluginStore, pluginStoreReady] = createPersistentObject("NEW_NEPTUNE_PLUGINS", true); 10 | export const enabled = store({}); 11 | 12 | export const getPluginById = (id) => pluginStore.find((p) => p.id == id); 13 | 14 | export async function disablePlugin(id) { 15 | getPluginById(id).enabled = false; 16 | const onUnload = enabled?.[id]?.onUnload; 17 | 18 | delete enabled[id]; 19 | 20 | try { 21 | await onUnload?.(); 22 | } catch (e) { 23 | console.error("Failed to completely clean up neptune plugin!\n", e); 24 | } 25 | } 26 | 27 | export function togglePlugin(id) { 28 | return getPluginById(id).enabled ? disablePlugin(id) : enablePlugin(id); 29 | } 30 | 31 | export async function enablePlugin(id) { 32 | const plugin = getPluginById(id); 33 | plugin.enabled = true; 34 | 35 | await runPlugin(plugin); 36 | } 37 | 38 | async function runPlugin(plugin) { 39 | try { 40 | const [persistentStorage, persistentStorageReady] = createPersistentObject( 41 | plugin.id + "_PERSISTENT_STORAGE", 42 | ); 43 | 44 | await persistentStorageReady; 45 | 46 | const unloadables = []; 47 | const pluginData = { 48 | id: plugin.id, 49 | manifest: plugin.manifest, 50 | storage: persistentStorage, 51 | addUnloadable(callback) { 52 | unloadables.push(callback); 53 | }, 54 | }; 55 | 56 | const { onUnload, Settings } = await quartz(plugin.code, { 57 | plugins: [ 58 | { 59 | resolve({ name }) { 60 | if (!name.startsWith("@neptune")) return; 61 | 62 | return `window${name 63 | .slice(1) 64 | .split("/") 65 | .map((i) => `[${JSON.stringify(i)}]`) 66 | .join("")}`; 67 | }, 68 | }, 69 | { 70 | resolve({ name, store, accessor }) { 71 | if (!name.startsWith("@plugin")) return; 72 | 73 | if (!store.plugin) store.plugin = { ...pluginData, default: pluginData }; 74 | 75 | return `${accessor}${name 76 | .slice(1) 77 | .split("/") 78 | .map((i) => `[${JSON.stringify(i)}]`) 79 | .join("")}`; 80 | }, 81 | }, 82 | urlImport(), 83 | ], 84 | }); 85 | 86 | enabled[plugin.id] = { 87 | onUnload: () => { 88 | onUnload?.(); 89 | for (const ul of unloadables) { 90 | ul(); 91 | } 92 | }, 93 | }; 94 | if (Settings) enabled[plugin.id].Settings = Settings; 95 | } catch (e) { 96 | await disablePlugin(plugin.id); 97 | 98 | console.error("Failed to load neptune plugin!\n", e); 99 | } 100 | } 101 | 102 | export async function installPlugin(id, code, manifest, enabled = true) { 103 | const plugin = { 104 | id, 105 | code, 106 | manifest, 107 | enabled, 108 | }; 109 | 110 | pluginStore.unshift(plugin); 111 | 112 | if (enabled) await runPlugin(plugin); 113 | } 114 | 115 | export async function removePlugin(id) { 116 | try { 117 | if (enabled[id]) await enabled[id].onUnload(); 118 | } catch { 119 | console.log("[neptune] failed to unload plugin upon removal"); 120 | } 121 | 122 | pluginStore.splice( 123 | pluginStore.findIndex((p) => p.id == id), 124 | 1, 125 | ); 126 | 127 | await del(id + "_PERSISTENT_STORAGE", neptuneIdbStore); 128 | } 129 | 130 | // This handles caching too! 131 | export async function fetchPluginFromURL(url) { 132 | let parsedURL = url; 133 | 134 | if (!parsedURL.endsWith("/")) parsedURL += "/"; 135 | 136 | const manifest = parseManifest( 137 | await (await fetch(parsedURL + "manifest.json", { cache: "no-store" })).json(), 138 | ); 139 | 140 | const plugin = getPluginById(url); 141 | let code = plugin?.code; 142 | 143 | if (plugin?.manifest?.hash != manifest.hash) 144 | code = await ( 145 | await fetch(parsedURL + (manifest.main ?? "index.js"), { cache: "no-store" }) 146 | ).text(); 147 | 148 | return [ 149 | code, 150 | { 151 | name: manifest.name, 152 | author: manifest.author, 153 | description: manifest.description, 154 | hash: manifest.hash, 155 | }, 156 | ]; 157 | } 158 | 159 | export async function reloadPlugin(plugin) { 160 | let pluginWasEnabled = plugin.enabled; 161 | 162 | if (pluginWasEnabled) disablePlugin(plugin.id); 163 | 164 | if (plugin.hasOwnProperty("update")) { 165 | try { 166 | const [code, manifest] = await fetchPluginFromURL(plugin.id); 167 | 168 | plugin.manifest = manifest; 169 | plugin.code = code; 170 | } catch {} 171 | } 172 | 173 | if (pluginWasEnabled) enablePlugin(plugin.id); 174 | } 175 | 176 | export async function installPluginFromURL(url, enabled = true) { 177 | if (getPluginById(url)) 178 | return actions.message.messageError({ message: "Plugin is already imported!" }); 179 | 180 | try { 181 | const [code, manifest] = await fetchPluginFromURL(url); 182 | 183 | const plugin = { 184 | id: url, 185 | code, 186 | manifest, 187 | enabled, 188 | update: true, 189 | }; 190 | 191 | pluginStore.unshift(plugin); 192 | 193 | if (enabled) runPlugin(plugin); 194 | } catch { 195 | actions.message.messageError({ message: "Failed to import neptune plugin!" }); 196 | } 197 | } 198 | 199 | // Cleanup plugins on reload 200 | addEventListener("beforeunload", () => { 201 | Object.values(enabled).forEach(p => p.onUnload()); 202 | }); 203 | 204 | // Load as early as we possibly can. 205 | intercept( 206 | "session/RECEIVED_COUNTRY_CODE", 207 | async () => { 208 | // We don't attempt to load plugins if CSP exists because loading every plugin will fail and automatically disable the plugin. 209 | if (document.querySelector(`[http-equiv="Content-Security-Policy"]`) || window.require) return; 210 | 211 | for (const plugin of pluginStore) { 212 | if (plugin.update) { 213 | try { 214 | const [code, manifest] = await fetchPluginFromURL(plugin.id); 215 | 216 | plugin.manifest = manifest; 217 | plugin.code = code; 218 | } catch { 219 | console.log("[neptune] failed to update plugin"); 220 | } 221 | } 222 | 223 | // We do not currently account for plugin updates, but this will be handled once 224 | // remote plugin installation is handled. 225 | if (plugin.enabled) runPlugin(plugin); 226 | } 227 | }, 228 | true, 229 | ); 230 | -------------------------------------------------------------------------------- /src/api/registerRoute.js: -------------------------------------------------------------------------------- 1 | import { ReactiveRoot } from "../ui/components.js"; 2 | import intercept from "./intercept.js"; 3 | import { observe } from "./observe.js"; 4 | 5 | const pageNotFoundSelector = `[class^="_pageNotFoundError_"]`; 6 | 7 | const replacePage = (page, component) => { 8 | page.style.display = "none"; 9 | 10 | const neptunePage = document.createElement("div"); 11 | neptunePage.className = "__NEPTUNE_PAGE"; 12 | 13 | page.insertAdjacentElement("afterend", neptunePage); 14 | neptunePage.appendChild(ReactiveRoot({ children: component })); 15 | }; 16 | 17 | intercept("router/NAVIGATED", () => { 18 | for (const page of document.getElementsByClassName("__NEPTUNE_PAGE")) 19 | page.parentElement.removeChild(page); 20 | }); 21 | 22 | export default function registerRoute(path, component) { 23 | return intercept("router/NAVIGATED", ([payload]) => { 24 | if (payload.search != `?neptuneRoute=${path}`) return; 25 | 26 | const pageNotFound = document.querySelector(pageNotFoundSelector); 27 | if (pageNotFound) return replacePage(pageNotFound, component); 28 | 29 | const unob = observe(pageNotFoundSelector, (page) => { 30 | unob.now(); 31 | replacePage(page, component); 32 | }); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/api/registerTab.js: -------------------------------------------------------------------------------- 1 | import registerRoute from "./registerRoute.js"; 2 | import intercept from "./intercept.js"; 3 | import { actions } from "../handleExfiltrations.js"; 4 | function makeInactive(tab) { 5 | tab.classList.remove(Array.from(tab.classList).find((c) => c.startsWith("_activeItem_"))); 6 | } 7 | 8 | const getTabs = () => document.querySelector(`.sidebarWrapper section[class^="_section_"]`); 9 | 10 | // Automatically set tab to unchecked. 11 | intercept("ROUTER_LOCATION_CHANGED", () => { 12 | for (const tab of document.getElementsByClassName("__NEPTUNE_TAB")) tab.style.color = ""; 13 | }); 14 | 15 | /* 16 | This entire function abuses their router's 404 handling to insert our own tabs. 17 | Because 404s count towards router history, our tabs function perfectly fine even when using the back arrows! 18 | */ 19 | export default function registerTab(name, path, component = () => {}) { 20 | const unobservers = []; 21 | 22 | const addTab = (tabs) => { 23 | const tab = tabs.children[0].cloneNode(true); 24 | makeInactive(tab); 25 | 26 | tab.querySelector(`[class^="_responsiveText_"]`).textContent = name; 27 | 28 | tab.classList.add("__NEPTUNE_TAB"); 29 | 30 | tab.addEventListener("click", (e) => { 31 | e.preventDefault(); 32 | 33 | actions.router.push({ 34 | pathname: `/neptune/${path}`, 35 | replace: true, 36 | }); 37 | }); 38 | 39 | const removeRouteHandler = registerRoute(path, () => { 40 | tab.style.color = "var(--wave-color-solid-accent-fill)"; 41 | 42 | return component; 43 | }); 44 | 45 | tabs.appendChild(tab); 46 | unobservers.push(removeRouteHandler, () => tabs.removeChild(tab)); 47 | }; 48 | 49 | const tabs = getTabs(); 50 | 51 | if (!tabs) { 52 | // Instead of DOM observing we just intercept the first action that gets called after the UI shows. Maybe we can DOM observe later? 53 | unobservers.push( 54 | intercept( 55 | "favorites/SET_FAVORITE_IDS", 56 | () => { 57 | addTab(getTabs()); 58 | }, 59 | true, 60 | ), 61 | ); 62 | } else addTab(tabs); 63 | 64 | return () => unobservers.forEach((u) => u()); 65 | } 66 | -------------------------------------------------------------------------------- /src/api/showModal.js: -------------------------------------------------------------------------------- 1 | import { ReactiveRoot } from "../ui/components.js"; 2 | import { actions } from "../handleExfiltrations.js"; 3 | import { observe } from "./observe.js"; 4 | 5 | export default function showModal(name, component) { 6 | actions.modal.showReleaseNotes(); 7 | const unob = observe(`[class^="_modalHeader_"]`, (header) => { 8 | unob.now(); 9 | 10 | header.getElementsByTagName("h4")[0].innerText = name; 11 | 12 | header.nextSibling.replaceChildren( 13 | ReactiveRoot({ 14 | children: component, 15 | }), 16 | ); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /src/api/themes.js: -------------------------------------------------------------------------------- 1 | import { store } from "voby"; 2 | import { appendStyle, createPersistentObject, parseManifest } from "./utils.js"; 3 | 4 | export const [themesStore, themesStoreReady] = createPersistentObject("NEPTUNE_THEMES", true); 5 | 6 | let updateThemeStyle = appendStyle(""); 7 | 8 | // Not sure why this didn't work previously? 9 | store.on(themesStore, reloadThemes) 10 | 11 | function reloadThemes() { 12 | updateThemeStyle(themesStore.filter(t => t.enabled).map((t) => `@import url("${t.url}")`).join(";")); 13 | } 14 | 15 | export function removeTheme(url) { 16 | themesStore.splice( 17 | themesStore.findIndex((t) => t.url == url), 18 | 1, 19 | ); 20 | } 21 | 22 | export function toggleTheme(url) { 23 | const theme = themesStore.find((t) => t.url == url); 24 | theme.enabled = !theme.enabled; 25 | } 26 | 27 | export async function importTheme(url, enabled = true) { 28 | let manifest; 29 | let text; 30 | 31 | try { 32 | text = await (await fetch(url)).text(); 33 | } catch { 34 | throw "Failed to fetch theme!"; 35 | } 36 | 37 | try { 38 | manifest = parseManifest(text); 39 | } catch (e) { 40 | manifest = { 41 | name: url.split("/").pop(), 42 | author: "Unknown", 43 | description: "No description provided.", 44 | } 45 | } 46 | 47 | themesStore.unshift({ 48 | name: manifest.name, 49 | author: manifest.author, 50 | description: manifest.description, 51 | enabled, 52 | url, 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /src/api/utils.js: -------------------------------------------------------------------------------- 1 | import { createStore as createIdbStore, set as idbSet, get as idbGet } from "idb-keyval"; 2 | import { store } from "voby"; 3 | 4 | export function appendStyle(style) { 5 | const styleTag = document.createElement("style"); 6 | styleTag.innerHTML = style; 7 | 8 | // Forgive me, for I have sinned. 9 | if (document.head) { 10 | document.head.appendChild(styleTag); 11 | } else { 12 | document.addEventListener("DOMContentLoaded", () => { 13 | document.head.appendChild(styleTag); 14 | }); 15 | } 16 | 17 | return (newStyle) => { 18 | if (newStyle == undefined) return document.head.removeChild(styleTag); 19 | 20 | styleTag.innerHTML = newStyle; 21 | }; 22 | } 23 | 24 | export const neptuneIdbStore = createIdbStore("__NEPTUNE_IDB_STORAGE", "__NEPTUNE_IDB_STORAGE"); 25 | 26 | // store.on appears to not work upon isArray being true. This makes me a very sad toonlink. 27 | export function createPersistentObject(id, isArray = false) { 28 | // This is fucking moronic. But fine, we'll do this dumb shit just for you. 29 | const persistentObject = store(isArray ? { value: [] } : {}); 30 | 31 | store.on(persistentObject, () => { 32 | idbSet(id, store.unwrap(persistentObject), neptuneIdbStore); 33 | }); 34 | 35 | return [ 36 | isArray ? persistentObject.value : persistentObject, 37 | new Promise((res) => 38 | idbGet(id, neptuneIdbStore).then((obj) => { 39 | store.reconcile(persistentObject, obj ?? (isArray ? { value: [] } : {})); 40 | res(); 41 | }), 42 | ), 43 | ]; 44 | } 45 | 46 | export const parseManifest = (manifest) => { 47 | try { 48 | if (typeof manifest == "string") 49 | manifest = JSON.parse(manifest.slice(manifest.indexOf("/*") + 2, manifest.indexOf("*/"))); 50 | } catch { 51 | throw "Failed to parse manifest!"; 52 | } 53 | 54 | if (!["name", "author", "description"].every((i) => typeof manifest[i] === "string")) 55 | throw "Manifest doesn't contain required properties!"; 56 | 57 | return manifest; 58 | }; 59 | 60 | export const getMediaURLFromID = (id, path = "/1280x1280.jpg") => 61 | "https://resources.tidal.com/images/" + id.split("-").join("/") + path; 62 | 63 | export function convertHexToRGB(h) { 64 | let r = 0; 65 | let g = 0; 66 | let b = 0; 67 | 68 | // 3 digits 69 | if (h.length === 4) { 70 | r = Number("0x" + h[1] + h[1]); 71 | g = Number("0x" + h[2] + h[2]); 72 | b = Number("0x" + h[3] + h[3]); 73 | 74 | // 6 digits 75 | } else if (h.length === 7) { 76 | r = Number("0x" + h[1] + h[2]); 77 | g = Number("0x" + h[3] + h[4]); 78 | b = Number("0x" + h[5] + h[6]); 79 | } 80 | 81 | return `${r}, ${g}, ${b}`; 82 | } 83 | 84 | // this impl can be changed when things (probably) break again, lol 85 | export function pushVirtualRoute(route) { 86 | neptune.actions.router.push({ 87 | pathname: `/not-found`, 88 | search: `?neptuneRoute=${route}`, 89 | replace: true, 90 | }); 91 | } 92 | -------------------------------------------------------------------------------- /src/handleExfiltrations.js: -------------------------------------------------------------------------------- 1 | import windowObject from "./windowObject.js"; 2 | import * as patcher from "spitroast"; 3 | import { interceptors } from "./api/intercept.js"; 4 | import loadStyles from "./styles.js"; 5 | import quartz from "@uwu/quartz"; 6 | 7 | // abandon all hope, ye who enter here 8 | 9 | // Main store exfiltration 10 | export let store; 11 | 12 | // Built action handlers 13 | export const actions = {}; 14 | windowObject.actions = actions; 15 | 16 | // Interceptors 17 | windowObject.interceptors = interceptors; 18 | 19 | // Module cache 20 | const moduleCache = {}; 21 | windowObject.moduleCache = moduleCache; 22 | 23 | function resolvePath(basePath, relativePath) { 24 | // If the relative path starts with '/', it's already an absolute path 25 | if (relativePath.startsWith("/")) { 26 | return relativePath; // Return it as is 27 | } 28 | 29 | // Remove the base name (last part of the base path) to get the base directory 30 | const baseDir = basePath.replace(/\/[^/]+$/, ""); 31 | 32 | // Resolve the relative path against the base directory 33 | return resolvePathFromBase(baseDir, relativePath); 34 | } 35 | 36 | function resolvePathFromBase(baseDir, relativePath) { 37 | const stack = []; 38 | 39 | // Split the base directory into parts 40 | const baseParts = baseDir.split("/").filter(Boolean); 41 | stack.push(...baseParts); 42 | 43 | // Split the relative path into parts 44 | const parts = relativePath.split("/"); 45 | 46 | for (let part of parts) { 47 | if (part === "" || part === ".") { 48 | // Ignore empty or current directory ('.') 49 | continue; 50 | } else if (part === "..") { 51 | // Go up one directory if possible 52 | if (stack.length > 0) { 53 | stack.pop(); 54 | } 55 | } else { 56 | // Otherwise, it's a valid directory/file, add it to the stack 57 | stack.push(part); 58 | } 59 | } 60 | 61 | // Join the parts to form the resolved path, ensuring it starts with '/' 62 | return "/" + stack.join("/"); 63 | } 64 | 65 | // promises bubble 66 | const fetchScript = async (path) => (await fetch(path)).text(); 67 | 68 | const handleResolution = async ({ name, moduleId, config }) => { 69 | const path = resolvePath(moduleId, name); 70 | if (moduleCache[path]) return moduleCache[path]; 71 | 72 | const data = await fetchScript(path); 73 | 74 | moduleCache[path] = await quartz(data, config, path); 75 | return moduleCache[path]; 76 | }; 77 | 78 | function findStoreFunctionName(bundleCode) { 79 | const errorMessageIndex = bundleCode.indexOf('Error("No global store set")'); 80 | 81 | if (errorMessageIndex === -1) { 82 | return null; 83 | } 84 | 85 | for (let charIdx = errorMessageIndex - 1; charIdx > 0; charIdx--) { 86 | if (bundleCode[charIdx] + bundleCode[charIdx + 1] != "()") continue; 87 | 88 | let strBuf = []; 89 | for (let nameIdx = charIdx - 1; nameIdx > 0; nameIdx--) { 90 | const char = bundleCode[nameIdx]; 91 | 92 | if (char == " ") return strBuf.reverse().join(""); 93 | strBuf.push(char); 94 | } 95 | } 96 | 97 | return null; 98 | } 99 | 100 | function findPrepareActionNameAndIdx(bundleCode) { 101 | const searchIdx = bundleCode.indexOf(`.payload,..."meta"in `); 102 | 103 | if (searchIdx === -1) { 104 | return null; 105 | } 106 | 107 | const sliced = bundleCode.slice(0, searchIdx); 108 | const funcIndex = sliced.lastIndexOf("{function"); 109 | 110 | let strBuf = []; 111 | for (let nameIdx = bundleCode.slice(0, funcIndex).lastIndexOf("(") - 1; nameIdx > 0; nameIdx--) { 112 | const char = bundleCode[nameIdx]; 113 | 114 | if (char == " ") 115 | return { 116 | name: strBuf.reverse().join(""), 117 | idx: nameIdx + 1, 118 | }; 119 | 120 | strBuf.push(char); 121 | } 122 | } 123 | 124 | setTimeout(async () => { 125 | for (const script of document.querySelectorAll(`script[type="neptune/quartz"]`)) { 126 | const scriptPath = new URL(script.src).pathname; 127 | const scriptContent = await fetchScript(scriptPath); 128 | 129 | // For those reading this code: 130 | // 1. I'm sorry. 131 | // 2. quartz is a runtime-based ESM import transformation tool. 132 | // The reason I'm using it here is because I can have it take over the browser's native ESM import 133 | // functionality so I can hook the JS that runs on the page to do various things. 134 | // I have noticed that it seems slower than native browser imports, but it was the easiest solution for me personally. 135 | moduleCache[scriptPath] = await quartz( 136 | scriptContent, 137 | { 138 | plugins: [ 139 | { 140 | dynamicResolve: handleResolution, 141 | async resolve({ name, moduleId, config, accessor, store }) { 142 | const exports = await handleResolution({ name, moduleId, config }); 143 | 144 | store.exports = exports; 145 | 146 | return `${accessor}.exports`; 147 | }, 148 | 149 | transform({ code }) { 150 | const getStoreFuncName = findStoreFunctionName(code); 151 | 152 | if (getStoreFuncName) code += `; export { ${getStoreFuncName} as hijackedGetStore };`; 153 | const actionData = findPrepareActionNameAndIdx(code); 154 | 155 | if (actionData) { 156 | const { name: prepareActionName, idx: prepareActionIdx } = actionData; 157 | 158 | // rename function declaration 159 | code = 160 | code.slice(0, prepareActionIdx) + "$$$NEPTUNE_" + code.slice(prepareActionIdx); 161 | 162 | code = 163 | code.slice(0, prepareActionIdx - 9) + 164 | ` 165 | const $$$NEPTUNE_PATCHED_TEMPOBJ = { patchedFunc: $$$NEPTUNE_${prepareActionName} }; 166 | neptune.patcher.after("patchedFunc", $$$NEPTUNE_PATCHED_TEMPOBJ, ([type], resp) => { 167 | if (!neptune.interceptors[type]) neptune.interceptors[type] = []; 168 | const [parent, child] = type 169 | .split("/") 170 | .map((n) => 171 | n.toUpperCase() == n 172 | ? n 173 | .toLowerCase() 174 | .replace(/_([a-z])/g, (_, i) => i.toUpperCase()) 175 | : n, 176 | ); 177 | 178 | const builtAction = (...args) => { 179 | const act = resp(...args); 180 | 181 | if (!(act.__proto__.toString() == "[object Promise]")) 182 | return neptune.store.dispatch(act); 183 | 184 | return new Promise(async (res, rej) => { 185 | try { 186 | res(neptune.store.dispatch(await act)); 187 | } catch (e) { 188 | rej(e); 189 | } 190 | }); 191 | }; 192 | 193 | if (child) { 194 | if (!neptune.actions[parent]) neptune.actions[parent] = {}; 195 | 196 | neptune.actions[parent][child] = builtAction; 197 | } else { 198 | neptune.actions[parent] = builtAction; 199 | } 200 | 201 | return new Proxy(resp, { 202 | apply(orig, ctxt, args) { 203 | let shouldDispatch = true; 204 | 205 | for (let interceptor of neptune.interceptors[type]) { 206 | try { 207 | const resp = interceptor(args); 208 | 209 | if (resp === true) shouldDispatch = false; 210 | } catch (e) { 211 | console.error("Failed to run interceptor!\\n", e); 212 | } 213 | } 214 | 215 | return shouldDispatch 216 | ? orig.apply(ctxt, args) 217 | : { type: "NOOP" }; 218 | }, 219 | }); 220 | }); 221 | const ${prepareActionName} = $$$NEPTUNE_PATCHED_TEMPOBJ.patchedFunc; 222 | `.trim() + 223 | code.slice(prepareActionIdx - 9); 224 | } 225 | 226 | return code; 227 | }, 228 | }, 229 | ], 230 | }, 231 | scriptPath, 232 | ); 233 | 234 | for (const module of Object.values(moduleCache)) { 235 | const { hijackedGetStore } = module; 236 | 237 | if (!hijackedGetStore) continue; 238 | 239 | store = hijackedGetStore(); 240 | windowObject.store = store; 241 | } 242 | 243 | loadStyles(); 244 | } 245 | }, 0); 246 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import "./ui/settings.js"; 2 | import "./handleExfiltrations.js"; 3 | import windowObject from "./windowObject.js"; 4 | 5 | // Updater 3 6 | if (NeptuneNative.VITE_ACTIVE != true) { 7 | (async () => { 8 | const fsScope = NeptuneNative.createEvalScope(` 9 | const fs = require("fs"); 10 | const path = require("path"); 11 | 12 | var neptuneExports = { 13 | updateFile(name, contents) { 14 | fs.writeFileSync(path.join(process.resourcesPath, "app", name), contents); 15 | } 16 | } 17 | `); 18 | 19 | const updateFile = NeptuneNative.getNativeValue(fsScope, "updateFile"); 20 | 21 | const indexFetch = await fetch( 22 | "https://raw.githubusercontent.com/uwu/neptune/master/injector/index.js", 23 | ); 24 | const preloadFetch = await fetch( 25 | "https://raw.githubusercontent.com/uwu/neptune/master/injector/preload.js", 26 | ); 27 | 28 | if (!(indexFetch.ok || preloadFetch.ok)) return; 29 | 30 | updateFile("index.js", await indexFetch.text()) 31 | updateFile("preload.js", await preloadFetch.text()) 32 | 33 | alert("neptune has been updated. Please restart TIDAL."); 34 | })() 35 | } 36 | 37 | // Restore the console 38 | for (let key in console) { 39 | const orig = console[key]; 40 | 41 | Object.defineProperty(console, key, { 42 | set() { 43 | return true; 44 | }, 45 | get() { 46 | return orig; 47 | }, 48 | }); 49 | } 50 | 51 | // Force properties to be writable for patching 52 | const originalDefineProperty = Object.defineProperty; 53 | 54 | Object.defineProperty = function (...args) { 55 | args[2].configurable = true; 56 | 57 | try { 58 | return originalDefineProperty.apply(this, args); 59 | } catch {} 60 | }; 61 | 62 | Object.freeze = (arg) => arg; 63 | 64 | // If the app fails to load for any reason we simply reload the page. 65 | // setTimeout(() => { 66 | // if (!windowObject.store) window.location.reload(); 67 | // }, 7000); 68 | 69 | window.neptune = windowObject; 70 | -------------------------------------------------------------------------------- /src/styles.js: -------------------------------------------------------------------------------- 1 | import { appendStyle } from "./api/utils.js"; 2 | 3 | export default function loadStyles() { 4 | appendStyle(` 5 | .__NEPTUNE_PAGE { 6 | margin: 0px auto; 7 | max-width: 870px; 8 | width: calc(100% - var(--defaultPadding)*2); 9 | } 10 | 11 | .neptune-card { 12 | background-color: var(--wave-color-solid-base-brighter, var(--wave-color-opacity-contrast-fill-ultra-thick)); 13 | border: 1px solid var(--wave-color-opacity-contrast-fill-ultra-thin); 14 | border-radius: 12px; 15 | min-height: 70px; 16 | display: flex; 17 | flex-direction: column; 18 | justify-content: center; 19 | } 20 | 21 | .neptune-card-content { 22 | display: flex; 23 | justify-content: space-between; 24 | padding: 0 15px 0px 15px; 25 | } 26 | 27 | .neptune-active-tab { 28 | color: var(--wave-color-solid-accent-fill); 29 | box-shadow: 0px 2px var(--wave-color-solid-accent-fill); 30 | } 31 | 32 | .neptune-card-title { 33 | font-weight: 600; 34 | font-size: medium; 35 | } 36 | 37 | .neptune-switch-checkbox { 38 | cursor: pointer; 39 | margin-left: 0.8rem; 40 | opacity: 0; 41 | position: absolute; 42 | z-index: 1; 43 | } 44 | 45 | .neptune-switch { 46 | cursor: pointer; 47 | background-color: var(--wave-color-opacity-contrast-fill-ultra-thin); 48 | border-radius: 28px; 49 | display: block; 50 | height: 28px; 51 | margin-left: 0.8rem; 52 | min-width: 48px; 53 | position: relative; 54 | transition: background-color .25s ease,border-color .25s ease; 55 | -webkit-user-select: none; 56 | user-select: none; 57 | } 58 | 59 | .neptune-switch:after { 60 | background-color: var(--wave-color-solid-contrast-fill); 61 | border-radius: 50%; 62 | box-shadow: 0 1px 2px rgb(0 0 0 / 30%); 63 | content: ""; 64 | display: block; 65 | height: 24px; 66 | left: 2px; 67 | position: absolute; 68 | top: 2px; 69 | transition: all .25s ease; 70 | width: 24px; 71 | } 72 | 73 | .neptune-switch-checkbox:checked + .neptune-switch:after { 74 | left: calc(100% - 0.1em); 75 | transform: translateX(-100%); 76 | } 77 | 78 | .neptune-switch-checkbox:checked + .neptune-switch { 79 | background-color: var(--wave-color-solid-accent-fill); 80 | border-color: var(--wave-color-solid-accent-fill); 81 | } 82 | 83 | .neptune-text-input { 84 | background: transparent; 85 | border: 0; 86 | border-bottom: 1px solid var(--wave-color-opacity-contrast-fill-ultra-thin); 87 | color: var(--wave-color-opacity-contrast-fill-thick); 88 | display: block; 89 | width: 100%; 90 | height: 40px; 91 | box-sizing: border-box; 92 | } 93 | 94 | .neptune-text-input:not([readonly]):focus { 95 | border-bottom-color: var(--wave-color-solid-accent-fill); 96 | color: var(--wave-color-solid-contrast-fill); 97 | } 98 | 99 | .neptune-button { 100 | align-items: center; 101 | background-color: var(--wave-color-solid-contrast-fill); 102 | border-radius: 12px; 103 | display: inline-flex; 104 | height: 48px; 105 | justify-content: center; 106 | line-height: 49px; 107 | min-height: 48px; 108 | min-width: 0; 109 | overflow: hidden; 110 | padding: 0 24px; 111 | text-align: center; 112 | text-overflow: ellipsis; 113 | transition: background .35s ease; 114 | white-space: nowrap; 115 | } 116 | 117 | .neptune-round-button { 118 | color: var(--wave-color-opacity-contrast-fill-thick); 119 | background-color: var(--wave-color-solid-base-brighter); 120 | border: 1px solid var(--wave-color-opacity-contrast-fill-ultra-thin); 121 | transition: background-color 0.35s ease 0s; 122 | transition-property: background-color; 123 | transition-duration: 0.35s; 124 | transition-timing-function: ease; 125 | transition-delay: 0s; 126 | position: relative; 127 | border-radius: 100%; 128 | min-height: 40px; 129 | min-width: 40px; 130 | display: grid; 131 | place-items: center; 132 | } 133 | 134 | .neptune-round-button:active { 135 | background-color: rgba(255, 255, 255, 0.2); 136 | } 137 | `); 138 | } 139 | -------------------------------------------------------------------------------- /src/ui/components.js: -------------------------------------------------------------------------------- 1 | import { html, render, $, isObservable } from "voby"; 2 | 3 | customElements.define( 4 | "neptune-reactive-root", 5 | class extends HTMLElement { 6 | constructor() { 7 | super(); 8 | this.c = () => {}; 9 | } 10 | 11 | connectedCallback() { 12 | this.style.display = "contents"; 13 | this.dispose?.(); 14 | this.dispose = render(html`${this.c()}`, this); 15 | } 16 | 17 | disconnectedCallback() { 18 | this.dispose?.(); 19 | } 20 | }, 21 | ); 22 | 23 | export function ReactiveRoot({ children }) { 24 | const root = html``(); 25 | root.c = () => children; 26 | 27 | return root; 28 | } 29 | 30 | export function Switch(props) { 31 | let checked = props.checked ?? false; 32 | 33 | if (!props.onClick) { 34 | checked = $(!!checked); 35 | props.onClick = () => checked((c) => !c); 36 | } 37 | 38 | return html` 39 |
40 | 41 | 42 |
43 | `; 44 | } 45 | 46 | export function TextInput({ placeholder = "", type = "text", value = "", onEnter = () => {} }) { 47 | if (!isObservable(value)) value = $(value); 48 | 49 | return html` 50 | { 54 | if (e.key != "Enter") return; 55 | onEnter(e); 56 | }} 57 | onInput=${(e) => value(e.target.value)} 58 | placeholder=${placeholder} 59 | type=${type} /> 60 | `; 61 | } 62 | 63 | export function Button({ onClick = () => {}, children }) { 64 | return html` `; 65 | } 66 | 67 | export function ReloadIcon() { 68 | return html` 75 | 79 | `; 80 | } 81 | 82 | export function TrashIcon() { 83 | return html` 90 | 94 | `; 95 | } 96 | -------------------------------------------------------------------------------- /src/ui/pluginsTab.js: -------------------------------------------------------------------------------- 1 | import { Switch, TextInput, ReloadIcon, TrashIcon } from "./components.js"; 2 | import { actions } from "../handleExfiltrations.js"; 3 | import { 4 | pluginStore, 5 | togglePlugin, 6 | removePlugin, 7 | installPluginFromURL, 8 | installPlugin, 9 | reloadPlugin, 10 | enabled as enabledPlugins, 11 | } from "../api/plugins.js"; 12 | import showModal from "../api/showModal.js"; 13 | import { parseManifest } from "../api/utils.js"; 14 | import { $, For, If, html } from "voby"; 15 | 16 | async function importLocalPlugin() { 17 | const [fileHandle] = await showOpenFilePicker({ 18 | types: [{ description: "A neptune plugin file", accept: { "text/javascript": [".js"] } }], 19 | }); 20 | 21 | const file = await fileHandle.getFile(); 22 | const content = await file.text(); 23 | 24 | let manifest; 25 | try { 26 | manifest = parseManifest(content); 27 | 28 | if (!["name", "author", "description"].every((m) => typeof manifest[m] === "string")) 29 | throw "invalid manifest"; 30 | } catch (e) { 31 | return actions.message.messageError({ message: e }); 32 | } 33 | 34 | try { 35 | await installPlugin( 36 | manifest.name + "-" + (Math.random() + 1).toString(36).substring(7), 37 | content, 38 | { 39 | name: manifest.name, 40 | author: manifest.author, 41 | description: manifest.description, 42 | }, 43 | ); 44 | } catch { 45 | actions.message.messageError({ message: "Failed to install plugin!" }); 46 | } 47 | } 48 | 49 | export function PluginTab() { 50 | const pluginToImport = $(""); 51 | 52 | return html` 53 |
54 |
55 | 56 | <${TextInput} value=${pluginToImport} onEnter=${() => { 57 | installPluginFromURL(pluginToImport()); 58 | pluginToImport(""); 59 | }} placeholder="Import plugin from URL" /> 60 | 61 | 64 |
65 | <${For} values=${() => pluginStore}> 66 | ${(plugin) => html`<${PluginCard} plugin=${plugin} />`} 67 | 68 |
`; 69 | } 70 | 71 | function PluginCard({ plugin }) { 72 | return html`
73 |
74 |
75 |
76 | 79 |
80 |
81 | ${plugin.manifest.name} 82 | by 83 | ${plugin.manifest.author} 84 |
85 |
${plugin.manifest.description}
86 |
87 |
88 |
89 |
90 |
91 | <${If} when=${() => enabledPlugins?.[plugin.id]?.Settings}> 92 | 99 | 100 | 104 |
105 | <${Switch} 106 | checked=${() => plugin.enabled} 107 | onClick=${() => { 108 | togglePlugin(plugin.id); 109 | }} /> 110 |
111 |
112 |
`; 113 | } 114 | -------------------------------------------------------------------------------- /src/ui/settings.js: -------------------------------------------------------------------------------- 1 | import { $, html } from "voby"; 2 | import { PluginTab } from "./pluginsTab.js"; 3 | import { ThemesTab } from "./themesTab.js"; 4 | import registerRoute from "../api/registerRoute.js"; 5 | import hookContextMenu from "../api/hookContextMenu.js"; 6 | import { pushVirtualRoute } from "../api/utils.js"; 7 | 8 | let selectedTab = $(0); 9 | const tabs = [ 10 | { 11 | name: "Plugins", 12 | component: PluginTab, 13 | }, 14 | { 15 | name: "Themes", 16 | component: ThemesTab, 17 | }, 18 | { 19 | name: "Addon Store", 20 | component: () => html`[WIP]`, 21 | }, 22 | ]; 23 | 24 | function TabButton({ className = "", onClick = () => {}, children }) { 25 | return html``; 31 | } 32 | 33 | registerRoute( 34 | "settings", 35 | html`
36 |
37 |
38 | ${tabs.map( 39 | (tab, idx) => 40 | html`<${TabButton} onClick=${() => selectedTab(idx)} className=${() => 41 | idx == selectedTab() ? "neptune-active-tab" : ""}>${tab.name}`, 42 | )} 43 |
44 |
${() => tabs[selectedTab()].component}
45 |
46 |
`, 47 | ); 48 | 49 | hookContextMenu("USER_PROFILE", "neptune settings", () => pushVirtualRoute("settings")); 50 | -------------------------------------------------------------------------------- /src/ui/themesTab.js: -------------------------------------------------------------------------------- 1 | import { $, For, html, useCleanup } from "voby"; 2 | import { TextInput, Switch, TrashIcon } from "./components"; 3 | import { themesStore, toggleTheme, removeTheme, importTheme } from "../api/themes"; 4 | import { actions } from "../handleExfiltrations.js"; 5 | import { appendStyle } from "../api/utils.js"; 6 | 7 | export function ThemesTab() { 8 | const themeToImport = $(""); 9 | 10 | return html` 11 |
12 | <${TextInput} onEnter=${() => { 13 | importTheme(themeToImport(), false).catch((e) => actions.message.messageError({ message: e })); 14 | themeToImport(""); 15 | }} value=${themeToImport} placeholder="Import theme from URL" /> 16 | <${For} values=${() => themesStore}> 17 | ${(theme) => html`<${ThemeCard} theme=${theme} />`} 18 | 19 |
`; 20 | } 21 | 22 | function ThemeCard({ theme}) { 23 | // TODO: i have no fucking clue why this needs a try catch to not implode in on itself lmfao 24 | useCleanup(() => { try { removeStyle() } catch {} }); 25 | 26 | let removeStyle = () => {}; 27 | 28 | return html`
29 |
30 |
31 |
32 | ${theme.name} 33 | by 34 | ${theme.author} 35 |
36 |
${theme.description}
37 |
38 |
39 | 42 |
{ 43 | try { 44 | removeStyle(); 45 | } catch {} 46 | removeStyle = appendStyle(`@import url("${theme.url}");`) 47 | }} 48 | 49 | onmouseout=${() => removeStyle()} 50 | > 51 | <${Switch} checked=${() => theme.enabled} onClick=${() => toggleTheme(theme.url)} /> 52 |
53 |
54 |
55 |
` 56 | } -------------------------------------------------------------------------------- /src/windowObject.js: -------------------------------------------------------------------------------- 1 | import * as voby from "voby"; 2 | import * as patcher from "spitroast"; 3 | import * as utils from "./api/utils.js"; 4 | import * as plugins from "./api/plugins.js"; 5 | import * as themes from "./api/themes.js"; 6 | import * as components from "./ui/components.js"; 7 | import intercept from "./api/intercept.js"; 8 | import { observe } from "./api/observe.js"; 9 | import registerTab from "./api/registerTab.js"; 10 | import registerRoute from "./api/registerRoute.js"; 11 | import hookContextMenu from "./api/hookContextMenu.js"; 12 | import showModal from "./api/showModal.js"; 13 | // TODO: AWFUL VOMIT VOMIT KILL MURDER DIE KILL KILL DIE MURDER VOMIT 14 | import { store } from "./handleExfiltrations.js"; 15 | 16 | import quartz from "@uwu/quartz"; 17 | 18 | let currentMediaItem = {}; 19 | 20 | try { 21 | const vibrantColorStyle = utils.appendStyle(""); 22 | 23 | intercept("playbackControls/MEDIA_PRODUCT_TRANSITION", ([{ mediaProduct }]) => { 24 | Object.assign( 25 | currentMediaItem, 26 | store.getState().content.mediaItems[mediaProduct.productId], 27 | ); 28 | const vibrantColor = currentMediaItem?.item?.album?.vibrantColor; 29 | 30 | if (!vibrantColor) return; 31 | 32 | vibrantColorStyle(`:root{--track-vibrant-color:${vibrantColor};--track-vibrant-color-rgb:${utils.convertHexToRGB(vibrantColor)}}`); 33 | }); 34 | } catch {} 35 | 36 | export default { 37 | patcher, 38 | utils, 39 | intercept, 40 | observe, 41 | registerTab, 42 | registerRoute, 43 | hookContextMenu, 44 | showModal, 45 | voby, 46 | plugins, 47 | themes, 48 | components, 49 | currentMediaItem, 50 | quartz 51 | }; 52 | -------------------------------------------------------------------------------- /types/LICENSE: -------------------------------------------------------------------------------- 1 | Microsoft Public License (Ms-PL) 2 | 3 | This license governs use of the accompanying software. If you use the 4 | software, you accept this license. If you do not accept the license, 5 | do not use the software. 6 | 7 | 1. Definitions 8 | 9 | The terms "reproduce," "reproduction," "derivative works," and 10 | "distribution" have the same meaning here as under U.S. copyright 11 | law. 12 | 13 | A "contribution" is the original software, or any additions or 14 | changes to the software. 15 | 16 | A "contributor" is any person that distributes its contribution 17 | under this license. 18 | 19 | "Licensed patents" are a contributor's patent claims that read 20 | directly on its contribution. 21 | 22 | 2. Grant of Rights 23 | 24 | (A) Copyright Grant- Subject to the terms of this license, 25 | including the license conditions and limitations in section 3, 26 | each contributor grants you a non-exclusive, worldwide, 27 | royalty-free copyright license to reproduce its contribution, 28 | prepare derivative works of its contribution, and distribute its 29 | contribution or any derivative works that you create. 30 | 31 | (B) Patent Grant- Subject to the terms of this license, including 32 | the license conditions and limitations in section 3, each 33 | contributor grants you a non-exclusive, worldwide, royalty-free 34 | license under its licensed patents to make, have made, use, sell, 35 | offer for sale, import, and/or otherwise dispose of its 36 | contribution in the software or derivative works of the 37 | contribution in the software. 38 | 39 | 3. Conditions and Limitations 40 | 41 | (A) No Trademark License- This license does not grant you rights 42 | to use any contributors' name, logo, or trademarks. 43 | 44 | (B) If you bring a patent claim against any contributor over 45 | patents that you claim are infringed by the software, your patent 46 | license from such contributor to the software ends automatically. 47 | 48 | (C) If you distribute any portion of the software, you must retain 49 | all copyright, patent, trademark, and attribution notices that are 50 | present in the software. 51 | 52 | (D) If you distribute any portion of the software in source code 53 | form, you may do so only under this license by including a 54 | complete copy of this license with your distribution. If you 55 | distribute any portion of the software in compiled or object code 56 | form, you may only do so under a license that complies with this 57 | license. 58 | 59 | (E) The software is licensed "as-is." You bear the risk of using 60 | it. The contributors give no express warranties, guarantees, or 61 | conditions. You may have additional consumer rights under your 62 | local laws which this license cannot change. To the extent 63 | permitted under your local laws, the contributors exclude the 64 | implied warranties of merchantability, fitness for a particular 65 | purpose and non-infringement. 66 | -------------------------------------------------------------------------------- /types/api/hookContextMenu.d.ts: -------------------------------------------------------------------------------- 1 | import type { intercept } from "./intercept"; 2 | 3 | export function hookContextMenu( 4 | menuType: string, 5 | name: string, 6 | handler: (e: MouseEvent) => any, 7 | ): ReturnType; 8 | -------------------------------------------------------------------------------- /types/api/intercept.d.ts: -------------------------------------------------------------------------------- 1 | import type { ActionTypes } from "../tidal"; 2 | export type UninterceptFunction = () => void; 3 | 4 | export type ActionType = keyof ActionTypes; 5 | 6 | export type PayloadActionTypeTuple = [ActionTypes[AT], AT]; 7 | export type CallbackFunction = 8 | /** 9 | * @returns `true` to cancel dispatch 10 | * @returns anything else to continue 11 | */ 12 | ([payload, at]: PayloadActionTypeTuple) => 13 | | true 14 | | void 15 | | Promise; 16 | 17 | /** 18 | * intercept redux events 19 | * 20 | * return `true` from callback function to cancel the dispatch 21 | */ 22 | export function intercept( 23 | type: AT, 24 | cb: CallbackFunction, 25 | once?: boolean, 26 | ): UninterceptFunction; 27 | 28 | /** 29 | * if using this signature you will have to manually cast action payloads 30 | * to their proper types because narrowing generic types isn't 31 | * possible yet in TypeScript 32 | * 33 | * return `true` from callback function to cancel the dispatch 34 | */ 35 | export function intercept( 36 | types: AT[], 37 | cb: CallbackFunction, 38 | once?: boolean, 39 | ): UninterceptFunction; 40 | -------------------------------------------------------------------------------- /types/api/observe.d.ts: -------------------------------------------------------------------------------- 1 | interface Unobserver { 2 | (): void; 3 | now(): void; 4 | } 5 | export function observe(selector: string, cb: (el: HTMLElement | SVGElement) => any): Unobserver; 6 | -------------------------------------------------------------------------------- /types/api/plugins.d.ts: -------------------------------------------------------------------------------- 1 | import type { store } from "voby"; 2 | import type { createPersistentObject } from "./utils"; 3 | 4 | export const [pluginStore, pluginStoreReady]: ReturnType>; 5 | 6 | export const enabled: ReturnType; 7 | 8 | export interface PluginManifest { 9 | name: string; 10 | author: string; 11 | description: string; 12 | hash: string; 13 | } 14 | 15 | export interface Plugin { 16 | id: string; 17 | code: string; 18 | manifest: PluginManifest; 19 | enabled: boolean; 20 | update: boolean; 21 | } 22 | 23 | export function getPluginById(id: Plugin["id"]): Plugin | undefined; 24 | export function disablePlugin(id: Plugin["id"]): Promise; 25 | export function togglePlugin( 26 | id: Plugin["id"], 27 | ): ReturnType | ReturnType; 28 | export function enablePlugin(id: Plugin["id"]): Promise; 29 | export function installPlugin( 30 | id: Plugin["id"], 31 | code: Plugin["code"], 32 | manifest: PluginManifest, 33 | enabled?: boolean, 34 | ): Promise; 35 | export function removePlugin(id: Plugin["id"]): Promise; 36 | export function fetchPluginFromURL(url: string): Promise<[code: string, manifest: PluginManifest]>; 37 | export function reloadPlugin(plugin: Plugin): Promise; 38 | export function installPluginFromURL( 39 | url: Parameters[0], 40 | enabled?: boolean, 41 | ): Promise; 42 | -------------------------------------------------------------------------------- /types/api/registerRoute.d.ts: -------------------------------------------------------------------------------- 1 | import type { intercept } from "./intercept"; 2 | 3 | export function registerRoute( 4 | path: string, 5 | component: CallableFunction, 6 | ): ReturnType; 7 | -------------------------------------------------------------------------------- /types/api/registerTab.d.ts: -------------------------------------------------------------------------------- 1 | export function registerTab( 2 | name: HTMLElement["textContent"], 3 | path: string, 4 | component?: CallableFunction, 5 | ): () => void; 6 | -------------------------------------------------------------------------------- /types/api/showModal.d.ts: -------------------------------------------------------------------------------- 1 | export function showModal(name: HTMLElement["innerText"], component: CallableFunction): void; 2 | -------------------------------------------------------------------------------- /types/api/utils.d.ts: -------------------------------------------------------------------------------- 1 | import type { UseStore } from "idb-keyval"; 2 | 3 | interface StyleFn { 4 | /** 5 | * Removes