├── .github └── workflows │ ├── algolia_crawler.yml │ └── compress_images.yml ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── LICENSE ├── README.md ├── biome.json ├── blog ├── 2021-12-14-first-blog-post.md ├── 2022-01-02-your-first-extension.md ├── 2022-04-11-spicetify-org.md └── authors.yml ├── docs ├── advanced-usage │ ├── command-line-interface.md │ ├── custom-apps.md │ ├── extensions.md │ ├── index.md │ ├── installation.md │ ├── themes.md │ └── uninstallation.md ├── development │ ├── api-wrapper │ │ ├── classes │ │ │ ├── context-menu.md │ │ │ ├── menu.md │ │ │ ├── playbar.md │ │ │ └── topbar.md │ │ ├── functions │ │ │ ├── add-to-queue.md │ │ │ ├── color-extractor.md │ │ │ ├── get-audio-data.md │ │ │ ├── get-font-style.md │ │ │ ├── remove-from-queue.md │ │ │ └── show-notification.md │ │ ├── index.md │ │ ├── methods │ │ │ ├── app-title.md │ │ │ ├── cosmos-async.md │ │ │ ├── graphql.md │ │ │ ├── keyboard.md │ │ │ ├── local-storage.md │ │ │ ├── panel.md │ │ │ ├── platform.md │ │ │ ├── player.md │ │ │ ├── popup-modal.md │ │ │ └── uri.md │ │ ├── modules.md │ │ ├── properties │ │ │ ├── config.md │ │ │ ├── queue.md │ │ │ ├── react-components.md │ │ │ ├── react-hook.md │ │ │ ├── svgicons.md │ │ │ └── tippy-props.md │ │ └── types │ │ │ ├── context-menu │ │ │ ├── onclick-callback.md │ │ │ └── should-add-callback.md │ │ │ ├── context-option.md │ │ │ ├── context-track.md │ │ │ ├── cosmos-async │ │ │ ├── body.md │ │ │ ├── error.md │ │ │ ├── headers.md │ │ │ ├── method.md │ │ │ └── response.md │ │ │ ├── graphql │ │ │ └── query.md │ │ │ ├── keyboard │ │ │ ├── keysdefine.md │ │ │ └── validkey.md │ │ │ ├── metadata.md │ │ │ ├── panel │ │ │ └── panel-props.md │ │ │ ├── player-state.md │ │ │ ├── provided-track.md │ │ │ ├── react-component │ │ │ ├── confirm-dialog-props.md │ │ │ ├── context-menu-props.md │ │ │ ├── icon-component-props.md │ │ │ ├── menu-item-props.md │ │ │ ├── menu-props.md │ │ │ ├── panel-content-props.md │ │ │ ├── panel-header-props.md │ │ │ ├── panel-skeleton-props.md │ │ │ ├── slider-props.md │ │ │ ├── text-component-props.md │ │ │ ├── toggle-props.md │ │ │ └── tooltip-props.md │ │ │ ├── semantic-color.md │ │ │ ├── svgicon.md │ │ │ ├── uri │ │ │ ├── type.md │ │ │ └── validation-functions.md │ │ │ └── variant.md │ ├── compiling.md │ ├── custom-apps.md │ ├── index.md │ ├── js-modules.md │ ├── react-devtools.md │ ├── spicetify-creator │ │ ├── building-and-testing.md │ │ ├── create-custom-apps.md │ │ ├── create-extensions.md │ │ └── the-basics.md │ ├── spotify-cli-flags.md │ └── themes.md ├── faq.md └── getting-started.md ├── docusaurus.config.js ├── package.json ├── pnpm-lock.yaml ├── sidebars.js ├── src ├── components │ ├── HomepageFeatures.module.css │ ├── HomepageFeatures.tsx │ └── SwiperCarousel.tsx ├── css │ └── custom.css └── pages │ ├── index.module.css │ └── index.tsx ├── static ├── .nojekyll └── images │ ├── anthems.svg │ ├── app.png │ ├── extension.png │ ├── favicon.ico │ ├── homepage-carousel │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png │ ├── logo.png │ ├── powered-by-vercel.svg │ ├── spicetify-full-link.png │ ├── spicetify-full.png │ ├── spicetify-square.png │ ├── spicetify-svg-examples.png │ ├── spicetify.png │ └── theme.png └── tsconfig.json /.github/workflows/algolia_crawler.yml: -------------------------------------------------------------------------------- 1 | name: Trigger Algolia Crawler (Push) 2 | on: 3 | push: 4 | branches: [main] 5 | 6 | jobs: 7 | algolia_recrawl: 8 | name: Algolia Recrawl 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout Repo 12 | uses: actions/checkout@v2 13 | 14 | - name: Trigger Algolia Crawler 15 | uses: algolia/algoliasearch-crawler-github-actions@v1.1.0 16 | with: 17 | crawler-user-id: ${{ secrets.CRAWLER_USER_ID }} 18 | crawler-api-key: ${{ secrets.CRAWLER_API_KEY }} 19 | algolia-app-id: ${{ secrets.ALGOLIA_APP_ID }} 20 | algolia-api-key: ${{ secrets.ALGOLIA_API_KEY }} 21 | crawler-name: spicetify 22 | site-url: "https://spicetify.app/" 23 | -------------------------------------------------------------------------------- /.github/workflows/compress_images.yml: -------------------------------------------------------------------------------- 1 | name: Compress Images 2 | on: 3 | pull_request: 4 | # Run Image Actions when JPG, JPEG, PNG or WebP files are added or changed. 5 | # See https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#onpushpull_requestpaths for reference. 6 | paths: 7 | - '**.jpg' 8 | - '**.jpeg' 9 | - '**.png' 10 | - '**.webp' 11 | jobs: 12 | build: 13 | # Only run on Pull Requests within the same repository, and not from forks. 14 | if: github.event.pull_request.head.repo.full_name == github.repository 15 | name: calibreapp/image-actions 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout Repo 19 | uses: actions/checkout@v2 20 | 21 | - name: Compress Images 22 | uses: calibreapp/image-actions@main 23 | with: 24 | jpegQuality: '70' 25 | jpegProgressive: false 26 | pngQuality: '70' 27 | webpQuality: '70' 28 | githubToken: ${{ secrets.GITHUB_TOKEN }} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Docusaurus 2 | .docusaurus 3 | build 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | *.lcov 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (https://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | node_modules/ 46 | jspm_packages/ 47 | 48 | # TypeScript v1 declaration files 49 | typings/ 50 | 51 | # TypeScript cache 52 | *.tsbuildinfo 53 | 54 | # Optional npm cache directory 55 | .npm 56 | 57 | # Optional eslint cache 58 | .eslintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variables file 76 | .env 77 | .env.test 78 | 79 | # parcel-bundler cache (https://parceljs.org/) 80 | .cache 81 | 82 | # Next.js build output 83 | .next 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | 110 | # MacOS directory 111 | .DS_Store -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | bun lint-staged 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spicetify Docs 2 | 3 | This repository holds the documentation for Spicetify, which can be found [here](https://spicetify.app). 4 | 5 | ## Contributing 6 | 7 | If you feel like you can contribute, please do so by opening an issue or pull request. We are always open to expand our documentation and add new features. 8 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "linter": { 7 | "enabled": true, 8 | "rules": { 9 | "recommended": true 10 | } 11 | }, 12 | "vcs": { 13 | "enabled": true, 14 | "clientKind": "git", 15 | "useIgnoreFile": true 16 | }, 17 | "formatter": { 18 | "enabled": true, 19 | "indentStyle": "space", 20 | "indentWidth": 2 21 | }, 22 | "javascript": { 23 | "formatter": { 24 | "quoteStyle": "single", 25 | "jsxQuoteStyle": "double" 26 | } 27 | }, 28 | "json": { 29 | "parser": { 30 | "allowComments": true 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /blog/2021-12-14-first-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: first-blog-post 3 | title: First Blog Post 4 | authors: [afonsojramos] 5 | tags: [spicetify, documentation, community] 6 | --- 7 | 8 | Greetings Spicetifiers! 🔥🎶 9 | 10 | First and foremost, thank you all very much for your continued support of this project! The community that we have gathered on Discord over the past months has been amazing, and I would like to thank every single one of you that made this possible. 11 | 12 | Let's get our hands dirty now. Over the last few months, we've had a few issues with Spotify and a few of their breaking changes. To add to the fire, during all of this turmoil, the _Spicetify Developer Community_ was weak, we had very little support outside of the theming community, and there was an overwhelming amount of duplicated issues on GitHub because of this. 13 | 14 | Let's be honest, our wiki was not the most up-to-date, but at the same time, we also did not have the right tools to enable the community to contribute to it, as it was hosted as a GitHub integrated wiki. As such, we have decided to create this little documentation website that, we hope, will facilitate its maintainability and give power to the community in how they want the project to present itself to the world. 15 | 16 | Hope you can join us in this journey and thank you for coming on the ride! 🚀 17 | 18 | --- 19 | 20 | **PS:** Yes, this design is not final, please do contribute and help make it more appealing! 21 | 22 | ![](../static/images/spicetify-full.png) 23 | -------------------------------------------------------------------------------- /blog/2022-01-02-your-first-extension.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: your-first-extension 3 | title: Your First Extension 4 | authors: [charlies1103] 5 | tags: [spicetify, documentation, community, development] 6 | --- 7 | 8 | ## So you want to make an extension.... 9 | 10 | ### First step: 11 | 12 | Start out by setting up your environment: 13 | In terminal or powershell, run `spicetify enable-devtools` 14 | If at any point devtools stops working, simply run this command again. 15 | Ensure that you have a text editor or IDE ready. 16 | 17 | ### Second step: 18 | 19 | Firstly, run `spicetify config-dir`, this should open up your default file manager. Secondly, open the `Extensions` folder and create a file titled `extension.js`, or whichever title you choose, for this tutorial we will be using extension.js for consistency purposes. Finally, open this file, and paste the following code: 20 | 21 | ```js 22 | // The async modifier allows for the user of await, which converts a promise into an object, when not using await, async is not necessary. 23 | (async function extension() { 24 | // The following code segment waits for platform to load before running the code, this is important to avoid errors. When using things such as Player or URI, it is necessary to add those as well. 25 | const { Platform } = Spicetify; 26 | if (!Platform) { 27 | setTimeout(extension, 300); 28 | return; 29 | } 30 | console.log('Hello world!'); 31 | })(); 32 | ``` 33 | 34 | Next, run `spicetify config extensions extension.js`, and follow with `spicetify apply`. 35 | Open up the Spotify console, which can be done via right clicking anywhere on the page, however, there are some places that Spotify overrides this right click; if that is the case, right click somewhere else. Then, click `Inspect Element`, and open the console tab in the window that just popped up. You should see your new "Hello World" displayed! 36 | 37 | ### Third Step: 38 | 39 | Let's finish up this blog post by creating a message that welcomes the user on load. For that, you can paste the following code segment in place of the `console.log("Hello world!"); `statement: 40 | 41 | ```js 42 | const user = await Spicetify.Platform.UserAPI.getUser(); 43 | Spicetify.showNotification(`Hello ` + user.displayName); 44 | ``` 45 | -------------------------------------------------------------------------------- /blog/2022-04-11-spicetify-org.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: spicetify-org 3 | title: Spicetify's GitHub Organization 4 | authors: [afonsojramos] 5 | tags: [spicetify, documentation, community] 6 | --- 7 | 8 | Greetings Spicetifiers! 🔥🎶 9 | 10 | Our community has been growing a lot lately and some of the growing pains that we've had has been the information segmentation. This documentation website already tried to tackle that, as well as our Discord server. However, we've expanded through other projects other than the CLI, and, as such, we are ***happy*** to announce that **we are now officially a [Spicetify Organization](https://github.com/spicetify) on GitHub! 🎉** 11 | 12 | Essentially, this will not affect any of the inner workings of the community or Spicetify, but it will make it easier for you to get in touch with the community and to help us grow. 🙌 You can find all our projects under this new GitHub organization, and we will be adding more projects as we go along. 13 | 14 | > We hope that Spicetify will continue to be a great tool for everyone, and we are looking forward to enhancing it with new features and improvements. 🙌 15 | 16 | Additionally, we've also created an Open Collective page for whoever has the means to and wants to financially help our community. For now, we do not know what will be the adhesion to this, therefore we don't have many plans on how to split these donations, however, helping to pay the domain already helps! What you can be sure of, is that everything will be decided by the community, and all decisions will be as transparent as possible. 17 | 18 | 19 | Open Collective 20 | 21 | -------------------------------------------------------------------------------- /blog/authors.yml: -------------------------------------------------------------------------------- 1 | khanhas: 2 | name: khanhas 3 | title: Creator of Spicetify 4 | url: https://github.com/khanhas/ 5 | image_url: https://github.com/khanhas.png 6 | 7 | afonsojramos: 8 | name: Afonso Jorge Ramos 9 | title: Maintainer of Spicetify 10 | url: https://afonsojramos.me 11 | image_url: https://github.com/afonsojramos.png 12 | 13 | charlies1103: 14 | name: Charlie S 15 | title: Co-Developer of Marketplace & Contributor 16 | url: https://github.com/CharlieS1103 17 | image_url: https://github.com/CharlieS1103.png 18 | -------------------------------------------------------------------------------- /docs/advanced-usage/command-line-interface.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Command Line Interface (CLI) 3 | description: 👾 Using Spicetify from the command line. 4 | --- 5 | 6 | Run with no command once to generate config file 7 | 8 | ```bash 9 | spicetify 10 | ``` 11 | 12 | If you just want to use Custom Apps and Extensions head over to each specific section, if you want to create your own theme, keep reading below. 13 | 14 | Make sure config file is created successfully and there is no error, then run: 15 | 16 | ```bash 17 | spicetify backup apply enable-devtools 18 | ``` 19 | 20 | From now, after changing colors in `color.ini` or CSS in `user.css`, you just need to run: 21 | 22 | ```bash 23 | spicetify update 24 | ``` 25 | 26 | to update your theme. 27 | 28 | In Spotify, hit Ctrl Shift R / Command Shift R to reload and receive visual update of your theme. 29 | 30 | For other commands and additional flags information, please run: 31 | 32 | ```bash 33 | spicetify --help 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/advanced-usage/custom-apps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Custom Apps 3 | description: 💥 Manually installing Custom Apps. 4 | --- 5 | 6 | Custom Apps, similar to Extensions, are simply Javascript that will be injected into Spotify, that consists of a page that can be accessed from the sidebar. 7 | 8 | ## Installing 9 | 10 | Custom Apps files can be stored in: 11 | 12 | - `CustomApps` folder in Home directory: 13 | 14 | | Platform | Path | 15 | | ------------------- | -------------------------------------- | 16 | | **Windows** | `%appdata%\spicetify\CustomApps\` | 17 | | **Linux**/**MacOS** | `~/.config/spicetify/CustomApps` | 18 | 19 | - `CustomApps` folder in Spicetify executable directory. 20 | 21 | If there are 2 Custom Apps with the same name, the extension within the Home directory will be prioritized. 22 | 23 | After placing the Custom App file into correct folder, run following command to install it: 24 | 25 | ```bash 26 | spicetify config custom_apps 27 | spicetify apply 28 | ``` 29 | 30 | **Note:** Using `config` command to add Custom Apps always append file name to existed extensions list. It does not replace the whole key's value. 31 | 32 | ## Uninstalling 33 | 34 | If you want to remove a custom app from the current list of custom apps you can always append a `-` after the file name: 35 | 36 | ```bash 37 | spicetify config custom_apps - 38 | spicetify apply 39 | ``` 40 | 41 | ## Custom Apps 42 | 43 | Inject custom apps to Spotify and access them in left sidebar. 44 | Add your desired custom app folder names in config, separated them by `|` character. 45 | Example: 46 | 47 | ```ini 48 | [AdditionalOptions] 49 | ... 50 | custom_apps = reddit|yourownapp 51 | ``` 52 | 53 | App folders can be stored in: 54 | 55 | - `CustomApps` folder in Home directory: 56 | 57 | | Platform | Path | 58 | | ------------------- | -------------------------------------- | 59 | | **Windows** | `%appdata%\spicetify\CustomApps\` | 60 | | **Linux**/**MacOS** | `~/.config/spicetify/CustomApps` | 61 | 62 | - `CustomApps` folder in Spicetify executable directory. 63 | 64 | If there are 2 apps having same name, app in Home directory is prioritized. 65 | 66 | Three apps have been included to demonstrate how to create and inject an app: 67 | 68 | - [Reddit](#reddit) 69 | - [New Releases](#new-releases) 70 | - [Lyrics Plus](#lyrics-plus) 71 | 72 | ### Reddit 73 | 74 | Fetching posts from any Spotify link sharing subreddit. You can add, remove, arrange subreddits and customize post visual in config menu (in Profile menu, top right button with your username). 75 | 76 | ![Reddit](https://i.imgur.com/MC3tpNZ.png) 77 | 78 | To install, run following commands: 79 | 80 | ``` 81 | spicetify config custom_apps reddit 82 | spicetify apply 83 | ``` 84 | 85 | ### New Releases 86 | 87 | Aggregate all new releases from favorite artists, podcasts. Time range, release type, and other filters can be customized in config menu (in Profile menu, top right button with your username). Date format is based on your locale code (BCP47). 88 | 89 | ![New Releases](https://i.imgur.com/MP9dTjt.png) 90 | 91 | To install, run following commands: 92 | 93 | ``` 94 | spicetify config custom_apps new-releases 95 | spicetify apply 96 | ``` 97 | 98 | ### Lyrics Plus 99 | 100 | Get access to the current track's lyrics from various lyrics providers (Musixmatch, Netease, LRCLIB). Learn more [here](https://github.com/spicetify/cli/tree/main/CustomApps/lyrics-plus). 101 | 102 | Colors, lyrics providers can be customized in config menu (in Profile menu, top right button with your username). 103 | 104 | ![Lyrics Plus](https://i.imgur.com/WtD080A.png) 105 | 106 | To install, run following commands: 107 | 108 | ``` 109 | spicetify config custom_apps lyrics-plus 110 | spicetify apply 111 | ``` 112 | -------------------------------------------------------------------------------- /docs/advanced-usage/extensions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Extensions 3 | description: 🧩 Manually installing Extensions. 4 | --- 5 | 6 | Extensions, in a nutshell, are JavaScript files that will be evaluated along with Spotify main JavaScript. 7 | 8 | ## Installing 9 | 10 | Extension files can be stored in: 11 | 12 | - `Extensions` folder in Home directory: 13 | 14 | | Platform | Path | 15 | | ------------------- | -------------------------------------- | 16 | | **Windows** | `%appdata%\spicetify\Extensions\` | 17 | | **Linux**/**MacOS** | `~/.config/spicetify/Extensions` | 18 | 19 | - `Extensions` folder in Spicetify executable directory. 20 | 21 | If there are 2 extensions with the same name, the extension within the Home directory will be prioritized. 22 | 23 | Some Spotify API endpoints are exposed and can be found in the global object `Spicetify`. Check out `global.d.ts` for API documentation. 24 | 25 | After placing the extension file into correct folder, run following command to install it: 26 | 27 | ```bash 28 | spicetify config extensions 29 | spicetify apply 30 | ``` 31 | 32 | **Note:** Using `config` command to add extension always append file name to existed extensions list. It does not replace the whole key's value. 33 | 34 | ## Uninstalling 35 | 36 | If you want to remove an extension from the current list of extensions you can always append a `-` after the file name: 37 | 38 | ```bash 39 | spicetify config extensions - 40 | spicetify apply 41 | ``` 42 | 43 | ## Manual Install 44 | 45 | You can always manually edit the config file, add your desired extension filenames in `extensions` key, separated them by `|` character. 46 | Example: 47 | 48 | ```ini 49 | [AdditionalOptions] 50 | ... 51 | extensions = autoSkipExplicit.js|queueAll.js|djMode.js|shuffle+.js|trashbin.js 52 | ``` 53 | 54 | Afterwards, you will need to run the following: 55 | 56 | ``` 57 | spicetify apply 58 | ``` 59 | 60 | ## Extensions 61 | 62 | Below are list of default extensions that come with the distributed package: 63 | 64 | - [Auto Skip Videos](#auto-skip-videos) 65 | - [Bookmark](#bookmark) 66 | - [Christian Spotify](#christian-spotify) 67 | - [Full App Display](#full-app-display) 68 | - [Keyboard Shortcut](#keyboard-shortcut) 69 | - [Loopy Loop](#loopy-loop) 70 | - [Pop-up Lyrics](#pop-up-lyrics) 71 | - [Shuffle+](#shuffle) 72 | - [Trash Bin](#trash-bin) 73 | - [Web Now Playing](#web-now-playing) 74 | 75 | ### Auto Skip Videos 76 | 77 | **Filename:** `autoSkipVideo.js` 78 | 79 | Videos are unable to play in some regions because of Spotify's policy. Instead of jumping to next song in playlist, it just stops playing. And it's kinda annoying to open up the client to manually click next every times it happens. Use this extension to skip them automatically. 80 | 81 | ### Bookmark 82 | 83 | **Filename:** `bookmark.js` 84 | 85 | Easily store and browse pages, play tracks or tracks in specific time. Useful for who wants to check out an artist, album later without following them or writing their name down. 86 | 87 | ![Ext_bookmark](https://i.imgur.com/isgU4TS.png) 88 | 89 | ### Christian Spotify 90 | 91 | **Filename:** `autoSkipExplicit.js` 92 | 93 | Auto skip explicit tracks. Toggle option is in Profile menu (top right button). 94 | 95 | ![Ext_ChristianDemo](https://i.imgur.com/5reGrBb.png) 96 | 97 | ### Full App Display 98 | 99 | **Filename:** `fullAppDisplay.js` 100 | 101 | Full App Display: Minimal album cover art display with beautiful blur effect background. Activating button located in top bar. While in display mode, double click anywhere to exit. Right click anywhere to open setting menu. 102 | 103 | ![Ext_FAD](https://i.imgur.com/S7CPQ2s.png) 104 | 105 | ### Keyboard Shortcut 106 | 107 | **Filename:** `keyboardShortcut.js` 108 | 109 | Extends Spotify's default keybinds (toggle help modal with `?`) with vim like shortcuts. Less time touching the mouse. 110 | 111 | - Ctrl Tab / Ctrl Shift Tab: Navigate items in left sidebar menu. 112 | - PageUp/PageDown: Force scroll up/down app page only (because mouse focus is sometimes in sidebar region and they scroll sidebar instead of app page). 113 | - J/K: Scroll app page up/down. \*Tips hat to Vim users\* 114 | - G/Shift G: Scroll to top or bottom 115 | - F: Open up keyboard-driven navigation. Hit correct key sequences to open up place you want to go: 116 | 117 | ![KeyboardDemo](https://i.imgur.com/evkGv9q.png) 118 | 119 | ### Loopy Loop 120 | 121 | **Filename:** `loopyLoop.js` 122 | 123 | Provide ability to mark start and end points on progress bar and automatically loop over that track portion. 124 | 125 | ![LoopyLoop](https://i.imgur.com/YEkbjLC.png) 126 | 127 | ### Pop-up Lyrics 128 | 129 | **Filename:** `popupLyrics.js` 130 | 131 | Have easy access to a pop-up window with the current song's lyrics. Click at microphone icon on top bar to open lyrics windows. Right click at the same icon to open config menu to customize looks and lyrics providers priorities. 132 | 133 | ![Pop-up Lyrics](https://i.imgur.com/Nx9Lx7D.png) 134 | 135 | ### Shuffle+ 136 | 137 | **Filename:** `shuffle+.js` 138 | Shuffles using Fisher–Yates algorithm with zero bias. After installing extensions, right click album/playlist/artist item, there will be an option "Play with Shuffle+". You can also multiple select tracks and choose to "Play with Shuffle+". 139 | 140 | ![Shuffle](https://i.imgur.com/gxbnqSN.png) 141 | 142 | ### Trash Bin 143 | 144 | **Filename:** `trashbin.js` 145 | Throw songs/artists to trash bin and never hear them again (automatically skip). This extension will append a Throw to Trashbin option in tracks and artists link right click menu. 146 | 147 | ![Ext_Trash](https://i.imgur.com/ZFTy5Rm.png) 148 | 149 | ### Web Now Playing 150 | 151 | **Filename:** `webnowplaying.js` 152 | For Rainmeter users, establish connection with WebNowPlaying plugin to send track metadata and control players. 153 | 154 | If you just want WebNowPlaying without changing UI color, CSS, run this: 155 | 156 | ```powershell 157 | spicetify config inject_css 0 replace_colors 0 158 | spicetify config extensions webnowplaying.js 159 | spicetify apply 160 | ``` 161 | 162 | ## Legacy Extensions 163 | 164 | If you are running Spicetify 1.2.1 or below, and a supported Spotify version, you may also have access to the extensions listed below. 165 | 166 | - [DJ Mode](#dj-mode) 167 | - [New Release](#new-release) 168 | - [Queue All](#queue-all) 169 | 170 | ### DJ Mode 171 | 172 | **Filename:** `djMode.js` 173 | 174 | Easily setting up the client for your friends or audiences to choose, add song to queue but prevent them to control player. Plays button in album track list/playlist are re-purposed to add track to queue, instead of play track directly. Hide Controls option also allow you to hide all control button in player bar, Play/More/Follow buttons in cards. 175 | 176 | ![Ext_DJDemo](https://i.imgur.com/pOFEqtM.png) 177 | 178 | ### New Release 179 | 180 | **Filename:** `newRelease.js` 181 | 182 | Aggregate all new releases from favorite artists, podcasts. Setting menu could be opened by right clicking at Bell icon. 183 | 184 | ![Ext_newrelease](https://user-images.githubusercontent.com/26436809/86569793-50dd8480-bfb2-11ea-8d82-be238d719660.png) 185 | 186 | ### Queue All 187 | 188 | **Filename:** `queueAll.js` 189 | 190 | You like using Discover, New Releases page to find new music but adding each one of them to queue takes a lot of effort? If so, activate this extensions and apply. At top of every carousel now has a "Queue All" button to help you add all of them to queue. Note: Not available for playlist carousels. Just songs, albums ones. 191 | 192 | ![QueueAllDemo](https://i.imgur.com/D9ytt7K.png) 193 | -------------------------------------------------------------------------------- /docs/advanced-usage/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | --- 4 | 5 | ```mdx-code-block 6 | import DocCardList from '@theme/DocCardList'; 7 | import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; 8 | 9 | 10 | ``` -------------------------------------------------------------------------------- /docs/advanced-usage/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | description: ⚡ An advanced view on how to install Spicetify. 4 | --- 5 | 6 | ## Windows 7 | 8 | ### Powershell (pre-built binary) - Recommended 9 | 10 | ```powershell 11 | iwr -useb https://raw.githubusercontent.com/spicetify/cli/main/install.ps1 | iex 12 | ``` 13 | 14 | ### Chocolatey 15 | 16 | Follow this guide: https://chocolatey.org/packages/spicetify-cli 17 | 18 | ### Scoop 19 | 20 | ```powershell 21 | scoop install spicetify-cli 22 | ``` 23 | 24 | #### Spotify installed from Scoop 25 | 26 | - To find the location of your Spotify installation, run `scoop prefix spotify`. 27 | 28 | ```console 29 | $ scoop prefix spotify 30 | C:\Users\\scoop\apps\spotify\current 31 | ``` 32 | 33 | After you have located it, set `spotify_path` to that directory in Spicetify's config file: 34 | 35 | ![scoop-spotify-path](https://user-images.githubusercontent.com/56180050/158084602-99428adf-93bb-4983-968f-14e1f4f5b253.png) 36 | 37 | ### Winget 38 | 39 | ```powershell 40 | winget install Spicetify.Spicetify 41 | ``` 42 | 43 | ## Linux and MacOS 44 | 45 | ### Shell (pre-built binary) - Recommended 46 | 47 | ```bash 48 | curl -fsSL https://raw.githubusercontent.com/spicetify/cli/main/install.sh | sh 49 | ``` 50 | 51 | ### Homebrew or LinuxBrew 52 | 53 | ```bash 54 | brew install spicetify-cli 55 | ``` 56 | 57 | On macOS, you will need to set `spotify_path` to `/Applications/Spotify.app/Contents/Resources` in the `~/.config/spicetify/config-xpui.ini` config file. 58 | 59 | ### AUR 60 | 61 | ```bash 62 | yay -S spicetify-cli 63 | ``` 64 | 65 | ### Note for Linux users 66 | 67 | #### Spotify installed from AUR 68 | 69 | Before applying Spicetify, you need to gain write permission on Spotify files, by running command: 70 | 71 | ```bash 72 | sudo chmod a+wr /opt/spotify 73 | sudo chmod a+wr /opt/spotify/Apps -R 74 | ``` 75 | 76 | **Note:** Your Spotify client location might be different. 77 | 78 | #### Spotify installed via `spotify-launcher` package (Arch Linux) 79 | 80 | If Spotify is installed through the `spotify-launcher` package, then Spotify won't install to `/opt/spotify` and is instead in this folder: `$HOME/.local/share/spotify-launcher/install/usr/share/spotify/` 81 | 82 | This directory will need to be added to the `spotify-path` section of the config (and you won't need to change any permissions like the AUR method). 83 | 84 | **Note:** `spotify-path` must be an absolute path. Do not use `~` to reference the home folder. 85 | 86 | #### Spotify installed from Snap 87 | 88 | Apps installed from Snap **cannot be modified** so you need to follow these steps to get Spicetify working: 89 | 90 | 1. Uninstall Spotify in Snap or run command `snap remove spotify` 91 | 2. Install Spotify using `apt`: 92 | 93 | ```sh 94 | curl -sS https://download.spotify.com/debian/pubkey_C85668DF69375001.gpg | sudo gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/spotify.gpg 95 | echo "deb http://repository.spotify.com stable non-free" | sudo tee /etc/apt/sources.list.d/spotify.list 96 | sudo apt-get update && sudo apt-get install spotify-client 97 | ``` 98 | 99 | 3. After Spotify is installed successfully, you need to gain read write permissions on Spotify files, by running commands: 100 | 101 | ```bash 102 | sudo chmod a+wr /usr/share/spotify 103 | sudo chmod a+wr /usr/share/spotify/Apps -R 104 | ``` 105 | 106 | **Note:** Your Spotify client location might be different. 107 | 108 | #### Spotify installed from Flatpak 109 | 110 | - You need to find where Flatpak stores your Spotify client. In Manjaro and Fedora, it is stored in: 111 | 112 | ``` 113 | /var/lib/flatpak/app/com.spotify.Client/x86_64/stable/active/files/extra/share/spotify/ 114 | ``` 115 | 116 | - In some distros it is stored in: 117 | ``` 118 | ~/.local/share/flatpak/app/com.spotify.Client/x86_64/stable/active/files/extra/share/spotify/ 119 | ``` 120 | 121 | - Yours might be different, try these steps: 122 | 123 | 1. Find flatpak installation place with command: `flatpak --installations` 124 | 2. Go to that directory and dig in until you find folder which contain items like these: 125 | 126 | ![flat2](https://user-images.githubusercontent.com/26436809/57563050-81408780-73dc-11e9-92e8-d0cc60502ff3.png) 127 | 128 | After you have Spotify location, set `spotify_path` in config file to that directory: 129 | 130 | ![flat2](https://user-images.githubusercontent.com/26436809/57563057-9ddcbf80-73dc-11e9-82d8-d31cdf7e9cef.png) 131 | 132 | 3. Find your `prefs` file: 133 | It could be either in these two locations: 134 | 135 | - `~/.config/spotify/prefs` 136 | - `~/.var/app/com.spotify.Client/config/spotify/prefs` 137 | 138 | Check both, expand the right one to absolute path and set it to `prefs_path` in config file. 139 | 140 | ```bash 141 | spicetify config prefs_path ~/.var/app/com.spotify.Client/config/spotify/prefs 142 | ``` 143 | 144 | 4. Finally in terminal, set read/write permission for it: 145 | 146 | ```bash 147 | sudo chmod a+wr /var/lib/flatpak/app/com.spotify.Client/x86_64/stable/active/files/extra/share/spotify 148 | sudo chmod a+wr -R /var/lib/flatpak/app/com.spotify.Client/x86_64/stable/active/files/extra/share/spotify/Apps 149 | ``` 150 | 151 | ## Legacy Installations 152 | 153 | If, for some reason, you are not using the most up to date Spotify client, you may need to install a specific version of Spicetify. 154 | This is not recommended as our prime focus will always be the latest Spotify version. 155 | 156 | As such, you will need to run either of the below commands with the desired version. 157 | If you wish to use old Spotify client v1.1.56 or older, you have to install spicetify v1.2.1. 158 | 159 | **Windows**: In powershell 160 | 161 | ```powershell 162 | $v="1.2.1"; Invoke-WebRequest -UseBasicParsing "https://raw.githubusercontent.com/spicetify/cli/main/install.ps1" | Invoke-Expression 163 | ``` 164 | 165 | **Linux/MacOS:** In bash 166 | 167 | ```bash 168 | curl -fsSL https://raw.githubusercontent.com/spicetify/cli/main/install.sh -o /tmp/install.sh 169 | sh /tmp/install.sh 1.2.1 170 | ``` 171 | 172 | spicetify v1 code is available in branch [`legacy`](https://github.com/spicetify/cli/tree/legacy) if you want to build from source. 173 | 174 | If you want legacy themes, you can find them [here](https://github.com/spicetify/spicetify-themes/tree/legacy). 175 | -------------------------------------------------------------------------------- /docs/advanced-usage/themes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Themes 3 | description: ✨ Themes for Spicetify. 4 | --- 5 | 6 | One of the most popular features in Spicetify is theming. 7 | You can customize your Spotify to your **heart's** desire! 8 | **However**, this is a very cumbersome task. 9 | 10 | For this reason, the theming heroes of the Spicetify community have created a huge library of themes which can be found in the following repositories: 11 | 12 | 1. [spicetify/spicetify-themes](https://github.com/spicetify/spicetify-themes) - The official Spicetify themes repository. Feel free to contribute with more themes! 13 | 2. [NYRI4/Comfy-spicetify](https://github.com/NYRI4/Comfy-spicetify) 14 | 3. [williamckha/spicetify-fluent](https://github.com/williamckha/spicetify-fluent) 15 | 4. [Catppuccin/spicetify](https://github.com/catppuccin/spicetify) 16 | 5. [nimsandu/spicetify-bloom](https://github.com/nimsandu/spicetify-bloom) 17 | 6. [Tetrax-10/Nord-Spotify](https://github.com/Tetrax-10/Nord-Spotify) (not maintained) 18 | 7. [JulienMaille/dribbblish-dynamic-theme](https://github.com/JulienMaille/dribbblish-dynamic-theme) (not maintained) 19 | 8. [sanoojes/spicetify-lucid](https://github.com/sanoojes/spicetify-lucid) 20 | 9. [Skaytacium/Gruvify](https://github.com/Skaytacium/Gruvify) 21 | 10. [dracula/spicetify](https://github.com/dracula/spicetify) (not maintained) 22 | 11. [Spotify Dark](https://github.com/SyndiShanX/Spotify-Dark) 23 | 12. [bluedrift/Spicetify-Throwback](https://github.com/bluedrift/Spicetify-Throwback) 24 | 13. [m0squdev/dracula-spicetify-theme](https://github.com/m0squdev/dracula-spicetify-theme) 25 | 14. Insert your theme here! 26 | -------------------------------------------------------------------------------- /docs/advanced-usage/uninstallation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Uninstallation 3 | description: 🗑 How to remove Spicetify. 4 | --- 5 | 6 | ## Windows 7 | 8 | ### Powershell 9 | ```cmd 10 | spicetify restore 11 | rmdir -r -fo $env:APPDATA\spicetify 12 | rmdir -r -fo $env:LOCALAPPDATA\spicetify 13 | ``` 14 | 15 | ## Linux and MacOS 16 | 17 | :::note 18 | 19 | If you used a package manager to install Spicetify, please use its default methods for removing packages. 20 | 21 | ::: 22 | 23 | ### Shell 24 | ```bash 25 | spicetify restore 26 | rm -rf ~/.spicetify 27 | rm -rf ~/.config/spicetify 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/classes/context-menu.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ContextMenu 3 | description: Create custom menu item and prepend to right click context menu. 4 | --- 5 | 6 | Create custom menu item and prepend to right click context menu. 7 | 8 | Useful for adding custom actions to context menu (when a user right-clicks on a track, album, artist, etc.) 9 | 10 | ## Classes 11 | 12 | ### Item 13 | 14 | Single context menu item. 15 | 16 | 17 | ```ts 18 | new Spicetify.ContextMenu.Item( 19 | name: string, 20 | onClick: OnClickCallback, 21 | shouldAdd?: ShouldAddCallback, 22 | icon?: SVGIcon | string, 23 | disabled?: boolean, 24 | ) 25 | ``` 26 | 27 | #### Parameters 28 | 29 | | Parameter | Type | Description | 30 | | :--- | :--- | :--- | 31 | | name | `string` | Name of the menu item. | 32 | | onClick | `OnClickCallback` | Callback function when the menu item is clicked. | 33 | | shouldAdd | `ShouldAddCallback` | Callback function to determine if the menu item should be added. | 34 | | icon | [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) | `string` | Icon of the menu item. | 35 | | disabled | `boolean` | Whether the menu item is disabled. | 36 | 37 | #### Properties 38 | 39 | :::tip 40 | 41 | All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information. 42 | 43 | ::: 44 | 45 | | Name | Type | Description | 46 | | :--- | :--- | :--- | 47 | | iconList | [`readonly SVGIcon[]`](/docs/development/api-wrapper/types/svgicon) | List of icons. | 48 | | name | `string` | Name of the menu item. | 49 | | icon | [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) | `string` | Icon at the end of the menu item. | 50 | | disabled | `boolean` | Whether the menu item is disabled. | 51 | | shouldAdd | [`ShouldAddCallback`](/docs/development/api-wrapper/types/context-menu/should-add-callback) | Callback function to determine if the menu item should be added. | 52 | | onClick | [`OnClickCallback`](/docs/development/api-wrapper/types/context-menu/onclick-callback) | Callback function when the menu item is clicked. | 53 | 54 | #### Methods 55 | 56 | ##### `register` 57 | 58 | Register the menu item to context menu. 59 | 60 | ```ts 61 | register(): void 62 | ``` 63 | 64 | ##### `deregister` 65 | 66 | Remove the menu item from context menu. 67 | 68 | ```ts 69 | deregister(): void 70 | ``` 71 | #### Example 72 | 73 | ```ts 74 | // This function will determine if the selected item is a track 75 | function ifItemIsTrack(uri) { 76 | let uriObj = Spicetify.URI.fromString(uri[0]); 77 | switch (uriObj.type) { 78 | case Type.TRACK: 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | // Create a new menu item that only appears when a track is selected 85 | const menuItem = new Spicetify.ContextMenu.Item( 86 | "My Menu Item", 87 | () => { 88 | Spicetify.showNotification("My Menu Item clicked!"); 89 | }, 90 | ifItemIsTrack, 91 | Spicetify.SVGIcons["play"], 92 | false, 93 | ); 94 | 95 | // Register the menu item 96 | menuItem.register(); 97 | 98 | // Deregister the menu item 99 | menuItem.deregister(); 100 | 101 | // Change the menu item's name 102 | menuItem.name = "My New Menu Item"; 103 | 104 | // Change the menu item's icon 105 | menuItem.icon = "pause" 106 | ``` 107 | 108 | ### SubMenu 109 | 110 | Create a sub menu to contain `Item`s. 111 | 112 | `Item`s in `subItems` array shouldn't be registered. 113 | 114 | 115 | ```ts 116 | new Spicetify.ContextMenu.SubMenu( 117 | name: string, 118 | subItems: Iterable, 119 | shouldAdd?: ShouldAddCallback, 120 | disabled?: boolean, 121 | ) 122 | ``` 123 | 124 | #### Parameters 125 | 126 | | Parameter | Type | Description | 127 | | :--- | :--- | :--- | 128 | | name | `string` | Name of the menu item. | 129 | | subItems | [`Iterable`](#item) | Array of `Item`s to be added to the sub menu. | 130 | | shouldAdd | [`ShouldAddCallback`](/docs/development/api-wrapper/types/context-menu/should-add-callback) | Callback function to determine if the menu item should be added. | 131 | | disabled | `boolean` | Whether the menu item is disabled. | 132 | 133 | #### Properties 134 | 135 | :::tip 136 | 137 | All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information. 138 | 139 | ::: 140 | 141 | | Name | Type | Description | 142 | | :--- | :--- | :--- | 143 | | name | `string` | Name of the menu item. | 144 | | disabled | `boolean` | Whether the menu item is disabled. | 145 | | shouldAdd | [`ShouldAddCallback`](/docs/development/api-wrapper/types/context-menu/should-add-callback) | Callback function to determine if the menu item should be added. | 146 | 147 | #### Methods 148 | 149 | ##### `addItem` 150 | 151 | Add an `Item` to the sub menu. 152 | 153 | ```ts 154 | addItem(item: Item): void 155 | ``` 156 | 157 | | Parameter | Type | Description | 158 | | :--- | :--- | :--- | 159 | | item | [`Item`](#item) | `Item` to be added to the sub menu. | 160 | 161 | ##### `removeItem` 162 | 163 | Remove an `Item` from the sub menu. 164 | 165 | ```ts 166 | removeItem(item: Item): void 167 | ``` 168 | 169 | | Parameter | Type | Description | 170 | | :--- | :--- | :--- | 171 | | item | [`Item`](#item) | `Item` to be removed from the sub menu. | 172 | 173 | ##### `register` 174 | 175 | Register the sub menu to context menu. 176 | 177 | ```ts 178 | register(): void 179 | ``` 180 | 181 | ##### `deregister` 182 | 183 | Remove the sub menu from context menu. 184 | 185 | ```ts 186 | deregister(): void 187 | ``` 188 | 189 | #### Example 190 | 191 | ```ts 192 | // Create a new menu item 193 | const menuItem = new Spicetify.ContextMenu.Item( 194 | "My Menu Item", 195 | () => { 196 | Spicetify.showNotification("My Menu Item clicked!"); 197 | }, 198 | () => true, 199 | Spicetify.SVGIcons["play"], 200 | false, 201 | ); 202 | 203 | // Create a new sub menu 204 | const subMenu = new Spicetify.ContextMenu.SubMenu( 205 | "My Sub Menu", 206 | [menuItem], 207 | () => true, 208 | false, 209 | ); 210 | 211 | // Register the sub menu 212 | subMenu.register(); 213 | 214 | // Deregister the sub menu 215 | subMenu.deregister(); 216 | 217 | // Change the sub menu's name 218 | subMenu.name = "My New Sub Menu"; 219 | 220 | // Add a new menu item to the sub menu 221 | subMenu.addItem(new Spicetify.ContextMenu.Item( 222 | "My New Menu Item", 223 | () => { 224 | Spicetify.showNotification("My New Menu Item clicked!"); 225 | }, 226 | () => true, 227 | Spicetify.SVGIcons["play"], 228 | false, 229 | )); 230 | ``` 231 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/classes/menu.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Menu 3 | description: Create and prepend custom menu items in the profile menu. 4 | --- 5 | 6 | Create and prepend custom menu items in the profile menu. 7 | 8 | ## Classes 9 | 10 | ### Item 11 | 12 | Single menu item. 13 | 14 | ```ts 15 | new Spicetify.Menu.Item( 16 | name: string, 17 | isEnabled: boolean, 18 | onClick: (self: Item) => void, 19 | icon?: SVGIcon | string, 20 | ) 21 | ``` 22 | 23 | #### Parameters 24 | 25 | | Parameter | Type | Description | 26 | | :--- | :--- | :--- | 27 | | name | `string` | Name of the menu item. | 28 | | isEnabled | `boolean` | Whether the menu item is enabled. | 29 | | onClick | `(self: Item) => void` | Callback function when the menu item is clicked. | 30 | | icon | [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) | `string` | Icon of the menu item. | 31 | 32 | #### Properties 33 | 34 | :::tip 35 | 36 | All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information. 37 | 38 | ::: 39 | 40 | | Name | Type | Description | 41 | | :--- | :--- | :--- | 42 | | name | `string` | Name of the menu item. | 43 | | isEnabled | `boolean` | Whether the menu item is enabled. | 44 | | icon | [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) | `string` | Icon of the menu item. | 45 | 46 | #### Methods 47 | 48 | ##### setName 49 | 50 | Set the label of the menu item. 51 | 52 | ```ts 53 | setName(name: string): void 54 | ``` 55 | 56 | | Parameter | Type | Description | 57 | | :--- | :--- | :--- | 58 | | name | `string` | Name of the menu item. | 59 | 60 | ##### setState 61 | 62 | Set the state of the menu item. The item will have a tick icon next to it if its state is enabled. 63 | 64 | ```ts 65 | setState(isEnabled: boolean): void 66 | ``` 67 | 68 | | Parameter | Type | Description | 69 | | :--- | :--- | :--- | 70 | | isEnabled | `boolean` | Whether the menu item is enabled. | 71 | 72 | ##### setIcon 73 | 74 | Set the icon at the end of the menu item. 75 | 76 | ```ts 77 | setIcon(icon: SVGIcon | string): void 78 | ``` 79 | 80 | | Parameter | Type | Description | 81 | | :--- | :--- | :--- | 82 | | icon | [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) | `string` | Icon of the menu item. | 83 | 84 | ##### register 85 | 86 | Register the menu item to the profile menu. 87 | 88 | ```ts 89 | register(): void 90 | ``` 91 | 92 | ##### deregister 93 | 94 | Remove the menu item from the profile menu. 95 | 96 | ```ts 97 | deregister(): void 98 | ``` 99 | 100 | #### Example 101 | 102 | ```ts 103 | const item = new Spicetify.Menu.Item("My Item", true, () => { 104 | console.log("My Item is clicked"); 105 | }); 106 | 107 | item.register(); 108 | 109 | // item.name = "My Item (Updated)"; 110 | item.setName("My Item (Updated)"); 111 | 112 | // item.isEnabled = false; 113 | item.setState(false); 114 | 115 | // item.icon = "heart"; 116 | item.setIcon("heart"); 117 | ``` 118 | 119 | ### SubMenu 120 | 121 | Create a sub menu to contain `Item` toggles. 122 | 123 | `Item`s in `subItems` array shouldn't be registered. 124 | 125 | ```ts 126 | new Spicetify.Menu.SubMenu( 127 | name: string, 128 | subItems: Item[], 129 | ) 130 | ``` 131 | 132 | #### Parameters 133 | 134 | | Parameter | Type | Description | 135 | | :--- | :--- | :--- | 136 | | name | `string` | Name of the menu item. | 137 | | subItems | [`Item[]`](/docs/development/api-wrapper/classes/menu#item) | Array of sub menu items. | 138 | 139 | #### Properties 140 | 141 | :::tip 142 | 143 | All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information. 144 | 145 | ::: 146 | 147 | | Name | Type | Description | 148 | | :--- | :--- | :--- | 149 | | name | `string` | Name of the menu item. | 150 | 151 | #### Methods 152 | 153 | ##### setName 154 | 155 | Set the label of the menu item. 156 | 157 | ```ts 158 | setName(name: string): void 159 | ``` 160 | 161 | | Parameter | Type | Description | 162 | | :--- | :--- | :--- | 163 | | name | `string` | Name of the menu item. | 164 | 165 | ##### addItem 166 | 167 | Add a sub menu item. 168 | 169 | ```ts 170 | addItem(item: Item): void 171 | ``` 172 | 173 | | Parameter | Type | Description | 174 | | :--- | :--- | :--- | 175 | | item | [`Item`](/docs/development/api-wrapper/classes/menu#item) | Sub menu item. | 176 | 177 | ##### removeItem 178 | 179 | Remove a sub menu item. 180 | 181 | ```ts 182 | removeItem(item: Item): void 183 | ``` 184 | 185 | | Parameter | Type | Description | 186 | | :--- | :--- | :--- | 187 | | item | [`Item`](/docs/development/api-wrapper/classes/menu#item) | Sub menu item. | 188 | 189 | ##### register 190 | 191 | Register the menu item to profile menu. 192 | 193 | ```ts 194 | register(): void 195 | ``` 196 | 197 | ##### deregister 198 | 199 | Remove the menu item from profile menu. 200 | 201 | ```ts 202 | deregister(): void 203 | ``` 204 | 205 | #### Example 206 | 207 | ```ts 208 | const item1 = new Spicetify.Menu.Item("My Item 1", true, () => { 209 | console.log("My Item 1 is clicked"); 210 | }); 211 | 212 | const item2 = new Spicetify.Menu.Item("My Item 2", true, () => { 213 | console.log("My Item 2 is clicked"); 214 | }); 215 | 216 | const subMenu = new Spicetify.Menu.SubMenu("My Sub Menu", [item1, item2]); 217 | 218 | subMenu.register(); 219 | 220 | // subMenu.name = "My Sub Menu (Updated)"; 221 | subMenu.setName("My Sub Menu (Updated)"); 222 | 223 | // subMenu.addItem(item3); 224 | subMenu.addItem( 225 | new Spicetify.Menu.Item("My Item 3", true, () => { 226 | console.log("My Item 3 is clicked"); 227 | }) 228 | ); 229 | ``` 230 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/classes/playbar.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Playbar 3 | description: Create buttons in the player. 4 | --- 5 | 6 | Create buttons in the player. 7 | 8 | ```ts 9 | namespace Playbar { 10 | class Button { 11 | constructor(label: string, icon: SVGIcon | string, onClick: (self: Button) => void, disabled?: boolean, active?: boolean, registerOnCreate?: boolean); 12 | label: string; 13 | icon: string; 14 | onClick: (self: Button) => void; 15 | disabled: boolean; 16 | active: boolean; 17 | element: HTMLButtonElement; 18 | tippy: any; 19 | register: () => void; 20 | deregister: () => void; 21 | } 22 | 23 | class Widget { 24 | constructor(label: string, icon: SVGIcon | string, onClick?: (self: Widget) => void, disabled?: boolean, active?: boolean, registerOnCreate?: boolean); 25 | label: string; 26 | icon: string; 27 | onClick: (self: Widget) => void; 28 | disabled: boolean; 29 | active: boolean; 30 | element: HTMLButtonElement; 31 | tippy: any; 32 | register: () => void; 33 | deregister: () => void; 34 | } 35 | }; 36 | ``` 37 | 38 | ## `Button` 39 | 40 | Create buttons next to the player extra control buttons (e.g. queue, lyrics, Now Playing View, etc.). 41 | 42 | This is useful for creating buttons whose actions have an impact on or relate to the player, and are generally dynamic/stateful, such as a button that toggles the player's loop mode. 43 | 44 | #### Parameters 45 | 46 | | Parameter | Type | Description | 47 | | :--- | :--- | :--- | 48 | | label | `string` | Label of the button. | 49 | | icon | [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) | `string` | Icon of the button. | 50 | | onClick | `(self: Button) => void` | Callback function when the button is clicked. | 51 | | disabled | `boolean` | `undefined` | Whether the button is disabled. | 52 | | active | `boolean` | `undefined` | Whether the button is active. | 53 | | registerOnCreate | `boolean` | `undefined` | Whether the button should be registered to the player on creation. | 54 | 55 | #### Properties 56 | 57 | :::tip 58 | 59 | All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information. 60 | 61 | ::: 62 | 63 | | Name | Type | Description | 64 | | :--- | :--- | :--- | 65 | | label | `string` | Label of the button. | 66 | | icon | `string` | Icon of the button. | 67 | | disabled | `boolean` | Whether the button is disabled. | 68 | | active | `boolean` | Whether the button is active. | 69 | | onClick | `(self: Button) => void` | Callback function when the button is clicked. | 70 | | element | `HTMLButtonElement` | HTML element of the button. | 71 | | tippy | `any` | Tippy instance of the button. For more information, see [Tippy.js](https://atomiks.github.io/tippyjs/v6/tippy-instance/). | 72 | 73 | #### Methods 74 | 75 | ##### `register` 76 | 77 | Register the button to the player. 78 | 79 | ```ts 80 | register(): void; 81 | ``` 82 | 83 | ##### `deregister` 84 | 85 | Deregister the button from the player. 86 | 87 | ```ts 88 | deregister(): void; 89 | ``` 90 | 91 | #### Example 92 | 93 | :::caution 94 | 95 | Tippy, `onclick` or any other click events will **not** work if `disabled` is set to `true`. You will need to manually enable the button inside your extension. 96 | 97 | This is due to the limitations of Tippy itself and how HTML elements work. 98 | 99 | ::: 100 | 101 | ```ts 102 | // By default, the button will be registered to the player on creation. 103 | // You can disable this by setting registerOnCreate to false. 104 | // Each button comes with a preconfigured Tippy instance that aims to mimic the original Spotify tooltip. 105 | const button = new Spicetify.Playbar.Button( 106 | "My Button", 107 | "play", 108 | (self) => { 109 | // Do something when the button is clicked. 110 | }, 111 | false, // Whether the button is disabled. 112 | false, // Whether the button is active. 113 | ); 114 | 115 | // You can also register the button to the player later. 116 | button.register(); 117 | 118 | // Remove the button from the player when it is no longer needed. 119 | button.deregister(); 120 | // If you don't want to remove the button entirely, you can also disable it. 121 | button.disabled = true; 122 | 123 | // Change button properties. 124 | // Changing label will also change the tooltip content. 125 | button.label = "Hello world!"; 126 | button.icon = "play"; 127 | 128 | // You can also set properties of the HTML element. 129 | button.element.style.color = "red"; 130 | button.element.oncontextmenu = () => { 131 | Spicetify.showNotification("You right-clicked me!"); 132 | }; 133 | button.element.addEventListener("click", () => { 134 | // Do something else. 135 | Spicetify.showNotification("You clicked me!"); 136 | }); 137 | 138 | // You can also change properties of the Tippy instance. For more information, see https://atomiks.github.io/tippyjs/v6/tippy-instance/. 139 | button.tippy.setContent("Hello world!"); 140 | 141 | // Or if you want to use HTML. 142 | button.tippy.setProps({ 143 | content: "Hello world!", 144 | allowHTML: true, 145 | }); 146 | ``` 147 | 148 | ## `Widget` 149 | 150 | Create widgets in the player, next to track information similar to the Heart button. 151 | 152 | This is useful for creating buttons whose actions have an impact on the state of the player and the track being played, such as a button that adds the current track to a playlist. 153 | 154 | #### Parameters 155 | 156 | | Parameter | Type | Description | 157 | | :--- | :--- | :--- | 158 | | label | `string` | Label of the widget. | 159 | | icon | [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) | `string` | Icon of the widget. | 160 | | onClick | `(self: Widget) => void` | Callback function when the widget is clicked. | 161 | | disabled | `boolean` | `undefined` | Whether the widget is disabled. | 162 | | active | `boolean` | `undefined` | Whether the widget is active. | 163 | | registerOnCreate | `boolean` | `undefined` | Whether the widget should be registered to the player on creation. | 164 | 165 | #### Properties 166 | 167 | :::tip 168 | 169 | All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information. 170 | 171 | ::: 172 | 173 | | Name | Type | Description | 174 | | :--- | :--- | :--- | 175 | | label | `string` | Label of the widget. | 176 | | icon | `string` | Icon of the widget. | 177 | | disabled | `boolean` | Whether the widget is disabled. | 178 | | active | `boolean` | Whether the widget is active. | 179 | | onClick | `(self: Widget) => void` | Callback function when the widget is clicked. | 180 | | element | `HTMLButtonElement` | HTML element of the widget. | 181 | | tippy | `any` | Tippy instance of the widget. For more information, see [Tippy.js](https://atomiks.github.io/tippyjs/v6/tippy-instance/). | 182 | 183 | #### Methods 184 | 185 | ##### `register` 186 | 187 | Register the widget to the player. 188 | 189 | ```ts 190 | register(): void; 191 | ``` 192 | 193 | ##### `deregister` 194 | 195 | Deregister the widget from the player. 196 | 197 | ```ts 198 | deregister(): void; 199 | ``` 200 | 201 | #### Example 202 | 203 | :::caution 204 | 205 | Tippy, `onclick` or any other click events will **not** work if `disabled` is set to `true`. You will need to manually enable the widget inside your extension. 206 | 207 | This is due to the limitations of Tippy itself and how HTML elements work. 208 | 209 | ::: 210 | 211 | ```ts 212 | // By default, the widget will be registered to the player on creation. 213 | // You can disable this by setting registerOnCreate to false. 214 | // Each widget comes with a preconfigured Tippy instance that aims to mimic the original Spotify tooltip. 215 | const widget = new Spicetify.Playbar.Widget( 216 | "My Widget", 217 | "play", 218 | (self) => { 219 | // Do something when the widget is clicked. 220 | }, 221 | false, // Whether the widget is disabled. 222 | false, // Whether the widget is active. 223 | ); 224 | 225 | // You can also register the widget to the player later. 226 | widget.register(); 227 | 228 | // Remove the widget from the player when it is no longer needed. 229 | widget.deregister(); 230 | 231 | // Change widget properties. 232 | // Changing label will also change the tooltip content. 233 | widget.label = "Hello world!"; 234 | widget.icon = "play"; 235 | 236 | // You can also set properties of the HTML element. 237 | widget.element.style.color = "red"; 238 | widget.element.oncontextmenu = () => { 239 | Spicetify.showNotification("You right-clicked me!"); 240 | }; 241 | widget.element.addEventListener("click", () => { 242 | // Do something else. 243 | Spicetify.showNotification("You clicked me!"); 244 | }); 245 | 246 | // You can also change properties of the Tippy instance. For more information, see https://atomiks.github.io/tippyjs/v6/tippy-instance/. 247 | widget.tippy.setContent("Hello world!"); 248 | 249 | // Or if you want to use HTML. 250 | widget.tippy.setProps({ 251 | content: "Hello world!", 252 | allowHTML: true, 253 | }); 254 | ``` 255 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/classes/topbar.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Topbar 3 | description: Create buttons in the top bar. 4 | --- 5 | 6 | Create buttons in the top bar, next to the navigation buttons. 7 | 8 | This is useful for creating buttons that are generally static and whose actions have an impact on the whole app, such as a button that opens a settings menu. 9 | 10 | ```ts 11 | namespace Topbar { 12 | class Button { 13 | constructor(label: string, icon: SVGIcon | string, onClick: (self: Button) => void, disabled?: boolean, isRight?: boolean); 14 | label: string; 15 | icon: string; 16 | onClick: (self: Button) => void; 17 | disabled: boolean; 18 | isRight: boolean; 19 | element: HTMLButtonElement; 20 | tippy: any; 21 | } 22 | }; 23 | ``` 24 | 25 | #### Parameters 26 | 27 | | Parameter | Type | Description | 28 | | :--- | :--- | :--- | 29 | | label | `string` | Label of the button. | 30 | | icon | [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) | `string` | Icon of the button. | 31 | | onClick | `(self: Button) => void` | Callback function when the button is clicked. | 32 | | disabled | `boolean` | Whether the button is disabled. | 33 | | isRight | `boolean` | Whether the button is button placed on the right side. | 34 | 35 | #### Properties 36 | 37 | :::tip 38 | 39 | All of the listed properties are dynamic and can be changed at any time. Look into the example below for more information. 40 | 41 | ::: 42 | 43 | | Name | Type | Description | 44 | | :--- | :--- | :--- | 45 | | label | `string` | Label of the button. | 46 | | icon | `string` | Icon of the button. | 47 | | disabled | `boolean` | Whether the button is disabled. | 48 | | onClick | `(self: Button) => void` | Callback function when the button is clicked. | 49 | | element | `HTMLButtonElement` | HTML element of the button. | 50 | | tippy | `any` | Tippy instance of the button. For more information, see [Tippy.js](https://atomiks.github.io/tippyjs/v6/tippy-instance/). | 51 | 52 | #### Example 53 | 54 | :::caution 55 | 56 | Tippy, `onclick` or any other click events will **not** work if `disabled` is set to `true`. You will need to manually enable the button inside your extension. 57 | 58 | This is due to the limitations of Tippy itself and how HTML elements work. 59 | 60 | ::: 61 | 62 | ```ts 63 | // Button is automatically added to the top bar when created. 64 | // Each button comes with a preconfigured Tippy instance that aims to mimic the original Spotify tooltip. 65 | const button = new Spicetify.Topbar.Button("Hello", "download", () => { 66 | Spicetify.showNotification("Hello world!"); 67 | }); 68 | 69 | // Change button properties. 70 | // Changing label will also change the tooltip content. 71 | button.label = "Hello world!"; 72 | button.icon = "play"; 73 | button.disabled = true; 74 | 75 | // You can also set properties of the HTML element. 76 | button.element.style.color = "red"; 77 | button.element.oncontextmenu = () => { 78 | Spicetify.showNotification("You right-clicked me!"); 79 | }; 80 | button.element.addEventListener("click", () => { 81 | // Do something else. 82 | Spicetify.showNotification("You clicked me!"); 83 | }); 84 | 85 | // You can also change properties of the Tippy instance. For more information, see https://atomiks.github.io/tippyjs/v6/tippy-instance/. 86 | button.tippy.setContent("Hello world!"); 87 | 88 | // Or if you want to use HTML. 89 | button.tippy.setProps({ 90 | content: "Hello world!", 91 | allowHTML: true, 92 | }); 93 | ``` 94 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/functions/add-to-queue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: addToQueue 3 | description: Adds a track/album or array of tracks/albums to prioritized queue. 4 | --- 5 | 6 | Adds a track or array of tracks to the bottom of the prioritized queue. 7 | 8 | :::tip 9 | 10 | This works similarly to [`Spicetify.Platform.PlayerAPI.addToQueue`](/docs/development/api-wrapper/methods/platform#addtoqueue) but works silently, meaning no notification will be shown. 11 | 12 | If you want default Spotify behavior, use [`Spicetify.Platform.PlayerAPI.addToQueue`](/docs/development/api-wrapper/methods/platform#addtoqueue) instead. 13 | 14 | ::: 15 | 16 | ```ts 17 | function addToQueue(uri: ContextTrack[]): Promise; 18 | ``` 19 | 20 | #### Parameters 21 | 22 | | Name | Type | Description | 23 | | :--- | :--- | :--- | 24 | | `uri` | [`ContextTrack[]`](/docs/development/api-wrapper/types/context-track) | Array of tracks to add to queue. | 25 | 26 | #### Example 27 | 28 | ```ts 29 | // Add current track to queue 30 | const currentTrack = Spicetify.Player.data.item; 31 | 32 | await Spicetify.addToQueue([currentTrack]); 33 | 34 | // Add a track to queue 35 | const trackUri = "spotify:track:4iV5W9uYEdYUVa79Axb7Rh"; 36 | 37 | await Spicetify.addToQueue([ { uri: trackUri } ]); 38 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/functions/color-extractor.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: colorExtractor 3 | description: Extracts colors from a playlist, track, album, artist, show, etc. 4 | --- 5 | 6 | Extracts colors from a playlist, track, album, artist, show, etc. 7 | 8 | ```ts 9 | function colorExtractor(uri: string): Promise<{ 10 | DARK_VIBRANT: string; 11 | DESATURATED: string; 12 | LIGHT_VIBRANT: string; 13 | PROMINENT: string; 14 | VIBRANT: string; 15 | VIBRANT_NON_ALARMING: string; 16 | }>; 17 | ``` 18 | 19 | #### Parameters 20 | 21 | | Parameter | Type | Description | 22 | | :--- | :--- | :--- | 23 | | uri | `string` | URI of anything that has artwork (playlist, track, album, artist, show, etc.) | 24 | 25 | #### Returns 26 | 27 | | Name | Type | Description | 28 | | :--- | :--- | :--- | 29 | | DARK_VIBRANT | `string` | Dark vibrant color in hex format. | 30 | | DESATURATED | `string` | Desaturated color in hex format. | 31 | | LIGHT_VIBRANT | `string` | Light vibrant color in hex format. | 32 | | PROMINENT | `string` | Prominent color in hex format. | 33 | | VIBRANT | `string` | Vibrant color in hex format. | 34 | | VIBRANT_NON_ALARMING | `string` | Vibrant non alarming color in hex format. | 35 | 36 | #### Example 37 | 38 | ```ts 39 | // Get color from current track 40 | const currentTrack = Spicetify.Player.data.item; 41 | const colors = await Spicetify.colorExtractor(currentTrack.uri); 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/functions/get-audio-data.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: getAudioData 3 | description: Get the audio data from a track. 4 | --- 5 | 6 | Fetch track analyzed audio data. 7 | 8 | Under the hood, it uses the `wg://audio-attributes/v1/audio-analysis/` endpoint, which is identical to Spotify Web API's [Get Track's Audio Analysis](https://developer.spotify.com/documentation/web-api/reference/get-audio-analysis). The only difference is that it doesn't require authentication. 9 | 10 | :::caution 11 | 12 | Beware, not all tracks have audio data. 13 | 14 | ::: 15 | 16 | ```ts 17 | function getAudioData(uri?: string): Promise; 18 | ``` 19 | 20 | #### Parameters 21 | 22 | | Parameter | Type | Description | 23 | | :--- | :--- | :--- | 24 | | uri | `string` | `undefined` | URI of the track. If not provided, it will use the current track. | 25 | 26 | #### Returns 27 | 28 | An object containing the audio data. See the [Spotify Web API reference](https://developer.spotify.com/documentation/web-api/reference/get-audio-analysis) for more details. 29 | 30 | #### Example 31 | 32 | ```ts 33 | // Get audio data from current track 34 | const audioData = await Spicetify.getAudioData(); 35 | 36 | // Get audio data from a specific track 37 | const audioData = await Spicetify.getAudioData("spotify:track:1qDrWA6lyx8cLECdZE7TV7"); 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/functions/get-font-style.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: getFontStyle 3 | description: Returns the font style for a given variant. 4 | --- 5 | 6 | Spicetify provides a function that returns the CSS style for a given font variant used in the Spotify app. 7 | 8 | :::tip 9 | 10 | This function is used to provide backwards compatibility for older Spicetify extensions and custom apps that use `main-type-` classes. 11 | 12 | Instead of using this function to get Spotify stylings, you can simply add the `main-type-` class to your element. 13 | 14 | ::: 15 | 16 | ```ts 17 | function getFontStyle(font: Variant): string; 18 | ``` 19 | 20 | #### Parameters 21 | 22 | | Parameter | Type | Description | 23 | | :--- | :--- | :--- | 24 | | font | [`Variant`](/docs/development/api-wrapper/types/variant) | Font variant | 25 | 26 | #### Returns 27 | 28 | `string` - CSS style for the given font variant. 29 | 30 | #### Example 31 | 32 | ```ts 33 | const style = getFontStyle("forte"); 34 | 35 | // Returns "viola" if given an invalid variant 36 | // Equivalent to `getFontStyle("viola");` 37 | const style = getFontStyle("invalid-variant"); 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/functions/remove-from-queue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: removeFromQueue 3 | description: Removes a track or array of tracks from prioritized queue. 4 | --- 5 | 6 | :::tip 7 | 8 | This works similarly to [`Spicetify.Platform.PlayerAPI.removeFromQueue`](/docs/development/api-wrapper/methods/platform#removefromqueue). 9 | 10 | ::: 11 | 12 | :::caution 13 | 14 | If a `uid` is not provided, all tracks with the same `uri` will be removed. 15 | 16 | ::: 17 | 18 | ```ts 19 | function removeFromQueue(uri: ContextTrack[]): Promise; 20 | ``` 21 | 22 | #### Parameters 23 | 24 | | Name | Type | Description | 25 | | :--- | :--- | :--- | 26 | | `uri` | [`ContextTrack[]`](/docs/development/api-wrapper/types/context-track) | Array of tracks to remove from queue. | 27 | 28 | #### Example 29 | 30 | ```ts 31 | // Remove current track from queue 32 | const currentTrack = Spicetify.Player.data.item; 33 | 34 | await Spicetify.removeFromQueue([currentTrack]); 35 | 36 | // Remove a track from queue 37 | const trackUri = "spotify:track:4iV5W9uYEdYUVa79Axb7Rh"; 38 | 39 | await Spicetify.removeFromQueue([ { uri: trackUri } ]); 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/functions/show-notification.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: showNotification 3 | description: Show a toast notification inside Spotify. 4 | --- 5 | 6 | Show a toast notification inside Spotify. 7 | 8 | ```ts 9 | function showNotification(text: string, isError?: boolean, msTimeout?: number): void; 10 | ``` 11 | 12 | | Parameter | Type | Description | 13 | | :--- | :--- | :--- | 14 | | text | `string` | Message to display. Can use inline HTML for styling. | 15 | | isError | `boolean` | If true, toast will be red. Defaults to false. | 16 | | msTimeout | `number` | Time in milliseconds to display the toast. Defaults to Spotify's value. | 17 | 18 | #### Example 19 | 20 | ```ts 21 | // Display a notification 22 | Spicetify.showNotification("My Menu Item clicked!"); 23 | 24 | // Display a notification with a custom timeout 25 | Spicetify.showNotification("My Menu Item clicked!", false, 1000); 26 | 27 | // Display an error notification 28 | Spicetify.showNotification("Something wrong happened", true); 29 | 30 | // Display a bolded error notification 31 | Spicetify.showNotification("Something wrong happened", true); 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Wrapper 3 | description: 🧰 Everything you need to know about the Spicetify object and API Wrapper. 4 | --- 5 | 6 | Making an extension from scratch can be a daunting task. Luckily, Spicetify provides a powerful API Wrapper that makes it easy to interact with Spotify's internal APIs as well as provide out-of-the-box methods to help you easily create extensions. 7 | 8 | ## Spicetify Object 9 | You can access the Spicetify object by typing `Spicetify` in the DevTools console, inside your extension, or `window.top.Spicetify` if you're developing an app inside an `iframe`. 10 | 11 | ```ts 12 | Spicetify 13 | ``` 14 | 15 | Navigate the sidebar to see all the methods and properties available in the Spicetify object! 16 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/methods/app-title.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: AppTitle 3 | description: Set of API methods to interact with the Spotify client app title. 4 | --- 5 | 6 | Spicetify provides a set of API methods to interact with the Spotify client app title. 7 | 8 | :::note 9 | 10 | These methods only work for the default app title. 11 | 12 | ::: 13 | 14 | ```ts 15 | namespace AppTitle { 16 | function set(title: string): Promise<{ clear: () => void }>; 17 | function reset(): Promise; 18 | function get(): Promise; 19 | function sub(callback: (title: string) => void): { clear: () => void }; 20 | } 21 | ``` 22 | 23 | ## Methods 24 | 25 | ### `set` 26 | 27 | Set the default app title and force it until canceled. This will override any previous forced title. 28 | 29 | :::note 30 | 31 | This will temporarily override the current title if a track is being played until the player changes track or the user interacts with the player. 32 | 33 | ::: 34 | 35 | ```ts 36 | function set(title: string): Promise<{ clear: () => void }>; 37 | ``` 38 | 39 | #### Parameters 40 | 41 | | Name | Type | Description | 42 | | ------ | -------- | ----------- | 43 | | `title` | `string` | Title to set | 44 | 45 | #### Returns 46 | 47 | Promise that resolves to a function to cancel forced title. This doesn't reset the title. 48 | 49 | #### Example 50 | 51 | ```ts 52 | await Spicetify.AppTitle.set("My Extension"); 53 | ``` 54 | 55 | ### `reset` 56 | 57 | Reset app title to default. 58 | 59 | ```ts 60 | function reset(): Promise; 61 | ``` 62 | 63 | #### Example 64 | 65 | ```ts 66 | await Spicetify.AppTitle.reset(); // Spotify Premium 67 | ``` 68 | 69 | ### `get` 70 | 71 | Get current default app title. 72 | 73 | :::note 74 | 75 | This method cannot get the title of the currently played track. 76 | 77 | ::: 78 | 79 | ```ts 80 | function get(): Promise; 81 | ``` 82 | 83 | #### Returns 84 | 85 | Current default app title. 86 | 87 | #### Example 88 | 89 | ```ts 90 | const title = await Spicetify.AppTitle.get(); 91 | console.log(title); // Spotify Premium 92 | ``` 93 | 94 | ### `sub` 95 | 96 | Subscribe to title changes. 97 | 98 | :::note 99 | 100 | This event is not fired when the player changes app title. 101 | 102 | ::: 103 | 104 | ```ts 105 | function sub(callback: (title: string) => void): { clear: () => void }; 106 | ``` 107 | 108 | #### Parameters 109 | 110 | | Name | Type | Description | 111 | | ---- | ---- | ----------- | 112 | | `callback` | `(title: string) => void` | Callback to call when title changes | 113 | 114 | #### Returns 115 | 116 | Object with method to unsubscribe. 117 | 118 | #### Example 119 | 120 | ```ts 121 | const { clear } = Spicetify.AppTitle.sub((title) => { 122 | console.log(title); 123 | }); 124 | 125 | await Spicetify.AppTitle.set("My Extension"); // Console: My Extension 126 | 127 | clear(); 128 | ``` -------------------------------------------------------------------------------- /docs/development/api-wrapper/methods/cosmos-async.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CosmosAsync 3 | description: Asynchronous Cosmos API wrapper used by the Spotify client. 4 | --- 5 | 6 | Asynchronous Cosmos API wrapper used by the Spotify client. It is used to make requests to the Spotify client's internal API as well as external URLs. 7 | 8 | ```ts 9 | Spicetify.CosmosAsync 10 | ``` 11 | 12 | It works similarly to `fetch` or `axios` but for each request it will automatically add the required headers and cookies (such as user session token). All responses are parsed as JSON. 13 | 14 | :::caution 15 | 16 | Be mindful of where you're making a request to, especially if you're making a request to an external URL as it *may* compromise the user's account. 17 | 18 | If you're not certain, only use `CosmosAsync` for internal Spotify URLs, or use `fetch` for external URLs. 19 | 20 | ::: 21 | 22 | :::tip 23 | 24 | Feel free to reach out to the developers' community on [**Discord**](https://discord.gg/VnevqPp2Rr) if you need help with any of these methods, or if you need a list of all available internal/useful endpoints. 25 | 26 | ::: 27 | 28 | ## Methods 29 | 30 | ```ts 31 | namespace CosmosAsync { 32 | function head(url: string, headers?: Headers): Promise; 33 | function get(url: string, body?: Body, headers?: Headers): Promise; 34 | function post(url: string, body?: Body, headers?: Headers): Promise; 35 | function put(url: string, body?: Body, headers?: Headers): Promise; 36 | function del(url: string, body?: Body, headers?: Headers): Promise; 37 | function patch(url: string, body?: Body, headers?: Headers): Promise; 38 | function sub(url: string, callback: ((b: Response["body"]) => void), onError?: ((e: Error) => void), body?: Body, headers?: Headers): Promise; 39 | function postSub(url: string, body: Body | null, callback: ((b: Response["body"]) => void), onError?: ((e: Error) => void)): Promise; 40 | function request(method: Method, url: string, body?: Body, headers?: Headers): Promise; 41 | function resolve(method: Method, url: string, body?: Body, headers?: Headers): Promise; 42 | } 43 | ``` 44 | 45 | It is worth noting that you can either make a request using the [`request`](#request) method, or use the shorthand methods for each HTTP method. 46 | 47 | For example, you can fetch the current client version using either of the following: 48 | 49 | ```ts 50 | await Spicetify.CosmosAsync.get("sp://desktop/v1/version"); 51 | ``` 52 | 53 | or 54 | 55 | ```ts 56 | await Spicetify.CosmosAsync.request("GET", "sp://desktop/v1/version"); 57 | ``` 58 | 59 | For a complete list of available HTTP methods, see [`Method`](/docs/development/api-wrapper/types/cosmos-async/method). 60 | 61 | You can also use `CosmosAsync` for Spotify Web API endpoints without having to manually add the required headers and cookies. 62 | 63 | ```ts 64 | // All endpoints that uses the `sp`, `wg`, and `hm` protocol are internal Spotify endpoints 65 | await Spicetify.CosmosAsync.get("sp://desktop/v1/version"); 66 | 67 | // Spotify Web API endpoints also works 68 | await Spicetify.CosmosAsync.get("https://api.spotify.com/v1/me"); 69 | 70 | // Requests to external URLs are NOT safe and may compromise the user's account 71 | // Only use this if you're certain that the URL is safe 72 | // If you need to make a request to an external URL, use `fetch` instead 73 | await fetch("https://example.com"); 74 | ``` 75 | 76 | ### `head` 77 | 78 | Make a `HEAD` request to the specified URL. 79 | 80 | ```ts 81 | function head(url: string, headers?: Headers): Promise; 82 | ``` 83 | 84 | | Parameter | Type | Description | 85 | | --- | --- | --- | 86 | | `url` | `string` | URL to make the request to. | 87 | 88 | ### `get` 89 | 90 | Make a `GET` request to the specified URL. 91 | 92 | ```ts 93 | function get(url: string, body?: Body, headers?: Headers): Promise; 94 | ``` 95 | 96 | | Parameter | Type | Description | 97 | | --- | --- | --- | 98 | | `url` | `string` | URL to make the request to. | 99 | | `body` | [`Body`](/docs/development/api-wrapper/types/cosmos-async/body) | Request body. | 100 | | `headers` | [`Headers`](/docs/development/api-wrapper/types/cosmos-async/headers) | Request headers. | 101 | 102 | Example: 103 | 104 | ```ts 105 | // Get all playlists in user's library 106 | const res = await Spicetify.CosmosAsync.get("sp://core-playlist/v1/rootlist"); 107 | const playlists = res.rows.filter((row) => row.type === "playlist"); 108 | ``` 109 | 110 | ### `post` 111 | 112 | Make a `POST` request to the specified URL. 113 | 114 | ```ts 115 | function post(url: string, body?: Body, headers?: Headers): Promise; 116 | ``` 117 | 118 | | Parameter | Type | Description | 119 | | --- | --- | --- | 120 | | `url` | `string` | URL to make the request to. | 121 | | `body` | [`Body`](/docs/development/api-wrapper/types/cosmos-async/body) | Request body. | 122 | | `headers` | [`Headers`](/docs/development/api-wrapper/types/cosmos-async/headers) | Request headers. | 123 | 124 | Example: 125 | 126 | ```ts 127 | // Skip to the next track in queue 128 | const res = await Spicetify.CosmosAsync.post("sp://player/v2/main/skip_next"); 129 | ``` 130 | 131 | ### `put` 132 | 133 | Make a `PUT` request to the specified URL. 134 | 135 | ```ts 136 | function put(url: string, body?: Body, headers?: Headers): Promise; 137 | ``` 138 | 139 | | Parameter | Type | Description | 140 | | --- | --- | --- | 141 | | `url` | `string` | URL to make the request to. | 142 | | `body` | [`Body`](/docs/development/api-wrapper/types/cosmos-async/body) | Request body. | 143 | | `headers` | [`Headers`](/docs/development/api-wrapper/types/cosmos-async/headers) | Request headers. | 144 | 145 | Example: 146 | 147 | ```ts 148 | // Enable/disable incognito mode 149 | const res = await Spicetify.CosmosAsync.put("sp://scrobble/v1/incognito", { enabled: boolean }); 150 | ``` 151 | 152 | ### `del` 153 | 154 | Make a `DELETE` request to the specified URL. 155 | 156 | ```ts 157 | function del(url: string, body?: Body, headers?: Headers): Promise; 158 | ``` 159 | 160 | | Parameter | Type | Description | 161 | | --- | --- | --- | 162 | | `url` | `string` | URL to make the request to. | 163 | | `body` | [`Body`](/docs/development/api-wrapper/types/cosmos-async/body) | Request body. | 164 | | `headers` | [`Headers`](/docs/development/api-wrapper/types/cosmos-async/headers) | Request headers. | 165 | 166 | ### `patch` 167 | 168 | Make a `PATCH` request to the specified URL. 169 | 170 | ```ts 171 | function patch(url: string, body?: Body, headers?: Headers): Promise; 172 | ``` 173 | 174 | | Parameter | Type | Description | 175 | | --- | --- | --- | 176 | | `url` | `string` | URL to make the request to. | 177 | | `body` | [`Body`](/docs/development/api-wrapper/types/cosmos-async/body) | Request body. | 178 | | `headers` | [`Headers`](/docs/development/api-wrapper/types/cosmos-async/headers) | Request headers. | 179 | 180 | ### `sub` 181 | 182 | Make a `SUB` request to the specified URL. 183 | 184 | ```ts 185 | function sub(url: string, callback: ((b: Response["body"]) => void), onError?: ((e: Error) => void), body?: Body, headers?: Headers): Promise; 186 | ``` 187 | 188 | | Parameter | Type | Description | 189 | | --- | --- | --- | 190 | | `url` | `string` | URL to make the request to. | 191 | | `callback` | `(b: Response["body"]) => void` | Callback function to run when the request is successful. | 192 | | `onError` | `(e: Error) => void` | Callback function to run when the request fails. | 193 | | `body` | [`Body`](/docs/development/api-wrapper/types/cosmos-async/body) | Request body. | 194 | | `headers` | [`Headers`](/docs/development/api-wrapper/types/cosmos-async/headers) | Request headers. | 195 | 196 | ### `postSub` 197 | 198 | Make a `POST` request to the specified URL, and subscribe to the response. 199 | 200 | ```ts 201 | function postSub(url: string, body: Body | null, callback: ((b: Response["body"]) => void), onError?: ((e: Error) => void)): Promise; 202 | ``` 203 | 204 | | Parameter | Type | Description | 205 | | --- | --- | --- | 206 | | `url` | `string` | URL to make the request to. | 207 | | `body` | [`Body`](/docs/development/api-wrapper/types/cosmos-async/body) | Request body. | 208 | | `callback` | `(b: Response["body"]) => void` | Callback function to run when the request is successful. | 209 | | `onError` | `(e: Error) => void` | Callback function to run when the request fails. | 210 | 211 | ### `request` 212 | 213 | Make a request to the specified URL. 214 | 215 | ```ts 216 | function request(method: Method, url: string, body?: Body, headers?: Headers): Promise; 217 | ``` 218 | 219 | | Parameter | Type | Description | 220 | | --- | --- | --- | 221 | | `method` | [`Method`](/docs/development/api-wrapper/types/cosmos-async/method) | HTTP method to use. | 222 | | `url` | `string` | URL to make the request to. | 223 | | `body` | [`Body`](/docs/development/api-wrapper/types/cosmos-async/body) | Request body. | 224 | | `headers` | [`Headers`](/docs/development/api-wrapper/types/cosmos-async/headers) | Request headers. | 225 | 226 | ### `resolve` 227 | 228 | Make a request to the specified URL, and resolve the response. 229 | 230 | ```ts 231 | function resolve(method: Method, url: string, body?: Body, headers?: Headers): Promise; 232 | ``` 233 | 234 | | Parameter | Type | Description | 235 | | --- | --- | --- | 236 | | `method` | [`Method`](/docs/development/api-wrapper/types/cosmos-async/method) | HTTP method to use. | 237 | | `url` | `string` | URL to make the request to. | 238 | | `body` | [`Body`](/docs/development/api-wrapper/types/cosmos-async/body) | Request body. | 239 | | `headers` | [`Headers`](/docs/development/api-wrapper/types/cosmos-async/headers) | Request headers. | 240 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/methods/keyboard.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Keyboard 3 | description: A wrapper for keyboard shortcuts. 4 | --- 5 | 6 | Spicetify provides its own method for global keyboard shortcuts. You can specify actions for your extension when the user presses a keyboard shortcut. 7 | 8 | :::tip 9 | 10 | `Spicetify.Keyboard` is a wrapper of [`Spicetify.Mousetrap`](/docs/development/api-wrapper/modules#mousetrap) configured to be compatible with legacy Spotify. 11 | 12 | New extensions are advised to use the module instead. 13 | 14 | ::: 15 | 16 | :::caution 17 | 18 | All shortcuts registered by `Spicetify.Keyboard` are global. Be mindful of conflicts with other extensions or the Spotify client itself. 19 | 20 | ::: 21 | 22 | ```ts 23 | namespace Keyboard { 24 | const KEYS: Record; 25 | function registerShortcut(keys: KeysDefine, callback: (event: KeyboardEvent) => void): void; 26 | function _deregisterShortcut(keys: KeysDefine): void; 27 | function changeShortcut(keys: KeysDefine, newKeys: KeysDefine): void; 28 | }; 29 | ``` 30 | 31 | ## Properties 32 | 33 | ### `KEYS` 34 | 35 | An object containing a list of valid keys, mapped to their valid names. 36 | 37 | ```ts 38 | const { KEYS } = Spicetify.Keyboard; 39 | ``` 40 | 41 | Refer to [this table](/docs/development/api-wrapper/types/keyboard/validkey) for a list of valid keys. 42 | 43 | #### Example 44 | 45 | ```ts 46 | const { KEYS } = Spicetify.Keyboard; 47 | console.log(KEYS["CAPS"]); // "Capslock" 48 | ``` 49 | 50 | ## Methods 51 | 52 | ### `registerShortcut` 53 | 54 | Register a global keyboard shortcut. 55 | 56 | ```ts 57 | function registerShortcut(keys: KeysDefine, callback: (event: KeyboardEvent) => void): void; 58 | ``` 59 | 60 | #### Parameters 61 | 62 | | Parameter | Type | Description | 63 | | :--- | :--- | :--- | 64 | | keys | [`KeysDefine`](/docs/development/api-wrapper/types/keyboard/keysdefine) | Keyboard shortcut to register. | 65 | | callback | `(event: KeyboardEvent) => void` | Callback function to run when the shortcut is triggered. | 66 | 67 | #### Example 68 | 69 | ```ts 70 | // Equivalent to `Spicetify.Keyboard.registerShortcut({ key: "p", ctrl: true, shift: true }, (event) => { ... })` 71 | Spicetify.Keyboard.registerShortcut("ctrl+shift+p", (event) => { 72 | // Do something with the event 73 | Spicetify.showNotification("Shortcut triggered!"); 74 | }); 75 | ``` 76 | 77 | ### `_deregisterShortcut` 78 | 79 | Deregister a global keyboard shortcut. 80 | 81 | ```ts 82 | function _deregisterShortcut(keys: KeysDefine): void; 83 | ``` 84 | 85 | #### Parameters 86 | 87 | | Parameter | Type | Description | 88 | | :--- | :--- | :--- | 89 | | keys | [`KeysDefine`](/docs/development/api-wrapper/types/keyboard/keysdefine) | Keyboard shortcut to deregister. | 90 | 91 | #### Example 92 | 93 | ```ts 94 | Spicetify.Keyboard._deregisterShortcut("ctrl+shift+p"); 95 | ``` 96 | 97 | ### `changeShortcut` 98 | 99 | Change a global keyboard shortcut to a new shortcut while keeping the callback. 100 | 101 | ```ts 102 | function changeShortcut(keys: KeysDefine, newKeys: KeysDefine): void; 103 | ``` 104 | 105 | #### Parameters 106 | 107 | | Parameter | Type | Description | 108 | | :--- | :--- | :--- | 109 | | keys | [`KeysDefine`](/docs/development/api-wrapper/types/keyboard/keysdefine) | Keyboard shortcut to change. | 110 | | newKeys | [`KeysDefine`](/docs/development/api-wrapper/types/keyboard/keysdefine) | New keyboard shortcut to change to. | 111 | 112 | #### Example 113 | 114 | ```ts 115 | Spicetify.Keyboard.changeShortcut("ctrl+shift+p", "ctrl+shift+o"); 116 | ``` 117 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/methods/local-storage.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: LocalStorage 3 | description: Get and set data in local storage. 4 | --- 5 | 6 | Spicetify provides a wrapper for `localStorage` to make it easier to use. 7 | 8 | :::tip 9 | 10 | All keys created via this method are generic and stored as-is. 11 | 12 | If you wish to store values that are specific for each user account, you can use [`Platform.LocalStorageAPI`](/docs/development/api-wrapper/methods/platform#localstorageapi) instead. 13 | 14 | ::: 15 | 16 | ```ts 17 | namespace LocalStorage { 18 | function clear(): void; 19 | function get(key: string): string | null; 20 | function remove(key: string): void; 21 | function set(key: string, value: string): void; 22 | }; 23 | ``` 24 | 25 | ## Methods 26 | 27 | ### `clear` 28 | 29 | Empties the list associated with the object of all key/value pairs, if there are any. 30 | 31 | :::warning 32 | 33 | This method will remove all data in local storage, not just the data that Spicetify uses. This essentially resets the client to its default state. 34 | 35 | It will also wipe all data stored by extensions and custom apps (e.g. Marketplace, Lyrics Plus, etc.) 36 | 37 | ::: 38 | 39 | ```ts 40 | clear(): void 41 | ``` 42 | 43 | ### `get` 44 | 45 | Get key value from local storage. 46 | 47 | ```ts 48 | get(key: string): string | null 49 | ``` 50 | 51 | | Parameter | Type | Description | 52 | | :--- | :--- | :--- | 53 | | key | `string` | Key to get value from. | 54 | 55 | #### Example 56 | 57 | ```ts 58 | const value = Spicetify.LocalStorage.get("foo"); 59 | ``` 60 | 61 | ### `remove` 62 | 63 | Delete key from local storage. 64 | 65 | ```ts 66 | remove(key: string): void 67 | ``` 68 | 69 | | Parameter | Type | Description | 70 | | :--- | :--- | :--- | 71 | | key | `string` | Key to delete. | 72 | 73 | #### Example 74 | 75 | ```ts 76 | Spicetify.LocalStorage.remove("foo"); 77 | ``` 78 | 79 | ### `set` 80 | 81 | Set new value for key in local storage. 82 | 83 | ```ts 84 | set(key: string, value: string): void 85 | ``` 86 | 87 | | Parameter | Type | Description | 88 | | :--- | :--- | :--- | 89 | | key | `string` | Key to set value for. | 90 | | value | `string` | Value to set. | 91 | 92 | #### Example 93 | 94 | ```ts 95 | Spicetify.LocalStorage.set("foo", "bar"); 96 | ``` 97 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/methods/popup-modal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PopupModal 3 | description: Set of methods to create and control popup modals. 4 | --- 5 | 6 | Spicetify provides a set of methods to create and control popup modals. This will display a modal on top of the client, which can be used to display information or ask for user input. 7 | 8 | ```ts 9 | namespace PopupModal { 10 | interface Content { 11 | title: string; 12 | content: string | Element; 13 | isLarge?: boolean; 14 | } 15 | 16 | function display(content: Content): void; 17 | function hide(): void; 18 | }; 19 | ``` 20 | 21 | ## Interface 22 | 23 | ### `Content` 24 | 25 | `Content` is an object that contains the information needed to display the modal. 26 | 27 | | Property | Type | Description | 28 | | --- | --- | --- | 29 | | `title` | `string` | Title of the modal. | 30 | | `content` | `string` | Content of the modal. You can specify a string for simple text display or an HTML element for interactive config/setting menu. | 31 | | `isLarge` | `boolean` | `undefined` | Bigger modal. | 32 | 33 | ## Methods 34 | 35 | ### `display` 36 | 37 | Displays a modal on top of the client. 38 | 39 | :::note 40 | 41 | This method will replace the current modal if there is one. 42 | 43 | ::: 44 | 45 | | Parameter | Type | Description | 46 | | --- | --- | --- | 47 | | `content` | [`Content`](#content) | Information about the modal. | 48 | 49 | ```ts 50 | Spicetify.PopupModal.display({ 51 | title: 'Hello World', 52 | content: 'This is a simple text', 53 | }); 54 | ``` 55 | 56 | ### `hide` 57 | 58 | Hides the current modal. 59 | 60 | :::note 61 | 62 | This method will hide *any* modal currently displayed via `Spicetify.PopupModal.display`. 63 | 64 | ::: 65 | 66 | ```ts 67 | Spicetify.PopupModal.hide(); 68 | ``` 69 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/modules.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Modules 3 | description: 🧩 Modules exposed via Spicetify object. 4 | --- 5 | 6 | Spicetify exposes some modules via `Spicetify` object. 7 | 8 | You can access them by typing `Spicetify.` in the DevTools console, inside your extension, or `window.top.Spicetify.` if you're developing an app inside an `iframe`. 9 | 10 | Utilizing these modules can help you create more powerful extensions without having to include the whole module in your extension. 11 | 12 | ```js 13 | Spicetify.React; 14 | ``` 15 | 16 | For usage of these modules, please refer to their official documentation. 17 | 18 | ### React 19 | 20 | [React](https://reactjs.org/) is a JavaScript library for building user interfaces. It is used by Spotify to build their UI. 21 | 22 | :::note 23 | 24 | Spotify versions *below* 1.2.26 use version **17.0.2**, *after* - **18.2.0**. 25 | 26 | ::: 27 | 28 | ```js 29 | Spicetify.React; 30 | ``` 31 | 32 | ### ReactDOM 33 | 34 | [ReactDOM](https://reactjs.org/docs/react-dom.html) is a package that provides DOM-specific methods that can be used at the top level of your app and as an escape hatch to get outside of the React model if you need to. It is used by Spotify to render React components to the DOM. 35 | 36 | ```js 37 | Spicetify.ReactDOM; 38 | ``` 39 | 40 | ### Tippy.js 41 | 42 | [Tippy.js](https://atomiks.github.io/tippyjs/) is a highly customizable tooltip and popover library powered by Popper. 43 | 44 | ```js 45 | Spicetify.Tippy; 46 | ``` 47 | 48 | ### Mousetrap 49 | 50 | [Mousetrap](https://craig.is/killing/mice) is a simple library for handling keyboard shortcuts in JavaScript. 51 | 52 | ```js 53 | Spicetify.Mousetrap; 54 | ``` 55 | 56 | ### React Flip Toolkit 57 | 58 | [React Flip Toolkit](https://github.com/aholachek/react-flip-toolkit) is a collection of easy-to-use animation effects and utilities that can be used to enhance your React project. 59 | 60 | ```js 61 | Spicetify.ReactFlipToolkit; 62 | ``` 63 | 64 | ### React Query (v3) 65 | 66 | [React Query](https://react-query.tanstack.com/) is a library for managing, caching, syncing, and refetching server state in React. 67 | 68 | :::note 69 | 70 | Spotify uses React Query v3, instead of the current latest version (v4). As such, the API may be different from the official documentation. 71 | 72 | ::: 73 | 74 | ```js 75 | Spicetify.ReactQuery; 76 | ``` 77 | 78 | ### classnames 79 | 80 | [classnames](https://github.com/JedWatson/classnames) is a simple JavaScript utility for conditionally joining class names together. 81 | 82 | ```js 83 | Spicetify.classnames; 84 | ``` 85 | 86 | ### Snackbar 87 | 88 | [Notistack](https://github.com/iamhosseindhv/notistack) is a JavaScript library for creating highly customizable notification snackbars (toasts) that can be stacked on top of each other. 89 | 90 | :::note 91 | 92 | Be aware that only `SnackbarProvider` and `useSnackbar` work as described in the official Notistack documentation. 93 | 94 | ::: 95 | 96 | ```js 97 | Spicetify.Snackbar; 98 | ``` 99 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/properties/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Config 3 | description: 🛠️ Accessing a copy of Spicetify's `config-xpui.ini` file inside your extension. 4 | --- 5 | 6 | To make it easier for you to validate and debug your extensions, Spicetify provides a filtered copy of the user's `config-xpui.ini` in the `Spicetify` object. 7 | 8 | ```ts 9 | interface Config { 10 | version: string; 11 | current_theme: string; 12 | color_scheme: string; 13 | extensions: string[]; 14 | custom_apps: string[]; 15 | } 16 | ``` 17 | 18 | | Property | Type | Description | 19 | | --- | --- | --- | 20 | | `version` | `string` | Spicetify version. | 21 | | `current_theme` | `string` | Current theme name. | 22 | | `color_scheme` | `string` | Current color scheme name. | 23 | | `extensions` | `string[]` | List of enabled extensions. | 24 | | `custom_apps` | `string[]` | List of enabled custom apps. | 25 | 26 | ## Usage 27 | 28 | You can validate if the user currently has a custom app or a theme enabled by checking if the app or theme's name is included in the `custom_apps` or `current_theme` property of the `Config` object. 29 | 30 | ```ts 31 | const { Config } = Spicetify; 32 | 33 | if (Config.custom_apps.includes("lyrics-plus")) { 34 | // Do something 35 | } 36 | ``` 37 | 38 | This can ensure that your extension doesn't break if the user doesn't have the required app or theme installed. 39 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/properties/queue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Queue 3 | description: An object containing information about the current queue. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | The `Queue` object contains a list of queuing tracks, history of played tracks, and current track metadata. 13 | 14 | ```ts 15 | Spicetify.Queue 16 | ``` 17 | 18 | ## Return 19 | 20 | ```ts 21 | const Queue: { 22 | nextTracks: ProvidedTrack[]; 23 | prevTracks: ProvidedTrack[]; 24 | queueRevision: string; 25 | track: ProvidedTrack; 26 | }; 27 | ``` 28 | 29 | | Property | Type | Description | 30 | | --- | --- | --- | 31 | | `nextTracks` | [`ProvidedTrack[]`](/docs/development/api-wrapper/types/provided-track) | List of next tracks. | 32 | | `prevTracks` | [`ProvidedTrack[]`](/docs/development/api-wrapper/types/provided-track) | List of previous tracks. | 33 | | `queueRevision` | `string` | Queue revision ID used internally by Spotify. | 34 | | `track` | [`ProvidedTrack`](/docs/development/api-wrapper/types/provided-track) | Current track. | 35 | 36 | ## Usage 37 | 38 | If you plan on developing extensions that need to access the current queue, you can use the `Spicetify.Queue` object. 39 | 40 | ```ts 41 | const queue = Spicetify.Queue; 42 | const currentTrack = queue.track; 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/properties/react-hook.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ReactHook 3 | description: Set of React hooks used by the Spotify client. 4 | --- 5 | 6 | Spicetify provides a set of React hooks used by the Spotify client. You can use these hooks to create a React component interactive with the client. 7 | 8 | :::note 9 | 10 | It is recommended that you be familiar with [`React`](https://react.dev/) before using these hooks. 11 | 12 | ::: 13 | 14 | ```ts 15 | namespace ReactHook { 16 | function DragHandler( 17 | uris?: string[], 18 | label?: string, 19 | contextUri?: string, 20 | sectionIndex?: number, 21 | dropOriginUri?: string 22 | ): (event: React.DragEvent, uris?: string[], label?: string, contextUri?: string, sectionIndex?: number) => void; 23 | function usePanelState(id: number): { toggle: () => void, isActive: boolean }; 24 | function useExtractedColor(uri: string, fallbackColor?: string, variant?: "colorRaw" | "colorLight" | "colorDark"): string; 25 | } 26 | ``` 27 | 28 | ## Hooks 29 | 30 | ### `DragHandler` 31 | 32 | React Hook to create interactive drag-and-drop element. 33 | 34 | Used to create a draggable element that can be dropped into Spotify's components (e.g. Playlist, Folder, Sidebar, Queue) 35 | 36 | ```ts 37 | function DragHandler( 38 | uris?: string[], 39 | label?: string, 40 | contextUri?: string, 41 | sectionIndex?: number, 42 | dropOriginUri?: string 43 | ): (event: React.DragEvent, uris?: string[], label?: string, contextUri?: string, sectionIndex?: number) => void; 44 | ``` 45 | 46 | #### Parameters 47 | 48 | | Parameter | Type | Description | 49 | | :--- | :--- | :--- | 50 | | uris | `string[]` | `undefined` | List of URIs to be dragged. | 51 | | label | `string` | `undefined` | Label to be displayed when dragging. | 52 | | contextUri | `string` | `undefined` | Context URI of the element from which the drag originated (e.g. Playlist URI). | 53 | | sectionIndex | `number` | `undefined` | Index of the section in which the drag originated. | 54 | | dropOriginUri | `string` | `undefined` | URI of the desired drop target. Leave empty to allow drop anywhere. | 55 | 56 | #### Returns 57 | 58 | Function to handle drag event. Should be passed to `onDragStart` prop of the element. All parameters passed onto the hook will be passed onto the handler unless declared otherwise. 59 | 60 | #### Example 61 | 62 | ```tsx 63 | const DraggableComponent = () => { 64 | // Do I Wanna Know? by Arctic Monkeys 65 | const uri = "spotify:track:5FVd6KXrgO9B3JPmC8OPst"; 66 | const label = "Do I Wanna Know? - Arctic Monkeys"; 67 | 68 | const handleDragStart = Spicetify.ReactHook.DragHandler([uri], label); 69 | 70 | return ( 71 |
72 | {label} 73 |
74 | ); 75 | } 76 | ``` 77 | 78 | ### `usePanelState` 79 | 80 | React Hook to use panel state. 81 | 82 | ```ts 83 | function usePanelState(id: number): { toggle: () => void, isActive: boolean }; 84 | ``` 85 | 86 | #### Parameters 87 | 88 | | Parameter | Type | Description | 89 | | :--- | :--- | :--- | 90 | | id | `number` | ID of the panel to use. | 91 | 92 | #### Returns 93 | 94 | Object with methods of the panel. 95 | 96 | | Property | Type | Description | 97 | | :--- | :--- | :--- | 98 | | toggle | `() => void` | Toggle the panel. | 99 | | isActive | `boolean` | Whether the panel is active. | 100 | 101 | #### Example 102 | 103 | ```tsx 104 | const PanelComponent = () => { 105 | // The ID can be either Spotify's default panel IDs or your custom panel ID registered via `Spicetify.Panel.registerPanel` 106 | const { toggle, isActive } = Spicetify.ReactHook.usePanelState(5); 107 | 108 | return ( 109 |
110 | 113 |
114 | ); 115 | } 116 | ``` 117 | 118 | ### `useExtractedColor` 119 | 120 | React Hook to use extracted color from GraphQL. 121 | 122 | ```ts 123 | function useExtractedColor(uri: string, fallbackColor?: string, variant?: "colorRaw" | "colorLight" | "colorDark"): string; 124 | ``` 125 | 126 | :::note 127 | 128 | This is a wrapper of ReactQuery's `useQuery` hook. The component using this hook must be wrapped in a `QueryClientProvider` component. 129 | 130 | Look into the example below for more information. 131 | 132 | ::: 133 | 134 | #### Parameters 135 | 136 | | Parameter | Type | Description | 137 | | :--- | :--- | :--- | 138 | | uri | `string` | URI of the Spotify image to extract color from. | 139 | | fallbackColor | `string` | `undefined` | Fallback color to use if the image is not available. Defaults to `#535353`. | 140 | | variant | `"colorRaw"` | `"colorLight"` | `"colorDark"` | `undefined` | Variant of the color to use. Defaults to `colorRaw`. | 141 | 142 | #### Returns 143 | 144 | Extracted color hex code. 145 | 146 | #### Example 147 | 148 | ```tsx 149 | import { useEffect, useState } from "react"; 150 | 151 | const { QueryClient, QueryClientProvider } = Spicetify.ReactQuery; 152 | const { useExtractedColor } = Spicetify.ReactHook; 153 | 154 | const queryClient = new QueryClient(); 155 | 156 | const Component = () => { 157 | const [imageUri, setImageUri] = useState(Spicetify.Player.data?.item?.metadata?.image_xlarge_url ?? ""); 158 | const color = useExtractedColor(imageUri); 159 | 160 | useEffect(() => { 161 | // Listen to track change 162 | const listener = () => { 163 | setImageUri(Spicetify.Player.data?.item?.metadata?.image_xlarge_url ?? ""); 164 | }; 165 | Spicetify.Player.addEventListener("songchange", listener); 166 | 167 | return () => Spicetify.Player.removeEventListener("songchange", listener); 168 | }, []); 169 | 170 | return ( 171 |
172 | Hello World 173 |
174 | ); 175 | } 176 | 177 | const App = () => { 178 | return ( 179 | 180 | 181 | 182 | ); 183 | } 184 | ``` 185 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/properties/svgicons.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SVGIcons 3 | description: A set of SVG icons used throughout the Spotify client. 4 | --- 5 | 6 | Spicetify has a predefined set of SVG icons that are used by Spotify throughout the client. These are strings of SVG `innerHTML` that are used to create `` elements. 7 | 8 | ```ts 9 | const SVGIcons = Record; 10 | ``` 11 | 12 | | Property | Type | Description | 13 | | --- | --- | --- | 14 | | `key` | [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) | SVG icon name. | 15 | 16 | ## Usage 17 | 18 | You can use these icons to create custom menu items or other custom components. 19 | 20 | In vanilla JavaScript, you can create an `` element and set its `innerHTML` to the SVG icon string. 21 | 22 | ```ts 23 | const icon = document.createElement("svg"); 24 | icon.innerHTML = Spicetify.SVGIcons["play"]; 25 | ``` 26 | 27 | In React, you can use the `dangerouslySetInnerHTML` prop to set the SVG icon string as the inner HTML of the `` element. 28 | 29 | ```tsx 30 | const icon = ; 31 | ``` 32 | 33 | In Spicetify's own methods, you can simply pass an [`SVGIcon`](/docs/development/api-wrapper/types/svgicon) to the `icon` parameter. 34 | 35 | ```ts 36 | new Spicetify.ContextMenu.Item( 37 | name: "My Custom Item". 38 | onClick: () => Spicetify.showNotification("Hello World!"), 39 | shouldAdd: () => true, 40 | icon: "play", 41 | disabled: false, 42 | ) 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/properties/tippy-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: TippyProps 3 | description: Predefined props for Tippy.js tooltips. 4 | --- 5 | 6 | Spicetify provides a set of predefined props for Tippy.js tooltips. This is aimed to create tooltips that mimic the style of Spotify's tooltips. 7 | 8 | This is utilized for [`Topbar`](/docs/development/api-wrapper/classes/topbar) and [`Playbar`](/docs/development/api-wrapper/classes/playbar) tooltips. 9 | 10 | ```ts 11 | Spicetify.TippyProps = { 12 | delay: [200, 0], 13 | animation: true, 14 | render(instance) { 15 | const popper = document.createElement('div'); 16 | const box = document.createElement('div'); 17 | 18 | popper.id = "context-menu"; 19 | popper.appendChild(box); 20 | 21 | box.className = "main-contextMenu-tippy" 22 | box.textContent = instance.props.content; 23 | 24 | function onUpdate(prevProps, nextProps) { 25 | if (prevProps.content !== nextProps.content) { 26 | if (nextProps.allowHTML) box.innerHTML = nextProps.content; 27 | else box.textContent = nextProps.content; 28 | } 29 | } 30 | 31 | return { popper, onUpdate } 32 | }, 33 | onShow(instance) { 34 | instance.popper.firstChild.classList.add("main-contextMenu-tippyEnter"); 35 | }, 36 | onMount(instance) { 37 | requestAnimationFrame(() => { 38 | instance.popper.firstChild.classList.remove("main-contextMenu-tippyEnter"); 39 | instance.popper.firstChild.classList.add("main-contextMenu-tippyEnterActive"); 40 | }); 41 | }, 42 | onHide(instance) { 43 | requestAnimationFrame(() => { 44 | instance.popper.firstChild.classList.remove("main-contextMenu-tippyEnterActive"); 45 | instance.unmount(); 46 | }); 47 | }, 48 | }, 49 | ``` 50 | 51 | #### Usage 52 | 53 | If you want to use this set of props for your own Tippy.js tooltips, you can simply spread the `Spicetify.TippyProps` object into your Tippy.js instance. 54 | 55 | ```ts 56 | const element = document.createElement("div"); 57 | 58 | const tooltip = tippy(element, { 59 | ...Spicetify.TippyProps, 60 | content: "Tooltip content", 61 | // For example, if you want to override the delay 62 | delay: [100, 0], 63 | }); 64 | ``` 65 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/context-menu/onclick-callback.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: OnClickCallback 3 | description: Type definition for callback function when menu item is clicked. 4 | --- 5 | 6 | ```ts 7 | type OnClickCallback = ( 8 | uris: string[], 9 | uids?: string[], 10 | contextUri?: string 11 | ) => void; 12 | ``` 13 | 14 | #### Parameters 15 | 16 | | Parameter | Type | Description | 17 | | :--- | :--- | :--- | 18 | | uris | `string[]` | List of URIs of the selected items. | 19 | | uids | `string[]` | `undefined` | List of UIDs of the selected items. **Note:** Not all context menu items have UIDs. | 20 | | contextUri | `string` | `undefined` | URI of the context menu where the item was called from. This could be a playlist, album, artist, or a track. | 21 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/context-menu/should-add-callback.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ShouldAddCallback 3 | description: Type definition for callback function to determine if menu item should be added. 4 | --- 5 | 6 | ```ts 7 | type ShouldAddCallback = ( 8 | uris: string[], 9 | uids?: string[], 10 | contextUri?: string 11 | ) => boolean; 12 | ``` 13 | 14 | #### Parameters 15 | 16 | | Parameter | Type | Description | 17 | | :--- | :--- | :--- | 18 | | uris | `string[]` | List of URIs of the selected items. | 19 | | uids | `string[]` | `undefined` | List of UIDs of the selected items. **Note:** Not all context menu items have UIDs. | 20 | | contextUri | `string` | `undefined` | URI of the context menu where the item was called from. This could be a playlist, album, artist, or a track. | 21 | 22 | #### Return value 23 | 24 | `boolean` - Whether the menu item should be added. 25 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/context-option.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ContextOption 3 | description: ContextOption type definition. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | ```ts 13 | type ContextOption = { 14 | contextURI?: string; 15 | index?: number; 16 | trackUri?: string; 17 | page?: number; 18 | trackUid?: string; 19 | sortedBy?: string; 20 | filteredBy?: string; 21 | shuffleContext?: boolean; 22 | repeatContext?: boolean; 23 | repeatTrack?: boolean; 24 | offset?: number; 25 | next_page_url?: string; 26 | restrictions?: Record; 27 | referrer?: string; 28 | }; 29 | ``` 30 | 31 | | Property | Type | Description | 32 | | --- | --- | --- | 33 | | `contextURI` | `string` | `undefined` | Context URI. | 34 | | `index` | `number` | `undefined` | Track index. | 35 | | `trackUri` | `string` | `undefined` | Track URI. | 36 | | `page` | `number` | `undefined` | Page number. | 37 | | `trackUid` | `string` | `undefined` | Track UID. | 38 | | `sortedBy` | `string` | `undefined` | Sorted by timestamp. | 39 | | `filteredBy` | `string` | `undefined` | Filtered by timestamp. | 40 | | `shuffleContext` | `boolean` | `undefined` | Shuffle context URI. | 41 | | `repeatContext` | `boolean` | `undefined` | Repeat context URI. | 42 | | `repeatTrack` | `boolean` | `undefined` | Repeat track URI. | 43 | | `offset` | `number` | `undefined` | Offset. | 44 | | `next_page_url` | `string` | `undefined` | Next page URL. | 45 | | `restrictions` | `Record` | `undefined` | Restrictions. | 46 | | `referrer` | `string` | `undefined` | Referrer. | 47 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/context-track.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ContextTrack 3 | description: ContextTrack type definition. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | ```ts 13 | type ContextTrack = { 14 | uri: string; 15 | uid?: string | null; 16 | metadata?: Metadata; 17 | } 18 | ``` 19 | 20 | | Property | Type | Description | 21 | | --- | --- | --- | 22 | | `uri` | `string` | Track URI. | 23 | | `uid` | `string` | `undefined` | `null` | Track UID. | 24 | | `metadata` | [`Metadata`](/docs/development/api-wrapper/types/metadata) | `undefined` | Track metadata. | 25 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/cosmos-async/body.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Body 3 | description: CosmosAsync Body type definition. 4 | --- 5 | 6 | Parsed JSON response body. 7 | 8 | ```ts 9 | type Body = Record; 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/cosmos-async/error.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Error 3 | description: CosmosAsync Error type definition. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | ```ts 13 | interface Error { 14 | code: number; 15 | error: string; 16 | message: string; 17 | stack?: string; 18 | }; 19 | ``` 20 | 21 | | Property | Type | Description | 22 | | --- | --- | --- | 23 | | `code` | `number` | HTML error code. | 24 | | `error` | `string` | HTML error name. | 25 | | `message` | `string` | Error message. | 26 | | `stack` | `string` | `undefined` | Error stack. | 27 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/cosmos-async/headers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Headers 3 | description: CosmosAsync Headers type definition. 4 | --- 5 | 6 | Equivalent to `XMLHttpRequest`'s `headers` property. 7 | 8 | ```ts 9 | type Headers = Record; 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/cosmos-async/method.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Method 3 | description: CosmosAsync Method type definition. 4 | --- 5 | 6 | Equivalent to `XMLHttpRequest`'s `method` property. 7 | 8 | ```ts 9 | type Method = "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | "SUB"; 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/cosmos-async/response.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Response 3 | description: CosmosAsync Response type definition. 4 | --- 5 | 6 | Represents a response from a CosmosAsync request. 7 | 8 | :::note 9 | 10 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 11 | 12 | ::: 13 | 14 | ```ts 15 | interface Response { 16 | body: any; 17 | headers: Headers; 18 | status: number; 19 | uri?: string; 20 | } 21 | ``` 22 | 23 | | Property | Type | Description | 24 | | --- | --- | --- | 25 | | `body` | [`Body`](./body.md) | Parsed JSON response body. | 26 | | `headers` | [`Headers`](./headers.md) | Response headers. | 27 | | `status` | `number` | HTTP status code. | 28 | | `uri` | `string` | `undefined` | Request URI. | 29 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/graphql/query.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Query 3 | description: List of GraphQL definitions used by Spotify. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | ```ts 13 | type Query = "decorateItemsForEnhance" | 14 | "imageURLAndSize" | 15 | "imageSources" | 16 | "audioItems" | 17 | "creator" | 18 | "extractedColors" | 19 | "extractedColorsAndImageSources" | 20 | "fetchExtractedColorAndImageForAlbumEntity" | 21 | "fetchExtractedColorAndImageForArtistEntity" | 22 | "fetchExtractedColorAndImageForEpisodeEntity" | 23 | "fetchExtractedColorAndImageForPlaylistEntity" | 24 | "fetchExtractedColorAndImageForPodcastEntity" | 25 | "fetchExtractedColorAndImageForTrackEntity" | 26 | "fetchExtractedColorForAlbumEntity" | 27 | "fetchExtractedColorForArtistEntity" | 28 | "fetchExtractedColorForEpisodeEntity" | 29 | "fetchExtractedColorForPlaylistEntity" | 30 | "fetchExtractedColorForPodcastEntity" | 31 | "fetchExtractedColorForTrackEntity" | 32 | "getAlbumNameAndTracks" | 33 | "getEpisodeName" | 34 | "getTrackName" | 35 | "queryAlbumTrackUris" | 36 | "queryTrackArtists" | 37 | "decorateContextEpisodesOrChapters" | 38 | "decorateContextTracks" | 39 | "fetchTracksForRadioStation" | 40 | "decoratePlaylists" | 41 | "playlistUser" | 42 | "FetchPlaylistMetadata" | 43 | "playlistContentsItemTrackArtist" | 44 | "playlistContentsItemTrackAlbum" | 45 | "playlistContentsItemTrack" | 46 | "playlistContentsItemLocalTrack" | 47 | "playlistContentsItemEpisodeShow" | 48 | "playlistContentsItemEpisode" | 49 | "playlistContentsItemResponse" | 50 | "playlistContentsItem" | 51 | "FetchPlaylistContents" | 52 | "episodeTrailerUri" | 53 | "podcastEpisode" | 54 | "podcastMetadataV2" | 55 | "minimalAudiobook" | 56 | "audiobookChapter" | 57 | "audiobookMetadataV2" | 58 | "fetchExtractedColors" | 59 | "queryFullscreenMode" | 60 | "queryNpvEpisode" | 61 | "queryNpvArtist" | 62 | "albumTrack" | 63 | "getAlbum" | 64 | "queryAlbumTracks" | 65 | "queryArtistOverview" | 66 | "queryArtistAppearsOn" | 67 | "discographyAlbum" | 68 | "albumMetadataReleases" | 69 | "albumMetadata" | 70 | "queryArtistDiscographyAlbums" | 71 | "queryArtistDiscographySingles" | 72 | "queryArtistDiscographyCompilations" | 73 | "queryArtistDiscographyAll" | 74 | "queryArtistDiscographyOverview" | 75 | "artistPlaylist" | 76 | "queryArtistPlaylists" | 77 | "queryArtistDiscoveredOn" | 78 | "queryArtistFeaturing" | 79 | "queryArtistRelated" | 80 | "queryArtistMinimal" | 81 | "searchModalResults" | 82 | "queryWhatsNewFeed" | 83 | "whatsNewFeedNewItems" | 84 | "SetItemsStateInWhatsNewFeed" | 85 | "browseImageURLAndSize" | 86 | "browseImageSources" | 87 | "browseAlbum" | 88 | "browseArtist" | 89 | "browseEpisode" | 90 | "browseChapter" | 91 | "browsePlaylist" | 92 | "browsePodcast" | 93 | "browseAudiobook" | 94 | "browseTrack" | 95 | "browseUser" | 96 | "browseMerch" | 97 | "browseArtistConcerts" | 98 | "browseContent" | 99 | "browseSectionContainer" | 100 | "browseClientFeature" | 101 | "browseItem" | 102 | "browseAll" | 103 | "browsePage"; 104 | ``` -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/keyboard/keysdefine.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: KeysDefine 3 | description: Keyboard KeyDefine type definition. 4 | --- 5 | 6 | ```ts 7 | type KeysDefine = string | { 8 | key: string; 9 | ctrl?: boolean; 10 | shift?: boolean; 11 | alt?: boolean; 12 | meta?: boolean; 13 | }; 14 | ``` 15 | 16 | `KeysDefine` is a type that defines a keyboard shortcut. It can be a string or an object. 17 | 18 | In the string format, it should be a list of keys separated by `+`. For example, `ctrl+shift+p` is a valid shortcut. 19 | 20 | In the object format, it should be an object with the following properties: 21 | 22 | | Property | Type | Description | 23 | | --- | --- | --- | 24 | | `key` | `string` | Key name. Refer to [this table](/docs/development/api-wrapper/types/keyboard/validkey) for a list of valid keys. | 25 | | `ctrl` | `boolean` | `undefined` | Whether to require `CTRL` key. | 26 | | `shift` | `boolean` | `undefined` | Whether to require `SHIFT` key. | 27 | | `alt` | `boolean` | `undefined` | Whether to require `ALT` key. | 28 | | `meta` | `boolean` | `undefined` | Whether to require the meta key. This could be the 🪟 key on Windows or the key on Mac. | 29 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/keyboard/validkey.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ValidKey 3 | description: A list of valid keys for keyboard shortcuts. 4 | --- 5 | 6 | :::tip 7 | 8 | For the list of valid keys, refer to the [type definition](#type-definition). 9 | 10 | ::: 11 | 12 | ### Key/value pairs 13 | 14 | |Key|Value| 15 | |---|---| 16 | |BACKSPACE|backspace| 17 | |TAB|tab| 18 | |ENTER|enter| 19 | |SHIFT|shift| 20 | |CTRL|ctrl| 21 | |ALT|alt| 22 | |CAPS|capslock| 23 | |ESCAPE|esc| 24 | |SPACE|space| 25 | |PAGE_UP|pageup| 26 | |PAGE_DOWN|pagedown| 27 | |END|end| 28 | |HOME|home| 29 | |ARROW_LEFT|left| 30 | |ARROW_UP|up| 31 | |ARROW_RIGHT|right| 32 | |ARROW_DOWN|down| 33 | |INSERT|ins| 34 | |DELETE|del| 35 | |A|a| 36 | |B|b| 37 | |C|c| 38 | |D|d| 39 | |E|e| 40 | |F|f| 41 | |G|g| 42 | |H|h| 43 | |I|i| 44 | |J|j| 45 | |K|k| 46 | |L|l| 47 | |M|m| 48 | |N|n| 49 | |O|o| 50 | |P|p| 51 | |Q|q| 52 | |R|r| 53 | |S|s| 54 | |T|t| 55 | |U|u| 56 | |V|v| 57 | |W|w| 58 | |X|x| 59 | |Y|y| 60 | |Z|z| 61 | |WINDOW_LEFT|meta| 62 | |WINDOW_RIGHT|meta| 63 | |SELECT|meta| 64 | |NUMPAD_0|0| 65 | |NUMPAD_1|1| 66 | |NUMPAD_2|2| 67 | |NUMPAD_3|3| 68 | |NUMPAD_4|4| 69 | |NUMPAD_5|5| 70 | |NUMPAD_6|6| 71 | |NUMPAD_7|7| 72 | |NUMPAD_8|8| 73 | |NUMPAD_9|9| 74 | |MULTIPLY|*| 75 | |ADD|+| 76 | |SUBTRACT|-| 77 | |DECIMAL_POINT|.| 78 | |DIVIDE|/| 79 | |F1|f1| 80 | |F2|f2| 81 | |F3|f3| 82 | |F4|f4| 83 | |F5|f5| 84 | |F6|f6| 85 | |F7|f7| 86 | |F8|f8| 87 | |F9|f9| 88 | |F10|f10| 89 | |F11|f11| 90 | |F12|f12| 91 | |;|;| 92 | |=|=| 93 | |,|,| 94 | |-|-| 95 | |.|.| 96 | |/|/| 97 | |`|`| 98 | |[|[| 99 | |\\|\\| 100 | |]|]| 101 | |"|'| 102 | |~|`| 103 | |!|1| 104 | |@|2| 105 | |#|3| 106 | |$|4| 107 | |%|5| 108 | |^|6| 109 | |&|7| 110 | |*|8| 111 | |(|9| 112 | |)|0| 113 | |_|-| 114 | |+|=| 115 | |:|;| 116 | |\<|,| 117 | |\>|.| 118 | |?|/| 119 | |||\\| 120 | 121 | ### Type definition 122 | 123 | ```ts 124 | type ValidKey = "BACKSPACE" | 125 | "TAB" | 126 | "ENTER" | 127 | "SHIFT" | 128 | "CTRL" | 129 | "ALT" | 130 | "CAPS" | 131 | "ESCAPE" | 132 | "SPACE" | 133 | "PAGE_UP" | 134 | "PAGE_DOWN" | 135 | "END" | 136 | "HOME" | 137 | "ARROW_LEFT" | 138 | "ARROW_UP" | 139 | "ARROW_RIGHT" | 140 | "ARROW_DOWN" | 141 | "INSERT" | 142 | "DELETE" | 143 | "A" | 144 | "B" | 145 | "C" | 146 | "D" | 147 | "E" | 148 | "F" | 149 | "G" | 150 | "H" | 151 | "I" | 152 | "J" | 153 | "K" | 154 | "L" | 155 | "M" | 156 | "N" | 157 | "O" | 158 | "P" | 159 | "Q" | 160 | "R" | 161 | "S" | 162 | "T" | 163 | "U" | 164 | "V" | 165 | "W" | 166 | "X" | 167 | "Y" | 168 | "Z" | 169 | "WINDOW_LEFT" | 170 | "WINDOW_RIGHT" | 171 | "SELECT" | 172 | "NUMPAD_0" | 173 | "NUMPAD_1" | 174 | "NUMPAD_2" | 175 | "NUMPAD_3" | 176 | "NUMPAD_4" | 177 | "NUMPAD_5" | 178 | "NUMPAD_6" | 179 | "NUMPAD_7" | 180 | "NUMPAD_8" | 181 | "NUMPAD_9" | 182 | "MULTIPLY" | 183 | "ADD" | 184 | "SUBTRACT" | 185 | "DECIMAL_POINT" | 186 | "DIVIDE" | 187 | "F1" | 188 | "F2" | 189 | "F3" | 190 | "F4" | 191 | "F5" | 192 | "F6" | 193 | "F7" | 194 | "F8" | 195 | "F9" | 196 | "F10" | 197 | "F11" | 198 | "F12" | 199 | ";" | 200 | "=" | 201 | " | 202 | " | 203 | "-" | 204 | "." | 205 | "/" | 206 | "`" | 207 | "[" | 208 | "\\" | 209 | "]" | 210 | "\"" | 211 | "~" | 212 | "!" | 213 | "@" | 214 | "#" | 215 | "$" | 216 | "%" | 217 | "^" | 218 | "&" | 219 | "*" | 220 | "(" | 221 | ")" | 222 | "_" | 223 | "+" | 224 | ":" | 225 | "<" | 226 | ">" | 227 | "?" | 228 | "|"; 229 | ``` 230 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/metadata.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Metadata 3 | description: Type of metadata object. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | ```ts 13 | type Metadata = Partial>; 14 | ``` 15 | 16 | An example of metadata object, **not** a type definition, may not include all properties. 17 | 18 | ```ts 19 | type Metadata = { 20 | actions.skipping_next_past_track: string; 21 | actions.skipping_prev_past_track: string; 22 | added_at: `${bigint}`; 23 | album_artist_name: string; 24 | album_disc_count: `${number}`; 25 | album_disc_number: `${number}`; 26 | album_title: string; 27 | album_track_count: `${number}`; 28 | album_track_number: `${number}`; 29 | album_uri: string; 30 | artist_name: string; 31 | artist_uri: string; 32 | // URL 33 | canvas.artist.avatar: string; 34 | canvas.artist.name: string; 35 | canvas.artist.uri: string; 36 | canvas.canvasUri: string; 37 | canvas.entityUri: string; 38 | canvas.explicit: "true" | "false"; 39 | canvas.fileId: string; 40 | canvas.id: string; 41 | canvas.type: string; 42 | canvas.uploadedBy: string; 43 | // URL 44 | canvas.url: string; 45 | collection.can_add: "true" | "false"; 46 | collection.can_ban: "true" | "false"; 47 | collection.in_collection: "true" | "false"; 48 | collection.is_banned: "true" | "false"; 49 | context_uri: string; 50 | duration: `${bigint}`; 51 | entity_uri: string; 52 | has_lyrics: "true" | "false"; 53 | // Internal URL paths, not URLs 54 | image_large_url: string; 55 | image_small_url: string; 56 | image_url: string; 57 | image_xlarge_url: string; 58 | interaction_id: string; 59 | iteration: `${number}`; 60 | marked_for_download: "true" | "false"; 61 | page_instance_id: string; 62 | popularity: `${number}`; 63 | title: string; 64 | track_player: string; 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/panel/panel-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PanelProps 3 | description: Properties that are used by the `registerPanel` function. 4 | --- 5 | 6 | ```ts 7 | type PanelProps = { 8 | label?: string; 9 | children: React.ReactNode; 10 | isCustom?: boolean; 11 | style?: React.CSSProperties; 12 | wrapperClassname?: string; 13 | headerClassname?: string; 14 | headerVariant?: Variant; 15 | headerSemanticColor?: SemanticColor; 16 | headerLink?: string; 17 | headerActions?: React.ReactNode; 18 | headerOnClose?: () => void; 19 | headerPreventDefaultClose?: boolean; 20 | headerOnBack?: (event: React.MouseEvent) => void; 21 | }; 22 | ``` 23 | 24 | | Property | Type | Description | 25 | | --- | --- | --- | 26 | | `label` | `string` | `undefined` | Label of the Panel. | 27 | | `children` | `React.ReactNode` | Children to render inside the Panel.
Must be a React Component. | 28 | | `isCustom` | `boolean` | `undefined` | Determine if the children passed is a custom Panel.
If true, the children will be rendered as is.
**Note**: All passed props except `children` will be ignored if enabled. | 29 | | `style` | `React.CSSProperties` | `undefined` | Inline styles to apply to the Panel skeleton. | 30 | | `wrapperClassname` | `string` | `undefined` | Additional class name to apply to the Panel content wrapper. | 31 | | `headerClassname` | `string` | `undefined` | Additional class name to apply to the Panel header. | 32 | | `headerVariant` | [`Variant`](/docs/development/api-wrapper/types/variant) | `undefined` | Font variant for the Panel header title. | 33 | | `headerSemanticColor` | [`SemanticColor`](/docs/development/api-wrapper/types/semantic-color) | `undefined` | Semantic color name for the Panel header title. | 34 | | `headerLink` | `string` | `undefined` | Href for the header link.
Can be either a URI, a path within the app, or a URL for an external link. | 35 | | `headerActions` | `React.ReactNode` | `undefined` | Additional actions to render in the header.
Will be rendered next to the close button. | 36 | | `headerOnClose` | `() => void` | `undefined` | Function to call when clicking on the header close button.
Called before the panel is closed. | 37 | | `headerPreventDefaultClose` | `boolean` | `undefined` | Prevent the panel from closing when clicking on the header close button. | 38 | | `headerOnBack` | `(event: React.MouseEvent) => void` | `undefined` | Function to call when clicking on the header back button.
If not provided, the back button will not be rendered. | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/player-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PlayerState 3 | description: PlayerState type definition. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | ```ts 13 | type PlayerState = { 14 | timestamp: number; 15 | context_uri: string; 16 | context_url: string; 17 | context_restrictions: Record; 18 | index?: { 19 | page: number; 20 | track: number; 21 | }; 22 | track?: ProvidedTrack; 23 | playback_id?: string; 24 | playback_quality?: { 25 | bitrate_level: string; 26 | hifi_status?: string; 27 | strategy?: string; 28 | target_bitrate_available?: boolean; 29 | target_bitrate_level?: string; 30 | } 31 | playback_speed?: number; 32 | position_as_of_timestamp: number; 33 | duration: number; 34 | is_playing: boolean; 35 | is_paused: boolean; 36 | is_buffering: boolean; 37 | play_origin: { 38 | feature_identifier: string; 39 | feature_version: string; 40 | view_uri?: string; 41 | external_referrer?: string; 42 | referrer_identifier?: string; 43 | device_identifier?: string; 44 | }; 45 | options: { 46 | shuffling_context?: boolean; 47 | repeating_context?: boolean; 48 | repeating_track?: boolean; 49 | }; 50 | restrictions: Record; 51 | suppressions: { 52 | providers: string[]; 53 | }; 54 | debug?: { 55 | log: string[]; 56 | }; 57 | prev_tracks?: ProvidedTrack[]; 58 | next_tracks?: ProvidedTrack[]; 59 | context_metadata: Metadata; 60 | page_metadata: Metadata; 61 | session_id: string; 62 | queue_revision?: string; 63 | }; 64 | ``` 65 | 66 | | Property | Type | Description | 67 | | --- | --- | --- | 68 | | `timestamp` | `number` | Timestamp. | 69 | | `context_uri` | `string` | Context URI from which the track was played. | 70 | | `context_url` | `string` | Context internal URL. | 71 | | `context_restrictions` | `Record` | Context restrictions. | 72 | | `index` | `object` | `undefined` | Track index. | 73 | | `track` | [`ProvidedTrack`](/docs/development/api-wrapper/types/provided-track) | `undefined` | Current track. | 74 | | `playback_id` | `string` | `undefined` | Playback ID. | 75 | | `playback_quality` | `object` | `undefined` | Playback quality. | 76 | | `playback_speed` | `number` | `undefined` | Playback speed. | 77 | | `position_as_of_timestamp` | `number` | Position as of timestamp. Relative to the track's start. | 78 | | `duration` | `number` | Track duration. | 79 | | `is_playing` | `boolean` | Whether the track is playing. | 80 | | `is_paused` | `boolean` | Whether the track is paused. | 81 | | `is_buffering` | `boolean` | Whether the track is buffering. | 82 | | `play_origin` | `object` | Play origin (client info). | 83 | | `options` | `object` | Repeat and shuffle state. | 84 | | `restrictions` | `Record` | Restrictions. | 85 | | `suppressions` | `object` | Suppressions from providers. | 86 | | `debug` | `object` | `undefined` | Debug info. | 87 | | `prev_tracks` | [`ProvidedTrack[]`](/docs/development/api-wrapper/types/provided-track) | `undefined` | Previous tracks. | 88 | | `next_tracks` | [`ProvidedTrack[]`](/docs/development/api-wrapper/types/provided-track) | `undefined` | Next tracks. | 89 | | `context_metadata` | [`Metadata`](/docs/development/api-wrapper/types/metadata) | Context metadata. | 90 | | `page_metadata` | [`Metadata`](/docs/development/api-wrapper/types/metadata) | Page metadata. | 91 | | `session_id` | `string` | Session ID. | 92 | | `queue_revision` | `string` | `undefined` | Queue revision. | 93 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/provided-track.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ProvidedTrack 3 | description: ProvidedTrack type definition. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | ```ts 13 | type ProvidedTrack = ContextTrack & { 14 | removed?: string[]; 15 | blocked?: string[]; 16 | provider?: string; 17 | }; 18 | ``` 19 | 20 | Extends [`ContextTrack`](/docs/development/api-wrapper/types/context-track). 21 | 22 | | Property | Type | Description | 23 | | --- | --- | --- | 24 | | `removed` | `string[]` | `undefined` | List of removed providers. | 25 | | `blocked` | `string[]` | `undefined` | List of blocked providers. | 26 | | `provider` | `string` | Current provider, eg. from `context` | 27 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/confirm-dialog-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ConfirmDialogProps 3 | description: Type definition for props of ReactComponent.ConfirmDialog 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | The `ConfirmDialogProps` object is used to create a confirm dialog. 13 | 14 | ```ts 15 | type ConfirmDialogProps = { 16 | isOpen?: boolean; 17 | allowHTML?: boolean; 18 | titleText: string; 19 | descriptionText?: string; 20 | confirmText?: string; 21 | cancelText?: string; 22 | confirmLabel?: string; 23 | onConfirm?: (event: React.MouseEvent) => void; 24 | onClose?: (event: React.MouseEvent) => void; 25 | onOutside?: (event: React.MouseEvent) => void; 26 | }; 27 | ``` 28 | 29 | #### Properties 30 | 31 | | Property | Type | Description | 32 | | :--- | :--- | :--- | 33 | | `isOpen` | `boolean` | `undefined` | Boolean to determine if the dialog should be opened. Defaults to `true` | 34 | | `allowHTML` | `boolean` | `undefined` | Whether to allow inline HTML in component text. Defaults to `false` | 35 | | `titleText` | `string` | Dialog title. Can be inline HTML if `allowHTML` is true | 36 | | `descriptionText` | `string` | `undefined` | Dialog description. Can be inline HTML if `allowHTML` is true | 37 | | `confirmText` | `string` | `undefined` | Confirm button text | 38 | | `cancelText` | `string` | `undefined` | Cancel button text | 39 | | `confirmLabel` | `string` | `undefined` | Confirm button `aria-label` | 40 | | `onConfirm` | `(event: React.MouseEvent) => void` | `undefined` | Function to run when confirm button is clicked.
The dialog does not close automatically, a handler must be included. | 41 | | `onClose` | `(event: React.MouseEvent) => void` | `undefined` | Function to run when cancel button is clicked.
The dialog does not close automatically, a handler must be included. | 42 | | `onOutside` | `(event: React.MouseEvent) => void` | `undefined` | Function to run when dialog is clicked outside of.
By default, this will run `onClose`.
A handler must be included to close the dialog. | 43 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/context-menu-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ContextMenuProps 3 | description: Type definition for props of ReactComponent.ContextMenu 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | The `ContextMenuProps` object is used to create a context menu. 13 | 14 | ```ts 15 | type ContextMenuProps = { 16 | renderInline?: boolean; 17 | trigger?: 'click' | 'right-click'; 18 | action?: 'toggle' | 'open'; 19 | placement?: 'top' | 'top-start' | 'top-end' | 'right' | 'right-start' | 'right-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end'; 20 | offset?: [number, number]; 21 | preventScrollingWhileOpen?: boolean; 22 | menu: typeof Spicetify.ReactComponent.Menu; 23 | children: Element | ((isOpen?: boolean, handleContextMenu?: (e: MouseEvent) => void, ref?: (e: Element) => void) => Element); 24 | }; 25 | ``` 26 | 27 | #### Properties 28 | 29 | | Property | Type | Description | 30 | | :--- | :--- | :--- | 31 | | `renderInline` | `boolean` | Decide whether to use the global singleton context menu (rendered in ``) or a new inline context menu (rendered in a sibling element to `children`) | 32 | | `trigger` | `'click'` | `'right-click'` | Determines what will trigger the context menu. For example, a click, or a right-click | 33 | | `action` | `'toggle'` | `'open'` | Determines if the context menu should open or toggle when triggered | 34 | | `placement` | `'top'` | `'top-start'` | `'top-end'` | `'right'` | `'right-start'` | `'right-end'` | `'bottom'` | `'bottom-start'` | `'bottom-end'` | `'left'` | `'left-start'` | `'left-end'` | The preferred placement of the context menu when it opens. Relative to trigger element. | 35 | | `offset` | `[number, number]` | The x and y offset distances at which the context menu should open. Relative to trigger element and `position`. | 36 | | `preventScrollingWhileOpen` | `boolean` | Will stop the client from scrolling while the context menu is open | 37 | | `menu` | `typeof Spicetify.ReactComponent.Menu` | The menu UI to render inside of the context menu. | 38 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/icon-component-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: IconComponentProps 3 | description: Type definition for props of ReactComponent.IconComponent. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | The `IconComponentProps` object is used to create an icon component. 13 | 14 | ```ts 15 | type IconComponentProps = { 16 | iconSize?: number; 17 | color?: string; 18 | semanticColor?: SemantiColor; 19 | title?: string; 20 | titleId?: string; 21 | desc?: string; 22 | descId?: string; 23 | autoMirror?: boolean; 24 | }; 25 | ``` 26 | 27 | #### Properties 28 | 29 | | Property | Type | Description | 30 | | :--- | :--- | :--- | 31 | | iconSize | `number` | `undefined` | Icon size | 32 | | color | `string` | `undefined` | Icon color. Might not be used by component | 33 | | semanticColor | [`SemanticColor`](../semantic-color) | `undefined` | Semantic color name. Matches color variables used in xpui | 34 | | title | `string` | `undefined` | Icon title | 35 | | titleId | `string` | `undefined` | Title ID (internal) | 36 | | desc | `string` | `undefined` | Icon description | 37 | | descId | `string` | `undefined` | Description ID (internal) | 38 | | autoMirror | `boolean` | `undefined` | Whether the icon can be auto mirrored | 39 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/menu-item-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: MenuItemProps 3 | description: Type definition for props of ReactComponent.MenuItem. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | The `MenuItemProps` object is used to create a menu item. 13 | 14 | ```ts 15 | type MenuItemProps = { 16 | onClick?: React.MouseEventHandler; 17 | disabled?: boolean; 18 | divider?: 'before' | 'after' | 'both'; 19 | icon?: React.ReactNode; 20 | leadingIcon?: React.ReactNode; 21 | trailingIcon?: React.ReactNode; 22 | }; 23 | ``` 24 | 25 | #### Properties 26 | 27 | | Property | Type | Description | 28 | | :--- | :--- | :--- | 29 | | `onClick` | `React.MouseEventHandler` | `undefined` | Function that runs when `MenuItem` is clicked | 30 | | `disabled` | `boolean` | `undefined` | Indicates if `MenuItem` is disabled. Disabled items will not cause the `Menu` to close when clicked. | 31 | | `divider` | `'before' | 'after' | 'both'` | `undefined` | Indicate that a divider line should be added `before` or `after` this `MenuItem` | 32 | | `icon` | `React.ReactNode` | `undefined` | React component icon that will be rendered at the end of the `MenuItem`. **Deprecated**: Since Spotify `1.2.8`. Use `leadingIcon` or `trailingIcon` instead | 33 | | `leadingIcon` | `React.ReactNode` | `undefined` | React component icon that will be rendered at the start of the `MenuItem`. **Since Spotify `1.2.8`** | 34 | | `trailingIcon` | `React.ReactNode` | `undefined` | React component icon that will be rendered at the start of the `MenuItem`. **Since Spotify `1.2.8`** | 35 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/menu-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: MenuProps 3 | description: Type definition for props of ReactComponent.Menu. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | The `MenuProps` object is used to create a menu. 13 | 14 | ```ts 15 | type MenuProps = { 16 | onClose?: () => void; 17 | getInitialFocusElement?: (el: HTMLElement | null) => HTMLElement | undefined | null; 18 | }; 19 | ``` 20 | 21 | #### Properties 22 | 23 | | Property | Type | Description | 24 | | :--- | :--- | :--- | 25 | | onClose | `() => void` | `undefined` | Function that is called when the menu is closed | 26 | | getInitialFocusElement | `(el: HTMLElement | null) => HTMLElement | undefined | null` | `undefined` | Function that provides the element that focus should jump to when the menu is opened | 27 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/panel-content-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PanelContentProps 3 | description: Type definition for props of ReactComponent.PanelContent. 4 | --- 5 | 6 | The `PanelContentProps` object is used to render a panel content wrapper. 7 | 8 | :::note 9 | 10 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 11 | 12 | ::: 13 | 14 | ```ts 15 | type PanelContentProps = { 16 | className?: string; 17 | children?: React.ReactNode; 18 | }; 19 | ``` 20 | 21 | #### Properties 22 | 23 | | Property | Type | Description | 24 | | :--- | :--- | :--- | 25 | | className | `string` | `undefined` | Additional class name to apply to the panel. | 26 | | children | `React.ReactNode` | `undefined` | Children to render inside the panel. | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/panel-header-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PanelHeaderProps 3 | description: Type definition for props of ReactComponent.PanelHeader. 4 | --- 5 | 6 | The `PanelHeaderProps` object is used to render a panel header. 7 | 8 | :::note 9 | 10 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 11 | 12 | ::: 13 | 14 | ```ts 15 | type PanelHeaderProps = { 16 | link?: string; 17 | title?: string; 18 | panel: number; 19 | isAdvert?: boolean; 20 | actions?: React.ReactNode; 21 | onClose?: () => void; 22 | preventDefaultClose?: boolean; 23 | onBack?: (event: React.MouseEvent) => void; 24 | titleVariant?: Variant; 25 | titleSemanticColor?: SemanticColor; 26 | }; 27 | ``` 28 | 29 | #### Properties 30 | 31 | | Property | Type | Description | 32 | | :--- | :--- | :--- | 33 | | link | `string` | `undefined` | Href for the header link.
Can be either a URI, a path within the app, or a URL for an external link. | 34 | | title | `string` | `undefined` | Title of the header. | 35 | | panel | `number` | Panel ID. Used to toggle panel open/closed state. | 36 | | isAdvert | `boolean` | `undefined` | Whether or not the panel contains advertisements. Defaults to `false` | 37 | | actions | `React.ReactNode` | `undefined` | Actions to render in the header. | 38 | | onClose | `() => void` | `undefined` | Function to call when clicking on the close button.
Called before the panel is closed. | 39 | | preventDefaultClose | `boolean` | `undefined` | Prevent the panel from closing when clicking on the header close button. Defaults to `false` | 40 | | onBack | `(event: React.MouseEvent) => void` | `undefined` | Function to call when clicking on the header back button.
If not provided, the back button will not be rendered. | 41 | | titleVariant | [`Variant`](/docs/development/api-wrapper/types/variant) | `undefined` | Font variant for the header title. Defaults to `"balladBold"` | 42 | | titleSemanticColor | [`SemanticColor`](/docs/development/api-wrapper/types/semantic-color) | `undefined` | Semantic color name for the header title. Defaults to `"textBase"` | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/panel-skeleton-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PanelSkeletonProps 3 | description: Type definition for props of ReactComponent.PanelSkeleton. 4 | --- 5 | 6 | The `PanelSkeletonProps` object is used to render a panel skeleton. 7 | 8 | :::note 9 | 10 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 11 | 12 | ::: 13 | 14 | ```ts 15 | type PanelSkeletonProps = { 16 | label?: string; 17 | itemUri?: string; 18 | className?: string; 19 | style?: React.CSSProperties; 20 | children?: React.ReactNode; 21 | }; 22 | ``` 23 | 24 | #### Properties 25 | 26 | | Property | Type | Description | 27 | | :--- | :--- | :--- | 28 | | label | `string` | `undefined` | Aria label for the panel. Does not set the panel header content. | 29 | | itemUri | `string` | `undefined` | Item URI of the panel. Used as reference for Spotify's internal Event Factory. | 30 | | className | `string` | `undefined` | Additional class name to apply to the panel. **Deprecated**: Since Spotify `1.2.12` | 31 | | style | `React.CSSProperties` | `undefined` | Additional styles to apply to the panel. | 32 | | children | `React.ReactNode` | `undefined` | Children to render inside the panel. | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/slider-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SliderProps 3 | description: Type definition for props of ReactComponent.PanelSkeleton. 4 | --- 5 | 6 | The `SliderProps` object is used to render a slider. 7 | 8 | :::note 9 | 10 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 11 | 12 | ::: 13 | 14 | ```ts 15 | type SliderProps = { 16 | value: number; 17 | max: number; 18 | step: number; 19 | labelText?: string; 20 | isInteractive?: boolean; 21 | forceActiveStyles?: boolean; 22 | onDragStart: (value: number) => void; 23 | onDragMove: (value: number) => void; 24 | onDragEnd: (value: number) => void; 25 | onStepForward?: (value: number) => void; 26 | onStepBackward?: (value: number) => void; 27 | } 28 | ``` 29 | 30 | #### Properties 31 | 32 | | Property | Type | Description | 33 | | :--- | :--- | :--- | 34 | | value | `number` | The current value of the slider. | 35 | | max | `number` | The maximum value the slider can have. | 36 | | step | `number` | The increment/decrement value when the slider is moved. | 37 | | labelText | `string` | `undefined` | The label text displayed for the slider. | 38 | | isInteractive | `boolean` | `undefined` | Determines if the slider is interactive. | 39 | | forceActiveStyles | `boolean` | `undefined` | Forces the active styles regardless of interaction state. | 40 | | onDragStart | `(value: number) => void` | Callback function when dragging starts. | 41 | | onDragMove | `(value: number) => void` | Callback function when the slider is being dragged. | 42 | | onDragEnd | `(value: number) => void` | Callback function when dragging ends. | 43 | | onStepForward | `(value: number) => void` | `undefined` | Callback function when the slider steps forward. **Deprecated.** | 44 | | onStepBackward | `(value: number) => void` | `undefined` | Callback function when the slider steps backward. **Deprecated.** | 45 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/text-component-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: TextComponentProps 3 | description: Type definition for props of ReactComponent.TextComponent. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | The `TextComponentProps` object is used to create a text component. 13 | 14 | ```ts 15 | type TextComponentProps = { 16 | color?: string; 17 | semanticColor?: SemanticColor; 18 | variant?: Variant; 19 | paddingBottom?: string; 20 | weight?: 'book' | 'bold' | 'black'; 21 | }; 22 | ``` 23 | 24 | #### Properties 25 | 26 | | Property | Type | Description | 27 | | -------- | ---- | ----------- | 28 | | color | `string` | `undefined` | Text color. Might not be used by component. | 29 | | semanticColor | [`SemanticColor`](../semantic-color) | `undefined` | Semantic color name. Matches color variables used in xpui | 30 | | variant | [`Variant`](../variant) | `undefined` | Font variant | 31 | | paddingBottom | `string` | `undefined` | Bottom padding size | 32 | | weight | `'book'` | `'bold'` | `'black'` | `undefined` | Font weight | 33 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/toggle-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ToggleProps 3 | description: Type definition for props of ReactComponent.Toggle. 4 | --- 5 | 6 | The `ToggleProps` object is used to render a toggle. 7 | 8 | :::note 9 | 10 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 11 | 12 | ::: 13 | 14 | ```ts 15 | type ToggleProps = { 16 | value: boolean; 17 | disabled?: boolean; 18 | onSelected: (value: boolean) => void; 19 | id?: string; 20 | className?: string; 21 | } 22 | ``` 23 | 24 | #### Properties 25 | 26 | | Property | Type | Description | 27 | | :--- | :--- | :--- | 28 | | value | `boolean` | The current state of the toggle. `true` means it's on, `false` means it's off. | 29 | | disabled | `boolean` | `undefined` | Determines if the toggle is disabled. If `true`, the toggle is not interactive. | 30 | | onSelected | `(value: boolean) => void` | Callback function that is called when the toggle is clicked. The function receives the new state of the toggle. | 31 | | id | `string` | `undefined` | The ID for the toggle, useful for associating with a label for accessibility. | 32 | | className | `string` | `undefined` | Additional CSS class name to apply to the toggle. | 33 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/react-component/tooltip-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: TooltipProps 3 | description: Type definition for props of ReactComponent.TooltipWrapper. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | The `TooltipProps` object is used to create a tooltip. 13 | 14 | ```ts 15 | type TooltipProps = { 16 | label: string; 17 | children: React.ReactNode; 18 | renderInline?: boolean; 19 | showDelay?: number; 20 | disabled?: boolean; 21 | placement?: 'top' | 'top-start' | 'top-end' | 'right' | 'right-start' | 'right-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end'; 22 | labelClassName?: string; 23 | }; 24 | ``` 25 | 26 | #### Properties 27 | 28 | | Property | Type | Description | 29 | | :--- | :--- | :--- | 30 | | `label` | `string` | Label to display in the tooltip | 31 | | `children` | `React.ReactNode` | The child element that the tooltip will be attached to and will display when hovered over | 32 | | `renderInline` | `boolean` | Decide whether to use the global singleton tooltip (rendered in ``) or a new inline tooltip (rendered in a sibling element to `children`) | 33 | | `showDelay` | `number` | Delay in milliseconds before the tooltip is displayed after the user hovers over the child element | 34 | | `disabled` | `boolean` | Determine whether the tooltip should be displayed | 35 | | `placement` | `'top'` | `'top-start'` | `'top-end'` | `'right'` | `'right-start'` | `'right-end'` | `'bottom'` | `'bottom-start'` | `'bottom-end'` | `'left'` | `'left-start'` | `'left-end'` | The preferred placement of the context menu when it opens. Relative to trigger element. | 36 | | `labelClassName` | `string` | Class name to apply to the tooltip | 37 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/semantic-color.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SemanticColor 3 | description: Semantic color names used in the Spotify app. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | ```ts 13 | type SemanticColor = "textBase" | 14 | "textSubdued" | 15 | "textBrightAccent" | 16 | "textNegative" | 17 | "textWarning" | 18 | "textPositive" | 19 | "textAnnouncement" | 20 | "essentialBase" | 21 | "essentialSubdued" | 22 | "essentialBrightAccent" | 23 | "essentialNegative" | 24 | "essentialWarning" | 25 | "essentialPositive" | 26 | "essentialAnnouncement" | 27 | "decorativeBase" | 28 | "decorativeSubdued" | 29 | "backgroundBase" | 30 | "backgroundHighlight" | 31 | "backgroundPress" | 32 | "backgroundElevatedBase" | 33 | "backgroundElevatedHighlight" | 34 | "backgroundElevatedPress" | 35 | "backgroundTintedBase" | 36 | "backgroundTintedHighlight" | 37 | "backgroundTintedPress" | 38 | "backgroundUnsafeForSmallTextBase" | 39 | "backgroundUnsafeForSmallTextHighlight" | 40 | "backgroundUnsafeForSmallTextPress"; 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/svgicon.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SVGIcon 3 | description: SVGIcon type definition. 4 | --- 5 | 6 | ```ts 7 | type SVGIcon = "album" | 8 | "artist" | 9 | "block" | 10 | "brightness" | 11 | "car" | 12 | "chart-down" | 13 | "chart-up" | 14 | "check" | 15 | "check-alt-fill" | 16 | "chevron-left" | 17 | "chevron-right" | 18 | "chromecast-disconnected" | 19 | "clock" | 20 | "collaborative" | 21 | "computer" | 22 | "copy" | 23 | "download" | 24 | "downloaded" | 25 | "edit" | 26 | "enhance" | 27 | "exclamation-circle" | 28 | "external-link" | 29 | "facebook" | 30 | "follow" | 31 | "fullscreen" | 32 | "gamepad" | 33 | "grid-view" | 34 | "heart" | 35 | "heart-active" | 36 | "instagram" | 37 | "laptop" | 38 | "library" | 39 | "list-view" | 40 | "location" | 41 | "locked" | 42 | "locked-active" | 43 | "lyrics" | 44 | "menu" | 45 | "minimize" | 46 | "minus" | 47 | "more" | 48 | "new-spotify-connect" | 49 | "offline" | 50 | "pause" | 51 | "phone" | 52 | "play" | 53 | "playlist" | 54 | "playlist-folder" | 55 | "plus-alt" | 56 | "plus2px" | 57 | "podcasts" | 58 | "projector" | 59 | "queue" | 60 | "repeat" | 61 | "repeat-once" | 62 | "search" | 63 | "search-active" | 64 | "shuffle" | 65 | "skip-back" | 66 | "skip-back15" | 67 | "skip-forward" | 68 | "skip-forward15" | 69 | "soundbetter" | 70 | "speaker" | 71 | "spotify" | 72 | "subtitles" | 73 | "tablet" | 74 | "ticket" | 75 | "twitter" | 76 | "visualizer" | 77 | "voice" | 78 | "volume" | 79 | "volume-off" | 80 | "volume-one-wave" | 81 | "volume-two-wave" | 82 | "watch" | 83 | "x"; 84 | ``` 85 | 86 | ### Examples 87 | ![svg-examples](/images/spicetify-svg-examples.png) 88 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/uri/type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Type 3 | description: Type of a URI. 4 | --- 5 | 6 | Type of a URI. 7 | 8 | :::note 9 | 10 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 11 | 12 | ::: 13 | 14 | ```ts 15 | class URI { 16 | static Type: { 17 | AD: string; 18 | ALBUM: string; 19 | GENRE: string; 20 | QUEUE: string; 21 | APPLICATION: string; 22 | ARTIST: string; 23 | ARTIST_TOPLIST: string; 24 | ARTIST_CONCERTS: string; 25 | AUDIO_FILE: string; 26 | COLLECTION: string; 27 | COLLECTION_ALBUM: string; 28 | COLLECTION_ARTIST: string; 29 | COLLECTION_MISSING_ALBUM: string; 30 | COLLECTION_TRACK_LIST: string; 31 | CONCERT: string; 32 | CONTEXT_GROUP: string; 33 | DAILY_MIX: string; 34 | EMPTY: string; 35 | EPISODE: string; 36 | /** URI particle; not an actual URI. */ 37 | FACEBOOK: string; 38 | FOLDER: string; 39 | FOLLOWERS: string; 40 | FOLLOWING: string; 41 | IMAGE: string; 42 | INBOX: string; 43 | INTERRUPTION: string; 44 | LIBRARY: string; 45 | LIVE: string; 46 | ROOM: string; 47 | EXPRESSION: string; 48 | LOCAL: string; 49 | LOCAL_TRACK: string; 50 | LOCAL_ALBUM: string; 51 | LOCAL_ARTIST: string; 52 | MERCH: string; 53 | MOSAIC: string; 54 | PLAYLIST: string; 55 | PLAYLIST_V2: string; 56 | PRERELEASE: string; 57 | PROFILE: string; 58 | PUBLISHED_ROOTLIST: string; 59 | RADIO: string; 60 | ROOTLIST: string; 61 | SEARCH: string; 62 | SHOW: string; 63 | SOCIAL_SESSION: string; 64 | SPECIAL: string; 65 | STARRED: string; 66 | STATION: string; 67 | TEMP_PLAYLIST: string; 68 | TOPLIST: string; 69 | TRACK: string; 70 | TRACKSET: string; 71 | USER_TOPLIST: string; 72 | USER_TOP_TRACKS: string; 73 | UNKNOWN: string; 74 | MEDIA: string; 75 | QUESTION: string; 76 | POLL: string; 77 | }; 78 | }; 79 | ``` 80 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/uri/validation-functions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Validation functions 3 | description: Functions that can be used to validate a URI type. 4 | --- 5 | 6 | ```ts 7 | class URI { 8 | static isAd(uri: any): boolean; 9 | static isAlbum(uri: any): boolean; 10 | static isGenre(uri: any): boolean; 11 | static isQueue(uri: any): boolean; 12 | static isApplication(uri: any): boolean; 13 | static isArtist(uri: any): boolean; 14 | static isArtistToplist(uri: any): boolean; 15 | static isArtistConcerts(uri: any): boolean; 16 | static isAudioFile(uri: any): boolean; 17 | static isCollection(uri: any): boolean; 18 | static isCollectionAlbum(uri: any): boolean; 19 | static isCollectionArtist(uri: any): boolean; 20 | static isCollectionMissingAlbum(uri: any): boolean; 21 | static isCollectionTrackList(uri: any): boolean; 22 | static isConcert(uri: any): boolean; 23 | static isContextGroup(uri: any): boolean; 24 | static isDailyMix(uri: any): boolean; 25 | static isEmpty(uri: any): boolean; 26 | static isEpisode(uri: any): boolean; 27 | static isFacebook(uri: any): boolean; 28 | static isFolder(uri: any): boolean; 29 | static isFollowers(uri: any): boolean; 30 | static isFollowing(uri: any): boolean; 31 | static isImage(uri: any): boolean; 32 | static isInbox(uri: any): boolean; 33 | static isInterruption(uri: any): boolean; 34 | static isLibrary(uri: any): boolean; 35 | static isLive(uri: any): boolean; 36 | static isRoom(uri: any): boolean; 37 | static isExpression(uri: any): boolean; 38 | static isLocal(uri: any): boolean; 39 | static isLocalTrack(uri: any): boolean; 40 | static isLocalAlbum(uri: any): boolean; 41 | static isLocalArtist(uri: any): boolean; 42 | static isMerch(uri: any): boolean; 43 | static isMosaic(uri: any): boolean; 44 | static isPlaylist(uri: any): boolean; 45 | static isPlaylistV2(uri: any): boolean; 46 | static isPrerelease(uri: any): boolean; 47 | static isProfile(uri: any): boolean; 48 | static isPublishedRootlist(uri: any): boolean; 49 | static isRadio(uri: any): boolean; 50 | static isRootlist(uri: any): boolean; 51 | static isSearch(uri: any): boolean; 52 | static isShow(uri: any): boolean; 53 | static isSocialSession(uri: any): boolean; 54 | static isSpecial(uri: any): boolean; 55 | static isStarred(uri: any): boolean; 56 | static isStation(uri: any): boolean; 57 | static isTempPlaylist(uri: any): boolean; 58 | static isToplist(uri: any): boolean; 59 | static isTrack(uri: any): boolean; 60 | static isTrackset(uri: any): boolean; 61 | static isUserToplist(uri: any): boolean; 62 | static isUserTopTracks(uri: any): boolean; 63 | static isUnknown(uri: any): boolean; 64 | static isMedia(uri: any): boolean; 65 | static isQuestion(uri: any): boolean; 66 | static isPoll(uri: any): boolean; 67 | static isPlaylistV1OrV2(uri: any): boolean; 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /docs/development/api-wrapper/types/variant.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Variant 3 | description: Font variants used in the Spotify app. 4 | --- 5 | 6 | :::note 7 | 8 | This type is deducted from Spotify's internal usage. It may not be accurate and may change in the future. 9 | 10 | ::: 11 | 12 | ```ts 13 | type Variant = "bass" | 14 | "forte" | 15 | "brio" | 16 | "altoBrio" | 17 | "alto" | 18 | "canon" | 19 | "celloCanon" | 20 | "cello" | 21 | "ballad" | 22 | "balladBold" | 23 | "viola" | 24 | "violaBold" | 25 | "mesto" | 26 | "mestoBold" | 27 | "metronome" | 28 | "finale" | 29 | "finaleBold" | 30 | "minuet" | 31 | "minuetBold"; 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/development/compiling.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Compiling 3 | description: 🧰 Compiling Spicetify. 4 | --- 5 | 6 | ### Requirements 7 | 8 | - [Go](https://golang.org/dl/) 9 | 10 | Clone repo and download dependencies: 11 | 12 | ```bash 13 | cd $HOME 14 | mkdir spicetify 15 | cd spicetify 16 | git clone https://github.com/spicetify/cli 17 | ``` 18 | 19 | ### Build 20 | 21 | #### Windows 22 | 23 | ```powershell 24 | cd $HOME\spicetify\cli 25 | go build -o spicetify.exe 26 | ``` 27 | 28 | #### Linux and MacOS 29 | 30 | ```bash 31 | cd ~/spicetify/cli 32 | go build -o spicetify 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/development/custom-apps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Custom Apps 3 | description: 🔧 Creating Custom Apps. 4 | --- 5 | 6 | ## How to start 7 | 8 | - Make a new folder for your custom app in your [CustomApps folder](/docs/advanced-usage/custom-apps). You'll install it like any other custom app. 9 | - Create an `index.js` and a `manifest.json` inside that folder. 10 | - The `index.js` file is the main file for the custom app. 11 | - The manifest includes some important information to make the custom app work. 12 | 13 | ## Manifest file 14 | 15 | - Your custom app needs a `manifest.json` file in the root folder with the following keys: 16 | - `name`: The name of the custom app. 17 | - `icon`: The escaped SVG markup for the sidebar icon. 18 | - `active-icon`: The escaped SVG markup for the active status of the sidebar icon (when your custom app is open). 19 | - `subfiles`: You can optionally include other JS files. These files will be concatenated together in the order defined here. Any variables you declare in the main `index.js`, or any subfiles will be accessible from all. This is useful for organizational purposes for more complex custom apps. 20 | - `subfiles_extension`: You can optionally include one or more extensions with your custom app. These are treated as regular extensions, and will run when Spotify starts. 21 | 22 | _Note: The `subfiles` can be in nested folders, while any `subfiles_extension` can not._ 23 | 24 | ```json 25 | { 26 | "name": "My Custom App", 27 | "icon": "", 28 | "active-icon": "", 29 | "subfiles": ["src/Subfile.js", "src/Subfile2.js"], 30 | "subfiles_extension": ["my_extension.js"] 31 | } 32 | ``` 33 | 34 | ## Creating your index.js 35 | 36 | Custom apps are written in [React](https://reactjs.org). You'll need to grab some React references from the `Spicetify` object, set up a component for your page, and return that component from a `render()` function in the main body of the file. Sadly, this does not support [`jsx`](https://reactjs.org/docs/introducing-jsx.html), so you must use [`react.createElement`](https://reactjs.org/docs/react-api.html#createelement). 37 | 38 | Example: 39 | 40 | ```js 41 | // Grab any variables you need 42 | const react = Spicetify.React; 43 | const reactDOM = Spicetify.ReactDOM; 44 | const { 45 | URI, 46 | React: { useState, useEffect, useCallback }, 47 | Platform: { History }, 48 | } = Spicetify; 49 | 50 | // The main custom app render function. The component returned is what is rendered in Spotify. 51 | function render() { 52 | return react.createElement(Grid, { title: "My Custom App" }); 53 | } 54 | 55 | // Our main component 56 | class Grid extends react.Component { 57 | constructor(props) { 58 | super(props); 59 | Object.assign(this, props); 60 | this.state = { 61 | foo: "bar", 62 | data: "etc" 63 | }; 64 | } 65 | 66 | render() { 67 | return react.createElement("section", { 68 | className: "contentSpacing", 69 | }, 70 | react.createElement("div", { 71 | className: "marketplace-header", 72 | }, react.createElement("h1", null, this.props.title), 73 | ), 74 | ), react.createElement("div", { 75 | id: "marketplace-grid", 76 | className: "main-gridContainer-gridContainer", 77 | "data-tab": CONFIG.activeTab, 78 | style: { 79 | "--minimumColumnWidth": "180px", 80 | }, 81 | }, [...cardList]), 82 | react.createElement("footer", { 83 | style: { 84 | margin: "auto", 85 | textAlign: "center", 86 | }, 87 | }, !this.state.endOfList && (this.state.rest ? react.createElement(LoadMoreIcon, { onClick: this.loadMore.bind(this) }) : react.createElement(LoadingIcon)), 88 | ), react.createElement(TopBarContent, { 89 | switchCallback: this.switchTo.bind(this), 90 | links: CONFIG.tabs, 91 | activeLink: CONFIG.activeTab, 92 | })); 93 | } 94 | } 95 | ``` 96 | 97 | ## Common questions: 98 | 99 | ### My custom app isn't running when Spotify starts 100 | 101 | Your custom app will only run when it is clicked on the sidebar and its page loads. In order to run code on startup, you need to include a separate JS file as `subfiles_extension` in your [manifest](#manifest-file). 102 | 103 | ### My subfile extension can't read my variables from my custom app 104 | 105 | Any subfile extensions are loaded separately from the main custom app, and do not have access to variables. You can use `localStorage` to save/load data between the two. 106 | 107 | ### How can I add a new sub page or path to my custom app? 108 | 109 | You can use `Spicetify.Platform.History.push(...)` to navigate to a new page. This can be a standard Spotify page, or a custom page for your app. You can include any data you need in the `state` key. 110 | 111 | ```js 112 | Spicetify.Platform.History.push({ 113 | pathname: '/marketplace/readme', 114 | state: { 115 | data: { 116 | title: 'My sub page title', 117 | content: 'My sub page content', 118 | }, 119 | }, 120 | }); 121 | ``` 122 | 123 | In order to render a different page, you can check the `pathname` of the current page within `index.js`'s main render method, and render a different page component for different paths. The main path for your custom app will be the name of the folder (which is the same that needs to be used in the [`config-xpui.ini`](/docs/development/themes) configuration file). 124 | In this example, if our `pathname` is "/marketplace/readme", we load the `ReadmePage` component, otherwise we load our main page component, `Grid`. 125 | 126 | ```js 127 | function render() { 128 | const { location } = Spicetify.Platform.History; 129 | 130 | // If page state set to display readme, render it 131 | // (This location state data comes from your Spicetify.Platform.History.push() call 132 | if (location.pathname === '/marketplace/readme') { 133 | return react.createElement(ReadmePage, { 134 | title: 'Spicetify Marketplace - Readme', 135 | data: location.state.data, 136 | }); 137 | } // Otherwise, render the main Grid 138 | else { 139 | return react.createElement(Grid, { title: 'Spicetify Marketplace' }); 140 | } 141 | } 142 | ``` 143 | -------------------------------------------------------------------------------- /docs/development/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Development 3 | --- 4 | 5 | ```mdx-code-block 6 | import DocCardList from '@theme/DocCardList'; 7 | import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; 8 | 9 | 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/development/js-modules.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Javascript NPM Modules 3 | description: 📦 Using NPM Modules for Spicetify. 4 | --- 5 | 6 | Since v0.9.8, Spicetify injects extension with file extension `.mjs` as a script with type="module" and automatically symlink `node_modules` folder found in user's Extensions folder to `zlink` app. 7 | 8 | In Javascript module, Javascript would work just the same as normal script but now you can use `import` to include other Javascript files. [Click here for details](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). 9 | 10 | Node Package Manager (NPM) is a commandline app bundled with NodeJS. You can use it to download and install hundreds of utility packages to ease your development process. Since Spicetify symlinks `node_modules` to Spotify main app folder, all packages files are mutually linked and available for you to use in your extension. Simply just use `import`: 11 | 12 | ```js 13 | import './node_modules/package_name/file.js'; 14 | ``` 15 | 16 | Careful! Javascript file you import has to be supported by Browser, not NodeJS. In other words, that Javascript should not use `require` function to include other packages or NodeJS API. Whenever you decide to use a package, check its readme page to see if its author already export distributed file as ES6 Module. 17 | 18 | ### Example 19 | 20 | For Japanese studying purpose, I'm developing an extension that shows the Romaji form of Japanese track titles or artist names. 21 | 22 | The idea is when I right click at track name and choose Show Romaji: 23 | 24 | ![img1](https://i.imgur.com/kkSOtwG.png) 25 | 26 | Result should show as a notification: 27 | 28 | ![img2](https://i.imgur.com/LLF5ZGh.png) 29 | 30 | To translate Japanese text to Romaji, I use a package named [kuroshiro](https://github.com/hexenq/kuroshiro). Luckily, this package will export distribution files as ES6 Module. This is quite important because package itself relies on other utilities packages too. When it is compiled as an ES6 module, everything is transpiled to Browser supported Javascript and combined in one file. Moreover, kuroshiro also needs [kuroshiro-analyzer-kuromoji](https://github.com/hexenq/kuroshiro-analyzer-kuromoji) package to be usable, which relies on dictionaries gzip files. You can see there is no easy way to utiltise both packages and their external files if we use traditional Javascript extension. 31 | 32 | **Following are steps to make and install this extension from scratch:** 33 | 34 | 1. First, change director to the user's `Extensions` folder: 35 | 36 | - **Windows, in Powershell:** 37 | 38 | ```powershell 39 | cd "$(dirname "$(spicetify -c)")/Extensions" 40 | ``` 41 | 42 | - **Linux and MacOS**, in Bash: 43 | 44 | ``` 45 | cd "$(dirname "$(spicetify -c)")/Extensions" 46 | ``` 47 | 48 | 2. Now install needed packages with NPM 49 | 50 | ```bash 51 | npm install kuroshiro kuroshiro-analyzer-kuromoji 52 | ``` 53 | 54 | Go to user's `Extensions` folder, you can see `node_modules` folder is created and contains all installed packages files. Next, go to the `kuroshiro` and `kuroshiro-analyzer-kuromoji`folder to locate ES6 Module distribution files. 55 | 56 | 3. After that I can comfortably include both of them in my extension. Following is extension code: 57 | 58 | **romaji.mjs**: 59 | 60 | ```javascript 61 | import './node_modules/kuroshiro/dist/kuroshiro.min.js'; 62 | import './node_modules/kuroshiro-analyzer-kuromoji/dist/kuroshiro-analyzer-kuromoji.min.js'; 63 | 64 | const kuroshiro = new Kuroshiro.default(); 65 | kuroshiro.init(new KuromojiAnalyzer()); 66 | 67 | function converter(input) { 68 | return kuroshiro.convert(input, { 69 | to: 'romaji', 70 | mode: 'spaced', 71 | romajiSystem: 'passport', 72 | }); 73 | } 74 | 75 | const fetchAlbum = async (uri) => { 76 | const res = await Spicetify.CosmosAsync.get(`hm://album/v1/album-app/album/${uri.split(':')[2]}/desktop`); 77 | return res.name; 78 | }; 79 | 80 | const fetchShow = async (uri) => { 81 | const res = await Spicetify.CosmosAsync.get( 82 | `sp://core-show/v1/shows/${uri.split(':')[2]}?responseFormat=protobufJson`, 83 | { 84 | policy: { list: { index: true } }, 85 | } 86 | ); 87 | return res.header.showMetadata.name; 88 | }; 89 | 90 | const fetchArtist = async (uri) => { 91 | const res = await Spicetify.CosmosAsync.get(`hm://artist/v1/${uri.split(':')[2]}/desktop?format=json`); 92 | return res.info.name; 93 | }; 94 | 95 | const fetchTrack = async (uri) => { 96 | const res = await Spicetify.CosmosAsync.get(`https://api.spotify.com/v1/tracks/${uri.split(':')[2]}`); 97 | return res.name; 98 | }; 99 | 100 | const fetchEpisode = async (uri) => { 101 | const res = await Spicetify.CosmosAsync.get(`https://api.spotify.com/v1/episodes/${uri.split(':')[2]}`); 102 | return res.name; 103 | }; 104 | 105 | const fetchPlaylist = async (uri) => { 106 | const res = await Spicetify.CosmosAsync.get(`sp://core-playlist/v1/playlist/${uri}/metadata`, { 107 | policy: { name: true }, 108 | }); 109 | return res.metadata.name; 110 | }; 111 | 112 | async function showRomaji([uri]) { 113 | const type = uri.split(':')[1]; 114 | let name; 115 | switch (type) { 116 | case Spicetify.URI.Type.TRACK: 117 | name = await fetchTrack(uri); 118 | break; 119 | case Spicetify.URI.Type.ALBUM: 120 | name = await fetchAlbum(uri); 121 | break; 122 | case Spicetify.URI.Type.ARTIST: 123 | name = await fetchArtist(uri); 124 | break; 125 | case Spicetify.URI.Type.SHOW: 126 | name = await fetchShow(uri); 127 | break; 128 | case Spicetify.URI.Type.EPISODE: 129 | name = await fetchEpisode(uri); 130 | break; 131 | case Spicetify.URI.Type.PLAYLIST: 132 | case Spicetify.URI.Type.PLAYLIST_V2: 133 | name = await fetchPlaylist(uri); 134 | break; 135 | } 136 | if (Kuroshiro.default.Util.hasJapanese(name)) { 137 | name = await converter(name); 138 | name = name.replace(/(^|\s)\S/g, (t) => t.toUpperCase()); 139 | } 140 | Spicetify.showNotification(name); 141 | } 142 | 143 | new Spicetify.ContextMenu.Item(`Show Romaji`, showRomaji).register(); 144 | ``` 145 | 146 | Save this file to `Extensions` folder. 147 | 148 | 4. Finally, push my extension to Spotify: 149 | 150 | ```bash 151 | spicetify config extensions romaji.mjs 152 | spicetify apply 153 | ``` 154 | -------------------------------------------------------------------------------- /docs/development/react-devtools.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: React Developer Tools 3 | description: Installing and using React Developer Tools in Spotify. 4 | --- 5 | 6 | To install React Developer Tools in Spotify: 7 | 8 | 1. Launch Spotify in the developer mode (and with the `--enable-chrome-runtime` flag if you are using **Spotify versions older than 1.2.34**; without it, React Developer Tools won't work) 9 | 2. Press *Ctrl + Shift + T* 10 | 3. Press *Ctrl + N* 11 | 4. Navigate to the React Developer Tools page on Chrome Web Store using the address bar: `https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi` 12 | 5. Press the "Add to Chrome" button 13 | 6. Confirm your choice 14 | 15 | You may need to press *F5* to get the extension working. 16 | 17 | If you get an error saying that you're not allowed to install extensions, try using this switch: `--allowlisted-extension-id=fmkadmapgofadopljbjfkapdkoienihi`. 18 | 19 | You can find more info about React Developer Tools on the [official React website](https://react.dev/learn/react-developer-tools). 20 | -------------------------------------------------------------------------------- /docs/development/spicetify-creator/building-and-testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Building And Testing 3 | description: 🛠 Ensuring the quality of your creations. 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | ## Building 10 | 11 | Open a terminal in the project's directory and run this: 12 | 13 | 14 | 15 | ```shell 16 | npm run build 17 | ``` 18 | 19 | 20 | ```shell 21 | yarn run build 22 | ``` 23 | 24 | 25 | ```shell 26 | pnpm build 27 | ``` 28 | 29 | 30 | 31 | Then make sure you've added your app to Spicetify's config by running this: 32 | 33 | 34 | 35 | ```shell 36 | spicetify config extensions my-app.js 37 | ``` 38 | 39 | 40 | ```shell 41 | spicetify config custom_apps my-app 42 | ``` 43 | 44 | 45 | 46 | Finally, do 47 | 48 | ```shell 49 | spicetify apply 50 | ``` 51 | 52 | and you'll see your app in Spotify. 53 | 54 | ## Watching 55 | 56 | Please first [build your app](#building) at least once before watching. 57 | 58 | Watching means that it'll rebuild the app every time the code changes. 59 | Go into your project's directory and enter the following command: 60 | 61 | 62 | ```shell 63 | npm run watch 64 | ``` 65 | 66 | 67 | ```shell 68 | yarn run watch 69 | ``` 70 | 71 | 72 | ```shell 73 | pnpm watch 74 | ``` 75 | 76 | 77 | 78 | Then, run Spotify in watch mode: 79 | 80 | 81 | 82 | ```shell 83 | spicetify watch -le 84 | ``` 85 | 86 | 87 | ```shell 88 | spicetify watch -la 89 | ``` 90 | 91 | 92 | 93 | ## Building locally 94 | 95 | If you want to upload the build files with your repository or just see them, you can do: 96 | 97 | 98 | 99 | ```shell 100 | npm run build-local 101 | ``` 102 | 103 | 104 | ```shell 105 | yarn run build-local 106 | ``` 107 | 108 | 109 | ```shell 110 | pnpm build-local 111 | ``` 112 | 113 | 114 | 115 | And the compiled files will be created in a local `dist` folder. 116 | -------------------------------------------------------------------------------- /docs/development/spicetify-creator/create-custom-apps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create Custom Apps 3 | description: 🔧 Creating single-page apps for Spicetify. 4 | --- 5 | 6 | Notes: 7 | 8 | - It is recommended to learn React before starting to create Custom Apps. 9 | - This tutorial assumes you have chosen to generate an example using Create Spicetify App. 10 | 11 | After creating a new Spicetify Creator project and choosing "Custom App" as your app's type, your project's structure should look like this (With the generated example): 12 | 13 | ``` 14 | my-app/ 15 | .gitattributes 16 | .gitignore 17 | package.json 18 | README.md 19 | tsconfig.json 20 | yarn.lock 21 | src/ 22 | ... 23 | node_modules/ 24 | ... 25 | ``` 26 | 27 | For now, we only care about the `src/` folder, whose structure looks like this 28 | 29 | ``` 30 | src/ 31 | app.tsx 32 | settings.json 33 | extensions/ 34 | extension.tsx 35 | css/ 36 | icon.svg 37 | app.module.scss 38 | types/ 39 | ... 40 | ``` 41 | 42 | `app.tsx` exports a React Component that will be mounted to Spotify every time the user enters your custom app. 43 | It comes with an example of a simple counter using React's logic, and usages for SCSS modules. 44 | `settings.json` is a simple JSON file containing 4 keys: 45 | 46 | ```json 47 | { 48 | "displayName": "My App", // The name of your app in the left sidebar 49 | "nameId": "my-app", // The id of your app 50 | "icon": "css/icon.svg", // The icon that will be displayed in the sidebar 51 | "activeIcon": "css/icon.svg" // The icon that will be displayed upon selecting the app in the sidebar. 52 | } 53 | ``` 54 | 55 | The `extensions/` folder takes all the files inside it and transforms them into extensions that will run on Spotify's startup. 56 | The `extensions/extension.tsx` file is an example that says "Welcome!" whenever Spotify starts. 57 | 58 |
59 | extension.tsx's content 60 | 61 | ```ts 62 | (async () => { 63 | while (!Spicetify?.showNotification) { 64 | await new Promise((resolve) => setTimeout(resolve, 100)); 65 | } 66 | 67 | // Show message on start. 68 | Spicetify.showNotification('Welcome!'); 69 | })(); 70 | ``` 71 | 72 |
73 | -------------------------------------------------------------------------------- /docs/development/spicetify-creator/create-extensions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create Extensions 3 | description: 🔨 Creating small addons for Spicetify. 4 | --- 5 | 6 | Notes: 7 | 8 | - This tutorial assumes you have chosen to generate an example using Create Spicetify App. 9 | 10 | After creating a new Spicetify Creator project and choosing "Extension" as your app's type, your project's structure should look like this (With the generated example): 11 | 12 | ``` 13 | my-app/ 14 | .gitattributes 15 | .gitignore 16 | package.json 17 | README.md 18 | tsconfig.json 19 | yarn.lock 20 | src/ 21 | ... 22 | node_modules/ 23 | ... 24 | ``` 25 | 26 | For now, we only care about the `src/` folder, whose structure looks like this 27 | 28 | ``` 29 | src/ 30 | app.tsx 31 | settings.json 32 | types/ 33 | ... 34 | ``` 35 | 36 | `app.tsx` exports a function that will be executed every time Spotify starts up. 37 | It comes with an example that says "Hello!" to the user when Spotify starts up. 38 | `settings.json` is a simple JSON file containing 1 key: 39 | 40 | ```json 41 | { 42 | "nameId": "my-app" // The id of your app 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/development/spicetify-creator/the-basics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Basics 3 | description: 🤠 Spicetify Creator 101. 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | ## Introduction 10 | 11 | [Spicetify Creator](https://github.com/FlafyDev/spicetify-creator) is a tool to compile modern TypeScript/JavaScript code to Spicetify extensions and custom apps. 12 | 13 | Its built-in features include: 14 | 15 | - TypeScript and React syntax 16 | - [Import node packages](#node-packages) 17 | - [CSS/SCSS with PostCSS support](#css) 18 | - Extremely fast compile time with esbuild. 19 | - [Plugins](#plugins) 20 | 21 | ## Getting Started 22 | 23 | The easiest way to start using Spicetify Creator is with Create Spicetify App. 24 | Create Spicetify App allows you to effortlessly create new Spicetify Creator projects through the terminal. 25 | 26 | Install Node.js and either npm or Yarn. 27 | Open the terminal in your desired directory and enter the following command 28 | 29 | 30 | 31 | ```shell 32 | npx create-spicetify-app 33 | ``` 34 | 35 | 36 | ```shell 37 | yarn create spicetify-app 38 | ``` 39 | 40 | 41 | ```shell 42 | pnpm create spicetify-app 43 | ``` 44 | 45 | 46 | 47 | The command will ask you 3-4 simple questions about the app you plan to create and generate a Spicetify Creator project accordingly. 48 | After creation, read one of the following pages depending on what type of app you chose to create. 49 | 50 | 54 | 55 | ## CSS 56 | 57 | To apply a CSS/SCSS file to your app you have to import it like this: 58 | 59 | ```ts 60 | import './my-css-file.css'; // For CSS 61 | import './my-scss-file.scss'; // For SCSS 62 | ``` 63 | 64 | There is also support for [CSS Modules](https://github.com/css-modules/css-modules) and you import them like this: 65 | 66 | ```ts 67 | import styles from './item-list.module.css'; // For CSS 68 | import styles from './item-list.module.scss'; // For SCSS 69 | ``` 70 | 71 | ## Node packages 72 | 73 | You can use node packages in your app by installing them with your package manager. 74 | 75 | 76 | 77 | ```shell 78 | npm install 79 | ``` 80 | 81 | 82 | ```shell 83 | yarn add 84 | ``` 85 | 86 | 87 | ```shell 88 | pnpm add 89 | ``` 90 | 91 | 92 | 93 | then simply import the package in the code and you're good to go. 94 | 95 | ```ts 96 | import packageName from ''; 97 | ``` 98 | 99 | ## Plugins 100 | 101 | Plugins are node packages designed for Spicetify Creator projects, and they support either extensions, custom apps, or both. 102 | The convention is to name every plugin like so: `spcr-`. 103 | 104 | For a list of plugins: https://github.com/FlafyDev/spicetify-creator-plugins 105 | 106 | To install and import a plugin: 107 | 108 | 109 | ```shell 110 | npm install spcr- 111 | ``` 112 | 113 | 114 | ```shell 115 | yarn add spcr- 116 | ``` 117 | 118 | 119 | ```shell 120 | pnpm add spcr- 121 | ``` 122 | 123 | 124 | 125 | ```ts 126 | import plugin from 'spcr-'; 127 | ``` 128 | 129 | #### Example of 2 plugins you can already use in your own apps: 130 | 131 | - [spcr-settings](https://github.com/FlafyDev/spicetify-creator-plugins/tree/main/packages/spcr-settings) 132 | - [spcr-navigation-bar](https://github.com/FlafyDev/spicetify-creator-plugins/tree/main/packages/spcr-navigation-bar) 133 | 134 | ## Update Spicetify Creator 135 | 136 | 137 | 138 | ```shell 139 | npm update spicetify-creator 140 | ``` 141 | 142 | 143 | ```shell 144 | yarn upgrade spicetify-creator 145 | ``` 146 | 147 | 148 | ```shell 149 | pnpm update spicetify-creator 150 | ``` 151 | 152 | 153 | -------------------------------------------------------------------------------- /docs/development/themes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Themes 3 | description: ✨ Creating Themes for Spicetify. 4 | --- 5 | 6 | There are 2 places you can put your themes: 7 | 8 | 1. `Themes` folder in Home directory 9 | 10 | | Platform | Path | 11 | | ------------------- | --------------------------------- | 12 | | **Windows** | `%appdata%\spicetify\Themes` | 13 | | **Linux**/**MacOS** | `~/.config/spicetify/Themes` | 14 | 15 | 2. `Themes` folder in Spicetify executable directory 16 | 17 | If there are 2 themes with the same name, the theme in the Home directory is prioritized. 18 | 19 | Every theme should contain: 20 | 21 | - `color.ini`: stores colors values that later will be converted to CSS variables 22 | - `user.css`: set of custom CSS rules to manipulate, hide, move UI elements. 23 | 24 | Color value can be in several formats and forms: 25 | 26 | - Hex: e.g `#FF0000`, `#1258F6`, `#F55` 27 | - Decimal: e.g `255,255,255`, `50,80,120` 28 | - Environment variables can be used in place of color. 29 | 30 | - Syntax: `${}` 31 | - Example usage: `text = ${LIGHT_GREY}` 32 | 33 | - **[Linux]** You can use XResources variable in place of color. Extremely useful for who uses `pywal` to generate color scheme. 34 | - Syntax: `${xrdb:}` or `${xrdb::}` 35 | - Example usage: 36 | 37 | ``` 38 | [Base] 39 | text = ${xrdb:color14} 40 | subtext = ${xrdb:foreground:#FFF} 41 | player = ${xrdb:background} 42 | ... 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: FAQ 3 | --- 4 | 5 | ## Where is the config file? 6 | 7 | The config file is generally located at: 8 | 9 | | Platform | Path | 10 | | ------------------- | ------------------------------------------ | 11 | | **Windows** | `%appdata%\spicetify\config-xpui.ini` | 12 | | **Linux**/**macOS** | `~/.config/spicetify/config-xpui.ini` | 13 | 14 | However, you can know specifically where it is with: 15 | 16 | ``` 17 | spicetify -c 18 | ``` 19 | 20 | Or, you can open the folder where it is located by entering the following in your terminal: 21 | 22 | ``` 23 | spicetify config-dir 24 | ``` 25 | 26 | For details about each config field, please run: 27 | 28 | ```bash 29 | spicetify --help config 30 | ``` 31 | 32 | ## Cannot find `pref_file` 33 | 34 | ### Windows 35 | 36 | 1. There is a great chance that you are using Microsoft Store Spotify. Please double check that in Spotify About page. 37 | 2. If you are actually using Microsoft Store Spotify, remove it completely. Go to Spotify website to download the normal version installer. 38 | 3. If you are not using the Microsoft Store Spotify, and are using the one from the Spotify website, check to see if you have a "prefs" file in `C:\Users\YOUR_USERNAME\AppData\Roaming\Spotify`. 39 | 4. If so, open your `config-xpui.ini` and set `prefs_path` to the absolute path of that prefs file. (e.g. `C:\Users\YOUR_USERNAME\AppData\Roaming\Spotify\prefs`) Then try running `spicetify` again. 40 | 41 | ### Linux 42 | 43 | 1. In `bash`, run `cd ~` and `find | grep "spotify/prefs$"` 44 | 2. If it returns a path to prefs file, copy its absolute path to `prefs_path` field in `config-xpui.ini`. 45 | 46 | ## After Spotify's update, running `spicetify apply` or `spicetify update` breaks Spotify. 47 | 48 | After any Spotify update, always run `spicetify backup apply`. 49 | Optionally, set the Spotify shortcut to run `spicetify auto` (instead of direct path to Spotify executable), so that Spicetify can backup and apply, when it needs to, then launch Spotify automatically. 50 | 51 | It may be the case that Spicetify does not yet support a new Spotify update. In that case, please check the Spicetify issue tracker. 52 | 53 | ## I can't play some songs after downgrading Spotify 54 | 55 | Delete all files in the following folder and launch spotify again. 56 | 57 | - **Windows**: `%LOCALAPPDATA%\Spotify` 58 | - **Linux**: `~/.config/spotify` 59 | - **macOS**: `~/Library/Application Support/Spotify` 60 | 61 | ## Sometimes **Popup Lyrics** and/or **Lyrics Plus** seem to not work 62 | 63 | This problem happens in the extension [Popup Lyrics](https://github.com/spicetify/cli/wiki/Extensions#pop-up-lyrics) and custom app [Lyrics Plus](https://github.com/spicetify/cli/wiki/Custom-Apps#lyrics-plus) mostly because your Musixmatch token has been flagged for doing too many requests. This can be fixed by just waiting without skipping songs too much, however, if it is still a problem for you, all you need to do is to install the Musixmatch official app, which is a web-based app like Spotify. 64 | 65 | 1. **Linux:** find an archive online 66 | **Windows:** go to [store.rg-adguard.net](https://store.rg-adguard.net/) and then select ProductID and enter `9wzdncrfj235` and click done. Download the .appxbundle and install. 67 | 68 | 2. **You don't need to log in!** 69 | 70 | 3. Now in Musixmatch app, hit `Ctrl + Shift + i` to bring up DevTools. 71 | 72 | ![mxm1](https://i.imgur.com/jMGMgCc.png) 73 | 74 | 4. Switch to Network tab. Hit `Ctrl + R`. Filter results with "apic": 75 | 76 | ![mxm2](https://i.imgur.com/QdwqtQa.png) 77 | 78 | 5. Click on any result. Click on the Headers tab. Scroll all the way down. Note down `usertoken` 79 | 80 | ![mxm3](https://i.imgur.com/ZsGwKG3.png) 81 | 82 | It should look like this: 83 | 84 | ``` 85 | 200501593b603a3fdc5c9b4a696389f6589dd988e5a1cf02dfdce1 86 | ``` 87 | 88 | 6. You can open the config for Popup Lyrics by right clicking on the Popup Lyrics button. Or if you're using Lyrics Plus, open the config by clicking on Lyrics in the sidebar and clicking on the profile menu and then clicking 'Lyrics Plus config'. You can then paste your personal token in the input field in the Musixmatch section and turn the switch on. 89 | 90 | ![mxm4](https://i.imgur.com/yvrkllb.png) 91 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | sidebar_position: 1 4 | --- 5 | 6 | Spicetify is a multiplatform command-line tool to customize the official Spotify client. 7 | 8 | ## Installation 9 | 10 | ### Windows 11 | 12 | This is the installation method we recommend for most users. It is the fastest and most reliable way to install Spicetify. 13 | 14 | #### Powershell (pre-built binary) 15 | 16 | ```powershell 17 | iwr -useb https://raw.githubusercontent.com/spicetify/cli/main/install.ps1 | iex 18 | ``` 19 | 20 | Also run the following if you would like to install the [**Spicetify Marketplace**](https://github.com/spicetify/marketplace), which gives you access to a tab in Spotify's sidebar that allows you to search for and install _themes_, _extensions_, and _snippets_. 21 | ```powershell 22 | iwr -useb https://raw.githubusercontent.com/spicetify/marketplace/main/resources/install.ps1 | iex 23 | ``` 24 | 25 | ### Linux and MacOS 26 | 27 | #### Shell (pre-built binary) 28 | Spicetify CLI 29 | ```sh 30 | curl -fsSL https://raw.githubusercontent.com/spicetify/cli/main/install.sh | sh 31 | ``` 32 | Spicetify Marketplace 33 | ```sh 34 | curl -fsSL https://raw.githubusercontent.com/spicetify/marketplace/main/resources/install.sh | sh 35 | ``` 36 | 37 |
38 | 39 | ## Basic Usage 40 | 41 | After installing Spicetify and Spicetify's Marketplace, you can use it to customize your Spotify client using all the available **extensions** and **themes** found in the Marketplace. 42 | 43 | ### Updating 44 | 45 | **_Spotify_**, every now and then, **updates** its client. Since we have no power over this process, you will likely need to **re-apply Spicetify**. 46 | 47 | However, the update might have major changes to the client, which means you will need to run `spicetify update` (`spicetify upgrade` in Spicetify versions **below 2.27.0**) every time you update Spotify. If no update for Spicetify is available, it means that it either still works by simply running `spicetify backup apply`, or that we are still **working on updating Spicetify** to work on the new version. 48 | -------------------------------------------------------------------------------- /docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require('prism-react-renderer').themes.github; 5 | const darkCodeTheme = require('prism-react-renderer').themes.dracula; 6 | 7 | /** @type {import('@docusaurus/types').Config} */ 8 | const config = { 9 | title: 'Spicetify', 10 | tagline: 'Powerful CLI tool to take control of the Spotify client.', 11 | url: 'https://spicetify.app', 12 | baseUrl: '/', 13 | onBrokenLinks: 'throw', 14 | onBrokenMarkdownLinks: 'warn', 15 | favicon: 'images/favicon.ico', 16 | organizationName: 'spicetify', 17 | projectName: 'spicetify-docs', 18 | presets: [ 19 | [ 20 | 'classic', 21 | /** @type {import('@docusaurus/preset-classic').Options} */ 22 | ({ 23 | docs: { 24 | sidebarPath: require.resolve('./sidebars.js'), 25 | // Please change this to your repo. 26 | editUrl: 'https://github.com/spicetify/spicetify-docs/edit/main', 27 | }, 28 | blog: { 29 | showReadingTime: true, 30 | // Please change this to your repo. 31 | editUrl: 'https://github.com/spicetify/spicetify-docs/edit/main', 32 | onUntruncatedBlogPosts: 'ignore', 33 | }, 34 | theme: { 35 | customCss: require.resolve('./src/css/custom.css'), 36 | }, 37 | }), 38 | ], 39 | ], 40 | 41 | plugins: [process.env.RSDOCTOR === 'true' && ['rsdoctor', {}]], 42 | 43 | themeConfig: 44 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 45 | ({ 46 | metadata: [ 47 | { 48 | name: 'keywords', 49 | content: 'spicetify, spotify, customization, documentation, blog', 50 | }, 51 | ], 52 | image: 'images/spicetify-full.png', 53 | colorMode: { 54 | defaultMode: 'light', 55 | respectPrefersColorScheme: true, 56 | }, 57 | docs: { 58 | sidebar: { 59 | autoCollapseCategories: false, 60 | hideable: false, 61 | }, 62 | }, 63 | navbar: { 64 | title: 'Spicetify', 65 | logo: { 66 | alt: 'Spicetify Logo', 67 | src: 'images/spicetify.png', 68 | }, 69 | items: [ 70 | { 71 | type: 'doc', 72 | docId: 'getting-started', 73 | position: 'left', 74 | label: 'Docs', 75 | }, 76 | { to: '/blog', label: 'Blog', position: 'left' }, 77 | { 78 | html: ` 79 |
80 | GitHub stars 81 |
82 | `, 83 | href: 'https://github.com/spicetify/cli', 84 | position: 'right', 85 | }, 86 | ], 87 | }, 88 | footer: { 89 | style: 'dark', 90 | links: [ 91 | { 92 | title: 'Docs', 93 | items: [ 94 | { 95 | label: 'Getting Started', 96 | to: '/docs/getting-started', 97 | }, 98 | { 99 | label: 'Advanced Usage', 100 | to: '/docs/advanced-usage', 101 | }, 102 | { 103 | label: 'Development', 104 | to: '/docs/development', 105 | }, 106 | { 107 | label: 'FAQ', 108 | to: '/docs/faq', 109 | }, 110 | ], 111 | }, 112 | { 113 | title: 'Community', 114 | items: [ 115 | { 116 | label: 'Community Themes', 117 | href: 'https://github.com/spicetify/spicetify-themes', 118 | }, 119 | { 120 | label: 'Documentation', 121 | href: 'https://github.com/spicetify/spicetify-docs', 122 | }, 123 | { 124 | label: 'Discord', 125 | href: 'https://discord.gg/VnevqPp2Rr', 126 | }, 127 | { 128 | label: 'Reddit', 129 | href: 'https://www.reddit.com/r/spicetify', 130 | }, 131 | ], 132 | }, 133 | { 134 | title: 'Supporters', 135 | items: [ 136 | { 137 | html: ` 138 |
139 |

Open Collective

140 | 141 | Open Collective 142 | 143 |
144 | `, 145 | }, 146 | ], 147 | }, 148 | ], 149 | copyright: `Copyright © ${new Date().getFullYear()} Spicetify. Built with 🎶 and 🦖.`, 150 | }, 151 | prism: { 152 | theme: lightCodeTheme, 153 | darkTheme: darkCodeTheme, 154 | }, 155 | algolia: { 156 | appId: '8V2X4EIOO7', 157 | apiKey: 'd538fac3f86b5167706ae2e5c3cce353', 158 | indexName: 'spicetify', 159 | }, 160 | }), 161 | future: { 162 | experimental_faster: true, 163 | }, 164 | }; 165 | 166 | module.exports = config; 167 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spicetify-docs", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "dev": "docusaurus start", 9 | "build": "docusaurus build", 10 | "rsdoctor": "RSDOCTOR=true docusaurus build", 11 | "swizzle": "docusaurus swizzle", 12 | "deploy": "docusaurus deploy", 13 | "clear": "docusaurus clear", 14 | "serve": "docusaurus serve", 15 | "write-translations": "docusaurus write-translations", 16 | "write-heading-ids": "docusaurus write-heading-ids", 17 | "prepare": "husky", 18 | "lint": "biome check" 19 | }, 20 | "dependencies": { 21 | "@algolia/client-search": "^5.20.0", 22 | "@docusaurus/core": "^3.7.0", 23 | "@docusaurus/faster": "^3.7.0", 24 | "@docusaurus/preset-classic": "^3.7.0", 25 | "@docusaurus/theme-common": "^3.7.0", 26 | "@docusaurus/types": "^3.7.0", 27 | "@mdx-js/react": "^3.1.0", 28 | "clsx": "^2.1.1", 29 | "prism-react-renderer": "^2.4.1", 30 | "react": "^19.0.0", 31 | "react-dom": "^19.0.0", 32 | "swiper": "^11.2.2" 33 | }, 34 | "devDependencies": { 35 | "@biomejs/biome": "1.9.4", 36 | "@docusaurus/module-type-aliases": "^3.7.0", 37 | "@docusaurus/plugin-rsdoctor": "^3.7.0", 38 | "@docusaurus/theme-classic": "^3.7.0", 39 | "@docusaurus/tsconfig": "^3.7.0", 40 | "@types/node": "^22.13.0", 41 | "@types/react": "^19.0.8", 42 | "husky": "^9.1.7", 43 | "lint-staged": "^15.4.3", 44 | "typescript": "^5.7.3" 45 | }, 46 | "lint-staged": { 47 | "*": "biome check --apply --no-errors-on-unmatched" 48 | }, 49 | "browserslist": { 50 | "production": [">0.5%", "not dead", "not op_mini all"], 51 | "development": [ 52 | "last 1 chrome version", 53 | "last 1 firefox version", 54 | "last 1 safari version" 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/HomepageFeatures.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | @media (max-width: 996px) { 9 | .featureImage { 10 | width: 50%; 11 | } 12 | } 13 | 14 | html[data-theme='dark'] .features { 15 | -webkit-animation: fadein 2s; 16 | -moz-animation: fadein 2s; 17 | -ms-animation: fadein 2s; 18 | -o-animation: fadein 2s; 19 | animation: fadein 2s; 20 | background: rgba(49, 54, 59, 0.8); 21 | } 22 | 23 | html[data-theme='light'] .features { 24 | -webkit-animation: fadein 2s; 25 | -moz-animation: fadein 2s; 26 | -ms-animation: fadein 2s; 27 | -o-animation: fadein 2s; 28 | animation: fadein 2s; 29 | background: rgb(255, 255, 255); 30 | } 31 | 32 | @keyframes fadein { 33 | 0% { 34 | opacity: 0; 35 | } 36 | 75% { 37 | opacity: 0.5; 38 | } 39 | 100% { 40 | opacity: 1; 41 | } 42 | } 43 | 44 | @-moz-keyframes fadein { 45 | 0% { 46 | opacity: 0; 47 | } 48 | 100% { 49 | opacity: 1; 50 | } 51 | } 52 | 53 | @-webkit-keyframes fadein { 54 | 0% { 55 | opacity: 0; 56 | } 57 | 100% { 58 | opacity: 1; 59 | } 60 | } 61 | 62 | @-o-keyframes fadein { 63 | 0% { 64 | opacity: 0; 65 | } 66 | 100% { 67 | opacity: 1; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/components/HomepageFeatures.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import styles from './HomepageFeatures.module.css'; 3 | 4 | const FeatureList = [ 5 | { 6 | title: 'Themes', 7 | image: require('@site/static/images/theme.png').default, 8 | description: ( 9 | <> 10 | The customization you've always needed, but didn't know you 11 | wanted. 12 | 13 | ), 14 | }, 15 | { 16 | title: 'Extensions', 17 | image: require('@site/static/images/extension.png').default, 18 | description: ( 19 | <>Small bonus features that can be sprinkled across Spotify. 20 | ), 21 | }, 22 | { 23 | title: 'Custom Apps', 24 | image: require('@site/static/images/app.png').default, 25 | description: ( 26 | <>Whole new pages that complement Spotify's missing ones. 27 | ), 28 | }, 29 | ]; 30 | 31 | function Feature({ image, title, description }) { 32 | return ( 33 |
34 |
35 | {title} 36 |
37 |
38 |

{title}

39 |

{description}

40 |
41 |
42 | ); 43 | } 44 | 45 | export default function HomepageFeatures() { 46 | return ( 47 |
48 |
49 |
50 | {FeatureList.map((feature) => ( 51 | 52 | ))} 53 |
54 |
55 |
56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /src/components/SwiperCarousel.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | A11y, 3 | Autoplay, 4 | EffectCoverflow, 5 | Navigation, 6 | Scrollbar, 7 | } from 'swiper/modules'; 8 | import { Swiper, SwiperSlide } from 'swiper/react'; 9 | 10 | import 'swiper/css'; 11 | import 'swiper/css/effect-coverflow'; 12 | import 'swiper/css/navigation'; 13 | import 'swiper/css/scrollbar'; 14 | 15 | // NOTE: These screenshots were generated with a window of size 1214x649. 16 | // (You can see the size when you resize if you have the dev tools open) 17 | // The shadow + blur is from macOS's screenshot tool. 18 | const data = [ 19 | // Sleek (Elementary) - Homepage 20 | { 21 | cover: require('@site/static/images/homepage-carousel/1.png').default, 22 | title: 'Preview 1', 23 | }, 24 | // Sleek (Wealthy) - Homepage 25 | { 26 | cover: require('@site/static/images/homepage-carousel/2.png').default, 27 | title: 'Preview 2', 28 | }, 29 | // Nord theme, new UI - Homepage 30 | { 31 | cover: require('@site/static/images/homepage-carousel/3.png').default, 32 | title: 'Preview 3', 33 | }, 34 | // Comfy - Homepage 35 | { 36 | cover: require('@site/static/images/homepage-carousel/4.png').default, 37 | title: 'Preview 4', 38 | }, 39 | // Dribbblish - Homepage 40 | { 41 | cover: require('@site/static/images/homepage-carousel/5.png').default, 42 | title: 'Preview 5', 43 | }, 44 | // Sleek (Elementary) - Marketplace extensions 45 | { 46 | cover: require('@site/static/images/homepage-carousel/6.png').default, 47 | title: 'Preview 6', 48 | }, 49 | // Nord theme, new UI - Marketplace themes 50 | { 51 | cover: require('@site/static/images/homepage-carousel/7.png').default, 52 | title: 'Preview 7', 53 | }, 54 | ]; 55 | 56 | export default () => { 57 | const slides = data.map((item, index) => ( 58 | 59 | {item.title} 60 | 61 | )); 62 | 63 | return ( 64 | console.log(swiper)} 73 | // onSlideChange={centeredSlide() => console.log('slide change')} 74 | autoplay={{ delay: 2000, disableOnInteraction: false }} 75 | effect="coverflow" 76 | coverflowEffect={{ 77 | rotate: 0, 78 | stretch: 0, 79 | depth: 250, 80 | modifier: 1, 81 | slideShadows: false, 82 | }} 83 | breakpoints={{ 84 | 700: { 85 | spaceBetween: -100, 86 | slidesPerView: 3, 87 | }, 88 | 500: { 89 | spaceBetween: -100, 90 | slidesPerView: 2, 91 | }, 92 | 300: { 93 | spaceBetween: -100, 94 | slidesPerView: 1, 95 | }, 96 | }} 97 | > 98 | {slides} 99 | 100 | ); 101 | }; 102 | -------------------------------------------------------------------------------- /src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #eb5a37; 10 | --ifm-color-primary-dark: rgb(33, 175, 144); 11 | --ifm-color-primary-darker: rgb(31, 165, 136); 12 | --ifm-color-primary-darkest: rgb(26, 136, 112); 13 | --ifm-color-primary-light: rgb(70, 203, 174); 14 | --ifm-color-primary-lighter: rgb(102, 212, 189); 15 | --ifm-color-primary-lightest: rgb(146, 224, 208); 16 | --ifm-code-font-size: 95%; 17 | 18 | /* Swiper colours */ 19 | --swiper-navigation-color: #fff; 20 | --swiper-theme-color: #fff; 21 | 22 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 23 | } 24 | 25 | .docusaurus-highlight-code-line { 26 | display: block; 27 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 28 | padding: 0 var(--ifm-pre-padding); 29 | } 30 | 31 | [data-theme='dark'] { 32 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 33 | } 34 | -------------------------------------------------------------------------------- /src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | background-color: #fbab7e; 12 | background-image: linear-gradient(62deg, #fbab7e 0%, #f7ce68 100%); 13 | flex-direction: column; 14 | } 15 | 16 | .heroCarousel { 17 | position: relative; 18 | width: 100%; 19 | } 20 | 21 | .buttons { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | } 26 | 27 | .sponsor { 28 | display: inline-block; 29 | text-align: center; 30 | margin: 0.5rem 0; 31 | height: min-content; 32 | } 33 | 34 | .sponsor > a { 35 | position: relative; 36 | } 37 | 38 | .sponsor > a:hover > .tooltipMsg { 39 | visibility: visible; 40 | opacity: 1; 41 | } 42 | 43 | .tooltipMsg { 44 | visibility: hidden; 45 | text-decoration: none; 46 | color: white; 47 | background-color: #555; 48 | border-radius: 6px; 49 | padding: 0.5rem 1rem; 50 | position: absolute; 51 | z-index: 1; 52 | width: 240px; 53 | max-width: 50vw; 54 | top: 150%; 55 | right: 50%; 56 | margin-right: -120px; 57 | text-align: center; 58 | opacity: 0; 59 | transition: opacity 0.2s; 60 | } 61 | 62 | @media screen and (max-width: 600px) { 63 | .tooltipMsg { 64 | right: 0; 65 | margin-right: 20px; 66 | } 67 | } 68 | 69 | @media screen and (max-width: 966px) { 70 | .heroBanner .container { 71 | padding: 2rem; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from '@docusaurus/Link'; 2 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 3 | import Layout from '@theme/Layout'; 4 | import clsx from 'clsx'; 5 | import HomepageFeatures from '../components/HomepageFeatures'; 6 | import SwiperCarousel from '../components/SwiperCarousel'; 7 | import styles from './index.module.css'; 8 | 9 | function HomepageHeader() { 10 | const { siteConfig } = useDocusaurusContext(); 11 | return ( 12 |
13 |
14 |

{siteConfig.title}

15 |

{siteConfig.tagline}

16 |
17 | 18 |
19 | 20 |
21 | 22 |
23 | 27 | Install Now! 28 | 29 |
30 |
31 | ); 32 | } 33 | 34 | export default function Home() { 35 | return ( 36 | 37 |
38 | 39 | Sponsor us 40 | {' '} 41 | and get your logo here! 42 |
43 | 44 |
45 | 46 |
47 |
48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/.nojekyll -------------------------------------------------------------------------------- /static/images/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/app.png -------------------------------------------------------------------------------- /static/images/extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/extension.png -------------------------------------------------------------------------------- /static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/favicon.ico -------------------------------------------------------------------------------- /static/images/homepage-carousel/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/homepage-carousel/1.png -------------------------------------------------------------------------------- /static/images/homepage-carousel/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/homepage-carousel/2.png -------------------------------------------------------------------------------- /static/images/homepage-carousel/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/homepage-carousel/3.png -------------------------------------------------------------------------------- /static/images/homepage-carousel/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/homepage-carousel/4.png -------------------------------------------------------------------------------- /static/images/homepage-carousel/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/homepage-carousel/5.png -------------------------------------------------------------------------------- /static/images/homepage-carousel/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/homepage-carousel/6.png -------------------------------------------------------------------------------- /static/images/homepage-carousel/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/homepage-carousel/7.png -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/logo.png -------------------------------------------------------------------------------- /static/images/powered-by-vercel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /static/images/spicetify-full-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/spicetify-full-link.png -------------------------------------------------------------------------------- /static/images/spicetify-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/spicetify-full.png -------------------------------------------------------------------------------- /static/images/spicetify-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/spicetify-square.png -------------------------------------------------------------------------------- /static/images/spicetify-svg-examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/spicetify-svg-examples.png -------------------------------------------------------------------------------- /static/images/spicetify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/spicetify.png -------------------------------------------------------------------------------- /static/images/theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spicetify/docs/fb82dab7113306e95425f545f30a9224973d1ec5/static/images/theme.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@docusaurus/tsconfig", 3 | "include": ["src/"] 4 | } 5 | --------------------------------------------------------------------------------