feat: player toolbar, across integration, content library#153
Conversation
|
Warning Review limit reached
More reviews will be available in 13 minutes and 17 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (39)
📝 WalkthroughWalkthroughIntroduces a persisted stream-panel store and placement-driven overlay stack; adds Apple Music and Blender catalog adapters; rewrites StreamPanel into a multi-overlay controller-based component; syncs preview expansion to URL; and integrates the new panel across block toolbars, previews, and audio/video players. ChangesUnified stream-panel refactor
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Pull request overview
This PR upgrades the docs-site player experience by introducing a richer Stream Panel (presets + playlists + saved/custom streams), integrating it into block previews and demo players, and updating the video-player block to support single-source loading in addition to playlist-backed loading.
Changes:
- Replaces the old docs dial store and basic Stream Panel UI with a persisted Stream Panel store + overlay stack (content overview, playlists, presets, saved/custom).
- Integrates Stream Panel controls into the block preview toolbar and home/demo players, including playlist preset loading and selection persistence.
- Updates the video-player block/docs to demonstrate
mediaProps={{ src }}single-source loading and hides the playlist menu when fewer than 2 items are queued.
Reviewed changes
Copilot reviewed 30 out of 31 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| bun.lock | Locks the newly added icon dependency. |
| apps/www/package.json | Adds @central-icons-react/... used by the new toolbar/overlays. |
| apps/www/registry/default/examples/player-root-demo.tsx | Wires Stream Panel sync for preset + playlist changes and adjusts panel placement. |
| apps/www/registry/default/blocks/video-player/page.tsx | Switches the block page from playlist demo to single-source mediaProps.src. |
| apps/www/registry/default/blocks/video-player/components/playlist.tsx | Hides playlist UI when the queue has fewer than 2 items. |
| apps/www/lib/stream-presets.ts | Tweaks preset display names used in the Stream Panel UI. |
| apps/www/lib/docs-dial-store.ts | Removes the old persisted docs dial Zustand store. |
| apps/www/content/docs/blocks/video-player.mdx | Updates block docs for single-source loading + clarifies behavior. |
| apps/www/components/stream-panel/use-stream-panel.ts | Adds the new persisted Stream Panel Zustand store (saved streams, selections, prefs). |
| apps/www/components/stream-panel/use-stream-panel-sync.ts | Adds playlist preset loading, selection restore, and Blender stream integration. |
| apps/www/components/stream-panel/saved-overlay.tsx | Updates Saved overlay UI and adds “Add Custom” affordance. |
| apps/www/components/stream-panel/provider.tsx | Expands provider to support a registered controller and shared open state. |
| apps/www/components/stream-panel/presets-overlay.tsx | Updates preset overlay UI and selection handling for overlay stack. |
| apps/www/components/stream-panel/playlists-overlay.tsx | New overlay for selecting playlist presets. |
| apps/www/components/stream-panel/panel-slider.tsx | Removes the unused slider component. |
| apps/www/components/stream-panel/panel-popover.tsx | Major Stream Panel UI refactor: overlay stack + new actions + new icons. |
| apps/www/components/stream-panel/panel-popover.config.ts | Centralizes Stream Panel overlay/position constants and prop types. |
| apps/www/components/stream-panel/overlay-shell.tsx | Refactors overlay shell for stacked overlays + animation/placement control. |
| apps/www/components/stream-panel/index.ts | Updates exports after removing slider. |
| apps/www/components/stream-panel/custom-overlay.tsx | Updates Custom overlay UI and switches to the new Stream Panel store. |
| apps/www/components/stream-panel/content-overview-overlay.tsx | New overlay: overview menu for Streams / Playlists / Live. |
| apps/www/components/stream-panel/content-catalog.ts | New content catalog + Blender playlist/stream fetching helpers. |
| apps/www/components/players/video-player/player-container.tsx | Integrates Stream Panel into the home video player with a selector trigger. |
| apps/www/components/players/video-player/demo-assets.ts | Removes hardcoded demo assets in favor of preset-driven selection. |
| apps/www/components/players/audio-player/hover-player.tsx | Wraps audio demo in StreamPanelProvider and syncs selections. |
| apps/www/components/players/audio-player/demo-player.tsx | Adds child slot + uses persisted selection to decide auto-load behavior. |
| apps/www/components/blocks/preview-pane.tsx | Persists “expanded” state in the URL and improves layout/animation handling. |
| apps/www/components/blocks/block-toolbar.tsx | Reworks toolbar to host Stream Panel content and register per-player controllers. |
| apps/www/components/blocks/block-showcase.tsx | Uses BlockStreamSync to provide stream selection within showcase players. |
| apps/www/app/layout.tsx | Changes head handling to use next/head (needs follow-up fix). |
| apps/www/app/blocks/[[...slug]]/page.tsx | Syncs expanded state early via inline script and adjusts panel transitions. |
| import { Inter } from "next/font/google" | ||
| import Head from "next/head" |
| <Head> | ||
| <UserJotProvider projectId="cmjs634l4043b15ldylgedgwi" /> | ||
| </head> | ||
| </Head> |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (6)
apps/www/components/stream-panel/content-catalog.ts (2)
98-100: ⚡ Quick winConsider adding fetch timeout for resilience.
The fetch call honors the
AbortSignalfor cancellation, but lacks an explicit timeout. If the Blender API becomes unresponsive, the request could hang until the caller aborts. Consider wrapping withAbortSignal.timeout()or setting a reasonable default timeout.⏱️ Example timeout pattern
export async function fetchBlenderStream( assetId: string, signal?: AbortSignal ): Promise<BlenderStreamResponse> { const timeoutSignal = AbortSignal.timeout(10000) // 10s timeout const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal const response = await fetch(`${BLENDER_API_BASE_URL}/stream/${assetId}`, { signal: combinedSignal, }) // ... }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/stream-panel/content-catalog.ts` around lines 98 - 100, The fetch to `${BLENDER_API_BASE_URL}/stream/${assetId}` in content-catalog.ts currently uses the provided signal but has no timeout; update the enclosing function (the fetch call that takes assetId and signal) to create a timeout signal (e.g., AbortSignal.timeout(10000) or configurable), combine it with the incoming signal via AbortSignal.any([...]) when present, and pass that combinedSignal to fetch so the request will abort after the timeout if the Blender API is unresponsive.
106-106: ⚖️ Poor tradeoffValidate API response shape to prevent runtime type errors.
The type assertion
as BlenderStreamResponseassumes the API returns the expected shape. If the API changes or returns malformed data, this could cause downstream runtime errors. Consider adding runtime validation (e.g., Zod schema) or defensive checks.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/stream-panel/content-catalog.ts` at line 106, The code currently asserts the fetch result with "return (await response.json()) as BlenderStreamResponse", which can cause runtime errors if the API returns an unexpected shape; replace the assertion with runtime validation: define a Zod schema (e.g., BlenderStreamResponseSchema) matching BlenderStreamResponse, call schema.parse or schema.safeParse on await response.json(), and handle validation failures (log/error/throw or return a safe fallback) so downstream code only receives a validated BlenderStreamResponse object; update the function that performs the fetch (the place using response and returning BlenderStreamResponse) to use this validation flow.apps/www/lib/stream-presets.ts (1)
203-340: ⚖️ Poor tradeoffConsider restoring format qualifiers for duplicate preset names.
Multiple presets now share identical display names:
- "Angel One" appears 3 times (lines 108, 203, 221)
- "Sintel 4K" appears 2 times (lines 118, 261)
- "Art of Motion" appears 2 times (lines 128, 271)
- "Big Buck Bunny" appears 3 times (lines 56, 307, 340)
While each preset has a unique
id, users selecting from a dropdown or list that displays only thenamefield will see duplicates and may struggle to differentiate between variants. Consider re-adding distinguishing qualifiers (e.g., "Angel One (Widevine)", "Angel One (ClearKey)") or ensuring the UI always displaysdescriptionorfeaturesalongside the name.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/lib/stream-presets.ts` around lines 203 - 340, Several presets in the presets array have duplicate name values which will be ambiguous in UI lists; update the name fields to include distinguishing qualifiers (e.g., append " (Widevine)", " (ClearKey)", " (HLS)" or resolution/format) for the relevant entries such as the Angel One presets (ids like shaka-angel-clearkey and shaka-angel-hls-widevine), the Sintel 4K entry (id shaka-sintel-widevine), and other duplicates (e.g., bitmovin-hls-aes128, mp4-bunny-progressive, dash-if-thumbnails) so the display name uniquely identifies the variant while keeping the existing unique id values intact.apps/www/components/blocks/preview-pane.tsx (1)
110-112: 💤 Low valuePotential hydration mismatch when
expanded=trueis in URL.The component initializes
expandedtofalse(Line 36), thenuseLayoutEffectreads from URL and may set it totrue. SinceuseLayoutEffectruns after hydration, this could cause a visual flash where the pane renders collapsed then immediately expands.Consider initializing from URL during SSR-safe initial render or using
useSyncExternalStorepattern for URL state to avoid the flash:💡 Suggested approach
// Initialize lazily from URL (client-only) const [expanded, setExpanded] = useState(() => { if (typeof window === "undefined") return false return getExpandedFromUrl() })This won't help SSR but prevents the double-render flash on client navigation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/blocks/preview-pane.tsx` around lines 110 - 112, The component currently initializes the expanded state to false and then updates it from the URL inside useLayoutEffect, causing a hydration/flash when expanded=true in the URL; change the initialization of the expanded state to derive its initial value from getExpandedFromUrl in a lazy useState initializer (checking typeof window to avoid SSR errors) so the initial render matches the URL, and retain the existing useLayoutEffect or remove redundant updates; update references to expanded, setExpanded, useLayoutEffect, and getExpandedFromUrl accordingly.apps/www/components/stream-panel/panel-popover.tsx (1)
59-59: ⚡ Quick winUse granular selectors for store state access.
Similar to
custom-overlay.tsx, this file accesses the entire store. Per coding guidelines, use granular selectors for each field and setter needed:const muted = useStreamPanelStore((s) => s.muted) const autoplay = useStreamPanelStore((s) => s.autoplay) const savedStreams = useStreamPanelStore((s) => s.savedStreams) const contentSelections = useStreamPanelStore((s) => s.contentSelections) const setMuted = useStreamPanelStore((s) => s.setMuted) const setAutoplay = useStreamPanelStore((s) => s.setAutoplay) const setContentSelection = useStreamPanelStore((s) => s.setContentSelection)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/stream-panel/panel-popover.tsx` at line 59, The file currently pulls the entire store via the store = useStreamPanelStore() call; replace that with granular selectors to avoid unnecessary re-renders by selecting only the fields and setters used (e.g., replace store usage with const muted = useStreamPanelStore(s => s.muted), const autoplay = useStreamPanelStore(s => s.autoplay), const savedStreams = useStreamPanelStore(s => s.savedStreams), const contentSelections = useStreamPanelStore(s => s.contentSelections), and the setters const setMuted = useStreamPanelStore(s => s.setMuted), const setAutoplay = useStreamPanelStore(s => s.setAutoplay), const setContentSelection = useStreamPanelStore(s => s.setContentSelection)); update any references that previously used store.* to use these new variables.Source: Coding guidelines
apps/www/components/stream-panel/custom-overlay.tsx (1)
27-27: ⚡ Quick winUse granular selectors for store state access.
Per coding guidelines, store access should use granular per-feature selectors like
useStreamPanelStore(s => s.field)rather than accessing the entire store. Consider selecting only the fields and setters needed:const customSrc = useStreamPanelStore((s) => s.customSrc) const customConfig = useStreamPanelStore((s) => s.customConfig) const setCustomSrc = useStreamPanelStore((s) => s.setCustomSrc) const setCustomConfig = useStreamPanelStore((s) => s.setCustomConfig) const saveStream = useStreamPanelStore((s) => s.saveStream)This prevents unnecessary re-renders when unrelated store fields change.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/stream-panel/custom-overlay.tsx` at line 27, Replace the single broad store access via useStreamPanelStore() with granular selectors that pull only the needed fields and setters to avoid extra re-renders: select customSrc, customConfig, setCustomSrc, setCustomConfig and saveStream individually using useStreamPanelStore(s => s.<field>) so components only subscribe to the pieces they use (refer to useStreamPanelStore, customSrc, customConfig, setCustomSrc, setCustomConfig, saveStream).Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/www/app/layout.tsx`:
- Line 10: Remove the incompatible next/head import from the App Router layout:
delete the import statement for "next/head" and any usage of the Head component
in app/layout.tsx (referenced by the import and any <Head> JSX). Instead rely on
the existing metadata export (and canonical App Router metadata APIs) to set
page title/meta; ensure no remaining references to the Head symbol remain in the
file.
- Around line 76-78: The layout currently uses the next/head <Head> component
which does not work in the App Router and causes UserJotProvider to silently
fail; fix by either reverting the <Head> usage to a raw <head> element in
layout.tsx so UserJotProvider(projectId="cmjs634l4043b15ldylgedgwi") is rendered
in the correct head, or move UserJotProvider out of <Head> into the top-level
<body> (or a client-side provider root) if the library supports body placement;
if unsure, consult UserJot's App Router integration docs for the recommended
placement and adjust the UserJotProvider location accordingly.
In `@apps/www/components/stream-panel/content-catalog.ts`:
- Around line 80-91: Replace the Promise.all that awaits adding captions so a
single failing caption doesn't abort playback: iterate stream.captions and for
each call player.addTextTrackAsync (referencing stream.captions and
player.addTextTrackAsync) and handle failures per-item (use Promise.allSettled
or attach .catch to each promise), log or warn on individual caption errors, and
continue without throwing so overall caption-loading is best-effort and does not
block main playback.
In `@apps/www/components/stream-panel/use-stream-panel.ts`:
- Around line 1-2: This module defines a client-only Zustand store (uses create
and persist and calls crypto.randomUUID()), so add the React "use client"
directive as the very first line of the file to force browser-only evaluation;
place the string "use client" above the existing imports (which reference create
and persist) so crypto.randomUUID() and the Zustand store initialization run
only on the client.
---
Nitpick comments:
In `@apps/www/components/blocks/preview-pane.tsx`:
- Around line 110-112: The component currently initializes the expanded state to
false and then updates it from the URL inside useLayoutEffect, causing a
hydration/flash when expanded=true in the URL; change the initialization of the
expanded state to derive its initial value from getExpandedFromUrl in a lazy
useState initializer (checking typeof window to avoid SSR errors) so the initial
render matches the URL, and retain the existing useLayoutEffect or remove
redundant updates; update references to expanded, setExpanded, useLayoutEffect,
and getExpandedFromUrl accordingly.
In `@apps/www/components/stream-panel/content-catalog.ts`:
- Around line 98-100: The fetch to `${BLENDER_API_BASE_URL}/stream/${assetId}`
in content-catalog.ts currently uses the provided signal but has no timeout;
update the enclosing function (the fetch call that takes assetId and signal) to
create a timeout signal (e.g., AbortSignal.timeout(10000) or configurable),
combine it with the incoming signal via AbortSignal.any([...]) when present, and
pass that combinedSignal to fetch so the request will abort after the timeout if
the Blender API is unresponsive.
- Line 106: The code currently asserts the fetch result with "return (await
response.json()) as BlenderStreamResponse", which can cause runtime errors if
the API returns an unexpected shape; replace the assertion with runtime
validation: define a Zod schema (e.g., BlenderStreamResponseSchema) matching
BlenderStreamResponse, call schema.parse or schema.safeParse on await
response.json(), and handle validation failures (log/error/throw or return a
safe fallback) so downstream code only receives a validated
BlenderStreamResponse object; update the function that performs the fetch (the
place using response and returning BlenderStreamResponse) to use this validation
flow.
In `@apps/www/components/stream-panel/custom-overlay.tsx`:
- Line 27: Replace the single broad store access via useStreamPanelStore() with
granular selectors that pull only the needed fields and setters to avoid extra
re-renders: select customSrc, customConfig, setCustomSrc, setCustomConfig and
saveStream individually using useStreamPanelStore(s => s.<field>) so components
only subscribe to the pieces they use (refer to useStreamPanelStore, customSrc,
customConfig, setCustomSrc, setCustomConfig, saveStream).
In `@apps/www/components/stream-panel/panel-popover.tsx`:
- Line 59: The file currently pulls the entire store via the store =
useStreamPanelStore() call; replace that with granular selectors to avoid
unnecessary re-renders by selecting only the fields and setters used (e.g.,
replace store usage with const muted = useStreamPanelStore(s => s.muted), const
autoplay = useStreamPanelStore(s => s.autoplay), const savedStreams =
useStreamPanelStore(s => s.savedStreams), const contentSelections =
useStreamPanelStore(s => s.contentSelections), and the setters const setMuted =
useStreamPanelStore(s => s.setMuted), const setAutoplay = useStreamPanelStore(s
=> s.setAutoplay), const setContentSelection = useStreamPanelStore(s =>
s.setContentSelection)); update any references that previously used store.* to
use these new variables.
In `@apps/www/lib/stream-presets.ts`:
- Around line 203-340: Several presets in the presets array have duplicate name
values which will be ambiguous in UI lists; update the name fields to include
distinguishing qualifiers (e.g., append " (Widevine)", " (ClearKey)", " (HLS)"
or resolution/format) for the relevant entries such as the Angel One presets
(ids like shaka-angel-clearkey and shaka-angel-hls-widevine), the Sintel 4K
entry (id shaka-sintel-widevine), and other duplicates (e.g.,
bitmovin-hls-aes128, mp4-bunny-progressive, dash-if-thumbnails) so the display
name uniquely identifies the variant while keeping the existing unique id values
intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f422db75-a47a-4a43-a0e1-035a87fc4ec3
⛔ Files ignored due to path filters (2)
apps/www/package.jsonis excluded by none and included by nonebun.lockis excluded by!**/*.lockand included by none
📒 Files selected for processing (29)
apps/www/app/blocks/[[...slug]]/page.tsxapps/www/app/layout.tsxapps/www/components/blocks/block-showcase.tsxapps/www/components/blocks/block-toolbar.tsxapps/www/components/blocks/preview-pane.tsxapps/www/components/players/audio-player/demo-player.tsxapps/www/components/players/audio-player/hover-player.tsxapps/www/components/players/video-player/demo-assets.tsapps/www/components/players/video-player/player-container.tsxapps/www/components/stream-panel/content-catalog.tsapps/www/components/stream-panel/content-overview-overlay.tsxapps/www/components/stream-panel/custom-overlay.tsxapps/www/components/stream-panel/index.tsapps/www/components/stream-panel/overlay-shell.tsxapps/www/components/stream-panel/panel-popover.config.tsapps/www/components/stream-panel/panel-popover.tsxapps/www/components/stream-panel/panel-slider.tsxapps/www/components/stream-panel/playlists-overlay.tsxapps/www/components/stream-panel/presets-overlay.tsxapps/www/components/stream-panel/provider.tsxapps/www/components/stream-panel/saved-overlay.tsxapps/www/components/stream-panel/use-stream-panel-sync.tsapps/www/components/stream-panel/use-stream-panel.tsapps/www/content/docs/blocks/video-player.mdxapps/www/lib/docs-dial-store.tsapps/www/lib/stream-presets.tsapps/www/registry/default/blocks/video-player/components/playlist.tsxapps/www/registry/default/blocks/video-player/page.tsxapps/www/registry/default/examples/player-root-demo.tsx
💤 Files with no reviewable changes (4)
- apps/www/components/stream-panel/index.ts
- apps/www/components/players/video-player/demo-assets.ts
- apps/www/lib/docs-dial-store.ts
- apps/www/components/stream-panel/panel-slider.tsx
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
apps/www/components/stream-panel/content-catalog.ts (2)
252-274: 💤 Low valueConsider adding timeout and validation consistency with Blender fetch.
fetchBlenderStreamuses both a timeout wrapper and zod validation, butfetchAppleMusicChartAssetshas neither. If the Apple Music API is slow or returns unexpected shapes, this could cause hangs or runtime errors.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/stream-panel/content-catalog.ts` around lines 252 - 274, The fetchAppleMusicChartAssets function lacks the timeout and response schema validation used in fetchBlenderStream; wrap the fetch call in the same timeout helper (the same pattern used by fetchBlenderStream), validate the parsed JSON against the AppleMusicChartsResponse zod schema (or create one if missing) before using chart.items, and throw a clear error when validation fails; update the function that calls getAppleMusicLocaleHeaders / uses APPLE_MUSIC_API_BASE_URL and keep mapping via toAppleMusicChartAsset only after successful validation to avoid hangs and runtime shape errors.
321-337:AppleMusicChartAsset.genreis repurposed as the secondary label (fallback)
IntoAppleMusicChartAsset(apps/www/components/stream-panel/content-catalog.ts),genre: item.artistNameis consistent with the fact thatAppleMusicChartItemdoesn’t include anygenrefield. The audio-player UI rendersasset.genreas an unlabeled secondary line under the track title (apps/www/registry/default/blocks/audio-player/components/track-info.tsx,playlist.tsx), so this mapping likely shows the artist when “genre” is unavailable.
If this reuse isn’t intended, omitgenrewhen Apple doesn’t provide it or update the UI/asset contract to renderartistNameseparately instead of overloadinggenre.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/stream-panel/content-catalog.ts` around lines 321 - 337, The mapping in toAppleMusicChartAsset currently sets AppleMusicChartAsset.genre = item.artistName which repurposes genre as a fallback label; instead, stop overloading genre: change to only set genre when a real genre exists (omit or set undefined/null when AppleMusicChartItem has no genre), and ensure artistName remains on artistName so the UI can render it explicitly if needed; update the toAppleMusicChartAsset function to leave genre unset when AppleMusicChartItem lacks genre information and keep artistName populated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/www/components/blocks/block-page-shell.tsx`:
- Around line 25-27: The mobile branch in block-page-shell.tsx currently returns
only info (the if (!isDesktop) return ...), which omits the preview; update the
non-desktop return to render both preview and info (e.g., stacked or column
layout) so the preview prop is not discarded. Locate the isDesktop check in the
BlockPageShell component and change the returned JSX to include both preview and
info (preserving existing classes like "w-full" and using a mobile-friendly
wrapper) so callers passing preview continue to see it on small screens.
In `@apps/www/components/blocks/preview-pane.tsx`:
- Around line 22-23: The effect that syncs the local expanded state reads
window.location.search but only depends on pathname, so query-only navigation
can desync expanded; change it to use Next's useSearchParams (or otherwise
derive from the router's search params) and include that searchParams value in
the effect dependencies, then compute expanded from searchParams.get('expanded')
(update the effect inside the component where expanded and the useLayoutEffect
are defined) so the effect re-runs on query changes and keeps expanded in sync.
In `@apps/www/components/stream-panel/content-catalog.ts`:
- Around line 252-274: fetchAppleMusicChartAssets is missing abort guards after
awaits; after the await fetch(...) and again after await response.json() check
the provided signal (signal?.aborted) and abort the function (e.g., throw new
DOMException('Aborted', 'AbortError') or return early) to mirror the pattern
used in fetchBlenderStream and the loaders in use-stream-panel-sync.ts, ensuring
you perform these checks before continuing to process response.ok and
chart.items.
---
Nitpick comments:
In `@apps/www/components/stream-panel/content-catalog.ts`:
- Around line 252-274: The fetchAppleMusicChartAssets function lacks the timeout
and response schema validation used in fetchBlenderStream; wrap the fetch call
in the same timeout helper (the same pattern used by fetchBlenderStream),
validate the parsed JSON against the AppleMusicChartsResponse zod schema (or
create one if missing) before using chart.items, and throw a clear error when
validation fails; update the function that calls getAppleMusicLocaleHeaders /
uses APPLE_MUSIC_API_BASE_URL and keep mapping via toAppleMusicChartAsset only
after successful validation to avoid hangs and runtime shape errors.
- Around line 321-337: The mapping in toAppleMusicChartAsset currently sets
AppleMusicChartAsset.genre = item.artistName which repurposes genre as a
fallback label; instead, stop overloading genre: change to only set genre when a
real genre exists (omit or set undefined/null when AppleMusicChartItem has no
genre), and ensure artistName remains on artistName so the UI can render it
explicitly if needed; update the toAppleMusicChartAsset function to leave genre
unset when AppleMusicChartItem lacks genre information and keep artistName
populated.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 794153bf-afed-41d8-953f-57f99472565f
📒 Files selected for processing (11)
apps/www/app/blocks/[[...slug]]/page.tsxapps/www/components/blocks/block-page-shell.tsxapps/www/components/blocks/preview-pane.tsxapps/www/components/players/audio-player/hover-player.tsxapps/www/components/stream-panel/content-catalog.tsapps/www/components/stream-panel/custom-overlay.tsxapps/www/components/stream-panel/panel-popover.tsxapps/www/components/stream-panel/use-stream-panel-sync.tsapps/www/components/stream-panel/use-stream-panel.tsapps/www/registry/default/blocks/audio-player/components/fixed-timeline-control.tsxapps/www/registry/default/hooks/use-volume.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/www/components/stream-panel/use-stream-panel.ts
- apps/www/components/stream-panel/panel-popover.tsx
- apps/www/components/stream-panel/custom-overlay.tsx
5cf35ff to
e8ed104
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/www/components/stream-panel/content-catalog.ts (2)
241-248:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winAdd abort guards and Zod validation for Blender playlist fetch.
The Blender playlist branch has multiple issues:
- Missing abort guards: No
throwIfAbortedchecks after thefetch(line 241) orresponse.json()(line 246) await points, violating the coding guideline for async load functions.- Unsafe type assertion: Line 246 uses type assertion instead of Zod validation, inconsistent with all other endpoints (
fetchAppleMusicChartAssetsusessafeParse,fetchBlenderStreamuses.parse). This bypasses runtime validation and can cause crashes from malformed API responses.- Missing timeout signal: Unlike
fetchAppleMusicChartAssetsandfetchBlenderStream, this fetch doesn't compose a timeout signal for resilience.🛡️ Proposed fix
First, add a Zod schema for the playlist response (place after line 165):
const BlenderPlaylistItemSchema = z.object({ description: z.string().optional(), duration: z.number().optional(), id: z.string(), images: BlenderOpenFilmImagesSchema.optional(), subtitle: z.string().optional(), title: z.string(), year: z.number().optional(), }) const BlenderPlaylistResponseSchema = z.object({ count: z.number(), items: z.array(BlenderPlaylistItemSchema), title: z.string(), })Then update the fetch logic:
if (playlistId !== BLENDER_OPEN_FILMS_PLAYLIST_ID) { throw new Error(`Unknown playlist preset "${playlistId}".`) } - const response = await fetch(`${BLENDER_API_BASE_URL}/playlist`, { signal }) + + const combinedSignal = createTimeoutSignal(signal, BLENDER_STREAM_TIMEOUT_MS) + const response = await fetch(`${BLENDER_API_BASE_URL}/playlist`, { + signal: combinedSignal, + }) + throwIfAborted(combinedSignal) + if (!response.ok) { throw new Error(`Failed to fetch Blender playlist: ${response.statusText}`) } - const playlist = (await response.json()) as BlenderPlaylistResponse + const responseJson: unknown = await response.json() + throwIfAborted(combinedSignal) + + const playlist = BlenderPlaylistResponseSchema.parse(responseJson) return playlist.items.map(toBlenderOpenFilmAsset) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/stream-panel/content-catalog.ts` around lines 241 - 248, The Blender playlist branch must add timeout signal composition, abort guards, and Zod validation: compose a timeout signal like other loaders (as used in fetchAppleMusicChartAssets/fetchBlenderStream), pass it into fetch, call throwIfAborted() immediately after the fetch and again after awaiting response.json(), and validate the parsed body with a new Zod schema (e.g., BlenderPlaylistResponseSchema/BlenderPlaylistItemSchema) using safeParse; on validation failure throw a descriptive error, and then map validated items with toBlenderOpenFilmAsset.Source: Coding guidelines
214-228:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winAdd abort guards after each
awaitinfetchBlenderStream.This function is missing abort checks after the
fetchandresponse.json()await points, violating the coding guideline to use abort guards after each await in async load functions. Without these guards, if the signal is aborted mid-execution, the function will continue processing stale data instead of terminating, creating a race condition.Compare to
fetchAppleMusicChartAssets(lines 291, 300), which properly includesthrowIfAbortedchecks after each await point.🛡️ Proposed fix
export async function fetchBlenderStream( assetId: string, signal?: AbortSignal ): Promise<BlenderStreamResponse> { const combinedSignal = createTimeoutSignal(signal, BLENDER_STREAM_TIMEOUT_MS) const response = await fetch(`${BLENDER_API_BASE_URL}/stream/${assetId}`, { signal: combinedSignal, }) + throwIfAborted(combinedSignal) if (!response.ok) { throw new Error(`Failed to fetch Blender stream: ${response.statusText}`) } - return BlenderStreamResponseSchema.parse(await response.json()) + const responseJson: unknown = await response.json() + throwIfAborted(combinedSignal) + + return BlenderStreamResponseSchema.parse(responseJson) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/components/stream-panel/content-catalog.ts` around lines 214 - 228, The fetchBlenderStream function needs abort guards after each await: after the fetch(...) await and after awaiting response.json(); use the same abort helper used elsewhere (e.g., throwIfAborted) with the combinedSignal created by createTimeoutSignal(BLENDER_STREAM_TIMEOUT_MS) to throw if aborted, and call it immediately after the fetch resolves and again before calling BlenderStreamResponseSchema.parse on the JSON result so the function stops instead of processing stale data.Source: Coding guidelines
apps/www/registry/collection/registry-blocks.ts (1)
8-15:⚠️ Potential issue | 🟠 MajorAdd
lucide-reactto thevideo-playerblockdependenciesinapps/www/registry/collection/registry-blocks.ts.
media-player.tsximportsRotateCwIconfrom"lucide-react", but thevideo-playerregistry block manifest doesn’t includelucide-reactin its npmdependencies, so installed builds can miss the package. Add"lucide-react"and rerunbun run registry:buildinapps/www/.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/registry/collection/registry-blocks.ts` around lines 8 - 15, The video-player block manifest is missing the lucide-react npm dependency while media-player.tsx imports RotateCwIcon from "lucide-react"; add "lucide-react" to the dependencies array for the video-player block in registry-blocks.ts (the same array containing "`@phosphor-icons/react`", "`@base-ui/react`", etc.), then rebuild the registry by running the registry build command (e.g., bun run registry:build).Sources: Coding guidelines, Learnings
🧹 Nitpick comments (1)
apps/www/registry/default/blocks/video-player/components/media-player.tsx (1)
116-121: ⚡ Quick winAvoid
useAsset()in this overlay; subscribe to only the fields you need.
PlayerErrorScreenonly needscurrentItemandloadAsset, butuseAsset()composes the broader asset/playlist API, so this component rerenders on unrelated state changes and breaks the selector-only access rule. Please switch this to the relevant store selectors directly.As per coding guidelines,
**/*.{ts,tsx}must use granular per-feature selectors likeuseXxxStore(s => s.field)for state access—never access entire slices. Based on learnings, store selectors must use the correctuseXxxStore(s => s.field)pattern instead of convenience hooks.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/www/registry/default/blocks/video-player/components/media-player.tsx` around lines 116 - 121, PlayerErrorScreen is using the broad useAsset() hook (currentItem, loadAsset) which causes unnecessary rerenders; replace it with granular store selectors—subscribe only to the fields you need via the per-feature selector pattern (e.g., useAssetStore(s => s.currentItem) and useAssetStore(s => s.loadAsset)) and update the retryStream callback to reference those selected values so the component no longer reads the entire asset/playlist slice.Sources: Coding guidelines, Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/www/components/blocks/preview-pane.tsx`:
- Around line 24-27: BlockPreviewWithToolbar calls the client-only hook
useSearchParams() but is rendered from static pages (via BlockPageShell /
block-showcase.tsx and the blocks layout) without a Suspense boundary; wrap the
preview subtree in a React.Suspense boundary so Next can hydrate client hooks
safely. Specifically, locate where BlockPreviewWithToolbar is injected (e.g., in
BlockPageShell or apps/www/app/blocks/layout.tsx / block-showcase.tsx) and wrap
that component (or its parent container) with <Suspense fallback={...}> to
provide a client-side boundary for useSearchParams(), ensuring the preview pane
can use client hooks without static render errors.
---
Outside diff comments:
In `@apps/www/components/stream-panel/content-catalog.ts`:
- Around line 241-248: The Blender playlist branch must add timeout signal
composition, abort guards, and Zod validation: compose a timeout signal like
other loaders (as used in fetchAppleMusicChartAssets/fetchBlenderStream), pass
it into fetch, call throwIfAborted() immediately after the fetch and again after
awaiting response.json(), and validate the parsed body with a new Zod schema
(e.g., BlenderPlaylistResponseSchema/BlenderPlaylistItemSchema) using safeParse;
on validation failure throw a descriptive error, and then map validated items
with toBlenderOpenFilmAsset.
- Around line 214-228: The fetchBlenderStream function needs abort guards after
each await: after the fetch(...) await and after awaiting response.json(); use
the same abort helper used elsewhere (e.g., throwIfAborted) with the
combinedSignal created by createTimeoutSignal(BLENDER_STREAM_TIMEOUT_MS) to
throw if aborted, and call it immediately after the fetch resolves and again
before calling BlenderStreamResponseSchema.parse on the JSON result so the
function stops instead of processing stale data.
In `@apps/www/registry/collection/registry-blocks.ts`:
- Around line 8-15: The video-player block manifest is missing the lucide-react
npm dependency while media-player.tsx imports RotateCwIcon from "lucide-react";
add "lucide-react" to the dependencies array for the video-player block in
registry-blocks.ts (the same array containing "`@phosphor-icons/react`",
"`@base-ui/react`", etc.), then rebuild the registry by running the registry build
command (e.g., bun run registry:build).
---
Nitpick comments:
In `@apps/www/registry/default/blocks/video-player/components/media-player.tsx`:
- Around line 116-121: PlayerErrorScreen is using the broad useAsset() hook
(currentItem, loadAsset) which causes unnecessary rerenders; replace it with
granular store selectors—subscribe only to the fields you need via the
per-feature selector pattern (e.g., useAssetStore(s => s.currentItem) and
useAssetStore(s => s.loadAsset)) and update the retryStream callback to
reference those selected values so the component no longer reads the entire
asset/playlist slice.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a7a7f85b-502c-48a7-afaa-04a0f64f6900
⛔ Files ignored due to path filters (2)
apps/www/package.jsonis excluded by none and included by nonebun.lockis excluded by!**/*.lockand included by none
📒 Files selected for processing (13)
apps/www/app/blocks/[[...slug]]/page.tsxapps/www/components/blocks/block-page-shell.tsxapps/www/components/blocks/preview-pane.tsxapps/www/components/cloud-search.tsxapps/www/components/markdown.tsxapps/www/components/players/video-player/player-container.tsxapps/www/components/stream-panel/content-catalog.tsapps/www/components/stream-panel/use-stream-panel-sync.tsapps/www/registry/collection/registry-blocks.tsapps/www/registry/default/blocks/video-player/components/media-player.tsxapps/www/registry/default/hooks/use-asset.tsapps/www/registry/default/hooks/use-playback.tsapps/www/registry/default/hooks/use-player.ts
💤 Files with no reviewable changes (2)
- apps/www/components/cloud-search.tsx
- apps/www/components/markdown.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/www/components/players/video-player/player-container.tsx
- apps/www/components/stream-panel/use-stream-panel-sync.ts
| const searchParams = useSearchParams() | ||
| const [expanded, setExpanded] = useState(() => { | ||
| return searchParams.get(EXPANDED_QUERY_PARAM) === "true" | ||
| }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Static route config ==="
sed -n '1,120p' 'apps/www/app/blocks/[[...slug]]/page.tsx'
echo
echo "=== BlockPreviewWithToolbar usages ==="
rg -n -C4 'BlockPreviewWithToolbar' --type=tsx
echo
echo "=== Suspense boundaries in related trees ==="
rg -n -C3 '\bSuspense\b' --type=tsx \
apps/www/app/blocks \
apps/www/components/blocks \
apps/www/components/players \
apps/www/registry/defaultRepository: WINOFFRG/limeplay
Length of output: 2871
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Static route config ==="
sed -n '1,160p' 'apps/www/app/blocks/[[...slug]]/page.tsx'
echo
echo "=== Locate BlockPreviewWithToolbar definition + usages ==="
rg -n "BlockPreviewWithToolbar" apps/www -g'*.ts' -g'*.tsx' -g'*.js' -g'*.jsx'
echo
echo "=== Locate Suspense boundaries around blocks/preview trees ==="
rg -n "\\bSuspense\\b" apps/www/app/blocks -g'*.ts' -g'*.tsx'
rg -n "\\bSuspense\\b" apps/www/components/blocks -g'*.ts' -g'*.tsx'
echo
echo "=== Inspect BlockPageShell implementation (likely caller boundary) ==="
ls -1 apps/www/components/blocks | sed -n '1,200p'
for f in \
apps/www/components/blocks/block-page-shell.tsx \
apps/www/components/blocks/preview-pane.tsx \
apps/www/components/blocks/.* 2>/dev/null; do
:
done
sed -n '1,240p' 'apps/www/components/blocks/block-page-shell.tsx' 2>/dev/null || true
sed -n '1,240p' 'apps/www/components/blocks/preview-pane.tsx' 2>/dev/null || trueRepository: WINOFFRG/limeplay
Length of output: 3429
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== preview-pane.tsx (full, expected small) ==="
sed -n '1,220p' apps/www/components/blocks/preview-pane.tsx
echo
echo "=== block-showcase.tsx (full, expected small) ==="
sed -n '1,240p' apps/www/components/blocks/block-showcase.tsx
echo
echo "=== block-page-shell.tsx (full, expected small) ==="
sed -n '1,260p' apps/www/components/blocks/block-page-shell.tsx
echo
echo "=== Suspense occurrences in the relevant block components ==="
rg -n "\\bSuspense\\b|<Suspense\\b|React\\.Suspense" apps/www/components/blocks -g'*.ts' -g'*.tsx'
echo
echo "=== Any Suspense in the immediate blocks route module tree ==="
rg -n "\\bSuspense\\b|<Suspense\\b|React\\.Suspense" apps/www/app/blocks -g'*.ts' -g'*.tsx' -SRepository: WINOFFRG/limeplay
Length of output: 9352
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Suspense boundaries in apps/www/app/blocks ==="
rg -n "\\bSuspense\\b|<Suspense\\b|React\\.Suspense" apps/www/app/blocks -g'*.ts' -g'*.tsx' || true
echo
echo "=== Route/layout files under apps/www/app/blocks (to inspect for Suspense) ==="
fd -t f -e '(png|jpg|jpeg|gif|webp|svg|ico|css)' 'layout.tsx' apps/www/app/blocks -d 6 || true
fd -t f -e '(png|jpg|jpeg|gif|webp|svg|ico|css)' 'layout.ts' apps/www/app/blocks -d 6 || true
fd -t f -e '(png|jpg|jpeg|gif|webp|svg|ico|css)' 'page.tsx' apps/www/app/blocks -d 6 || true
echo
echo "=== Inspect blocks route layouts (if any) ==="
for f in $(fd -t f 'layout.tsx' apps/www/app/blocks -d 6 2>/dev/null); do
echo "--- $f (first 200 lines) ---"
sed -n '1,200p' "$f"
doneRepository: WINOFFRG/limeplay
Length of output: 1308
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Suspense boundaries in apps/www/app ==="
rg -n "\\bSuspense\\b|<Suspense\\b|React\\.Suspense" apps/www/app -g'*.ts' -g'*.tsx' || true
echo
echo "=== Layout files above /blocks segment ==="
# print first-level layouts and any group layouts likely above blocks
find apps/www/app -maxdepth 4 -name 'layout.tsx' | sort | sed -n '1,200p'
echo
echo "=== Inspect top-level layouts likely involved ==="
for f in $(find apps/www/app -maxdepth 2 -name 'layout.tsx' | sort); do
echo "--- $f (first 220 lines) ---"
sed -n '1,220p' "$f"
doneRepository: WINOFFRG/limeplay
Length of output: 5079
Wrap the blocks useSearchParams() preview in Suspense
BlockPreviewWithToolbar(apps/www/components/blocks/preview-pane.tsx, lines ~24-27 and ~65-67) callsuseSearchParams().- It’s rendered on the static blocks route (
apps/www/app/blocks/[[...slug]]/page.tsx,dynamic = "force-static") viaBlockPageShell/block-showcase.tsx. - There’s no
<Suspense>boundary wrapping the preview subtree inBlockPageShellorapps/www/app/blocks/layout.tsx, so Next may bail out / error foruseSearchParams()on static rendering.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/www/components/blocks/preview-pane.tsx` around lines 24 - 27,
BlockPreviewWithToolbar calls the client-only hook useSearchParams() but is
rendered from static pages (via BlockPageShell / block-showcase.tsx and the
blocks layout) without a Suspense boundary; wrap the preview subtree in a
React.Suspense boundary so Next can hydrate client hooks safely. Specifically,
locate where BlockPreviewWithToolbar is injected (e.g., in BlockPageShell or
apps/www/app/blocks/layout.tsx / block-showcase.tsx) and wrap that component (or
its parent container) with <Suspense fallback={...}> to provide a client-side
boundary for useSearchParams(), ensuring the preview pane can use client hooks
without static render errors.
There was a problem hiding this comment.
4 issues found across 41 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/www/components/stream-panel/use-stream-panel.ts">
<violation number="1" location="apps/www/components/stream-panel/use-stream-panel.ts:74">
P2: `savedStreams` from storage is not validated; non-array persisted data can break save/remove actions at runtime.</violation>
</file>
<file name="apps/www/components/blocks/preview-pane.tsx">
<violation number="1" location="apps/www/components/blocks/preview-pane.tsx:83">
P2: Expanded/collapsed layout no longer animates; motion animate values are constant. This regresses the spring transition to an abrupt className layout switch.</violation>
</file>
<file name="apps/www/registry/collection/registry-ui.ts">
<violation number="1" location="apps/www/registry/collection/registry-ui.ts:16">
P1: Registry dependency "item" does not exist — no component with that name is registered in any collection file.</violation>
</file>
<file name="apps/www/components/stream-panel/use-stream-panel-sync.ts">
<violation number="1" location="apps/www/components/stream-panel/use-stream-panel-sync.ts:233">
P2: Stale persisted selection can prevent any initial media load. When preset lookup fails and `selection.src` is missing, restore exits without falling back to a default.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Fix all with cubic | Re-trigger cubic
| ], | ||
| name: "error-screen", | ||
| registryDependencies: ["media-provider"], | ||
| registryDependencies: ["media-provider", "item"], |
There was a problem hiding this comment.
P1: Registry dependency "item" does not exist — no component with that name is registered in any collection file.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/www/registry/collection/registry-ui.ts, line 16:
<comment>Registry dependency "item" does not exist — no component with that name is registered in any collection file.</comment>
<file context>
@@ -12,7 +13,7 @@ export const ui: Registry["items"] = [
],
name: "error-screen",
- registryDependencies: ["media-provider"],
+ registryDependencies: ["media-provider", "item"],
type: "registry:ui",
},
</file context>
| persistedState.contentSelections = contentSelections | ||
| } | ||
| if (muted !== undefined) persistedState.muted = muted | ||
| if (savedStreams !== undefined) persistedState.savedStreams = savedStreams |
There was a problem hiding this comment.
P2: savedStreams from storage is not validated; non-array persisted data can break save/remove actions at runtime.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/www/components/stream-panel/use-stream-panel.ts, line 74:
<comment>`savedStreams` from storage is not validated; non-array persisted data can break save/remove actions at runtime.</comment>
<file context>
@@ -0,0 +1,142 @@
+ persistedState.contentSelections = contentSelections
+ }
+ if (muted !== undefined) persistedState.muted = muted
+ if (savedStreams !== undefined) persistedState.savedStreams = savedStreams
+ if (volume !== undefined) persistedState.volume = volume
+
</file context>
| <div className="relative flex flex-1 flex-col overflow-hidden rounded-2xl bg-muted"> | ||
| <LazyMotion features={domAnimation}> | ||
| <m.div | ||
| animate={{ opacity: 1, scale: 1 }} |
There was a problem hiding this comment.
P2: Expanded/collapsed layout no longer animates; motion animate values are constant. This regresses the spring transition to an abrupt className layout switch.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/www/components/blocks/preview-pane.tsx, line 83:
<comment>Expanded/collapsed layout no longer animates; motion animate values are constant. This regresses the spring transition to an abrupt className layout switch.</comment>
<file context>
@@ -82,55 +76,52 @@ export function BlockPreviewWithToolbar({
- <div className="relative flex flex-1 flex-col overflow-hidden rounded-2xl bg-muted">
+ <LazyMotion features={domAnimation}>
+ <m.div
+ animate={{ opacity: 1, scale: 1 }}
+ className={cn(
+ `
</file context>
| animate={{ opacity: 1, scale: 1 }} | |
| layout |
| return | ||
| } | ||
|
|
||
| if (!selection.src) return |
There was a problem hiding this comment.
P2: Stale persisted selection can prevent any initial media load. When preset lookup fails and selection.src is missing, restore exits without falling back to a default.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/www/components/stream-panel/use-stream-panel-sync.ts, line 233:
<comment>Stale persisted selection can prevent any initial media load. When preset lookup fails and `selection.src` is missing, restore exits without falling back to a default.</comment>
<file context>
@@ -57,63 +148,232 @@ export function useStreamPanelSync() {
+ return
+ }
+
+ if (!selection.src) return
+ abortPlaylistRequest()
+ loadCustomStream(selection.src, selection.config, selection.id)
</file context>
Summary by CodeRabbit
New Features
Improvements
Documentation