gitextract_xkduu9og/ ├── .github/ │ └── workflows/ │ ├── ci-rtk.yml │ ├── ci-tanstack.yml │ ├── deploy-effector.yml │ ├── deploy-reatom.yml │ ├── deploy-root.yml │ ├── deploy-rtk.yml │ ├── deploy-tanstack.yml │ └── deploy.yml ├── .gitignore ├── .husky/ │ ├── pre-commit │ └── pre-push ├── .prettierignore ├── .prettierrc ├── CONTRIBUTING.md ├── FRONTEND_API_CHANGES.md ├── README.md ├── apps/ │ ├── nextjs/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── eslint.config.mjs │ │ ├── next.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── app/ │ │ │ │ ├── actions/ │ │ │ │ │ └── auth/ │ │ │ │ │ └── logout.action.tsx │ │ │ │ ├── api/ │ │ │ │ │ └── oauth/ │ │ │ │ │ └── callback/ │ │ │ │ │ └── route.ts │ │ │ │ ├── globals.css │ │ │ │ ├── layout.tsx │ │ │ │ ├── page.module.css │ │ │ │ ├── page.tsx │ │ │ │ ├── profile/ │ │ │ │ │ └── page.tsx │ │ │ │ └── redirect/ │ │ │ │ └── page.tsx │ │ │ ├── features/ │ │ │ │ └── auth/ │ │ │ │ └── ui/ │ │ │ │ ├── Login/ │ │ │ │ │ └── Login.tsx │ │ │ │ ├── Logout/ │ │ │ │ │ └── Logout.tsx │ │ │ │ ├── MeInfo/ │ │ │ │ │ └── MeInfo.tsx │ │ │ │ └── UserBlock.tsx │ │ │ ├── middleware.ts │ │ │ ├── reauth-middleware.ts │ │ │ └── shared/ │ │ │ ├── api/ │ │ │ │ ├── auth-api.ts │ │ │ │ ├── authApi.types.ts │ │ │ │ ├── base.ts │ │ │ │ └── tracks/ │ │ │ │ ├── tracksApi.ts │ │ │ │ └── tracksApi.types.ts │ │ │ ├── common.types.ts │ │ │ └── utils/ │ │ │ ├── cookieHelpers.ts │ │ │ └── jwt-util.ts │ │ └── tsconfig.json │ ├── react-effector-fsd/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── app/ │ │ │ │ ├── App.tsx │ │ │ │ ├── main.tsx │ │ │ │ ├── model/ │ │ │ │ │ └── init.ts │ │ │ │ ├── routes/ │ │ │ │ │ ├── Routing.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── styles/ │ │ │ │ ├── fonts.css │ │ │ │ ├── global.css │ │ │ │ ├── reset.css │ │ │ │ └── variables.css │ │ │ ├── features/ │ │ │ │ └── auth/ │ │ │ │ ├── api/ │ │ │ │ │ ├── login.ts │ │ │ │ │ ├── logout.ts │ │ │ │ │ └── me.ts │ │ │ │ ├── index.ts │ │ │ │ ├── model/ │ │ │ │ │ ├── auth-api.types.ts │ │ │ │ │ ├── model.ts │ │ │ │ │ └── user.types.ts │ │ │ │ └── ui/ │ │ │ │ ├── LoginButtonAndModal/ │ │ │ │ │ ├── LoginButtonAndModal.module.css │ │ │ │ │ ├── LoginButtonAndModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ProfileDropdownMenu/ │ │ │ │ │ ├── ProfileDropdownMenu.module.css │ │ │ │ │ ├── ProfileDropdownMenu.stories.tsx │ │ │ │ │ ├── ProfileDropdownMenu.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── pages/ │ │ │ │ ├── auth/ │ │ │ │ │ └── OAuthRedirect/ │ │ │ │ │ ├── OAuthCallback.module.css │ │ │ │ │ └── OAuthCallback.tsx │ │ │ │ ├── home/ │ │ │ │ │ ├── Home.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── user/ │ │ │ │ ├── UserPage.tsx │ │ │ │ └── index.ts │ │ │ ├── shared/ │ │ │ │ ├── api/ │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── utils/ │ │ │ │ │ ├── json-api-error.ts │ │ │ │ │ └── request-wrapper.ts │ │ │ │ ├── components/ │ │ │ │ │ ├── AudioPlayer/ │ │ │ │ │ │ ├── AudioPlayer.module.css │ │ │ │ │ │ ├── AudioPlayer.stories.tsx │ │ │ │ │ │ ├── AudioPlayer.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Autocomplete/ │ │ │ │ │ │ ├── Autocomplete.module.css │ │ │ │ │ │ ├── Autocomplete.stories.tsx │ │ │ │ │ │ ├── Autocomplete.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Button/ │ │ │ │ │ │ ├── Button.module.css │ │ │ │ │ │ ├── Button.stories.tsx │ │ │ │ │ │ ├── Button.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Card/ │ │ │ │ │ │ ├── Card.module.css │ │ │ │ │ │ ├── Card.stories.tsx │ │ │ │ │ │ ├── Card.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Dialog/ │ │ │ │ │ │ ├── Dialog.module.css │ │ │ │ │ │ ├── Dialog.stories.tsx │ │ │ │ │ │ ├── Dialog.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── DropdownMenu/ │ │ │ │ │ │ ├── DropdownMenu.module.css │ │ │ │ │ │ ├── DropdownMenu.stories.tsx │ │ │ │ │ │ ├── DropdownMenu.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Hashtag/ │ │ │ │ │ │ ├── Tag.module.css │ │ │ │ │ │ ├── Tag.stories.tsx │ │ │ │ │ │ ├── Tag.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── IconButton/ │ │ │ │ │ │ ├── IconButton.module.css │ │ │ │ │ │ ├── IconButton.stories.tsx │ │ │ │ │ │ ├── IconButton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ImageUploader/ │ │ │ │ │ │ ├── ImageUploader.module.css │ │ │ │ │ │ ├── ImageUploader.stories.tsx │ │ │ │ │ │ ├── ImageUploader.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Pagination/ │ │ │ │ │ │ ├── Pagination.module.css │ │ │ │ │ │ ├── Pagination.stories.tsx │ │ │ │ │ │ ├── Pagination.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Progress/ │ │ │ │ │ │ ├── Progress.module.css │ │ │ │ │ │ ├── Progress.stories.tsx │ │ │ │ │ │ ├── Progress.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ReactionButtons/ │ │ │ │ │ │ ├── ReactionButtons.module.css │ │ │ │ │ │ ├── ReactionButtons.stories.tsx │ │ │ │ │ │ ├── ReactionButtons.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SearchField/ │ │ │ │ │ │ ├── SearchField.module.css │ │ │ │ │ │ ├── SearchField.stories.tsx │ │ │ │ │ │ ├── SearchField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Select/ │ │ │ │ │ │ ├── Select.module.css │ │ │ │ │ │ ├── Select.stories.tsx │ │ │ │ │ │ ├── Select.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SortSelect/ │ │ │ │ │ │ └── Select.tsx │ │ │ │ │ ├── Table/ │ │ │ │ │ │ ├── Table.module.css │ │ │ │ │ │ ├── Table.stories.tsx │ │ │ │ │ │ ├── Table.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Tabs/ │ │ │ │ │ │ ├── Tabs.module.css │ │ │ │ │ │ ├── Tabs.stories.tsx │ │ │ │ │ │ ├── Tabs.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TagEditor/ │ │ │ │ │ │ ├── TagEditor.module.css │ │ │ │ │ │ ├── TagEditor.stories.tsx │ │ │ │ │ │ ├── TagEditor.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TextField/ │ │ │ │ │ │ ├── TextField.module.css │ │ │ │ │ │ ├── TextField.stories.tsx │ │ │ │ │ │ ├── TextField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Textarea/ │ │ │ │ │ │ ├── Textarea.module.css │ │ │ │ │ │ ├── Textarea.stories.tsx │ │ │ │ │ │ ├── Textarea.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Typography/ │ │ │ │ │ │ ├── Typography.module.css │ │ │ │ │ │ ├── Typography.stories.tsx │ │ │ │ │ │ ├── Typography.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── config/ │ │ │ │ │ └── config.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useDebounceValue.ts │ │ │ │ │ └── useGetId.ts │ │ │ │ └── icons/ │ │ │ │ ├── AddToPlaylistIcon.tsx │ │ │ │ ├── ArrowDownIcon.tsx │ │ │ │ ├── ClockIcon.tsx │ │ │ │ ├── CreateIcon.tsx │ │ │ │ ├── DeleteIcon.tsx │ │ │ │ ├── DislikeIcon.tsx │ │ │ │ ├── DownloadIcon.tsx │ │ │ │ ├── EditIcon.tsx │ │ │ │ ├── HomeIcon.tsx │ │ │ │ ├── ImageUploadIcon.tsx │ │ │ │ ├── KeyboardArrowLeftIcon.tsx │ │ │ │ ├── KeyboardArrowRightIcon.tsx │ │ │ │ ├── LibraryIcon.tsx │ │ │ │ ├── LikeIcon.tsx │ │ │ │ ├── LikeIconFill.tsx │ │ │ │ ├── LikeInSquareIcon.tsx │ │ │ │ ├── LiveWaveIcon/ │ │ │ │ │ ├── LiveWaveIcon.module.css │ │ │ │ │ ├── LiveWaveIcon.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── LogoutIcon.tsx │ │ │ │ ├── MoreIcon.tsx │ │ │ │ ├── PauseIcon.tsx │ │ │ │ ├── PlayIcon.tsx │ │ │ │ ├── PlaylistIcon.tsx │ │ │ │ ├── PlusIcon.tsx │ │ │ │ ├── ProfileIcon.tsx │ │ │ │ ├── RepeatIcon.tsx │ │ │ │ ├── SearchIcon.tsx │ │ │ │ ├── ShuffleIcon.tsx │ │ │ │ ├── SkipNextIcon.tsx │ │ │ │ ├── SkipPreviousIcon.tsx │ │ │ │ ├── TextIcon.tsx │ │ │ │ ├── TrackIcon.tsx │ │ │ │ ├── UploadIcon.tsx │ │ │ │ ├── VolumeIcon.tsx │ │ │ │ ├── VolumeMuteIcon.tsx │ │ │ │ └── index.ts │ │ │ └── widgets/ │ │ │ └── layout/ │ │ │ ├── index.ts │ │ │ └── ui/ │ │ │ ├── Header/ │ │ │ │ ├── Header.module.css │ │ │ │ └── Header.tsx │ │ │ ├── Layout.module.css │ │ │ ├── Layout.tsx │ │ │ └── Sidebar/ │ │ │ ├── MenuLinks/ │ │ │ │ ├── MenuLinks.module.css │ │ │ │ └── MenuLinks.tsx │ │ │ ├── Sidebar.module.css │ │ │ └── Sidebar.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── react-native-expo/ │ │ ├── .gitignore │ │ ├── .npmrc │ │ ├── app/ │ │ │ ├── (app)/ │ │ │ │ ├── _layout.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── library/ │ │ │ │ │ └── library.tsx │ │ │ │ ├── playlists/ │ │ │ │ │ └── playlists.tsx │ │ │ │ └── tracks/ │ │ │ │ └── tracks.tsx │ │ │ ├── (auth)/ │ │ │ │ ├── _layout.tsx │ │ │ │ └── login.tsx │ │ │ └── _layout.tsx │ │ ├── app.config.ts │ │ ├── babel.config.js │ │ ├── declarations.d.ts │ │ ├── features/ │ │ │ ├── auth/ │ │ │ │ ├── components/ │ │ │ │ │ ├── LoginButton/ │ │ │ │ │ │ └── LoginButton.tsx │ │ │ │ │ └── LogoutButton/ │ │ │ │ │ └── LogoutButton.tsx │ │ │ │ └── model/ │ │ │ │ ├── api/ │ │ │ │ │ ├── auth-instanse/ │ │ │ │ │ │ └── auth-instanse.ts │ │ │ │ │ └── hooks/ │ │ │ │ │ ├── use-login-mutatuion.ts │ │ │ │ │ ├── use-logout-mutation.ts │ │ │ │ │ └── use-me.query.ts │ │ │ │ ├── config/ │ │ │ │ │ └── oauth.ts │ │ │ │ ├── context/ │ │ │ │ │ └── AuthContext.tsx │ │ │ │ ├── types/ │ │ │ │ │ └── api.types.ts │ │ │ │ └── utils/ │ │ │ │ ├── expoUrlToHttpCallback.ts │ │ │ │ └── getOauthRedirectUrl.ts │ │ │ └── playlists/ │ │ │ └── model/ │ │ │ └── api/ │ │ │ └── playlist-instance/ │ │ │ └── playlist-instance.ts │ │ ├── index.ts │ │ ├── metro.config.js │ │ ├── package.json │ │ ├── pnpm-lock.yaml.2457664388 │ │ ├── shared/ │ │ │ ├── api/ │ │ │ │ ├── api-root/ │ │ │ │ │ ├── api-root-instanse.ts │ │ │ │ │ └── api-root.ts │ │ │ │ ├── query-client/ │ │ │ │ │ └── queryClient.ts │ │ │ │ └── query-persist/ │ │ │ │ └── query-presist.ts │ │ │ ├── consts/ │ │ │ │ ├── consts.ts │ │ │ │ └── key-storage/ │ │ │ │ └── key-storage.ts │ │ │ ├── providers/ │ │ │ │ └── reactQueryProviders/ │ │ │ │ └── ReactQueryProviders.tsx │ │ │ ├── storage/ │ │ │ │ └── tokenStorage.ts │ │ │ ├── styles/ │ │ │ │ ├── tokens.ts │ │ │ │ └── tokens.type.ts │ │ │ ├── ui/ │ │ │ │ ├── Button/ │ │ │ │ │ ├── Button.tsx │ │ │ │ │ └── Button.type.tsx │ │ │ │ └── Icons/ │ │ │ │ ├── navigation/ │ │ │ │ │ ├── IcAllPlaylist.tsx │ │ │ │ │ ├── IcAllTracks.tsx │ │ │ │ │ ├── IcHome.tsx │ │ │ │ │ └── IcYourLibrary.tsx │ │ │ │ └── screens/ │ │ │ │ └── login/ │ │ │ │ └── IcSmile.tsx │ │ │ └── utils/ │ │ │ └── makeFullUrl.ts │ │ └── tsconfig.json │ ├── reatom/ │ │ ├── .gitignore │ │ ├── .storybook/ │ │ │ ├── main.ts │ │ │ └── preview.tsx │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── app/ │ │ │ │ ├── App.tsx │ │ │ │ ├── entrypoint/ │ │ │ │ │ └── main.tsx │ │ │ │ ├── query-client/ │ │ │ │ │ └── query-client.tsx │ │ │ │ ├── routing/ │ │ │ │ │ ├── Routing.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── styles/ │ │ │ │ ├── fonts.css │ │ │ │ ├── global.css │ │ │ │ ├── reset.css │ │ │ │ └── variables.css │ │ │ ├── entities/ │ │ │ │ └── playlist/ │ │ │ │ ├── index.tsx │ │ │ │ └── ui/ │ │ │ │ ├── PlaylistCard/ │ │ │ │ │ ├── PlaylistCard.module.css │ │ │ │ │ ├── PlaylistCard.stories.tsx │ │ │ │ │ ├── PlaylistCard.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── PlaylistItem/ │ │ │ │ ├── PlaylistItem.tsx │ │ │ │ ├── PlaylistItem.types.ts │ │ │ │ └── index.ts │ │ │ ├── features/ │ │ │ │ ├── artists/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── artists-api.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ └── ArtistCard/ │ │ │ │ │ ├── ArtistCard.module.css │ │ │ │ │ ├── ArtistCard.stories.tsx │ │ │ │ │ ├── ArtistCard.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── auth/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── use-login.mutation.ts │ │ │ │ │ │ ├── use-logout.mutation.ts │ │ │ │ │ │ └── use-me.query.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── types/ │ │ │ │ │ │ └── auth-api.types.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── LoginButtonAndModal/ │ │ │ │ │ │ ├── LoginButtonAndModal.module.css │ │ │ │ │ │ ├── LoginButtonAndModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ProfileDropdownMenu/ │ │ │ │ │ │ ├── ProfileDropdownMenu.module.css │ │ │ │ │ │ ├── ProfileDropdownMenu.stories.tsx │ │ │ │ │ │ ├── ProfileDropdownMenu.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── playlists/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── playlistsApi.ts │ │ │ │ │ │ ├── query-key-factory.ts │ │ │ │ │ │ ├── types.ts │ │ │ │ │ │ └── use-playlists.query.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── model/ │ │ │ │ │ │ └── model.tsx │ │ │ │ │ └── ui/ │ │ │ │ │ ├── CreatePlaylistModal/ │ │ │ │ │ │ ├── CreatePlaylistModal.module.css │ │ │ │ │ │ ├── CreatePlaylistModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PlaylistOverview/ │ │ │ │ │ │ ├── PlaylistOverview.module.css │ │ │ │ │ │ ├── PlaylistOverview.stories.tsx │ │ │ │ │ │ ├── PlaylistOverview.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── reactions/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── ReactionProvider/ │ │ │ │ │ │ ├── ReactionProvider.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── tags/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── tags-api.ts │ │ │ │ │ │ └── use-tags.query.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── TagsList/ │ │ │ │ │ │ ├── TagsList.module.css │ │ │ │ │ │ ├── TagsList.stories.tsx │ │ │ │ │ │ ├── TagsList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ └── tracks/ │ │ │ │ ├── api/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── tracksApi.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── index.ts │ │ │ │ └── ui/ │ │ │ │ ├── TrackCard/ │ │ │ │ │ ├── TrackCard.module.css │ │ │ │ │ ├── TrackCard.stories.tsx │ │ │ │ │ ├── TrackCard.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TrackInfoCell/ │ │ │ │ │ ├── TrackInfoCell.module.css │ │ │ │ │ ├── TrackInfoCell.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TrackOverview/ │ │ │ │ │ ├── TrackOverview.module.css │ │ │ │ │ ├── TrackOverview.stories.tsx │ │ │ │ │ ├── TrackOverview.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TrackRow/ │ │ │ │ │ ├── TrackRow.module.css │ │ │ │ │ └── TrackRow.tsx │ │ │ │ ├── TracksTable/ │ │ │ │ │ ├── TrackTable.stories.tsx │ │ │ │ │ ├── TracksTable.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── layout/ │ │ │ │ ├── Header/ │ │ │ │ │ ├── Header.module.css │ │ │ │ │ ├── Header.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Layout.module.css │ │ │ │ ├── Layout.tsx │ │ │ │ ├── Sidebar/ │ │ │ │ │ ├── MenuLinks/ │ │ │ │ │ │ ├── MenuLinks.module.css │ │ │ │ │ │ ├── MenuLinks.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Sidebar.module.css │ │ │ │ │ ├── Sidebar.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── pages/ │ │ │ │ ├── MainPage/ │ │ │ │ │ ├── MainPage.module.css │ │ │ │ │ ├── MainPage.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PlaylistPage/ │ │ │ │ │ ├── PlaylistPage.module.css │ │ │ │ │ ├── PlaylistPage.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ └── ControlPanel/ │ │ │ │ │ ├── ControlPanel.module.css │ │ │ │ │ ├── ControlPanel.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PlaylistsPage/ │ │ │ │ │ ├── PlaylistsPage.module.css │ │ │ │ │ ├── PlaylistsPage.tsx │ │ │ │ │ ├── PlaylistsPage.types.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── TrackPage/ │ │ │ │ │ ├── TrackPage.module.css │ │ │ │ │ ├── TrackPage.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ └── ControlPanel/ │ │ │ │ │ ├── ControlPanel.module.css │ │ │ │ │ ├── ControlPanel.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TracksPage/ │ │ │ │ │ ├── TracksPage.module.css │ │ │ │ │ ├── TracksPage.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── UserPage/ │ │ │ │ │ ├── UserPage.module.css │ │ │ │ │ ├── UserPage.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── UserInfo/ │ │ │ │ │ │ ├── UserInfo.module.css │ │ │ │ │ │ ├── UserInfo.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── UserTabs/ │ │ │ │ │ │ ├── LikedTracksTab/ │ │ │ │ │ │ │ ├── LikedTracksTab.module.css │ │ │ │ │ │ │ ├── LikedTracksTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── MyLikedPlaylistsTab/ │ │ │ │ │ │ │ ├── MyLikedPlaylistsTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── PlaylistsTab/ │ │ │ │ │ │ │ ├── PlaylistsTab.module.css │ │ │ │ │ │ │ ├── PlaylistsTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── TracksTab/ │ │ │ │ │ │ │ ├── TracksTab.module.css │ │ │ │ │ │ │ ├── TracksTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── UserTabs.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── auth/ │ │ │ │ │ └── OAuthRedirect/ │ │ │ │ │ ├── OAuthCallback.module.css │ │ │ │ │ └── OAuthCallback.tsx │ │ │ │ ├── common/ │ │ │ │ │ ├── ContentList/ │ │ │ │ │ │ ├── ContentList.module.css │ │ │ │ │ │ ├── ContentList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PageWrapper/ │ │ │ │ │ │ ├── PageWrapper.module.css │ │ │ │ │ │ ├── PageWrapper.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SearchTextField/ │ │ │ │ │ │ ├── SearchTextField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SortSelect/ │ │ │ │ │ │ ├── SortSelect.module.css │ │ │ │ │ │ ├── SortSelect.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── shared/ │ │ │ │ ├── api/ │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils/ │ │ │ │ │ ├── json-api-error.ts │ │ │ │ │ └── request-wrapper.ts │ │ │ │ ├── components/ │ │ │ │ │ ├── AudioPlayer/ │ │ │ │ │ │ ├── AudioPlayer.module.css │ │ │ │ │ │ ├── AudioPlayer.stories.tsx │ │ │ │ │ │ ├── AudioPlayer.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Autocomplete/ │ │ │ │ │ │ ├── Autocomplete.module.css │ │ │ │ │ │ ├── Autocomplete.stories.tsx │ │ │ │ │ │ ├── Autocomplete.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Button/ │ │ │ │ │ │ ├── Button.module.css │ │ │ │ │ │ ├── Button.stories.tsx │ │ │ │ │ │ ├── Button.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Card/ │ │ │ │ │ │ ├── Card.module.css │ │ │ │ │ │ ├── Card.stories.tsx │ │ │ │ │ │ ├── Card.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Dialog/ │ │ │ │ │ │ ├── Dialog.module.css │ │ │ │ │ │ ├── Dialog.stories.tsx │ │ │ │ │ │ ├── Dialog.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── DropdownMenu/ │ │ │ │ │ │ ├── DropdownMenu.module.css │ │ │ │ │ │ ├── DropdownMenu.stories.tsx │ │ │ │ │ │ ├── DropdownMenu.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Hashtag/ │ │ │ │ │ │ ├── Tag.module.css │ │ │ │ │ │ ├── Tag.stories.tsx │ │ │ │ │ │ ├── Tag.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── IconButton/ │ │ │ │ │ │ ├── IconButton.module.css │ │ │ │ │ │ ├── IconButton.stories.tsx │ │ │ │ │ │ ├── IconButton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ImageUploader/ │ │ │ │ │ │ ├── ImageUploader.module.css │ │ │ │ │ │ ├── ImageUploader.stories.tsx │ │ │ │ │ │ ├── ImageUploader.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Pagination/ │ │ │ │ │ │ ├── Pagination.module.css │ │ │ │ │ │ ├── Pagination.stories.tsx │ │ │ │ │ │ ├── Pagination.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Progress/ │ │ │ │ │ │ ├── Progress.module.css │ │ │ │ │ │ ├── Progress.stories.tsx │ │ │ │ │ │ ├── Progress.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ReactionButtons/ │ │ │ │ │ │ ├── ReactionButtons.module.css │ │ │ │ │ │ ├── ReactionButtons.stories.tsx │ │ │ │ │ │ ├── ReactionButtons.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SearchField/ │ │ │ │ │ │ ├── SearchField.module.css │ │ │ │ │ │ ├── SearchField.stories.tsx │ │ │ │ │ │ ├── SearchField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Select/ │ │ │ │ │ │ ├── Select.module.css │ │ │ │ │ │ ├── Select.stories.tsx │ │ │ │ │ │ ├── Select.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SortSelect/ │ │ │ │ │ │ └── Select.tsx │ │ │ │ │ ├── Table/ │ │ │ │ │ │ ├── Table.module.css │ │ │ │ │ │ ├── Table.stories.tsx │ │ │ │ │ │ ├── Table.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Tabs/ │ │ │ │ │ │ ├── Tabs.module.css │ │ │ │ │ │ ├── Tabs.stories.tsx │ │ │ │ │ │ ├── Tabs.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TagEditor/ │ │ │ │ │ │ ├── TagEditor.module.css │ │ │ │ │ │ ├── TagEditor.stories.tsx │ │ │ │ │ │ ├── TagEditor.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TextField/ │ │ │ │ │ │ ├── TextField.module.css │ │ │ │ │ │ ├── TextField.stories.tsx │ │ │ │ │ │ ├── TextField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Textarea/ │ │ │ │ │ │ ├── Textarea.module.css │ │ │ │ │ │ ├── Textarea.stories.tsx │ │ │ │ │ │ ├── Textarea.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Typography/ │ │ │ │ │ │ ├── Typography.module.css │ │ │ │ │ │ ├── Typography.stories.tsx │ │ │ │ │ │ ├── Typography.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── config/ │ │ │ │ │ └── config.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useDebounceValue.ts │ │ │ │ │ └── useGetId.ts │ │ │ │ ├── icons/ │ │ │ │ │ ├── AddToPlaylistIcon.tsx │ │ │ │ │ ├── ArrowDownIcon.tsx │ │ │ │ │ ├── ClockIcon.tsx │ │ │ │ │ ├── CreateIcon.tsx │ │ │ │ │ ├── DeleteIcon.tsx │ │ │ │ │ ├── DislikeIcon.tsx │ │ │ │ │ ├── DownloadIcon.tsx │ │ │ │ │ ├── EditIcon.tsx │ │ │ │ │ ├── HomeIcon.tsx │ │ │ │ │ ├── ImageUploadIcon.tsx │ │ │ │ │ ├── KeyboardArrowLeftIcon.tsx │ │ │ │ │ ├── KeyboardArrowRightIcon.tsx │ │ │ │ │ ├── LibraryIcon.tsx │ │ │ │ │ ├── LikeIcon.tsx │ │ │ │ │ ├── LikeIconFill.tsx │ │ │ │ │ ├── LikeInSquareIcon.tsx │ │ │ │ │ ├── LiveWaveIcon/ │ │ │ │ │ │ ├── LiveWaveIcon.module.css │ │ │ │ │ │ ├── LiveWaveIcon.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── LogoutIcon.tsx │ │ │ │ │ ├── MoreIcon.tsx │ │ │ │ │ ├── PauseIcon.tsx │ │ │ │ │ ├── PlayIcon.tsx │ │ │ │ │ ├── PlaylistIcon.tsx │ │ │ │ │ ├── PlusIcon.tsx │ │ │ │ │ ├── ProfileIcon.tsx │ │ │ │ │ ├── RepeatIcon.tsx │ │ │ │ │ ├── SearchIcon.tsx │ │ │ │ │ ├── ShuffleIcon.tsx │ │ │ │ │ ├── SkipNextIcon.tsx │ │ │ │ │ ├── SkipPreviousIcon.tsx │ │ │ │ │ ├── TextIcon.tsx │ │ │ │ │ ├── TrackIcon.tsx │ │ │ │ │ ├── UploadIcon.tsx │ │ │ │ │ ├── VolumeIcon.tsx │ │ │ │ │ ├── VolumeMuteIcon.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ui/ │ │ │ │ │ ├── prerender-ready.tsx │ │ │ │ │ └── utils/ │ │ │ │ │ └── query-error-handler-for-rhf-factory.ts │ │ │ │ └── utils/ │ │ │ │ ├── index.ts │ │ │ │ └── validators/ │ │ │ │ ├── getType.ts │ │ │ │ ├── inNun.ts │ │ │ │ ├── index.ts │ │ │ │ ├── isArray.ts │ │ │ │ ├── isFunction.ts │ │ │ │ ├── isNull.ts │ │ │ │ ├── isObject.ts │ │ │ │ ├── isUndefined.ts │ │ │ │ ├── isValid.ts │ │ │ │ └── isValidArray.ts │ │ │ ├── vite-env.d.ts │ │ │ └── widgets/ │ │ │ └── Player/ │ │ │ ├── Player.module.css │ │ │ ├── Player.tsx │ │ │ └── index.ts │ │ ├── stylelint.config.js │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ └── vite.config.ts │ ├── rtk-query/ │ │ ├── .gitignore │ │ ├── .storybook/ │ │ │ ├── main.ts │ │ │ └── preview.tsx │ │ ├── CLAUDE.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── app/ │ │ │ │ ├── App.tsx │ │ │ │ ├── api/ │ │ │ │ │ ├── base-api.ts │ │ │ │ │ ├── base-query-with-refresh-token-flow-api.ts │ │ │ │ │ ├── handleError.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── routing/ │ │ │ │ │ ├── Routing.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── store/ │ │ │ │ ├── index.ts │ │ │ │ └── store.ts │ │ │ ├── features/ │ │ │ │ ├── artists/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── artists-api.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── ArtistCard/ │ │ │ │ │ │ ├── ArtistCard.module.css │ │ │ │ │ │ ├── ArtistCard.stories.tsx │ │ │ │ │ │ ├── ArtistCard.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ArtistsTagAutocomplete/ │ │ │ │ │ │ ├── ArtistsTagAutocomplete.module.css │ │ │ │ │ │ ├── ArtistsTagAutocomplete.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── auth/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── auth-api.ts │ │ │ │ │ │ ├── auth-api.types.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── auth-slice.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── LoginModal/ │ │ │ │ │ │ ├── LoginModal.module.css │ │ │ │ │ │ ├── LoginModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── OAuthRedirect/ │ │ │ │ │ │ ├── OAuthCallback.module.css │ │ │ │ │ │ ├── OAuthCallback.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── playlists/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── mocks.ts │ │ │ │ │ │ ├── playlistsApi.ts │ │ │ │ │ │ └── playlistsApi.types.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── useCreatePlaylistModal.ts │ │ │ │ │ │ │ └── useEditPlaylistModal.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── playlists-slice.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── ChoosePlaylistButtonAndModal/ │ │ │ │ │ │ ├── ChoosePlaylistButtonAndModal.module.css │ │ │ │ │ │ ├── ChoosePlaylistButtonAndModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ChoosePlaylistModal/ │ │ │ │ │ │ ├── ChoosePlaylistModal.module.css │ │ │ │ │ │ ├── ChoosePlaylistModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── CreateEditPlaylistModal/ │ │ │ │ │ │ ├── CreateEditPlaylistModal.module.css │ │ │ │ │ │ ├── CreateEditPlaylistModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PlaylistActions/ │ │ │ │ │ │ ├── PlaylistActions.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PlaylistCard/ │ │ │ │ │ │ ├── PlaylistCard.module.css │ │ │ │ │ │ ├── PlaylistCard.stories.tsx │ │ │ │ │ │ ├── PlaylistCard.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PlaylistCardSkeleton/ │ │ │ │ │ │ ├── PlaylistCardSkeleton.stories.tsx │ │ │ │ │ │ ├── PlaylistCardSkeleton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PlaylistOverview/ │ │ │ │ │ │ ├── PlaylistOverview.module.css │ │ │ │ │ │ ├── PlaylistOverview.stories.tsx │ │ │ │ │ │ ├── PlaylistOverview.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PlaylistRow/ │ │ │ │ │ │ ├── PlaylistRow.module.css │ │ │ │ │ │ └── PlaylistRow.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── profile/ │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── empty-profile.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── hook/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── useEditProfileModal.ts │ │ │ │ │ │ │ └── useEditProfileSchema.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── profile-schemas.ts │ │ │ │ │ │ └── profile-slice.ts │ │ │ │ │ ├── types/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── profile.type.ts │ │ │ │ │ ├── ui/ │ │ │ │ │ │ ├── EditProfileModal/ │ │ │ │ │ │ │ ├── EditProfileModal.module.css │ │ │ │ │ │ │ ├── EditProfileModal.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── utils/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── storage-key.ts │ │ │ │ ├── tags/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── tagsApi.ts │ │ │ │ │ │ └── tagsApi.types.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── PlaylistTagAutocomplete/ │ │ │ │ │ │ ├── PlaylistTagAutocomplete.module.css │ │ │ │ │ │ ├── PlaylistTagAutocomplete.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TagsList/ │ │ │ │ │ │ ├── TagsList.module.css │ │ │ │ │ │ ├── TagsList.stories.tsx │ │ │ │ │ │ ├── TagsList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ └── tracks/ │ │ │ │ ├── api/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── mocks.ts │ │ │ │ │ ├── tracksApi.ts │ │ │ │ │ └── tracksApi.types.ts │ │ │ │ ├── constants/ │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── model/ │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── useCreateTrackModal.ts │ │ │ │ │ │ └── useEditTrackModal.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── tracks-slice.ts │ │ │ │ ├── ui/ │ │ │ │ │ ├── CreateEditTrackModal/ │ │ │ │ │ │ ├── CreateEditTrackModal.module.css │ │ │ │ │ │ ├── CreateEditTrackModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TrackActions/ │ │ │ │ │ │ ├── TrackActions.tsx │ │ │ │ │ │ ├── TrackActionsMenu/ │ │ │ │ │ │ │ ├── TrackActionsMenu.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TrackCard/ │ │ │ │ │ │ ├── TrackCard.module.css │ │ │ │ │ │ ├── TrackCard.stories.tsx │ │ │ │ │ │ ├── TrackCard.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TrackInfoCell/ │ │ │ │ │ │ ├── TrackInfoCell.module.css │ │ │ │ │ │ ├── TrackInfoCell.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TrackOverview/ │ │ │ │ │ │ ├── TrackOverview.module.css │ │ │ │ │ │ ├── TrackOverview.stories.tsx │ │ │ │ │ │ ├── TrackOverview.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TrackRow/ │ │ │ │ │ │ ├── TrackRow.module.css │ │ │ │ │ │ └── TrackRow.tsx │ │ │ │ │ ├── TrackRowContainer/ │ │ │ │ │ │ ├── TrackRowContainer.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TracksTable/ │ │ │ │ │ │ ├── TrackTable.stories.tsx │ │ │ │ │ │ ├── TracksTable.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TracksTableSkeleton/ │ │ │ │ │ │ ├── TracksTableSkeleton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ └── utils/ │ │ │ │ └── playlistSync.ts │ │ │ ├── layout/ │ │ │ │ ├── AppLoader/ │ │ │ │ │ ├── AppLoader.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Header/ │ │ │ │ │ ├── AccountMenu/ │ │ │ │ │ │ ├── AccountMenu.module.css │ │ │ │ │ │ ├── AccountMenu.stories.tsx │ │ │ │ │ │ ├── AccountMenu.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Header.module.css │ │ │ │ │ ├── Header.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Layout.module.css │ │ │ │ ├── Layout.tsx │ │ │ │ ├── Sidebar/ │ │ │ │ │ ├── MenuLinks/ │ │ │ │ │ │ ├── MenuLinks.module.css │ │ │ │ │ │ ├── MenuLinks.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Sidebar.module.css │ │ │ │ │ ├── Sidebar.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── main.tsx │ │ │ ├── pages/ │ │ │ │ ├── MainPage/ │ │ │ │ │ ├── MainPage.module.css │ │ │ │ │ ├── MainPage.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PlaylistPage/ │ │ │ │ │ ├── PlaylistPage.module.css │ │ │ │ │ ├── PlaylistPage.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── ControlPanel/ │ │ │ │ │ │ ├── ControlPanel.module.css │ │ │ │ │ │ ├── ControlPanel.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── PlaylistPageSkeleton/ │ │ │ │ │ ├── PlaylistPageSkeleton.module.css │ │ │ │ │ ├── PlaylistPageSkeleton.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PlaylistsPage/ │ │ │ │ │ ├── PlaylistsPage.module.css │ │ │ │ │ ├── PlaylistsPage.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TrackLyricsPage/ │ │ │ │ │ ├── TrackLyricsPage.module.css │ │ │ │ │ ├── TrackLyricsPage.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TrackPage/ │ │ │ │ │ ├── TrackPage.module.css │ │ │ │ │ ├── TrackPage.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── ControlPanel/ │ │ │ │ │ │ ├── ControlPanel.module.css │ │ │ │ │ │ ├── ControlPanel.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── TrackPageSkeleton/ │ │ │ │ │ ├── TrackPageSkeleton.module.css │ │ │ │ │ ├── TrackPageSkeleton.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TracksPage/ │ │ │ │ │ ├── TracksPage.module.css │ │ │ │ │ ├── TracksPage.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── UserPage/ │ │ │ │ │ ├── UserPage.module.css │ │ │ │ │ ├── UserPage.tsx │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── useOwnerData.ts │ │ │ │ │ │ └── useUserPageBackgroundColor.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── UserInfo/ │ │ │ │ │ │ ├── UserInfo.module.css │ │ │ │ │ │ ├── UserInfo.tsx │ │ │ │ │ │ ├── UserInfoSkeleton/ │ │ │ │ │ │ │ ├── UserInfoSkeleton.module.css │ │ │ │ │ │ │ ├── UserInfoSkeleton.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── UserStats/ │ │ │ │ │ │ │ ├── UserStats.module.css │ │ │ │ │ │ │ ├── UserStats.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── UserTabs/ │ │ │ │ │ │ ├── LikedTracksTab/ │ │ │ │ │ │ │ ├── LikedTracksTab.module.css │ │ │ │ │ │ │ ├── LikedTracksTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── MyLikedPlaylistsTab/ │ │ │ │ │ │ │ ├── MyLikedPlaylistsTab.module.css │ │ │ │ │ │ │ ├── MyLikedPlaylistsTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── PlaylistsTab/ │ │ │ │ │ │ │ ├── PlaylistsTab.module.css │ │ │ │ │ │ │ ├── PlaylistsTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── TracksTab/ │ │ │ │ │ │ │ ├── TracksTab.module.css │ │ │ │ │ │ │ ├── TracksTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── UserTabs.tsx │ │ │ │ │ │ ├── UserTabsSkeleton/ │ │ │ │ │ │ │ ├── UserTabsSkeleton.module.css │ │ │ │ │ │ │ ├── UserTabsSkeleton.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── common/ │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── usePageBackgroundColor.ts │ │ │ │ │ │ └── usePageSearchParams.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── ContentList/ │ │ │ │ │ │ ├── ContentList.module.css │ │ │ │ │ │ ├── ContentList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PageWithHeader/ │ │ │ │ │ │ ├── PageWithHeader.module.css │ │ │ │ │ │ ├── PageWithHeader.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PageWithoutHeader/ │ │ │ │ │ │ ├── PageWithoutHeader.module.css │ │ │ │ │ │ ├── PageWithoutHeader.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SearchTags/ │ │ │ │ │ │ ├── SearchTags.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SearchTextField/ │ │ │ │ │ │ ├── SearchTextField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── SortSelect/ │ │ │ │ │ ├── SortSelect.module.css │ │ │ │ │ ├── SortSelect.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── player/ │ │ │ │ ├── MIGRATION_GUIDE.md │ │ │ │ ├── README.md │ │ │ │ ├── SPECIFICATION.md │ │ │ │ ├── index.ts │ │ │ │ ├── player.ts │ │ │ │ ├── playerHooks.ts │ │ │ │ ├── playerMiddleware.ts │ │ │ │ ├── playerSelectors.ts │ │ │ │ ├── playerSlice.ts │ │ │ │ ├── task.md │ │ │ │ ├── types/ │ │ │ │ │ └── player.types.ts │ │ │ │ └── utils/ │ │ │ │ ├── convert-api-track-to-player-track.ts │ │ │ │ ├── format-time.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shuffle.ts │ │ │ │ └── throttle.ts │ │ │ ├── shared/ │ │ │ │ ├── assets/ │ │ │ │ │ └── images/ │ │ │ │ │ └── no-cover-placeholder.avif │ │ │ │ ├── components/ │ │ │ │ │ ├── AudioPlayer/ │ │ │ │ │ │ ├── AudioPlayer.module.css │ │ │ │ │ │ ├── AudioPlayer.stories.tsx │ │ │ │ │ │ ├── AudioPlayer.tsx │ │ │ │ │ │ ├── AudioPlayerSceleton/ │ │ │ │ │ │ │ ├── AudioPlayerSkeleton.module.css │ │ │ │ │ │ │ ├── AudioPlayerSkeleton.stories.tsx │ │ │ │ │ │ │ └── AudioPlayerSkeleton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Autocomplete/ │ │ │ │ │ │ ├── Autocomplete.module.css │ │ │ │ │ │ ├── Autocomplete.stories.tsx │ │ │ │ │ │ ├── Autocomplete.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Avatar/ │ │ │ │ │ │ ├── Avatar.module.css │ │ │ │ │ │ ├── Avatar.stories.tsx │ │ │ │ │ │ ├── Avatar.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Button/ │ │ │ │ │ │ ├── Button.module.css │ │ │ │ │ │ ├── Button.stories.tsx │ │ │ │ │ │ ├── Button.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Card/ │ │ │ │ │ │ ├── Card.module.css │ │ │ │ │ │ ├── Card.stories.tsx │ │ │ │ │ │ ├── Card.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Dialog/ │ │ │ │ │ │ ├── Dialog.module.css │ │ │ │ │ │ ├── Dialog.stories.tsx │ │ │ │ │ │ ├── Dialog.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── DropdownMenu/ │ │ │ │ │ │ ├── DropdownMenu.module.css │ │ │ │ │ │ ├── DropdownMenu.stories.tsx │ │ │ │ │ │ ├── DropdownMenu.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── FileUploader/ │ │ │ │ │ │ ├── FileUploader.module.css │ │ │ │ │ │ ├── FileUploader.stories.tsx │ │ │ │ │ │ ├── FileUploader.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── FormControlledTextField/ │ │ │ │ │ │ ├── FormControlledTextField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Hashtag/ │ │ │ │ │ │ ├── Tag.module.css │ │ │ │ │ │ ├── Tag.stories.tsx │ │ │ │ │ │ ├── Tag.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── IconButton/ │ │ │ │ │ │ ├── IconButton.module.css │ │ │ │ │ │ ├── IconButton.stories.tsx │ │ │ │ │ │ ├── IconButton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ImageCropper/ │ │ │ │ │ │ ├── ImageCropper.module.css │ │ │ │ │ │ ├── ImageCropper.stories.tsx │ │ │ │ │ │ ├── ImageCropper.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ImageUploader/ │ │ │ │ │ │ ├── ImageUploader.module.css │ │ │ │ │ │ ├── ImageUploader.stories.tsx │ │ │ │ │ │ ├── ImageUploader.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Loader/ │ │ │ │ │ │ ├── Loader.module.css │ │ │ │ │ │ ├── Loader.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Pagination/ │ │ │ │ │ │ ├── Pagination.module.css │ │ │ │ │ │ ├── Pagination.stories.tsx │ │ │ │ │ │ ├── Pagination.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Progress/ │ │ │ │ │ │ ├── Progress.module.css │ │ │ │ │ │ ├── Progress.stories.tsx │ │ │ │ │ │ ├── Progress.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ReactionButtons/ │ │ │ │ │ │ ├── ReactionButtons.module.css │ │ │ │ │ │ ├── ReactionButtons.stories.tsx │ │ │ │ │ │ ├── ReactionButtons.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SearchField/ │ │ │ │ │ │ ├── SearchField.module.css │ │ │ │ │ │ ├── SearchField.stories.tsx │ │ │ │ │ │ ├── SearchField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Select/ │ │ │ │ │ │ ├── Select.module.css │ │ │ │ │ │ ├── Select.stories.tsx │ │ │ │ │ │ ├── Select.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Skeleton/ │ │ │ │ │ │ ├── Skeleton.module.css │ │ │ │ │ │ ├── Skeleton.stories.tsx │ │ │ │ │ │ ├── Skeleton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SortSelect/ │ │ │ │ │ │ └── Select.tsx │ │ │ │ │ ├── Spinner/ │ │ │ │ │ │ ├── Spinner.stories.tsx │ │ │ │ │ │ ├── Spinner.tsx │ │ │ │ │ │ └── spinner.module.css │ │ │ │ │ ├── Table/ │ │ │ │ │ │ ├── Table.module.css │ │ │ │ │ │ ├── Table.stories.tsx │ │ │ │ │ │ ├── Table.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Tabs/ │ │ │ │ │ │ ├── Tabs.module.css │ │ │ │ │ │ ├── Tabs.stories.tsx │ │ │ │ │ │ ├── Tabs.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TagEditor/ │ │ │ │ │ │ ├── TagEditor.module.css │ │ │ │ │ │ ├── TagEditor.stories.tsx │ │ │ │ │ │ ├── TagEditor.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TextField/ │ │ │ │ │ │ ├── TextField.module.css │ │ │ │ │ │ ├── TextField.stories.tsx │ │ │ │ │ │ ├── TextField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Textarea/ │ │ │ │ │ │ ├── Textarea.module.css │ │ │ │ │ │ ├── Textarea.stories.tsx │ │ │ │ │ │ ├── Textarea.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Typography/ │ │ │ │ │ │ ├── Typography.module.css │ │ │ │ │ │ ├── Typography.stories.tsx │ │ │ │ │ │ ├── Typography.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── configs/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── paths.ts │ │ │ │ ├── constants/ │ │ │ │ │ ├── constants.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useAppDispatch.ts │ │ │ │ │ ├── useAppSelector.ts │ │ │ │ │ ├── useCurrentPage.ts │ │ │ │ │ ├── useDebounce.ts │ │ │ │ │ ├── useGetId.ts │ │ │ │ │ ├── useGlobalLoading.ts │ │ │ │ │ └── useHover.ts │ │ │ │ ├── icons/ │ │ │ │ │ ├── AddToPlaylistIcon.tsx │ │ │ │ │ ├── AddTrackIcon.tsx │ │ │ │ │ ├── ArrowBackIcon.tsx │ │ │ │ │ ├── ArrowDownIcon.tsx │ │ │ │ │ ├── CheckedIcon.tsx │ │ │ │ │ ├── ClockIcon.tsx │ │ │ │ │ ├── CreateIcon.tsx │ │ │ │ │ ├── DeleteIcon.tsx │ │ │ │ │ ├── DeleteTagIconButton.tsx │ │ │ │ │ ├── DislikeIcon.tsx │ │ │ │ │ ├── DownloadIcon.tsx │ │ │ │ │ ├── EditIcon.tsx │ │ │ │ │ ├── HomeIcon.tsx │ │ │ │ │ ├── IconOneRepeat.tsx │ │ │ │ │ ├── ImageUploadIcon.tsx │ │ │ │ │ ├── KeyboardArrowLeftIcon.tsx │ │ │ │ │ ├── KeyboardArrowRightIcon.tsx │ │ │ │ │ ├── LanguageIcon.tsx │ │ │ │ │ ├── LibraryIcon.tsx │ │ │ │ │ ├── LikeIcon.tsx │ │ │ │ │ ├── LikeIconFill.tsx │ │ │ │ │ ├── LikeInSquareIcon.tsx │ │ │ │ │ ├── LiveWaveIcon/ │ │ │ │ │ │ ├── LiveWaveIcon.module.css │ │ │ │ │ │ ├── LiveWaveIcon.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── LogoutIcon.tsx │ │ │ │ │ ├── MoreIcon.tsx │ │ │ │ │ ├── PauseIcon.tsx │ │ │ │ │ ├── PlayIcon.tsx │ │ │ │ │ ├── PlaylistIcon.tsx │ │ │ │ │ ├── PlusIcon.tsx │ │ │ │ │ ├── ProfileIcon.tsx │ │ │ │ │ ├── RepeatIcon.tsx │ │ │ │ │ ├── SearchIcon.tsx │ │ │ │ │ ├── ShuffleIcon.tsx │ │ │ │ │ ├── SkipNextIcon.tsx │ │ │ │ │ ├── SkipPreviousIcon.tsx │ │ │ │ │ ├── StaticWaveIcon.tsx │ │ │ │ │ ├── TextIcon.tsx │ │ │ │ │ ├── TrackIcon.tsx │ │ │ │ │ ├── UncheckedIcon.tsx │ │ │ │ │ ├── UploadIcon.tsx │ │ │ │ │ ├── VolumeIcon.tsx │ │ │ │ │ ├── VolumeMuteIcon.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── translations/ │ │ │ │ │ ├── i18nConfiguration.ts │ │ │ │ │ └── languages/ │ │ │ │ │ ├── en.json │ │ │ │ │ └── ru.json │ │ │ │ ├── types/ │ │ │ │ │ ├── common.types.ts │ │ │ │ │ ├── commonApi.types.ts │ │ │ │ │ └── index.ts │ │ │ │ └── utils/ │ │ │ │ ├── build-query-string.ts │ │ │ │ ├── convert-file-to-base-64.ts │ │ │ │ ├── decode-file-from-base-64.ts │ │ │ │ ├── format-created-date.ts │ │ │ │ ├── get-image-by-type.ts │ │ │ │ ├── get-plural-key.ts │ │ │ │ ├── get-russian-plural-form.ts │ │ │ │ ├── get-user-initials.ts │ │ │ │ ├── index.ts │ │ │ │ ├── set-locale.ts │ │ │ │ └── show-error-toast.ts │ │ │ ├── styles/ │ │ │ │ ├── fonts.css │ │ │ │ ├── global.css │ │ │ │ ├── reset.css │ │ │ │ └── variables.css │ │ │ ├── vite-env.d.ts │ │ │ └── widgets/ │ │ │ └── Player/ │ │ │ ├── Player.module.css │ │ │ ├── Player.tsx │ │ │ └── index.ts │ │ ├── stylelint.config.js │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ └── vite.config.ts │ ├── tanstack-query-zustand/ │ │ ├── .claude/ │ │ │ └── skills/ │ │ │ └── i18n-rules/ │ │ │ └── SKILL.md │ │ ├── .gitignore │ │ ├── .storybook/ │ │ │ ├── main.ts │ │ │ └── preview.tsx │ │ ├── AGENTS.md │ │ ├── CLAUDE.md │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.css │ │ │ ├── App.tsx │ │ │ ├── app/ │ │ │ │ ├── App.tsx │ │ │ │ ├── entrypoint/ │ │ │ │ │ └── main.tsx │ │ │ │ ├── query-client/ │ │ │ │ │ └── query-client.tsx │ │ │ │ ├── routing/ │ │ │ │ │ ├── Routing.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── styles/ │ │ │ │ ├── fonts.css │ │ │ │ ├── global.css │ │ │ │ ├── reset.css │ │ │ │ └── variables.css │ │ │ ├── assets/ │ │ │ │ └── img/ │ │ │ │ └── no-cover-placeholder.avif │ │ │ ├── entities/ │ │ │ │ └── playlist/ │ │ │ │ ├── index.tsx │ │ │ │ └── ui/ │ │ │ │ ├── PlaylistCard/ │ │ │ │ │ ├── PlaylistCard.module.scss │ │ │ │ │ ├── PlaylistCard.stories.tsx │ │ │ │ │ ├── PlaylistCard.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PlaylistCardSkeleton/ │ │ │ │ │ ├── PlaylistCardSkeleton.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── PlaylistItem/ │ │ │ │ ├── PlaylistItem.tsx │ │ │ │ ├── PlaylistItem.types.ts │ │ │ │ └── index.ts │ │ │ ├── features/ │ │ │ │ ├── artists/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── artists-api.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── use-artists.query.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ └── ArtistCard/ │ │ │ │ │ ├── ArtistCard.module.css │ │ │ │ │ ├── ArtistCard.stories.tsx │ │ │ │ │ ├── ArtistCard.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── auth/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── use-login.mutation.ts │ │ │ │ │ │ ├── use-logout.mutation.ts │ │ │ │ │ │ └── use-me.query.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── types/ │ │ │ │ │ │ └── auth-api.types.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── LoginButtonAndModal/ │ │ │ │ │ │ ├── LoginButtonAndModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── LoginModal/ │ │ │ │ │ │ ├── LoginModal.module.css │ │ │ │ │ │ ├── LoginModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ProfileDropdownMenu/ │ │ │ │ │ │ ├── ProfileDropdownMenu.module.css │ │ │ │ │ │ ├── ProfileDropdownMenu.stories.tsx │ │ │ │ │ │ ├── ProfileDropdownMenu.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── playlists/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── playlistsApi.ts │ │ │ │ │ │ ├── query-key-factory.ts │ │ │ │ │ │ ├── types.ts │ │ │ │ │ │ ├── use-playlist-mutations.ts │ │ │ │ │ │ ├── use-playlist.query.ts │ │ │ │ │ │ └── use-playlists.query.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── model/ │ │ │ │ │ │ └── usePlaylistReactions.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── ChoosePlaylistModal/ │ │ │ │ │ │ ├── ChoosePlaylistModal.module.css │ │ │ │ │ │ └── ChoosePlaylistModal.tsx │ │ │ │ │ ├── CreatePlaylistModal/ │ │ │ │ │ │ ├── CreatePlaylistModal.module.css │ │ │ │ │ │ ├── CreatePlaylistModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PlaylistOverview/ │ │ │ │ │ │ ├── PlaylistOverview.module.css │ │ │ │ │ │ ├── PlaylistOverview.stories.tsx │ │ │ │ │ │ ├── PlaylistOverview.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PlaylistRow/ │ │ │ │ │ │ ├── PlaylistRow.module.css │ │ │ │ │ │ ├── PlaylistRow.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── profile/ │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── empty-profile.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── use-edit-profile-modal.ts │ │ │ │ │ │ │ ├── use-edit-profile-schema.ts │ │ │ │ │ │ │ └── use-hydrate-profile.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── profile-schemas.ts │ │ │ │ │ │ └── profile-store.ts │ │ │ │ │ ├── types/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── profile.types.ts │ │ │ │ │ ├── ui/ │ │ │ │ │ │ ├── EditProfileModal/ │ │ │ │ │ │ │ ├── EditProfileModal.module.css │ │ │ │ │ │ │ ├── EditProfileModal.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── utils/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── profile-storage.ts │ │ │ │ │ └── storage-key.ts │ │ │ │ ├── tags/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── tags-api.ts │ │ │ │ │ │ └── use-tags.query.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── TagsList/ │ │ │ │ │ │ ├── TagsList.module.css │ │ │ │ │ │ ├── TagsList.stories.tsx │ │ │ │ │ │ ├── TagsList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ └── tracks/ │ │ │ │ ├── api/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── query-key-factory.ts │ │ │ │ │ ├── tracksApi.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ ├── use-playlist-tracks.query.ts │ │ │ │ │ ├── use-track-mutations.ts │ │ │ │ │ ├── use-track.query.ts │ │ │ │ │ └── use-tracks.query.ts │ │ │ │ ├── index.ts │ │ │ │ ├── model/ │ │ │ │ │ └── useTrackReactions.ts │ │ │ │ ├── ui/ │ │ │ │ │ ├── CreateTrackForm/ │ │ │ │ │ │ ├── CreateTrackModal.module.css │ │ │ │ │ │ └── CreateTrackModal.tsx │ │ │ │ │ ├── TrackActions/ │ │ │ │ │ │ ├── TrackActions.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TrackActionsMenu/ │ │ │ │ │ │ └── TrackActionsMenu.tsx │ │ │ │ │ ├── TrackCard/ │ │ │ │ │ │ ├── TrackCard.module.css │ │ │ │ │ │ ├── TrackCard.stories.tsx │ │ │ │ │ │ ├── TrackCard.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TrackInfoCell/ │ │ │ │ │ │ ├── TrackInfoCell.module.css │ │ │ │ │ │ ├── TrackInfoCell.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TrackOverview/ │ │ │ │ │ │ ├── TrackOverview.module.css │ │ │ │ │ │ ├── TrackOverview.stories.tsx │ │ │ │ │ │ ├── TrackOverview.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TrackRow/ │ │ │ │ │ │ ├── TrackRow.module.css │ │ │ │ │ │ └── TrackRow.tsx │ │ │ │ │ ├── TrackRowContainer/ │ │ │ │ │ │ ├── TrackRowContainer.module.css │ │ │ │ │ │ └── TrackRowContainer.tsx │ │ │ │ │ ├── TracksTable/ │ │ │ │ │ │ ├── TrackTable.stories.tsx │ │ │ │ │ │ ├── TracksTable.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TracksTableSkeleton/ │ │ │ │ │ │ ├── TracksTableSkeleton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ └── utils/ │ │ │ │ └── playlistSync.ts │ │ │ ├── index.css │ │ │ ├── layout/ │ │ │ │ ├── Header/ │ │ │ │ │ ├── Header.module.css │ │ │ │ │ ├── Header.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Layout.module.css │ │ │ │ ├── Layout.tsx │ │ │ │ ├── Sidebar/ │ │ │ │ │ ├── MenuLinks/ │ │ │ │ │ │ ├── MenuLinks.module.css │ │ │ │ │ │ ├── MenuLinks.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Sidebar.module.css │ │ │ │ │ ├── Sidebar.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── main.tsx │ │ │ ├── pages/ │ │ │ │ ├── MainPage/ │ │ │ │ │ ├── MainPage.module.css │ │ │ │ │ ├── MainPage.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PlaylistPage/ │ │ │ │ │ ├── PlaylistPage.module.css │ │ │ │ │ ├── PlaylistPage.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── ControlPanel/ │ │ │ │ │ │ ├── ControlPanel.module.scss │ │ │ │ │ │ ├── ControlPanel.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── PlaylistPageSkeleton/ │ │ │ │ │ ├── PlaylistPageSkeleton.module.css │ │ │ │ │ ├── PlaylistPageSkeleton.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PlaylistsPage/ │ │ │ │ │ ├── PlaylistsPage.module.css │ │ │ │ │ ├── PlaylistsPage.tsx │ │ │ │ │ ├── PlaylistsPage.types.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── model/ │ │ │ │ │ ├── useCreatePlaylist.ts │ │ │ │ │ ├── useDeletePlaylist.ts │ │ │ │ │ └── useUploadPlaylistCover.ts │ │ │ │ ├── TrackLyricsPage/ │ │ │ │ │ ├── TrackLyricsPage.module.css │ │ │ │ │ ├── TrackLyricsPage.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TrackPage/ │ │ │ │ │ ├── TrackPage.module.css │ │ │ │ │ ├── TrackPage.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── ControlPanel/ │ │ │ │ │ │ ├── ControlPanel.module.css │ │ │ │ │ │ ├── ControlPanel.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── TrackPageSkeleton/ │ │ │ │ │ ├── TrackPageSkeleton.module.css │ │ │ │ │ ├── TrackPageSkeleton.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TracksPage/ │ │ │ │ │ ├── TracksPage.module.css │ │ │ │ │ ├── TracksPage.tsx │ │ │ │ │ ├── TracksSortFunction.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── useTrackDetails.ts │ │ │ │ │ │ ├── useTracksInfinityQuery.ts │ │ │ │ │ │ ├── useTracksQuery.tsx │ │ │ │ │ │ ├── useUploadTrack.ts │ │ │ │ │ │ └── useUploadTrackCover.ts │ │ │ │ │ └── tracksPageTypes/ │ │ │ │ │ └── TracksPageTypes.ts │ │ │ │ ├── UserPage/ │ │ │ │ │ ├── UserPage.module.css │ │ │ │ │ ├── UserPage.tsx │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── useUserPageBackgroundColor.ts │ │ │ │ │ │ └── useUserPageData.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ui/ │ │ │ │ │ ├── UserInfo/ │ │ │ │ │ │ ├── UserInfo.module.css │ │ │ │ │ │ ├── UserInfo.tsx │ │ │ │ │ │ ├── UserInfoSkeleton.module.css │ │ │ │ │ │ ├── UserInfoSkeleton.tsx │ │ │ │ │ │ ├── UserStats.module.css │ │ │ │ │ │ ├── UserStats.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── UserTabs/ │ │ │ │ │ │ ├── LikedTracksTab/ │ │ │ │ │ │ │ ├── LikedTracksTab.module.css │ │ │ │ │ │ │ ├── LikedTracksTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── MyLikedPlaylistsTab/ │ │ │ │ │ │ │ ├── MyLikedPlaylistsTab.module.css │ │ │ │ │ │ │ ├── MyLikedPlaylistsTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── PlaylistsTab/ │ │ │ │ │ │ │ ├── PlaylistsTab.module.css │ │ │ │ │ │ │ ├── PlaylistsTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── TracksTab/ │ │ │ │ │ │ │ ├── TracksTab.module.css │ │ │ │ │ │ │ ├── TracksTab.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── UserTabs.tsx │ │ │ │ │ │ ├── UserTabsSkeleton/ │ │ │ │ │ │ │ ├── UserTabsSkeleton.module.css │ │ │ │ │ │ │ ├── UserTabsSkeleton.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── auth/ │ │ │ │ │ └── OAuthRedirect/ │ │ │ │ │ ├── OAuthCallback.module.css │ │ │ │ │ └── OAuthCallback.tsx │ │ │ │ ├── common/ │ │ │ │ │ ├── ContentList/ │ │ │ │ │ │ ├── ContentList.module.css │ │ │ │ │ │ ├── ContentList.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PageWithHeader/ │ │ │ │ │ │ ├── PageWithHeader.module.css │ │ │ │ │ │ └── PageWithHeader.tsx │ │ │ │ │ ├── PageWithoutHeader/ │ │ │ │ │ │ ├── PageWithoutHeader.module.css │ │ │ │ │ │ └── PageWithoutHeader.tsx │ │ │ │ │ ├── PageWrapper/ │ │ │ │ │ │ ├── PageWrapper.module.css │ │ │ │ │ │ ├── PageWrapper.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SearchTextField/ │ │ │ │ │ │ ├── SearchTextField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SortSelect/ │ │ │ │ │ │ ├── SortSelect.module.css │ │ │ │ │ │ ├── SortSelect.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── player/ │ │ │ │ ├── README.md │ │ │ │ ├── SPECIFICATION.md │ │ │ │ ├── index.ts │ │ │ │ ├── model/ │ │ │ │ │ ├── audio-manager.ts │ │ │ │ │ ├── player-hooks.ts │ │ │ │ │ ├── player-store.ts │ │ │ │ │ └── player-track-hooks.ts │ │ │ │ ├── task.md │ │ │ │ ├── types/ │ │ │ │ │ └── player.types.ts │ │ │ │ └── utils/ │ │ │ │ ├── convert-api-track-to-player-track.ts │ │ │ │ ├── format-time.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shuffle.ts │ │ │ │ └── track-navigation.ts │ │ │ ├── shared/ │ │ │ │ ├── api/ │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils/ │ │ │ │ │ ├── json-api-error.ts │ │ │ │ │ └── unwrap.ts │ │ │ │ ├── auth/ │ │ │ │ │ └── types/ │ │ │ │ │ └── local-storage.keys.ts │ │ │ │ ├── components/ │ │ │ │ │ ├── AudioPlayer/ │ │ │ │ │ │ ├── AudioPlayer.module.css │ │ │ │ │ │ ├── AudioPlayer.stories.tsx │ │ │ │ │ │ ├── AudioPlayer.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Autocomplete/ │ │ │ │ │ │ ├── Autocomplete.module.css │ │ │ │ │ │ ├── Autocomplete.stories.tsx │ │ │ │ │ │ ├── Autocomplete.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Avatar/ │ │ │ │ │ │ ├── Avatar.module.css │ │ │ │ │ │ ├── Avatar.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Button/ │ │ │ │ │ │ ├── Button.module.css │ │ │ │ │ │ ├── Button.stories.tsx │ │ │ │ │ │ ├── Button.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Card/ │ │ │ │ │ │ ├── Card.module.css │ │ │ │ │ │ ├── Card.stories.tsx │ │ │ │ │ │ ├── Card.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── CoverImage/ │ │ │ │ │ │ ├── CoverImage.styles.module.scss │ │ │ │ │ │ ├── CoverImage.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Dialog/ │ │ │ │ │ │ ├── Dialog.module.css │ │ │ │ │ │ ├── Dialog.stories.tsx │ │ │ │ │ │ ├── Dialog.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── DropdownMenu/ │ │ │ │ │ │ ├── DropdownMenu.module.scss │ │ │ │ │ │ ├── DropdownMenu.stories.tsx │ │ │ │ │ │ ├── DropdownMenu.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── FormControlledTextField/ │ │ │ │ │ │ ├── FormControlledTextField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Hashtag/ │ │ │ │ │ │ ├── Tag.module.css │ │ │ │ │ │ ├── Tag.stories.tsx │ │ │ │ │ │ ├── Tag.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── IconButton/ │ │ │ │ │ │ ├── IconButton.module.css │ │ │ │ │ │ ├── IconButton.stories.tsx │ │ │ │ │ │ ├── IconButton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ImageCropper/ │ │ │ │ │ │ ├── ImageCropper.module.css │ │ │ │ │ │ ├── ImageCropper.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ImageUploader/ │ │ │ │ │ │ ├── ImageUploader.module.css │ │ │ │ │ │ ├── ImageUploader.stories.tsx │ │ │ │ │ │ ├── ImageUploader.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── LanguageSwitcher/ │ │ │ │ │ │ ├── LanguageSwitcher.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Pagination/ │ │ │ │ │ │ ├── Pagination.module.css │ │ │ │ │ │ ├── Pagination.stories.tsx │ │ │ │ │ │ ├── Pagination.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Progress/ │ │ │ │ │ │ ├── Progress.module.css │ │ │ │ │ │ ├── Progress.stories.tsx │ │ │ │ │ │ ├── Progress.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ReactionButtons/ │ │ │ │ │ │ ├── ReactionButtons.module.css │ │ │ │ │ │ ├── ReactionButtons.stories.tsx │ │ │ │ │ │ ├── ReactionButtons.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SearchField/ │ │ │ │ │ │ ├── SearchField.module.css │ │ │ │ │ │ ├── SearchField.stories.tsx │ │ │ │ │ │ ├── SearchField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Select/ │ │ │ │ │ │ ├── Select.module.css │ │ │ │ │ │ ├── Select.stories.tsx │ │ │ │ │ │ ├── Select.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Skeleton/ │ │ │ │ │ │ ├── Skeleton.module.css │ │ │ │ │ │ ├── Skeleton.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SortSelect/ │ │ │ │ │ │ └── Select.tsx │ │ │ │ │ ├── Spinner/ │ │ │ │ │ │ ├── Spinner.stories.tsx │ │ │ │ │ │ ├── Spinner.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── spinner.module.css │ │ │ │ │ ├── Table/ │ │ │ │ │ │ ├── Table.module.css │ │ │ │ │ │ ├── Table.stories.tsx │ │ │ │ │ │ ├── Table.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Tabs/ │ │ │ │ │ │ ├── Tabs.module.css │ │ │ │ │ │ ├── Tabs.stories.tsx │ │ │ │ │ │ ├── Tabs.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TagEditor/ │ │ │ │ │ │ ├── TagEditor.module.css │ │ │ │ │ │ ├── TagEditor.stories.tsx │ │ │ │ │ │ ├── TagEditor.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TextField/ │ │ │ │ │ │ ├── TextField.module.css │ │ │ │ │ │ ├── TextField.stories.tsx │ │ │ │ │ │ ├── TextField.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Textarea/ │ │ │ │ │ │ ├── Textarea.module.css │ │ │ │ │ │ ├── Textarea.stories.tsx │ │ │ │ │ │ ├── Textarea.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Typography/ │ │ │ │ │ │ ├── Typography.module.css │ │ │ │ │ │ ├── Typography.stories.tsx │ │ │ │ │ │ ├── Typography.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.ts │ │ │ │ │ └── paths.ts │ │ │ │ ├── featureFlags.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── debounceCallback/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── useDebounceCallback.ts │ │ │ │ │ │ └── useDebounceCallback.types.ts │ │ │ │ │ ├── debounceValue/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── useDebounceValue.ts │ │ │ │ │ ├── getId/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── useGetId.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── throttleCallback/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── useThrottleCallback.tsx │ │ │ │ │ │ └── useThrottleCallback.types.ts │ │ │ │ │ ├── useCurrentPage.ts │ │ │ │ │ ├── useDeletePlaylistAction.ts │ │ │ │ │ ├── useEntityReactions.ts │ │ │ │ │ ├── useHover.ts │ │ │ │ │ ├── usePageBackgroundColor.ts │ │ │ │ │ └── usePageSearchParams.ts │ │ │ │ ├── icons/ │ │ │ │ │ ├── AddToPlaylistIcon.tsx │ │ │ │ │ ├── ArrowBackIcon.tsx │ │ │ │ │ ├── ArrowDownIcon.tsx │ │ │ │ │ ├── CheckedIcon.tsx │ │ │ │ │ ├── ClockIcon.tsx │ │ │ │ │ ├── CreateIcon.tsx │ │ │ │ │ ├── DeleteIcon.tsx │ │ │ │ │ ├── DeleteTagIconButton.tsx │ │ │ │ │ ├── DislikeIcon.tsx │ │ │ │ │ ├── DownloadIcon.tsx │ │ │ │ │ ├── EditIcon.tsx │ │ │ │ │ ├── HomeIcon.tsx │ │ │ │ │ ├── ImageUploadIcon.tsx │ │ │ │ │ ├── KeyboardArrowLeftIcon.tsx │ │ │ │ │ ├── KeyboardArrowRightIcon.tsx │ │ │ │ │ ├── LanguageIcon.tsx │ │ │ │ │ ├── LibraryIcon.tsx │ │ │ │ │ ├── LikeIcon.tsx │ │ │ │ │ ├── LikeIconFill.tsx │ │ │ │ │ ├── LikeInSquareIcon.tsx │ │ │ │ │ ├── LiveWaveIcon/ │ │ │ │ │ │ ├── LiveWaveIcon.module.css │ │ │ │ │ │ ├── LiveWaveIcon.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── LogoutIcon.tsx │ │ │ │ │ ├── MoreIcon.tsx │ │ │ │ │ ├── PauseIcon.tsx │ │ │ │ │ ├── PlayIcon.tsx │ │ │ │ │ ├── PlaylistIcon.tsx │ │ │ │ │ ├── PlusIcon.tsx │ │ │ │ │ ├── ProfileIcon.tsx │ │ │ │ │ ├── RepeatIcon.tsx │ │ │ │ │ ├── SearchIcon.tsx │ │ │ │ │ ├── ShuffleIcon.tsx │ │ │ │ │ ├── SkipNextIcon.tsx │ │ │ │ │ ├── SkipPreviousIcon.tsx │ │ │ │ │ ├── TextIcon.tsx │ │ │ │ │ ├── TrackIcon.tsx │ │ │ │ │ ├── UncheckedIcon.tsx │ │ │ │ │ ├── UploadIcon.tsx │ │ │ │ │ ├── VolumeIcon.tsx │ │ │ │ │ ├── VolumeMuteIcon.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── model/ │ │ │ │ │ └── ui-store.ts │ │ │ │ ├── translations/ │ │ │ │ │ ├── i18nConfiguration.ts │ │ │ │ │ └── languages/ │ │ │ │ │ ├── en.json │ │ │ │ │ └── ru.json │ │ │ │ ├── types/ │ │ │ │ │ ├── api-track.types.ts │ │ │ │ │ └── strict.tsx │ │ │ │ ├── ui/ │ │ │ │ │ ├── prerender-ready.tsx │ │ │ │ │ └── utils/ │ │ │ │ │ └── query-error-handler-for-rhf-factory.ts │ │ │ │ └── utils/ │ │ │ │ ├── authStorage.ts │ │ │ │ ├── decode-file-from-base-64.ts │ │ │ │ ├── format-created-date.ts │ │ │ │ ├── get-artist-id.ts │ │ │ │ ├── get-artist-name.ts │ │ │ │ ├── get-artists-by-track.ts │ │ │ │ ├── get-audio-url.ts │ │ │ │ ├── get-cover-url.ts │ │ │ │ ├── get-image-by-type.ts │ │ │ │ ├── get-plural-key.ts │ │ │ │ ├── get-russian-plural-form.ts │ │ │ │ ├── get-user-initials.ts │ │ │ │ ├── index.ts │ │ │ │ ├── join-url.test.ts │ │ │ │ ├── join-url.ts │ │ │ │ ├── set-locale.ts │ │ │ │ └── validators/ │ │ │ │ ├── getType.ts │ │ │ │ ├── inNun.ts │ │ │ │ ├── index.ts │ │ │ │ ├── isArray.ts │ │ │ │ ├── isFunction.ts │ │ │ │ ├── isNotEmptyArray.ts │ │ │ │ ├── isNull.ts │ │ │ │ ├── isNumber.ts │ │ │ │ ├── isObject.ts │ │ │ │ ├── isString.ts │ │ │ │ ├── isUndefined.ts │ │ │ │ ├── isValid.ts │ │ │ │ ├── isValidNumber.ts │ │ │ │ ├── isValidObject.ts │ │ │ │ └── isValidString.ts │ │ │ └── widgets/ │ │ │ └── Player/ │ │ │ ├── Player.module.css │ │ │ ├── Player.tsx │ │ │ └── index.ts │ │ ├── stylelint.config.js │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ └── vite.config.ts │ └── ui-vanilla/ │ ├── .gitignore │ ├── .storybook/ │ │ ├── main.ts │ │ └── preview.tsx │ ├── README.md │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── app/ │ │ │ ├── App.tsx │ │ │ └── routing/ │ │ │ ├── Routing.tsx │ │ │ └── index.ts │ │ ├── features/ │ │ │ ├── artists/ │ │ │ │ ├── api/ │ │ │ │ │ ├── artists-api.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── ui/ │ │ │ │ └── ArtistCard/ │ │ │ │ ├── ArtistCard.module.css │ │ │ │ ├── ArtistCard.stories.tsx │ │ │ │ ├── ArtistCard.tsx │ │ │ │ └── index.ts │ │ │ ├── auth/ │ │ │ │ ├── index.ts │ │ │ │ └── ui/ │ │ │ │ ├── LoginButtonAndModal/ │ │ │ │ │ ├── LoginButtonAndModal.module.css │ │ │ │ │ ├── LoginButtonAndModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ProfileDropdownMenu/ │ │ │ │ │ ├── ProfileDropdownMenu.module.css │ │ │ │ │ ├── ProfileDropdownMenu.stories.tsx │ │ │ │ │ ├── ProfileDropdownMenu.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── playlists/ │ │ │ │ ├── api/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── playlistsApi.ts │ │ │ │ ├── index.ts │ │ │ │ └── ui/ │ │ │ │ ├── CreatePlaylistModal/ │ │ │ │ │ ├── CreatePlaylistModal.module.css │ │ │ │ │ ├── CreatePlaylistModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PlaylistCard/ │ │ │ │ │ ├── PlaylistCard.module.css │ │ │ │ │ ├── PlaylistCard.stories.tsx │ │ │ │ │ ├── PlaylistCard.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PlaylistOverview/ │ │ │ │ │ ├── PlaylistOverview.module.css │ │ │ │ │ ├── PlaylistOverview.stories.tsx │ │ │ │ │ ├── PlaylistOverview.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── tags/ │ │ │ │ ├── api/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── tags-api.ts │ │ │ │ ├── index.ts │ │ │ │ └── ui/ │ │ │ │ ├── TagsList/ │ │ │ │ │ ├── TagsList.module.css │ │ │ │ │ ├── TagsList.stories.tsx │ │ │ │ │ ├── TagsList.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ └── tracks/ │ │ │ ├── api/ │ │ │ │ ├── index.ts │ │ │ │ ├── tracksApi.ts │ │ │ │ └── types.ts │ │ │ ├── index.ts │ │ │ └── ui/ │ │ │ ├── TrackCard/ │ │ │ │ ├── TrackCard.module.css │ │ │ │ ├── TrackCard.stories.tsx │ │ │ │ ├── TrackCard.tsx │ │ │ │ └── index.ts │ │ │ ├── TrackInfoCell/ │ │ │ │ ├── TrackInfoCell.module.css │ │ │ │ ├── TrackInfoCell.tsx │ │ │ │ └── index.ts │ │ │ ├── TrackOverview/ │ │ │ │ ├── TrackOverview.module.css │ │ │ │ ├── TrackOverview.stories.tsx │ │ │ │ ├── TrackOverview.tsx │ │ │ │ └── index.ts │ │ │ ├── TrackRow/ │ │ │ │ ├── TrackRow.module.css │ │ │ │ └── TrackRow.tsx │ │ │ ├── TracksTable/ │ │ │ │ ├── TrackTable.stories.tsx │ │ │ │ ├── TracksTable.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── layout/ │ │ │ ├── Header/ │ │ │ │ ├── Header.module.css │ │ │ │ ├── Header.tsx │ │ │ │ └── index.ts │ │ │ ├── Layout.module.css │ │ │ ├── Layout.tsx │ │ │ ├── Sidebar/ │ │ │ │ ├── MenuLinks/ │ │ │ │ │ ├── MenuLinks.module.css │ │ │ │ │ ├── MenuLinks.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Sidebar.module.css │ │ │ │ ├── Sidebar.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── main.tsx │ │ ├── pages/ │ │ │ ├── MainPage/ │ │ │ │ ├── MainPage.module.css │ │ │ │ ├── MainPage.tsx │ │ │ │ └── index.ts │ │ │ ├── PlaylistPage/ │ │ │ │ ├── PlaylistPage.module.css │ │ │ │ ├── PlaylistPage.tsx │ │ │ │ ├── index.ts │ │ │ │ └── ui/ │ │ │ │ └── ControlPanel/ │ │ │ │ ├── ControlPanel.module.css │ │ │ │ ├── ControlPanel.tsx │ │ │ │ └── index.ts │ │ │ ├── PlaylistsPage/ │ │ │ │ ├── PlaylistsPage.module.css │ │ │ │ ├── PlaylistsPage.tsx │ │ │ │ └── index.ts │ │ │ ├── TrackPage/ │ │ │ │ ├── TrackPage.module.css │ │ │ │ ├── TrackPage.tsx │ │ │ │ ├── index.ts │ │ │ │ └── ui/ │ │ │ │ └── ControlPanel/ │ │ │ │ ├── ControlPanel.module.css │ │ │ │ ├── ControlPanel.tsx │ │ │ │ └── index.ts │ │ │ ├── TracksPage/ │ │ │ │ ├── TracksPage.module.css │ │ │ │ ├── TracksPage.tsx │ │ │ │ └── index.ts │ │ │ ├── UserPage/ │ │ │ │ ├── UserPage.module.css │ │ │ │ ├── UserPage.tsx │ │ │ │ ├── index.ts │ │ │ │ └── ui/ │ │ │ │ ├── UserInfo/ │ │ │ │ │ ├── UserInfo.module.css │ │ │ │ │ ├── UserInfo.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── UserTabs/ │ │ │ │ │ ├── LikedTracksTab/ │ │ │ │ │ │ ├── LikedTracksTab.module.css │ │ │ │ │ │ ├── LikedTracksTab.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── MyLikedPlaylistsTab/ │ │ │ │ │ │ ├── MyLikedPlaylistsTab.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── PlaylistsTab/ │ │ │ │ │ │ ├── PlaylistsTab.module.css │ │ │ │ │ │ ├── PlaylistsTab.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TracksTab/ │ │ │ │ │ │ ├── TracksTab.module.css │ │ │ │ │ │ ├── TracksTab.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── UserTabs.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── common/ │ │ │ │ ├── ContentList/ │ │ │ │ │ ├── ContentList.module.css │ │ │ │ │ ├── ContentList.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── PageWrapper/ │ │ │ │ │ ├── PageWrapper.module.css │ │ │ │ │ ├── PageWrapper.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SearchTextField/ │ │ │ │ │ ├── SearchTextField.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SortSelect/ │ │ │ │ │ ├── SortSelect.module.css │ │ │ │ │ ├── SortSelect.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── shared/ │ │ │ ├── components/ │ │ │ │ ├── AudioPlayer/ │ │ │ │ │ ├── AudioPlayer.module.css │ │ │ │ │ ├── AudioPlayer.stories.tsx │ │ │ │ │ ├── AudioPlayer.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Autocomplete/ │ │ │ │ │ ├── Autocomplete.module.css │ │ │ │ │ ├── Autocomplete.stories.tsx │ │ │ │ │ ├── Autocomplete.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Button/ │ │ │ │ │ ├── Button.module.css │ │ │ │ │ ├── Button.stories.tsx │ │ │ │ │ ├── Button.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Card/ │ │ │ │ │ ├── Card.module.css │ │ │ │ │ ├── Card.stories.tsx │ │ │ │ │ ├── Card.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Dialog/ │ │ │ │ │ ├── Dialog.module.css │ │ │ │ │ ├── Dialog.stories.tsx │ │ │ │ │ ├── Dialog.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── DropdownMenu/ │ │ │ │ │ ├── DropdownMenu.module.css │ │ │ │ │ ├── DropdownMenu.stories.tsx │ │ │ │ │ ├── DropdownMenu.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Hashtag/ │ │ │ │ │ ├── Tag.module.css │ │ │ │ │ ├── Tag.stories.tsx │ │ │ │ │ ├── Tag.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── IconButton/ │ │ │ │ │ ├── IconButton.module.css │ │ │ │ │ ├── IconButton.stories.tsx │ │ │ │ │ ├── IconButton.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ImageUploader/ │ │ │ │ │ ├── ImageUploader.module.css │ │ │ │ │ ├── ImageUploader.stories.tsx │ │ │ │ │ ├── ImageUploader.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Pagination/ │ │ │ │ │ ├── Pagination.module.css │ │ │ │ │ ├── Pagination.stories.tsx │ │ │ │ │ ├── Pagination.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Progress/ │ │ │ │ │ ├── Progress.module.css │ │ │ │ │ ├── Progress.stories.tsx │ │ │ │ │ ├── Progress.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ReactionButtons/ │ │ │ │ │ ├── ReactionButtons.module.css │ │ │ │ │ ├── ReactionButtons.stories.tsx │ │ │ │ │ ├── ReactionButtons.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SearchField/ │ │ │ │ │ ├── SearchField.module.css │ │ │ │ │ ├── SearchField.stories.tsx │ │ │ │ │ ├── SearchField.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Select/ │ │ │ │ │ ├── Select.module.css │ │ │ │ │ ├── Select.stories.tsx │ │ │ │ │ ├── Select.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SortSelect/ │ │ │ │ │ └── Select.tsx │ │ │ │ ├── Table/ │ │ │ │ │ ├── Table.module.css │ │ │ │ │ ├── Table.stories.tsx │ │ │ │ │ ├── Table.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Tabs/ │ │ │ │ │ ├── Tabs.module.css │ │ │ │ │ ├── Tabs.stories.tsx │ │ │ │ │ ├── Tabs.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TagEditor/ │ │ │ │ │ ├── TagEditor.module.css │ │ │ │ │ ├── TagEditor.stories.tsx │ │ │ │ │ ├── TagEditor.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── TextField/ │ │ │ │ │ ├── TextField.module.css │ │ │ │ │ ├── TextField.stories.tsx │ │ │ │ │ ├── TextField.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Textarea/ │ │ │ │ │ ├── Textarea.module.css │ │ │ │ │ ├── Textarea.stories.tsx │ │ │ │ │ ├── Textarea.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Typography/ │ │ │ │ │ ├── Typography.module.css │ │ │ │ │ ├── Typography.stories.tsx │ │ │ │ │ ├── Typography.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── hooks/ │ │ │ │ ├── index.ts │ │ │ │ └── useGetId.ts │ │ │ └── icons/ │ │ │ ├── AddToPlaylistIcon.tsx │ │ │ ├── ArrowDownIcon.tsx │ │ │ ├── ClockIcon.tsx │ │ │ ├── CreateIcon.tsx │ │ │ ├── DeleteIcon.tsx │ │ │ ├── DislikeIcon.tsx │ │ │ ├── DownloadIcon.tsx │ │ │ ├── EditIcon.tsx │ │ │ ├── HomeIcon.tsx │ │ │ ├── ImageUploadIcon.tsx │ │ │ ├── KeyboardArrowLeftIcon.tsx │ │ │ ├── KeyboardArrowRightIcon.tsx │ │ │ ├── LibraryIcon.tsx │ │ │ ├── LikeIcon.tsx │ │ │ ├── LikeIconFill.tsx │ │ │ ├── LikeInSquareIcon.tsx │ │ │ ├── LiveWaveIcon/ │ │ │ │ ├── LiveWaveIcon.module.css │ │ │ │ ├── LiveWaveIcon.tsx │ │ │ │ └── index.ts │ │ │ ├── LogoutIcon.tsx │ │ │ ├── MoreIcon.tsx │ │ │ ├── PauseIcon.tsx │ │ │ ├── PlayIcon.tsx │ │ │ ├── PlaylistIcon.tsx │ │ │ ├── PlusIcon.tsx │ │ │ ├── ProfileIcon.tsx │ │ │ ├── RepeatIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── ShuffleIcon.tsx │ │ │ ├── SkipNextIcon.tsx │ │ │ ├── SkipPreviousIcon.tsx │ │ │ ├── TextIcon.tsx │ │ │ ├── TrackIcon.tsx │ │ │ ├── UploadIcon.tsx │ │ │ ├── VolumeIcon.tsx │ │ │ ├── VolumeMuteIcon.tsx │ │ │ └── index.ts │ │ ├── styles/ │ │ │ ├── fonts.css │ │ │ ├── global.css │ │ │ ├── reset.css │ │ │ └── variables.css │ │ ├── vite-env.d.ts │ │ └── widgets/ │ │ └── Player/ │ │ ├── Player.module.css │ │ ├── Player.tsx │ │ └── index.ts │ ├── stylelint.config.js │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── architecture/ │ └── microfrontends/ │ ├── player/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.css │ │ │ ├── App.tsx │ │ │ ├── index.css │ │ │ └── main.tsx │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ ├── vite.config.readme.md │ │ └── vite.config.ts │ └── root/ │ ├── .gitignore │ ├── README.md │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── App.css │ │ ├── App.tsx │ │ ├── index.css │ │ └── main.tsx │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── content-thoughts/ │ └── search-input/ │ └── info.md ├── docs/ │ ├── feature-comparison.md │ └── todos-features.md ├── eslint.config.js ├── experiment-apps/ │ ├── musicfun-tanstack-query-orval-small-example/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── orval.config.cjs │ │ ├── package.json │ │ ├── src/ │ │ │ ├── app/ │ │ │ │ ├── entrypoint/ │ │ │ │ │ └── main.tsx │ │ │ │ ├── layouts/ │ │ │ │ │ ├── root-layout.module.css │ │ │ │ │ └── root-layout.tsx │ │ │ │ ├── providers/ │ │ │ │ │ └── web-socket-provider.tsx │ │ │ │ ├── query-client/ │ │ │ │ │ └── query-client.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── my-playlists.tsx │ │ │ │ │ ├── oauth/ │ │ │ │ │ │ └── callback.tsx │ │ │ │ │ └── playlists-with-filters.tsx │ │ │ │ └── styles/ │ │ │ │ ├── index.css │ │ │ │ └── reset.css │ │ │ ├── features/ │ │ │ │ ├── auth/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── auth-api.types.ts │ │ │ │ │ │ ├── use-login.mutation.ts │ │ │ │ │ │ ├── use-logout.mutation.ts │ │ │ │ │ │ └── use-me.query.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── ui/ │ │ │ │ │ ├── account-bar.module.css │ │ │ │ │ ├── account-bar.tsx │ │ │ │ │ ├── current-user/ │ │ │ │ │ │ └── current-user.tsx │ │ │ │ │ ├── login-button/ │ │ │ │ │ │ ├── login-button.tsx │ │ │ │ │ │ └── use-login.tsx │ │ │ │ │ └── logout-button/ │ │ │ │ │ ├── logout-button.tsx │ │ │ │ │ └── use-logout.ts │ │ │ │ └── playlists/ │ │ │ │ ├── add-playlist-form/ │ │ │ │ │ ├── add-playlist-form.module.css │ │ │ │ │ └── add-playlist-form.tsx │ │ │ │ ├── api/ │ │ │ │ │ └── use-playlists-query.tsx │ │ │ │ ├── edit-playlist-form/ │ │ │ │ │ └── edit-playlist-form.tsx │ │ │ │ ├── list/ │ │ │ │ │ ├── paginated-playlists.module.css │ │ │ │ │ ├── paginated-playlists.tsx │ │ │ │ │ └── playlists.tsx │ │ │ │ └── playlist-cover/ │ │ │ │ ├── playlist-cover.module.css │ │ │ │ └── playlist-cover.tsx │ │ │ ├── pages/ │ │ │ │ ├── auth/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── ui/ │ │ │ │ │ └── oauth-callback-page.tsx │ │ │ │ └── playlists/ │ │ │ │ └── ui/ │ │ │ │ ├── my-playlists/ │ │ │ │ │ ├── my-playlists-page.module.css │ │ │ │ │ └── my-playlists-page.tsx │ │ │ │ └── playlists-with-filters-page.tsx │ │ │ ├── shared/ │ │ │ │ ├── api/ │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── json-api-error.ts │ │ │ │ │ ├── orval/ │ │ │ │ │ │ ├── artists/ │ │ │ │ │ │ │ └── artists.ts │ │ │ │ │ │ ├── authentication/ │ │ │ │ │ │ │ └── authentication.ts │ │ │ │ │ │ ├── custom-instance.ts │ │ │ │ │ │ ├── musicfun.schemas.ts │ │ │ │ │ │ ├── musicfun.ts │ │ │ │ │ │ ├── playlists-owner/ │ │ │ │ │ │ │ └── playlists-owner.ts │ │ │ │ │ │ ├── playlists-public/ │ │ │ │ │ │ │ └── playlists-public.ts │ │ │ │ │ │ ├── tags/ │ │ │ │ │ │ │ └── tags.ts │ │ │ │ │ │ ├── tracks-owner/ │ │ │ │ │ │ │ └── tracks-owner.ts │ │ │ │ │ │ └── tracks-public/ │ │ │ │ │ │ └── tracks-public.ts │ │ │ │ │ ├── query-error-handler-for-rhf-factory.ts │ │ │ │ │ ├── request-wrapper.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── socket.ts │ │ │ │ ├── config/ │ │ │ │ │ └── api.config.ts │ │ │ │ ├── db/ │ │ │ │ │ └── localstorage-keys.ts │ │ │ │ ├── routes/ │ │ │ │ │ └── routes.ts │ │ │ │ └── ui/ │ │ │ │ ├── header/ │ │ │ │ │ ├── header.component.tsx │ │ │ │ │ └── header.module.css │ │ │ │ └── pagination/ │ │ │ │ ├── pagination-nav/ │ │ │ │ │ ├── pagination-nav.module.css │ │ │ │ │ └── pagination-nav.tsx │ │ │ │ ├── pagination.module.css │ │ │ │ ├── pagination.tsx │ │ │ │ └── utils/ │ │ │ │ └── get-pagination-pages.ts │ │ │ └── vite-env.d.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ ├── tsr.config.json │ │ └── vite.config.ts │ ├── musicfun-tanstack-query-small-example/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── eslint.config.js │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── app/ │ │ │ │ ├── entrypoint/ │ │ │ │ │ └── main.tsx │ │ │ │ ├── layouts/ │ │ │ │ │ ├── root-layout.module.css │ │ │ │ │ └── root-layout.tsx │ │ │ │ ├── providers/ │ │ │ │ │ └── web-socket-provider.tsx │ │ │ │ ├── query-client/ │ │ │ │ │ └── query-client.tsx │ │ │ │ ├── routes/ │ │ │ │ │ ├── __root.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── my-playlists.tsx │ │ │ │ │ ├── oauth/ │ │ │ │ │ │ └── callback.tsx │ │ │ │ │ └── playlists-with-filters.tsx │ │ │ │ └── styles/ │ │ │ │ ├── index.css │ │ │ │ └── reset.css │ │ │ ├── features/ │ │ │ │ ├── auth/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── auth-api.types.ts │ │ │ │ │ │ ├── use-login.mutation.ts │ │ │ │ │ │ ├── use-logout.mutation.ts │ │ │ │ │ │ └── use-me.query.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── ui/ │ │ │ │ │ ├── account-bar.module.css │ │ │ │ │ ├── account-bar.tsx │ │ │ │ │ ├── current-user/ │ │ │ │ │ │ └── current-user.tsx │ │ │ │ │ ├── login-button/ │ │ │ │ │ │ ├── login-button.tsx │ │ │ │ │ │ └── use-login.tsx │ │ │ │ │ └── logout-button/ │ │ │ │ │ ├── logout-button.tsx │ │ │ │ │ └── use-logout.ts │ │ │ │ └── playlists/ │ │ │ │ ├── add-playlist-form/ │ │ │ │ │ ├── add-playlist-form.module.css │ │ │ │ │ └── add-playlist-form.tsx │ │ │ │ ├── api/ │ │ │ │ │ └── use-playlists-query.tsx │ │ │ │ ├── edit-playlist-form/ │ │ │ │ │ └── edit-playlist-form.tsx │ │ │ │ ├── list/ │ │ │ │ │ ├── paginated-playlists.module.css │ │ │ │ │ ├── paginated-playlists.tsx │ │ │ │ │ └── playlists.tsx │ │ │ │ └── playlist-cover/ │ │ │ │ ├── playlist-cover.module.css │ │ │ │ └── playlist-cover.tsx │ │ │ ├── pages/ │ │ │ │ ├── auth/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── ui/ │ │ │ │ │ └── oauth-callback-page.tsx │ │ │ │ └── playlists/ │ │ │ │ └── ui/ │ │ │ │ ├── my-playlists/ │ │ │ │ │ ├── my-playlists-page.module.css │ │ │ │ │ └── my-playlists-page.tsx │ │ │ │ └── playlists-with-filters-page.tsx │ │ │ ├── shared/ │ │ │ │ ├── api/ │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── json-api-error.ts │ │ │ │ │ ├── query-error-handler-for-rhf-factory.ts │ │ │ │ │ ├── request-wrapper.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── socket.ts │ │ │ │ ├── config/ │ │ │ │ │ └── api.config.ts │ │ │ │ ├── db/ │ │ │ │ │ └── localstorage-keys.ts │ │ │ │ ├── routes/ │ │ │ │ │ └── routes.ts │ │ │ │ └── ui/ │ │ │ │ ├── header/ │ │ │ │ │ ├── header.component.tsx │ │ │ │ │ └── header.module.css │ │ │ │ └── pagination/ │ │ │ │ ├── pagination-nav/ │ │ │ │ │ ├── pagination-nav.module.css │ │ │ │ │ └── pagination-nav.tsx │ │ │ │ ├── pagination.module.css │ │ │ │ ├── pagination.tsx │ │ │ │ └── utils/ │ │ │ │ └── get-pagination-pages.ts │ │ │ └── vite-env.d.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ ├── tsr.config.json │ │ └── vite.config.ts │ └── trelly-rtk/ │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── index.html │ ├── openapi-config.js │ ├── package.json │ ├── src/ │ │ ├── app/ │ │ │ ├── api/ │ │ │ │ ├── baseApi.ts │ │ │ │ ├── baseQuery.ts │ │ │ │ └── baseQueryWithReauth.ts │ │ │ ├── model/ │ │ │ │ ├── app-slice.ts │ │ │ │ └── store.ts │ │ │ └── ui/ │ │ │ ├── App.module.css │ │ │ ├── App.tsx │ │ │ └── Main.tsx │ │ ├── common/ │ │ │ ├── actions/ │ │ │ │ ├── actions.ts │ │ │ │ └── index.ts │ │ │ ├── components/ │ │ │ │ ├── CreateItemForm/ │ │ │ │ │ └── CreateItemForm.tsx │ │ │ │ ├── EditableSpan/ │ │ │ │ │ └── EditableSpan.tsx │ │ │ │ ├── ErrorSnackbar/ │ │ │ │ │ └── ErrorSnackbar.tsx │ │ │ │ ├── Header/ │ │ │ │ │ └── Header.tsx │ │ │ │ ├── NavButton/ │ │ │ │ │ └── NavButton.ts │ │ │ │ ├── PageNotFound/ │ │ │ │ │ ├── PageNotFound.module.css │ │ │ │ │ └── PageNotFound.tsx │ │ │ │ ├── ProtectedRoute/ │ │ │ │ │ └── ProtectedRoute.tsx │ │ │ │ └── index.ts │ │ │ ├── constants/ │ │ │ │ ├── constants.ts │ │ │ │ └── index.ts │ │ │ ├── enums/ │ │ │ │ ├── enums.ts │ │ │ │ └── index.ts │ │ │ ├── hooks/ │ │ │ │ ├── index.ts │ │ │ │ ├── useAppDispatch.ts │ │ │ │ └── useAppSelector.ts │ │ │ ├── instance/ │ │ │ │ ├── index.ts │ │ │ │ └── instance.ts │ │ │ ├── routing/ │ │ │ │ ├── Routing.tsx │ │ │ │ └── index.ts │ │ │ ├── styles/ │ │ │ │ ├── container.styles.ts │ │ │ │ └── index.ts │ │ │ ├── theme/ │ │ │ │ ├── index.ts │ │ │ │ └── theme.ts │ │ │ ├── types/ │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ └── utils/ │ │ │ ├── createAppSlice.ts │ │ │ ├── handleError.ts │ │ │ ├── index.ts │ │ │ ├── isErrorWithMessage.ts │ │ │ └── isTokens.ts │ │ ├── features/ │ │ │ ├── auth/ │ │ │ │ ├── api/ │ │ │ │ │ ├── authApi.ts │ │ │ │ │ └── authApi.types.ts │ │ │ │ ├── lib/ │ │ │ │ │ └── schemas/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── loginSchema.ts │ │ │ │ └── ui/ │ │ │ │ ├── Login/ │ │ │ │ │ ├── Login.module.css │ │ │ │ │ └── Login.tsx │ │ │ │ ├── OAuthCallback/ │ │ │ │ │ └── OAuthCallback.tsx │ │ │ │ └── UserBlock/ │ │ │ │ └── UserBlock.tsx │ │ │ ├── boards/ │ │ │ │ ├── api/ │ │ │ │ │ ├── boardsApi.ts │ │ │ │ │ └── boardsApi.types.ts │ │ │ │ ├── lib/ │ │ │ │ │ └── utils/ │ │ │ │ │ ├── createTaskModel.ts │ │ │ │ │ └── index.ts │ │ │ │ └── ui/ │ │ │ │ └── Boards/ │ │ │ │ ├── BoardItem/ │ │ │ │ │ ├── BoardItem.tsx │ │ │ │ │ ├── BoardTitle/ │ │ │ │ │ │ ├── BoardTitle.module.css │ │ │ │ │ │ └── BoardTitle.tsx │ │ │ │ │ └── FilterButtons/ │ │ │ │ │ └── FilterButtons.tsx │ │ │ │ ├── BoardSkeleton/ │ │ │ │ │ ├── BoardSkeleton.module.css │ │ │ │ │ └── BoardSkeleton.tsx │ │ │ │ └── Boards.tsx │ │ │ └── tasks/ │ │ │ ├── api/ │ │ │ │ ├── tasksApi.ts │ │ │ │ └── tasksApi.types.ts │ │ │ └── ui/ │ │ │ └── Tasks/ │ │ │ ├── TaskItem/ │ │ │ │ ├── TaskItem.styles.ts │ │ │ │ └── TaskItem.tsx │ │ │ ├── Tasks.tsx │ │ │ ├── TasksPagination/ │ │ │ │ ├── TasksPagination.module.css │ │ │ │ └── TasksPagination.tsx │ │ │ └── TasksSkeleton/ │ │ │ └── TasksSkeleton.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── package.json ├── packages/ │ └── musicfun-api-sdk/ │ ├── package.json │ ├── src/ │ │ ├── api/ │ │ │ ├── artists/ │ │ │ │ ├── artistsApi.ts │ │ │ │ └── artistsApi.types.ts │ │ │ ├── auth/ │ │ │ │ ├── authApi.ts │ │ │ │ └── authApi.types.ts │ │ │ ├── playlists/ │ │ │ │ ├── playlistsApi.ts │ │ │ │ └── playlistsApi.types.ts │ │ │ ├── tags/ │ │ │ │ ├── tagsApi.ts │ │ │ │ └── tagsApi.types.ts │ │ │ └── tracks/ │ │ │ ├── tracksApi.ts │ │ │ └── tracksApi.types.ts │ │ ├── common/ │ │ │ ├── apiEntities/ │ │ │ │ └── apiEntities.ts │ │ │ ├── instance/ │ │ │ │ └── instance.ts │ │ │ ├── types/ │ │ │ │ ├── common.types.ts │ │ │ │ ├── enums.ts │ │ │ │ └── playlists-tracks.types.ts │ │ │ └── utils/ │ │ │ └── urlHelper.ts │ │ ├── index.ts │ │ └── v2/ │ │ └── request.ts │ └── tsconfig.json ├── public/ │ ├── 404.html │ └── index.html ├── type-comparison-examples.md └── youtube/ ├── markup/ │ ├── .gitignore │ ├── README.md │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── rtk-query/ │ └── lesson1/ │ ├── .gitignore │ ├── .prettierrc │ ├── AGENTS.md │ ├── README.md │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── app/ │ │ │ ├── api/ │ │ │ │ ├── baseApi.ts │ │ │ │ ├── baseQuery.ts │ │ │ │ └── baseQueryWithReauth.ts │ │ │ ├── model/ │ │ │ │ └── store.ts │ │ │ └── ui/ │ │ │ ├── App/ │ │ │ │ ├── App.module.css │ │ │ │ └── App.tsx │ │ │ └── MainPage/ │ │ │ └── MainPage.tsx │ │ ├── common/ │ │ │ ├── components/ │ │ │ │ ├── Header/ │ │ │ │ │ ├── Header.module.css │ │ │ │ │ └── Header.tsx │ │ │ │ ├── LinearProgress/ │ │ │ │ │ ├── LinearProgress.module.css │ │ │ │ │ └── LinearProgress.tsx │ │ │ │ ├── PageNotFound/ │ │ │ │ │ ├── PageNotFound.module.css │ │ │ │ │ └── PageNotFound.tsx │ │ │ │ ├── Pagination/ │ │ │ │ │ ├── Pagination.module.css │ │ │ │ │ └── Pagination.tsx │ │ │ │ └── index.tsx │ │ │ ├── constants/ │ │ │ │ ├── constants.ts │ │ │ │ └── index.ts │ │ │ ├── enums/ │ │ │ │ ├── enums.ts │ │ │ │ └── index.ts │ │ │ ├── hooks/ │ │ │ │ ├── index.ts │ │ │ │ ├── useDebounceValue.ts │ │ │ │ ├── useGlobalLoading.ts │ │ │ │ └── useInfiniteScroll.ts │ │ │ ├── routing/ │ │ │ │ ├── Routing.tsx │ │ │ │ └── index.ts │ │ │ ├── schemas/ │ │ │ │ ├── index.ts │ │ │ │ └── schemas.ts │ │ │ ├── socket/ │ │ │ │ ├── getSocket.ts │ │ │ │ ├── index.ts │ │ │ │ └── subscribeToEvent.ts │ │ │ ├── types/ │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ └── utils/ │ │ │ ├── errorToast.ts │ │ │ ├── getPaginationPages.ts │ │ │ ├── handleErrors.ts │ │ │ ├── index.ts │ │ │ ├── isErrorWithDetailArray.ts │ │ │ ├── isErrorWithProperty.ts │ │ │ ├── isTokens.ts │ │ │ ├── trimToMaxLength.ts │ │ │ └── withZodCatch.ts │ │ ├── features/ │ │ │ ├── auth/ │ │ │ │ ├── api/ │ │ │ │ │ ├── authApi.ts │ │ │ │ │ └── authApi.types.ts │ │ │ │ ├── model/ │ │ │ │ │ └── auth.schemas.ts │ │ │ │ └── ui/ │ │ │ │ ├── Login/ │ │ │ │ │ └── Login.tsx │ │ │ │ ├── OAuthCallback/ │ │ │ │ │ └── OAuthCallback.tsx │ │ │ │ └── ProfilePage/ │ │ │ │ ├── ProfilePage.module.css │ │ │ │ └── ProfilePage.tsx │ │ │ ├── playlists/ │ │ │ │ ├── api/ │ │ │ │ │ ├── playlistsApi.ts │ │ │ │ │ └── playlistsApi.types.ts │ │ │ │ ├── model/ │ │ │ │ │ └── playlists.schemas.ts │ │ │ │ └── ui/ │ │ │ │ ├── CreatePlaylistForm/ │ │ │ │ │ ├── CreatePlaylistForm.module.css │ │ │ │ │ └── CreatePlaylistForm.tsx │ │ │ │ ├── EditPlaylistForm/ │ │ │ │ │ └── EditPlaylistForm.tsx │ │ │ │ ├── PlaylistItem/ │ │ │ │ │ ├── PlaylistCover/ │ │ │ │ │ │ ├── PlaylistCover.module.css │ │ │ │ │ │ └── PlaylistCover.tsx │ │ │ │ │ ├── PlaylistDescription/ │ │ │ │ │ │ └── PlaylistDescription.tsx │ │ │ │ │ └── PlaylistItem.tsx │ │ │ │ ├── PlaylistsList/ │ │ │ │ │ ├── PlaylistsList.module.css │ │ │ │ │ └── PlaylistsList.tsx │ │ │ │ ├── PlaylistsPage.module.css │ │ │ │ └── PlaylistsPage.tsx │ │ │ └── tracks/ │ │ │ ├── api/ │ │ │ │ ├── tracksApi.ts │ │ │ │ └── tracksApi.types.ts │ │ │ ├── model/ │ │ │ │ └── tracks.schemas.ts │ │ │ └── ui/ │ │ │ ├── LoadingTrigger/ │ │ │ │ └── LoadingTrigger.tsx │ │ │ ├── TracksList/ │ │ │ │ ├── TracksList.module.css │ │ │ │ └── TracksList.tsx │ │ │ └── TracksPage.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── tanstack-query-router-fsd/ └── lesson1/ ├── .gitignore ├── README.md ├── eslint.config.js ├── index.html ├── package.json ├── src/ │ ├── app/ │ │ ├── entrypoint/ │ │ │ └── main.tsx │ │ ├── layouts/ │ │ │ ├── root-layout.module.css │ │ │ └── root-layout.tsx │ │ ├── routes/ │ │ │ ├── __root.tsx │ │ │ ├── index.tsx │ │ │ ├── my-playlists.tsx │ │ │ ├── oauth/ │ │ │ │ └── callback.tsx │ │ │ └── routeTree.gen.ts │ │ ├── styles/ │ │ │ ├── index.css │ │ │ └── reset.css │ │ ├── tanstack-query/ │ │ │ └── query-client-instance.tsx │ │ └── tanstack-router/ │ │ └── router-instance.tsx │ ├── features/ │ │ ├── auth/ │ │ │ ├── api/ │ │ │ │ ├── use-login-mutation.tsx │ │ │ │ ├── use-logout-mutation.tsx │ │ │ │ └── use-me-query.ts │ │ │ └── ui/ │ │ │ ├── account-bar.module.css │ │ │ ├── account-bar.tsx │ │ │ ├── current-user/ │ │ │ │ └── current-user.tsx │ │ │ ├── login-button.tsx │ │ │ └── logout-button.tsx │ │ └── playlists/ │ │ ├── add-playlist/ │ │ │ ├── api/ │ │ │ │ └── use-add-playlist-mutation.ts │ │ │ └── ui/ │ │ │ └── add-playlist-form.tsx │ │ ├── delete-playlist/ │ │ │ ├── api/ │ │ │ │ └── use-delete-mutation.ts │ │ │ └── ui/ │ │ │ └── delete-playlist.tsx │ │ └── edit-playlist/ │ │ ├── api/ │ │ │ ├── use-playlist-query.tsx │ │ │ └── use-update-playlist-mutation.ts │ │ └── ui/ │ │ └── edit-playlist-form.tsx │ ├── pages/ │ │ ├── auth/ │ │ │ └── oauth-callback-page.tsx │ │ ├── my-playlists-page.tsx │ │ └── playlists-page.tsx │ ├── shared/ │ │ ├── api/ │ │ │ ├── client.ts │ │ │ ├── keys-factories/ │ │ │ │ ├── auth-keys-factory.ts │ │ │ │ └── playlists-keys-factory.ts │ │ │ └── schema.ts │ │ ├── config/ │ │ │ ├── api-config.ts │ │ │ └── localstorage-keys.ts │ │ ├── ui/ │ │ │ ├── header/ │ │ │ │ ├── header.module.css │ │ │ │ └── header.tsx │ │ │ ├── pagination/ │ │ │ │ ├── pagination-nav/ │ │ │ │ │ ├── pagination-nav.module.css │ │ │ │ │ └── pagination-nav.tsx │ │ │ │ ├── pagination.module.css │ │ │ │ ├── pagination.tsx │ │ │ │ └── utils/ │ │ │ │ └── get-pagination-pages.ts │ │ │ └── util/ │ │ │ └── query-error-handler-for-rhf-factory.ts │ │ └── util/ │ │ └── json-api-error.ts │ ├── vite-env.d.ts │ └── widgets/ │ └── playlists/ │ ├── api/ │ │ └── use-playlists-query.ts │ └── ui/ │ └── playlists.tsx ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.node.json ├── tsr.config.json └── vite.config.ts