This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Sorta is a web application for sorting Spotify playlists using custom sort rules. Built with React, TypeScript, Vite, and Tailwind CSS.
# Start dev server (runs on http://127.0.0.1:5173)
npm start
# Build for production
npm run build
# Run tests
npm test
# Type checking
npm run typecheck
# Linting
npm run lint
npm run lint:fix
# Check for unused dependencies/exports
npm run knip
npm run knip:fix
# Full CI check (lint + typecheck + knip + test + build)
npm run ci
# Preview production build
npm run previewThe codebase uses a feature-based architecture under src/features/:
- auth: Spotify OAuth 2.0 PKCE authentication flow with token refresh
- playlists: Playlist loading, display, and selection
- sorting: Sort rule parsing, track sorting logic, and UI
- layout: Header and main content layout components
- api/spotify.ts: Spotify API client using
@spotify/web-api-ts-sdk - components/ui/: Reusable UI components (built with Radix UI + Tailwind)
- hooks/: Shared React hooks
TypeScript path alias @/* maps to src/* (configured in tsconfig.json and vite.config.ts).
The app uses Spotify's OAuth 2.0 PKCE flow via the @spotify/web-api-ts-sdk:
- SDK initialization:
SpotifyApi.withUserAuthorization()creates authenticated client - Built-in PKCE flow: SDK handles code verifier/challenge generation and token exchange
- Automatic token refresh: SDK manages token lifecycle and refreshes before expiry
- Persistent storage: SDK stores tokens in localStorage using its own keys
- App.tsx: Initializes SDK with client ID, redirect URI, and requested scopes
The SDK provides a simplified authentication experience compared to manual PKCE implementation:
- No manual token storage or refresh logic needed
- Built-in error handling for auth failures
- Seamless token refresh during API calls
The Spotify SDK provides strongly-typed methods for all API operations:
// Get current user profile
const profile = await sdk.currentUser.profile()
// Get user's playlists (paginated)
const playlists = await sdk.currentUser.playlists.playlists(limit, offset)
// Get playlist tracks
const tracks = await sdk.playlists.getPlaylist(playlistId)
// Update playlist (reorder, add, remove tracks)
await sdk.playlists.updatePlaylistItems(playlistId, { uris: trackUris })Key differences from legacy spotify-web-api-js:
- All methods are async (no callback-based APIs)
- Response types include full Spotify API object structure
- Paginated results use
.itemsarray pattern - Track URIs in format
spotify:track:{id}for mutations
Sort rules use the format: key/order key/order ... (space-separated)
- Keys:
artist,album,release_date,title - Orders:
asc(default),desc - Example:
artist release_date/desc(sort by artist ascending, then release date descending)
- parseSortRules(): Parses string format into typed SortRule tuples
- sortTracks(): Applies multiple sort rules in order (features/sorting/utils/sortTracks.ts)
- Sorting uses
localeCompare()for string comparison with proper locale handling
Required environment variables (prefix with VITE_):
VITE_SPOTIFY_CLIENT_ID: Spotify application client IDVITE_SPOTIFY_REDIRECT_URI: OAuth callback URL
- Framework: Vitest with globals enabled
- Test files:
*.test.tsco-located with source files - Run with
npm testornpm run cifor full validation
- Biome: Linting and formatting (replaces ESLint + Prettier)
- Knip: Detects unused dependencies and exports
- TypeScript: Strict mode with path aliases
- Vite: Build tool with React plugin
- Tailwind CSS: Styling (PostCSS v4)