feat: integrate AI Queue and Discord RPC features#31
Conversation
- Added support for AI Queue functionality to enable continuous music playback based on user mood or genre. - Implemented Discord Rich Presence integration to update user status with currently playing track information. - Updated settings to allow users to enable/disable Discord RPC and show/hide AI Queue border. - Enhanced Spotify client to support fetching user playlists and managing tracks within playlists. - Introduced new UI components for managing playlists and displaying AI Queue status. - Updated documentation to reflect new features and usage guidelines.
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdds Discord Rich Presence, AI Queue for continuous, AI-generated playlists (TOON-encoded), playlist browsing and add-to-playlist UI, a Volume view, token/request optimizations, and various UI wiring and settings to support these features. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as Desktop UI
participant AIS as aiQueueService
participant Store as AIQueueStore
participant SClient as SpotifyClient
participant AI as AI Provider
participant Spotify as Spotify API
User->>UI: Toggle "Start AI Queue" (optional mood)
UI->>AIS: startAIQueue(mood)
AIS->>Store: setLoading(true)
AIS->>SClient: getRecentlyPlayed(), getTopArtists()
SClient->>Spotify: GET /v1/me/...
Spotify-->>SClient: recent/top data (tracks/artists)
SClient-->>AIS: TOON-encoded data
AIS->>AI: generateText(system + TOON context + mood)
AI-->>AIS: suggestions (JSON)
AIS->>SClient: searchAndGetUri(track names) (resolve URIs)
SClient->>Spotify: GET /v1/search (fallbacks)
Spotify-->>SClient: track URIs
SClient-->>AIS: resolved URIs
AIS->>Store: setQueue(resolvedTracks)
AIS->>SClient: playTracks(batch)
SClient->>Spotify: PUT /v1/me/player/play
Spotify-->>SClient: OK
loop Background monitor (every 3s)
AIS->>SClient: getPlayerState()
SClient->>Spotify: GET /v1/me/player
Spotify-->>SClient: player state
SClient-->>AIS: current progress
AIS->>Store: advance or fetchNextBatch if nearing end
alt User manually plays other track
AIS->>Store: stopAIQueue()
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (8)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/lib/settingLib.ts (1)
106-129: Missing initialization fordiscord_rpc_enabledfield.The
discord_rpc_enabledfield was added to theSettingstype (line 59) but is not initialized in either the success path (line 114) or the fallback object (line 126). This will result inundefinedvalues when the backend doesn't provide this field, causing potential runtime errors and type inconsistencies.🔎 Proposed fix to initialize discord_rpc_enabled
return { ...settings, ai_providers: settings.ai_providers ?? [], active_ai_provider: settings.active_ai_provider ?? null, active_music_provider: settings.active_music_provider ?? "spotify", show_ai_queue_border: settings.show_ai_queue_border ?? true, + discord_rpc_enabled: settings.discord_rpc_enabled ?? true, }; } catch (err) { console.warn("Failed to read settings via Tauri, using defaults:", err); return { first_boot_done: false, spotify: { access_token: null, refresh_token: null }, layout: "LayoutA", theme: "dark", ai_providers: [], active_ai_provider: null, active_music_provider: "spotify", show_ai_queue_border: true, + discord_rpc_enabled: true, }; }
🧹 Nitpick comments (11)
apps/desktop/src/ui/views/AddToPlaylistView.tsx (2)
10-14: Consider requiringtrackIdto be non-null.The
trackIdprop is typed asstring | null, but the component's purpose requires a valid track ID. IftrackIdisnull, the add button becomes effectively non-functional. Consider either:
- Making
trackIdrequired (non-null) at the type level- Adding an early return or error state when
trackIdis nullThis would make the component's contract clearer and prevent rendering a non-functional UI.
45-56: Missing user-facing error feedback.When
addTrackToPlaylistfails, the error is only logged to the console (line 53). Users won't know why the action failed. Consider adding a toast notification or inline error message.🔎 Suggested enhancement
+const [error, setError] = useState<string | null>(null); + const handleAddToPlaylist = async (playlist: SimplifiedPlaylist) => { if (!trackId || addingTo) return; setAddingTo(playlist.id); + setError(null); try { await addTrackToPlaylist(playlist.id, `spotify:track:${trackId}`); onBack(); } catch (err) { console.error("Failed to add track to playlist:", err); + setError("Failed to add track. Please try again."); setAddingTo(null); } };apps/desktop/src/ui/views/VolumeView.tsx (2)
20-31: Consider handling the case when no active device is available.If
getPlayerState()returnsnullorstate.deviceis undefined, the component silently keeps the default volume of 50 and empty device name. This could be confusing if there's no active Spotify session.🔎 Suggested enhancement
+const [noDevice, setNoDevice] = useState<boolean>(false); + useEffect(() => { const loadVolume = async () => { setLoading(true); const state = await getPlayerState(); if (state?.device) { setLocalVolume(state.device.volume_percent); setDeviceName(state.device.name); + } else { + setNoDevice(true); } setLoading(false); }; loadVolume(); }, []);Then render a message when
noDeviceis true.
136-156: Inline<style>tag works but consider CSS modules or Tailwind plugin.The inline style tag for slider thumb customization is functional but could be moved to a global stylesheet or handled via a Tailwind plugin for consistency. This is a minor stylistic concern given that other components in the codebase use similar patterns.
apps/desktop/src/ui/layouts/LayoutB.tsx (1)
43-61: Consider using CSS for hover state instead of React state.The
plusHoveredstate withonMouseEnter/onMouseLeavehandlers works, but the hover color change could be achieved purely with CSS, reducing React re-renders on hover.🔎 CSS-based alternative
-const [plusHovered, setPlusHovered] = useState<boolean>(false); ... <button type="button" onClick={() => { if (track && onAddToPlaylist) { onAddToPlaylist(track.id, track.name); } }} disabled={!track || !onAddToPlaylist} aria-label="Add to playlist" - className="w-8 h-8 flex items-center justify-center active:scale-[0.95] transition-all duration-150 disabled:opacity-30 disabled:cursor-not-allowed" - onMouseEnter={() => setPlusHovered(true)} - onMouseLeave={() => setPlusHovered(false)} + className="w-8 h-8 flex items-center justify-center active:scale-[0.95] transition-all duration-150 disabled:opacity-30 disabled:cursor-not-allowed group" > <PlusCircle size={24} weight="fill" - color={plusHovered ? "var(--player-controls-color-active)" : "var(--player-controls-color)"} + className="text-[--player-controls-color] group-hover:text-[--player-controls-color-active] transition-colors" /> </button>apps/desktop/src/ui/views/AIDJView.tsx (1)
108-114: Missing error handling forstartAIQueue.The
handleToggleQueuefunction awaitsstartAIQueue()but doesn't handle potential errors. While the AI queue store sets an error state internally, the UI might benefit from local error handling or at least a try-catch for unexpected failures.🔎 Suggested improvement
const handleToggleQueue = async () => { if (aiQueueActive) { stopAIQueue(); } else { - await startAIQueue(); + try { + await startAIQueue(); + } catch (err) { + console.error("Failed to start AI Queue:", err); + } } };apps/desktop/src/ui/views/Settings.tsx (1)
821-833: Consider extracting toggle switch to a reusable component.The toggle switch UI is duplicated between Discord RPC (lines 821-833) and AI Queue Border (lines 1101-1113). Extracting this to a shared
ToggleSwitchcomponent would improve maintainability.🔎 Example component
type ToggleSwitchProps = { enabled: boolean; onToggle: () => void; activeColor?: string; }; function ToggleSwitch({ enabled, onToggle, activeColor = "bg-[--settings-accent]" }: ToggleSwitchProps) { return ( <button type="button" onClick={onToggle} className={`relative w-10 h-5 rounded-full transition-colors duration-200 flex-shrink-0 ${ enabled ? activeColor : "bg-white/20" }`} > <span className={`absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-all duration-200 ${ enabled ? "translate-x-5" : "translate-x-0" }`} /> </button> ); }Also applies to: 1101-1113
apps/desktop/src/ui/views/PlaylistView.tsx (1)
116-125:playingIdcleared immediately may cause premature UI state reset.The
playingIdis set beforeplayTrackand cleared infinally, which runs immediately after the API call resolves. However, the track doesn't start playing instantly on Spotify. The spinner will disappear before the track actually begins playing, which may confuse users.Consider either:
- Using a timeout before clearing
- Not clearing
playingIdand letting the next track change update the state naturally- Polling player state briefly to confirm playback started
apps/desktop/src/lib/aiQueueStore.ts (1)
44-50: Potential mutation before state update insetQueue.The current implementation mutates the existing
playedUrisSet before creating a new one. While a new Set is created at the end, mutating the original could cause issues if other code holds a reference to it.🔎 Suggested fix
setQueue: (queue) => { - const playedUris = get().playedUris; - for (const track of queue) { - playedUris.add(track.uri); - } - set({ queue, currentIndex: 0, playedUris: new Set(playedUris) }); + const newPlayedUris = new Set(get().playedUris); + for (const track of queue) { + newPlayedUris.add(track.uri); + } + set({ queue, currentIndex: 0, playedUris: newPlayedUris }); },apps/desktop/src-tauri/src/discord_rpc.rs (1)
184-189: Reconnection doesn't retry setting the activity.When
set_activityfails and reconnection succeeds, the activity is not retried on the new client. The presence update is effectively lost for this call.Consider retrying the activity set after successful reconnection, or document that the next update will use the new client.
apps/desktop/src/ui/index.tsx (1)
229-238: Consider extracting inline styles to Tailwind classes.The hardcoded color values and box-shadow could be defined as CSS custom properties or Tailwind classes for better maintainability and theme consistency.
🔎 Example approach
{showBorder && ( <div - className="absolute inset-0 pointer-events-none z-50" - style={{ - border: "1.5px solid #7f1d1d", - borderRadius: "12px", - boxShadow: "inset 0 0 30px rgba(127, 29, 29, 0.4), inset 0 0 60px rgba(127, 29, 29, 0.15)", - }} + className="absolute inset-0 pointer-events-none z-50 rounded-xl border-[1.5px] border-red-900 shadow-[inset_0_0_30px_rgba(127,29,29,0.4),inset_0_0_60px_rgba(127,29,29,0.15)]" /> )}
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (6)
apps/desktop/src-tauri/Cargo.lockis excluded by!**/*.lockapps/desktop/src-tauri/gen/schemas/acl-manifests.jsonis excluded by!**/gen/**apps/desktop/src-tauri/gen/schemas/capabilities.jsonis excluded by!**/gen/**apps/desktop/src-tauri/gen/schemas/desktop-schema.jsonis excluded by!**/gen/**apps/desktop/src-tauri/gen/schemas/windows-schema.jsonis excluded by!**/gen/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (30)
.changeset/brave-playlists-flow.md.changeset/clever-llms-save.md.changeset/discord-rich-presence.md.changeset/smart-ai-context.md.changeset/smart-dj-queue.md.changeset/swift-api-optimizations.mdapps/desktop/package.jsonapps/desktop/src-tauri/Cargo.tomlapps/desktop/src-tauri/src/discord_rpc.rsapps/desktop/src-tauri/src/lib.rsapps/desktop/src-tauri/src/resize.rsapps/desktop/src-tauri/src/settings.rsapps/desktop/src-tauri/src/spotify_auth.rsapps/desktop/src/hooks/useCurrentlyPlaying.tsapps/desktop/src/hooks/useWindowLayout.tsapps/desktop/src/lib/aiClient.tsapps/desktop/src/lib/aiQueueService.tsapps/desktop/src/lib/aiQueueStore.tsapps/desktop/src/lib/settingLib.tsapps/desktop/src/lib/spotifyTools.tsapps/desktop/src/ui/components/TrackControls/PlaybackBar.tsxapps/desktop/src/ui/components/TrackControls/TrackControls.tsxapps/desktop/src/ui/index.tsxapps/desktop/src/ui/layouts/LayoutB.tsxapps/desktop/src/ui/spotifyClient.tsapps/desktop/src/ui/views/AIDJView.tsxapps/desktop/src/ui/views/AddToPlaylistView.tsxapps/desktop/src/ui/views/PlaylistView.tsxapps/desktop/src/ui/views/Settings.tsxapps/desktop/src/ui/views/VolumeView.tsx
🧰 Additional context used
📓 Path-based instructions (13)
.changeset/*.md
📄 CodeRabbit inference engine (.cursor/rules/020-changesets.mdc)
.changeset/*.md: Changeset markdown files must follow the format with frontmatter containing package name and version type, followed by description with maximum 99 characters per line
Usemajorversion type for breaking changes in changesets
Useminorversion type for new features in changesets
Usepatchversion type for bug fixes and improvements in changesets
Files:
.changeset/clever-llms-save.md.changeset/smart-dj-queue.md.changeset/swift-api-optimizations.md.changeset/brave-playlists-flow.md.changeset/discord-rich-presence.md.changeset/smart-ai-context.md
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/020-coding-style.mdc)
**/*.{ts,tsx}: NEVER useanytypes - Always provide proper type definitions in TypeScript
Useunknowninstead ofanywhen the type is truly unknown in TypeScript
Use interfaces for object shapes that will be extended in TypeScript
Use type aliases for complex types and unions in TypeScript
Use the latest TypeScript features appropriately
Document public APIs and interfaces in codeUse TypeScript strict types and avoid
anytype
Files:
apps/desktop/src/hooks/useWindowLayout.tsapps/desktop/src/ui/components/TrackControls/PlaybackBar.tsxapps/desktop/src/ui/views/Settings.tsxapps/desktop/src/ui/components/TrackControls/TrackControls.tsxapps/desktop/src/lib/aiQueueService.tsapps/desktop/src/hooks/useCurrentlyPlaying.tsapps/desktop/src/lib/settingLib.tsapps/desktop/src/ui/views/PlaylistView.tsxapps/desktop/src/ui/views/AddToPlaylistView.tsxapps/desktop/src/ui/views/AIDJView.tsxapps/desktop/src/lib/aiClient.tsapps/desktop/src/ui/views/VolumeView.tsxapps/desktop/src/lib/spotifyTools.tsapps/desktop/src/lib/aiQueueStore.tsapps/desktop/src/ui/layouts/LayoutB.tsxapps/desktop/src/ui/spotifyClient.tsapps/desktop/src/ui/index.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/020-coding-style.mdc)
**/*.{ts,tsx,js,jsx}: Avoid dynamic imports - use static imports at the top of files for better performance and code clarity
Write self-documenting code with meaningful names instead of extra comments
Avoid obvious comments that just repeat what the code does
Add comments only for complex logic, business rules, or non-obvious decisions
Use meaningful variable and function names instead of explanatory comments
Always use static imports at the top of files instead of dynamic imports
Import all dependencies at the file beginning for better bundling and performance
Use tree-shaking friendly named imports when possible
Use streaming APIs instead of loading large files into memory
Avoid buffer accumulation for large data processing
Implement proper cleanup for temporary files and streams
Set appropriate file size limits based on available memory
Maintain consistency with existing code style when modifying files
Keep code DRY (Don't Repeat Yourself)
Prefer verbose variable names and maintainability over concise code
Optimize for memory efficiency in data processing applicationsPlace static imports at the top of files
Files:
apps/desktop/src/hooks/useWindowLayout.tsapps/desktop/src/ui/components/TrackControls/PlaybackBar.tsxapps/desktop/src/ui/views/Settings.tsxapps/desktop/src/ui/components/TrackControls/TrackControls.tsxapps/desktop/src/lib/aiQueueService.tsapps/desktop/src/hooks/useCurrentlyPlaying.tsapps/desktop/src/lib/settingLib.tsapps/desktop/src/ui/views/PlaylistView.tsxapps/desktop/src/ui/views/AddToPlaylistView.tsxapps/desktop/src/ui/views/AIDJView.tsxapps/desktop/src/lib/aiClient.tsapps/desktop/src/ui/views/VolumeView.tsxapps/desktop/src/lib/spotifyTools.tsapps/desktop/src/lib/aiQueueStore.tsapps/desktop/src/ui/layouts/LayoutB.tsxapps/desktop/src/ui/spotifyClient.tsapps/desktop/src/ui/index.tsx
apps/desktop/src/**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/030-codebase-structure.mdc)
Keep React components focused with typed props
Files:
apps/desktop/src/hooks/useWindowLayout.tsapps/desktop/src/ui/components/TrackControls/PlaybackBar.tsxapps/desktop/src/ui/views/Settings.tsxapps/desktop/src/ui/components/TrackControls/TrackControls.tsxapps/desktop/src/lib/aiQueueService.tsapps/desktop/src/hooks/useCurrentlyPlaying.tsapps/desktop/src/lib/settingLib.tsapps/desktop/src/ui/views/PlaylistView.tsxapps/desktop/src/ui/views/AddToPlaylistView.tsxapps/desktop/src/ui/views/AIDJView.tsxapps/desktop/src/lib/aiClient.tsapps/desktop/src/ui/views/VolumeView.tsxapps/desktop/src/lib/spotifyTools.tsapps/desktop/src/lib/aiQueueStore.tsapps/desktop/src/ui/layouts/LayoutB.tsxapps/desktop/src/ui/spotifyClient.tsapps/desktop/src/ui/index.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/020-coding-style.mdc)
**/*.{tsx,jsx}: Use functional components with hooks in React
Use named exports for React components
Keep React components small and focused on a single responsibility
Use proper prop typing in React components
Use Tailwind CSS for styling in React applications
Follow component-based styling practices with Tailwind CSS
Use Tailwind CSS utility classes for one-off styling needs
Extract common styling patterns to shared components in React
Files:
apps/desktop/src/ui/components/TrackControls/PlaybackBar.tsxapps/desktop/src/ui/views/Settings.tsxapps/desktop/src/ui/components/TrackControls/TrackControls.tsxapps/desktop/src/ui/views/PlaylistView.tsxapps/desktop/src/ui/views/AddToPlaylistView.tsxapps/desktop/src/ui/views/AIDJView.tsxapps/desktop/src/ui/views/VolumeView.tsxapps/desktop/src/ui/layouts/LayoutB.tsxapps/desktop/src/ui/index.tsx
apps/desktop/src/ui/components/**
📄 CodeRabbit inference engine (.cursor/rules/030-codebase-structure.mdc)
Reusable UI components should be organized under
apps/desktop/src/ui/components/*
Files:
apps/desktop/src/ui/components/TrackControls/PlaybackBar.tsxapps/desktop/src/ui/components/TrackControls/TrackControls.tsx
apps/desktop/src-tauri/src/spotify_auth.rs
📄 CodeRabbit inference engine (.cursor/rules/030-codebase-structure.mdc)
Spotify authentication module should be located at
apps/desktop/src-tauri/src/spotify_auth.rs
Files:
apps/desktop/src-tauri/src/spotify_auth.rs
apps/desktop/src-tauri/src/**/*.rs
📄 CodeRabbit inference engine (.cursor/rules/030-codebase-structure.mdc)
Store secrets and tokens in OS keychain via
keyring
Files:
apps/desktop/src-tauri/src/spotify_auth.rsapps/desktop/src-tauri/src/resize.rsapps/desktop/src-tauri/src/discord_rpc.rsapps/desktop/src-tauri/src/lib.rsapps/desktop/src-tauri/src/settings.rs
apps/desktop/src/ui/views/**
📄 CodeRabbit inference engine (.cursor/rules/030-codebase-structure.mdc)
View components should be organized under
apps/desktop/src/ui/views/*
Files:
apps/desktop/src/ui/views/Settings.tsxapps/desktop/src/ui/views/PlaylistView.tsxapps/desktop/src/ui/views/AddToPlaylistView.tsxapps/desktop/src/ui/views/AIDJView.tsxapps/desktop/src/ui/views/VolumeView.tsx
apps/desktop/src-tauri/src/lib.rs
📄 CodeRabbit inference engine (.cursor/rules/030-codebase-structure.mdc)
Tauri library crate should be defined in
apps/desktop/src-tauri/src/lib.rs
Files:
apps/desktop/src-tauri/src/lib.rs
apps/desktop/src-tauri/src/settings.rs
📄 CodeRabbit inference engine (.cursor/rules/030-codebase-structure.mdc)
apps/desktop/src-tauri/src/settings.rs: Settings module implementation should be located atapps/desktop/src-tauri/src/settings.rs
Persist app settings under platform config directory as JSON
Files:
apps/desktop/src-tauri/src/settings.rs
apps/desktop/src/ui/layouts/**
📄 CodeRabbit inference engine (.cursor/rules/030-codebase-structure.mdc)
Layout components should be organized under
apps/desktop/src/ui/layouts/*
Files:
apps/desktop/src/ui/layouts/LayoutB.tsx
apps/desktop/src/ui/index.tsx
📄 CodeRabbit inference engine (.cursor/rules/030-codebase-structure.mdc)
UI root component should be located at
apps/desktop/src/ui/index.tsx
Files:
apps/desktop/src/ui/index.tsx
🧠 Learnings (11)
📚 Learning: 2025-12-29T21:56:02.721Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/030-codebase-structure.mdc:0-0
Timestamp: 2025-12-29T21:56:02.721Z
Learning: Applies to apps/desktop/src-tauri/src/spotify_auth.rs : Spotify authentication module should be located at `apps/desktop/src-tauri/src/spotify_auth.rs`
Applied to files:
apps/desktop/src-tauri/src/spotify_auth.rsapps/desktop/src-tauri/src/discord_rpc.rsapps/desktop/src-tauri/src/lib.rs
📚 Learning: 2025-12-29T21:56:02.721Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/030-codebase-structure.mdc:0-0
Timestamp: 2025-12-29T21:56:02.721Z
Learning: Applies to apps/desktop/src/ui/settingLib.ts : Settings helper utilities should be located at `apps/desktop/src/ui/settingLib.ts`
Applied to files:
apps/desktop/src/ui/views/Settings.tsxapps/desktop/src/lib/settingLib.ts
📚 Learning: 2025-12-29T21:56:02.721Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/030-codebase-structure.mdc:0-0
Timestamp: 2025-12-29T21:56:02.721Z
Learning: Applies to apps/desktop/src-tauri/src/settings.rs : Settings module implementation should be located at `apps/desktop/src-tauri/src/settings.rs`
Applied to files:
apps/desktop/src/lib/settingLib.tsapps/desktop/src-tauri/src/lib.rsapps/desktop/src-tauri/src/settings.rs
📚 Learning: 2025-12-29T21:56:02.721Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/030-codebase-structure.mdc:0-0
Timestamp: 2025-12-29T21:56:02.721Z
Learning: Applies to apps/desktop/src/ui/index.tsx : UI root component should be located at `apps/desktop/src/ui/index.tsx`
Applied to files:
apps/desktop/src/ui/views/PlaylistView.tsxapps/desktop/src/ui/views/AddToPlaylistView.tsxapps/desktop/src/ui/views/VolumeView.tsxapps/desktop/src/ui/index.tsx
📚 Learning: 2025-12-29T21:56:02.721Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/030-codebase-structure.mdc:0-0
Timestamp: 2025-12-29T21:56:02.721Z
Learning: Applies to apps/desktop/src/main.tsx : Entry point for desktop frontend is at `apps/desktop/src/main.tsx`
Applied to files:
apps/desktop/src/ui/views/PlaylistView.tsxapps/desktop/src/ui/index.tsx
📚 Learning: 2026-01-01T15:42:43.329Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/020-changesets.mdc:0-0
Timestamp: 2026-01-01T15:42:43.329Z
Learning: Applies to .changeset/*.md : Use `minor` version type for new features in changesets
Applied to files:
.changeset/discord-rich-presence.md.changeset/smart-ai-context.md
📚 Learning: 2025-12-29T21:56:02.721Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/030-codebase-structure.mdc:0-0
Timestamp: 2025-12-29T21:56:02.721Z
Learning: Applies to apps/desktop/src-tauri/src/lib.rs : Tauri library crate should be defined in `apps/desktop/src-tauri/src/lib.rs`
Applied to files:
apps/desktop/src-tauri/src/lib.rsapps/desktop/src-tauri/Cargo.toml
📚 Learning: 2025-12-29T21:56:02.721Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/030-codebase-structure.mdc:0-0
Timestamp: 2025-12-29T21:56:02.721Z
Learning: Applies to apps/desktop/src-tauri/src/main.rs : Tauri backend entry point should be at `apps/desktop/src-tauri/src/main.rs`
Applied to files:
apps/desktop/src-tauri/src/lib.rs
📚 Learning: 2025-12-29T21:55:52.410Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/020-coding-style.mdc:0-0
Timestamp: 2025-12-29T21:55:52.410Z
Learning: Use Tauri commands for system interactions in desktop applications
Applied to files:
apps/desktop/src-tauri/src/lib.rs
📚 Learning: 2025-12-29T21:56:02.721Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/030-codebase-structure.mdc:0-0
Timestamp: 2025-12-29T21:56:02.721Z
Learning: Applies to apps/desktop/src-tauri/src/settings.rs : Persist app settings under platform config directory as JSON
Applied to files:
apps/desktop/src-tauri/src/settings.rs
📚 Learning: 2025-12-29T21:56:02.721Z
Learnt from: CR
Repo: ModioStudio/MiniFy PR: 0
File: .cursor/rules/030-codebase-structure.mdc:0-0
Timestamp: 2025-12-29T21:56:02.721Z
Learning: Applies to apps/desktop/src-tauri/src/bin/clear.rs : CLI utility for clearing settings should be located at `apps/desktop/src-tauri/src/bin/clear.rs`
Applied to files:
apps/desktop/src-tauri/src/settings.rs
🧬 Code graph analysis (12)
apps/desktop/src/ui/components/TrackControls/PlaybackBar.tsx (1)
apps/desktop/src/ui/spotifyClient.ts (1)
seek(194-206)
apps/desktop/src-tauri/src/resize.rs (1)
apps/desktop/src/hooks/useWindowLayout.ts (1)
Layout(4-4)
apps/desktop/src/ui/views/Settings.tsx (1)
apps/desktop/src/lib/settingLib.ts (1)
writeSettings(131-139)
apps/desktop/src/ui/components/TrackControls/TrackControls.tsx (1)
apps/desktop/src/ui/spotifyClient.ts (4)
previousTrack(142-144)nextTrack(138-140)play(130-132)pause(134-136)
apps/desktop/src/hooks/useCurrentlyPlaying.ts (2)
apps/desktop/src/ui/spotifyClient.ts (1)
CurrentlyPlaying(26-30)apps/desktop/src/lib/aiQueueStore.ts (1)
useAIQueueStore(32-86)
apps/desktop/src/ui/views/PlaylistView.tsx (2)
apps/desktop/src/hooks/useWindowLayout.ts (1)
useWindowLayout(6-12)apps/desktop/src/ui/spotifyClient.ts (6)
SimplifiedPlaylist(417-429)SimplifiedTrack(18-24)fetchUserPlaylists(438-445)fetchPlaylistTracks(457-466)playTrack(234-239)getLargestImageUrl(213-217)
apps/desktop/src-tauri/src/discord_rpc.rs (1)
apps/desktop/src-tauri/src/settings.rs (1)
default(47-59)
apps/desktop/src/ui/views/AddToPlaylistView.tsx (2)
apps/desktop/src/hooks/useWindowLayout.ts (1)
useWindowLayout(6-12)apps/desktop/src/ui/spotifyClient.ts (3)
SimplifiedPlaylist(417-429)fetchUserPlaylists(438-445)addTrackToPlaylist(468-474)
apps/desktop/src/ui/views/AIDJView.tsx (3)
apps/desktop/src/ui/spotifyClient.ts (4)
fetchUserProfile(389-392)fetchCurrentlyPlaying(123-128)fetchRecentlyPlayed(248-263)fetchTopArtists(294-298)apps/desktop/src/lib/aiQueueStore.ts (1)
useAIQueueStore(32-86)apps/desktop/src/lib/aiQueueService.ts (2)
stopAIQueue(248-259)startAIQueue(218-246)
apps/desktop/src-tauri/src/lib.rs (3)
apps/desktop/src-tauri/src/discord_rpc.rs (7)
new(15-20)default(24-26)enable_discord_rpc(45-85)disable_discord_rpc(88-108)update_discord_presence(111-193)is_discord_rpc_enabled(196-202)init_discord_rpc(204-223)apps/desktop/src-tauri/src/settings.rs (1)
default(47-59)apps/desktop/src-tauri/src/custom_themes.rs (1)
validate_theme_json(183-187)
apps/desktop/src/ui/views/VolumeView.tsx (2)
apps/desktop/src/hooks/useWindowLayout.ts (1)
useWindowLayout(6-12)apps/desktop/src/ui/spotifyClient.ts (2)
getPlayerState(169-176)setVolume(178-192)
apps/desktop/src/ui/layouts/LayoutB.tsx (3)
apps/desktop/src/hooks/useWindowLayout.ts (1)
useWindowLayout(6-12)apps/desktop/src/hooks/useCurrentlyPlaying.ts (1)
useCurrentlyPlaying(22-86)apps/desktop/src/ui/components/LayoutTrackInfo/TrackInfoLayout.tsx (1)
TrackInfoLayout(17-48)
🔇 Additional comments (59)
.changeset/swift-api-optimizations.md (1)
1-19: Perfect! Changeset adheres to all guidelines.The frontmatter is properly formatted with the correct package name and
minorversion type (appropriate for new features). All description lines comply with the 99-character limit, and the content is well-structured and clear..changeset/smart-ai-context.md (1)
1-13: Changeset is well-structured and follows conventions.The
minorversion type is appropriate for these new features. The description clearly documents the AI DJ enhancements, privacy notice, and queue monitoring improvements. Based on learnings, minor version type is correct for new features in changesets..changeset/brave-playlists-flow.md (1)
1-12: Changeset correctly documents the playlist feature additions.The
minorversion type is appropriate for these new playlist browsing and add-to-playlist capabilities.apps/desktop/src/lib/aiClient.ts (2)
11-41: Well-documented AI Queue and TOON format integration in system prompt.The system prompt additions provide clear guidance for when to use AI Queue vs. single track playback. The TOON format documentation with examples will help the AI model understand the compact data format.
58-65: Strategy guidelines clearly distinguish continuous vs. single-track playback.Good prioritization of AI Queue for mood-based and continuous playback requests while preserving single-track functionality for specific song requests.
apps/desktop/src/lib/aiQueueService.ts (1)
261-320: Monitoring logic is well-implemented with appropriate safeguards.Good handling of:
- Auto-stop when user starts manual playback outside AI queue
- Proactive batch fetching when remaining tracks ≤ 2
- Proper cleanup of interval on stop
- Error isolation that doesn't crash the monitoring loop
apps/desktop/src-tauri/src/spotify_auth.rs (1)
361-361: OAuth scopes correctly expanded for playlist modification.The addition of
playlist-modify-publicandplaylist-modify-privatescopes enables the new add-to-playlist functionality. This aligns with the PR's playlist management features.Note: Existing users will need to re-authenticate to grant the new playlist modification permissions. Consider documenting this in release notes or triggering a re-auth flow when users first attempt to add tracks to playlists.
apps/desktop/package.json (1)
22-22: TOON format dependency added for token-efficient data encoding.The
@toon-format/toonpackage enables the compact data format used in the AI Queue and DJ features to reduce token usage when communicating with LLMs. The package is imported and used in aiQueueService.ts, spotifyTools.ts, and AIDJView.tsx.apps/desktop/src/ui/components/TrackControls/PlaybackBar.tsx (2)
62-100: LGTM! Correctly removed unnecessary async/await.The removal of
asyncfromhandlePointerandendDrag, along with the removal ofawaitonseek()calls, is correct. Theseek()function returnsvoidand uses a debounced fire-and-forget pattern, so there was never a Promise to await.
124-136: LGTM! Keyboard seek handling correctly simplified.The removal of
awaitonseek()calls within theonKeyDownhandler is correct and consistent with the other changes in this file.apps/desktop/src/hooks/useWindowLayout.ts (1)
4-4: LGTM! Layout type correctly extended for Volume view.The addition of
"Volume"to theLayoutunion is consistent with the corresponding Rust-sideLayout::Volumevariant inapps/desktop/src-tauri/src/resize.rs..changeset/clever-llms-save.md (1)
1-13: LGTM! Changeset format is correct.The changeset properly follows the required format with correct frontmatter (
"MiniFy": minor), appropriate version type for a new feature, and all description lines under the 99-character limit.apps/desktop/src-tauri/src/resize.rs (2)
12-12: LGTM! Volume variant correctly added to Layout enum.The
Volumevariant addition is consistent with the TypeScript-sideLayouttype inapps/desktop/src/hooks/useWindowLayout.ts(line 4).
25-25: LGTM! Volume layout dimensions are appropriate.The 300×280 logical size is reasonable for a volume control UI.
apps/desktop/src/ui/components/TrackControls/TrackControls.tsx (2)
12-18: LGTM! Correctly removed unnecessary async from navigation handlers.The removal of
asyncfromhandlePrevandhandleNextis correct. BothpreviousTrack()andnextTrack()returnvoidand use a fire-and-forget pattern (as shown inapps/desktop/src/ui/spotifyClient.ts), so there was never a Promise to await.
20-28: LGTM! Toggle handler correctly simplified.The removal of
asyncand the busy state fromhandleToggleis correct. Theplay()andpause()functions returnvoidand use fire-and-forget, making the async handling unnecessary overhead. The optimistic UI update pattern (callingonTogglePlayingbefore the API call) is consistent with the fire-and-forget approach used throughout the codebase..changeset/discord-rich-presence.md (1)
1-13: LGTM!The changeset is properly formatted with the correct
minorversion type for a new feature. All description lines are under the 99-character limit, and the content clearly documents the Discord Rich Presence integration..changeset/smart-dj-queue.md (1)
1-14: LGTM!The changeset follows the correct format with
minorversion type for the new AI Queue feature. All lines comply with the 99-character limit, and the feature description is clear and comprehensive.apps/desktop/src/lib/settingLib.ts (1)
58-59: LGTM!The new Settings fields are properly typed as booleans, consistent with TypeScript strict typing guidelines.
apps/desktop/src/hooks/useCurrentlyPlaying.ts (4)
1-3: LGTM!The new imports follow the coding guidelines by using static imports at the top of the file with proper named imports.
6-20: LGTM!The
updateDiscordPresencefunction correctly uses Tauri's invoke API and appropriately silences errors since Discord RPC is an optional feature that shouldn't break core functionality if it fails.
24-26: LGTM!The refs are properly typed and initialized for change detection. Note that on component remount, these refs will reset to their initial values, potentially triggering one redundant Discord presence update. This is acceptable behavior for this use case.
36-53: LGTM!The change detection logic correctly tracks changes to track ID, playing state, and AI Queue activity. The direct
getState()access on line 38 is appropriate here since the function runs on a polling interval, ensuring AI Queue state changes are detected on the next poll cycle (default 3 seconds). This is consistent with how track changes are detected throughout this hook.apps/desktop/src-tauri/src/lib.rs (4)
4-9: LGTM!The new module declarations for
custom_themesanddiscord_rpcare properly added to the public module list, maintaining consistency with the Tauri app structure guidelines.
12-17: LGTM!The Discord state is correctly created before the app builder and properly registered with
.manage()for dependency injection. This follows the proper initialization pattern for Tauri managed state.
46-51: LGTM!All new command handlers are properly registered with correct module namespacing. The Discord RPC handlers (
enable_discord_rpc,disable_discord_rpc,update_discord_presence,is_discord_rpc_enabled) and thevalidate_theme_jsonhandler are correctly formatted in the invoke handler list.
54-57: LGTM!The Discord RPC initialization is correctly placed in the setup hook after the app is built. The state is properly retrieved using
app.state::<discord_rpc::DiscordState>()and passed toinit_discord_rpcfor startup initialization. The placement after the token refresh task is reasonable.apps/desktop/src/ui/views/AddToPlaylistView.tsx (1)
30-43: Good implementation of data fetching with proper loading state management.The effect correctly handles loading state, error catching, and uses a finally block to ensure loading is reset. The empty dependency array is appropriate for a one-time fetch on mount.
apps/desktop/src-tauri/src/settings.rs (2)
18-26: Well-structured addition of new settings fields.The use of
#[serde(default = "default_true")]ensures backward compatibility when deserializing settings files that predate these fields. Thedefault_true()helper is appropriately scoped and reusable. The implementation aligns with the coding guideline to persist settings as JSON under the platform config directory.
56-57: Default implementation correctly mirrors serde defaults.Both the
Defaulttrait implementation and serde defaults are aligned, ensuring consistent behavior whether settings are created fresh or deserialized from incomplete JSON.apps/desktop/src/ui/views/VolumeView.tsx (1)
33-42: Good use ofuseCallbackfor event handlers.The handlers are properly memoized with empty dependency arrays since they only use state setters (which are stable) and
setVolume(imported function).apps/desktop/src/ui/layouts/LayoutB.tsx (1)
9-13: Good use of typed props with optional callback.The
LayoutBPropstype properly marksonAddToPlaylistas optional, allowing the component to be used without the add-to-playlist feature while maintaining type safety.apps/desktop/src/ui/views/AIDJView.tsx (2)
43-89: Well-designed context building with graceful error handling.The
buildUserContextfunction usesPromise.allwith individual.catch()handlers, ensuring partial context is still provided even if some API calls fail. The TOON encoding for track/artist data is a good optimization for token efficiency.One minor observation: the outer
try-catchat line 82 is redundant since all promises already have individual catch handlers, but it's harmless defensive code.
505-508: Safe optional chaining for next track display.Good use of optional chaining (
aiQueueTracks[aiQueueCurrentIndex + 1]?.name) to safely handle the case when no next track exists.apps/desktop/src/ui/views/Settings.tsx (3)
365-385: Good error handling for Discord RPC toggle.The
handleToggleDiscordRpcfunction properly persists the setting first, then attempts to invoke the Tauri command. Errors are caught and logged without breaking the UI. Consider whether the setting should be reverted on failure, but the current behavior (optimistic update with warning log) is acceptable for a non-critical feature.
1158-1205: Excellent privacy disclosure for AI DJ data usage.The privacy section clearly documents what user data is sent to LLM providers (display name, currently playing, recent tracks, top artists, time of day). This transparency is important for user trust and aligns with good privacy practices.
262-263: Good use of nullish coalescing for settings initialization.The
?? truefallback ensures backward compatibility when reading settings that may not have these new fields yet, though the serde defaults in Rust should handle this. The defensive approach is appropriate.apps/desktop/src/ui/views/PlaylistView.tsx (2)
255-259: Good implementation of infinite scroll with loading indicator.The scroll handler with a 150px threshold, combined with the loading spinner at the bottom of the list, provides a good user experience. The
loading="lazy"attribute on images is also a nice performance touch.Also applies to: 336-343
21-26: ExtractformatDurationto a shared utility file to avoid duplication.This function is duplicated in
SearchBar.tsxwith identical implementation. Additionally,PlaybackBar.tsxhas an equivalentmsToTimefunction that does the same thing. Extract this utility to a shared location (e.g.,apps/desktop/src/ui/utils.ts) and import it in all three components.apps/desktop/src/lib/aiQueueStore.ts (2)
77-85: Verify:reset()preservescachedUserProfileandlastFetchTime.The
reset()function doesn't clearcachedUserProfileorlastFetchTime. If this is intentional (to preserve the profile cache across queue resets), consider adding a comment. Otherwise, add these fields to the reset.
1-41: LGTM!Well-structured Zustand store with clean TypeScript interfaces. The
QueuedTrackinterface is properly exported, and the state shape is well-defined with appropriate types includingSet<string>for deduplication.apps/desktop/src-tauri/src/discord_rpc.rs (5)
9-27: LGTM!Clean struct design with appropriate thread-safe state management using
Mutex. TheDefaulttrait implementation delegates tonew()following Rust best practices.
29-42: LGTM!Helper functions are well-implemented with appropriate error mapping.
44-85: LGTM!The
enable_discord_rpccommand properly acquires locks in sequence and handles client creation with appropriate error propagation.
87-108: LGTM!The
disable_discord_rpccommand properly clears activity, closes the client, and resets state.
204-223: LGTM!The
init_discord_rpcfunction appropriately silences errors during initialization, as Discord may not be running. This is the correct behavior for optional integrations.apps/desktop/src/ui/index.tsx (4)
26-31: LGTM!Good TypeScript type definitions for
AppViewandAddToPlaylistTrack. The union type for views is clear and the nullable track type is well-defined.
96-122: LGTM!Context menu items are properly added for the new Playlist and Volume views, with consistent handling for both AI-enabled and non-AI menu variants.
204-221: LGTM!View rendering logic is clean with proper prop passing. The
AddToPlaylistViewcorrectly receives nullabletrackIdandtrackNameprops, and theonBackhandler properly clears the track state.
169-179: Verify:onAddToPlaylistonly passed to LayoutB.Only
LayoutBreceives theonAddToPlaylisthandler. If this is intentional (different layouts have different feature sets), this is fine. Otherwise, consider extending to other layouts.apps/desktop/src/lib/spotifyTools.ts (4)
28-51: LGTM!Well-implemented TOON format helpers with concise property names (
n,a,u,g,p,id) for token efficiency. The functions are pure and properly typed.
139-160: LGTM!Tool descriptions clearly document the TOON format output structure, helping AI models understand the response shape.
451-484: LGTM!The
startAIQueueWithMoodtool properly usesuseAIQueueStore.getState()to access the store outside React, includes appropriate pre-condition checking for active queues, and has good error handling.
486-522: LGTM!Both tools properly access store state and return appropriate status information. The
getAIQueueStatusprovides useful queue metadata including remaining tracks and next track preview.apps/desktop/src/ui/spotifyClient.ts (5)
32-47: LGTM!Good token caching implementation with a 1-minute buffer before expiry to prevent edge cases. The cache invalidation on 401 (line 82) ensures stale tokens are refreshed.
56-114: LGTM!Request deduplication is well-implemented with proper cleanup in
finally(). The cache key construction and GET-only deduplication are appropriate design choices.
146-206: LGTM!Debounced implementations for seek and volume are well-designed to reduce API flooding during slider interactions. Value clamping and timeout management are correct.
405-474: LGTM!New playlist and queue APIs are well-implemented with proper typing. The
fetchPlaylistTrackscorrectly filters out null tracks (which can occur for unavailable/deleted tracks in playlists).
116-121: LGTM!The
fireAndForgetpattern is appropriate for player commands where we don't need to block on the response. Error logging to console.warn provides visibility without breaking the UI.
| discord-rich-presence = "0.2" | ||
| log = "0.4" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check latest versions of added crates
echo "Checking discord-rich-presence:"
curl -s https://crates.io/api/v1/crates/discord-rich-presence | jq '.crate.max_stable_version'
echo "Checking log:"
curl -s https://crates.io/api/v1/crates/log | jq '.crate.max_stable_version'Repository: ModioStudio/MiniFy
Length of output: 124
🏁 Script executed:
# Search for usage of discord-rich-presence and log crates in the codebase
echo "=== Searching for discord-rich-presence usage ==="
rg "discord" --type rust -i
echo "=== Searching for log crate usage ==="
rg "use log" --type rustRepository: ModioStudio/MiniFy
Length of output: 3108
🏁 Script executed:
# Check if discord_rpc.rs module exists and examine it
fd "discord" --type fRepository: ModioStudio/MiniFy
Length of output: 103
Update discord-rich-presence to version 1.0.0; clarify purpose of log dependency.
The discord-rich-presence crate version 0.2 is severely outdated—the latest stable version is 1.0.0. Update this dependency to benefit from bug fixes, security patches, and API improvements. Additionally, the log crate has been added but appears unused in the codebase. Either integrate logging into the discord_rpc module or remove this unnecessary dependency.
🤖 Prompt for AI Agents
In apps/desktop/src-tauri/Cargo.toml around lines 40-41, update the
discord-rich-presence dependency from "0.2" to "1.0.0" to use the latest stable
release, and address the added log = "0.4" entry by either removing it from
Cargo.toml if unused or integrating it: add appropriate log calls (and a logger
initialization like env_logger or tracing) in the discord_rpc module so the
dependency is actually used; ensure Cargo.toml reflects the change and run cargo
update/build to verify compatibility with the new discord-rich-presence API.
- Removed unused splash screen and context menu components to streamline the UI. - Enhanced AI queue service with better error handling for AI suggestions parsing. - Updated playlist view to utilize a ref for tracking loaded tracks, improving performance and accuracy in loading more tracks. - Simplified keyboard shortcuts in the app for better user experience. - Cleaned up CSS by removing obsolete splash screen animations.
Description
Brief description of the changes in this PR.
Type of Change
Related Issues
Fixes #(issue number)
Closes #(issue number)
Related to #(issue number)
Changes Made
Testing
Screenshots (if applicable)
Add screenshots to help explain your changes.
Checklist
Additional Notes
Add any additional notes about the PR here.
Summary by CodeRabbit
New Features
Bug Fixes
Performance Improvements
✏️ Tip: You can customize this high-level summary in your review settings.