From f78fc855effd85208dec04d3258f862d49247a37 Mon Sep 17 00:00:00 2001 From: winoffrg Date: Mon, 29 Jun 2026 00:42:01 +0530 Subject: [PATCH 1/6] feat: blocks revamp, registry cleanup --- README.md | 2 +- apps/www/app/(home)/layout.tsx | 7 +- apps/www/app/(home)/page.tsx | 13 +- apps/www/app/eslint.css | 5 + apps/www/app/global.css | 146 +++++- apps/www/app/limeplay.css | 77 ---- .../components/blocks/block-page-shell.tsx | 2 +- apps/www/components/blocks/block-showcase.tsx | 6 +- .../components/blocks/block-stream-sync.tsx | 40 ++ apps/www/components/blocks/block-toolbar.tsx | 33 -- apps/www/components/component-preview.tsx | 2 +- apps/www/components/hero-buttons.tsx | 6 +- apps/www/components/hero.tsx | 6 +- .../components/immersive-scroll-player.tsx | 19 +- .../player-root-demo.tsx | 0 .../players/audio-player/demo-player.tsx | 4 +- .../components/players/audio-player/demo.ts | 2 +- .../players/video-player/player-container.tsx | 152 ++---- .../stream-panel/content-catalog.ts | 432 ------------------ .../stream-panel/panel-popover.config.ts | 4 +- .../components/stream-panel/panel-popover.tsx | 2 +- .../stream-panel/playlists-overlay.tsx | 6 +- apps/www/components/stream-panel/provider.tsx | 4 +- .../stream-panel/use-stream-panel-sync.ts | 48 +- apps/www/content/docs/blocks/audio-player.mdx | 4 +- apps/www/content/docs/blocks/video-player.mdx | 6 +- .../content/docs/components/player-layout.mdx | 2 +- .../docs/components/timeline-control.mdx | 2 +- .../docs/components/volume-control.mdx | 2 +- apps/www/content/docs/concepts.mdx | 2 +- apps/www/content/docs/hooks/index.mdx | 13 +- apps/www/content/docs/hooks/meta.json | 1 + apps/www/content/docs/hooks/use-asset.mdx | 11 +- .../docs/hooks/use-controls-visibility.mdx | 53 +++ .../docs/hooks/use-playback-source.mdx | 10 +- apps/www/content/docs/usage.mdx | 8 +- apps/www/eslint.config.mjs | 14 +- apps/www/lib/catalogs/apple-music.ts | 208 +++++++++ apps/www/lib/catalogs/blender-open-films.ts | 187 ++++++++ apps/www/lib/catalogs/index.ts | 5 + apps/www/lib/catalogs/player-assets.ts | 121 +++++ apps/www/lib/catalogs/playlists.ts | 56 +++ apps/www/lib/catalogs/types.ts | 20 + apps/www/lib/catalogs/utils.ts | 17 + apps/www/lib/stream-presets.ts | 1 - apps/www/lib/utils.ts | 7 +- apps/www/next-env.d.ts | 2 +- apps/www/package.json | 3 +- .../registry/collection/registry-blocks.ts | 36 +- .../registry/collection/registry-examples.ts | 35 +- .../www/registry/collection/registry-hooks.ts | 12 + apps/www/registry/collection/registry-ui.ts | 23 - .../audio-player/audio-player.module.css | 35 -- .../audio-player/components/audio-source.tsx | 32 +- .../components/fixed-timeline-control.tsx | 2 +- .../audio-player/components/playlist.tsx | 7 +- .../audio-player/components/track-info.tsx | 11 +- .../audio-player/hooks/use-playlist-asset.ts | 2 +- .../media-player.tsx => player.tsx} | 73 +-- .../default/blocks/audio-player/styles.css | 74 +++ .../components/asset-metadata-overlay.tsx | 46 -- .../components/bottom-controls.tsx | 58 ++- .../blocks/video-player/components/button.tsx | 47 +- .../components/captions-state-control.tsx | 2 +- .../video-player/components/media-player.tsx | 134 ------ .../video-player/components/pip-control.tsx | 2 +- .../components/playback-rate-control.tsx | 19 +- .../components/player-error-screen.tsx | 69 +++ .../components/player-root-container.tsx | 47 ++ .../playlist-navigation-controls.tsx | 8 +- .../video-player/components/playlist.tsx | 2 +- .../components/timeline-slider-control.tsx | 74 +-- .../components/top-overlay-container.tsx | 51 +++ .../components/volume-group-control.tsx | 31 +- .../components/volume-slider-control.tsx | 46 +- .../default/blocks/video-player/page.tsx | 12 - .../default/blocks/video-player/player.tsx | 169 +++++++ .../default/blocks/video-player/styles.css | 48 ++ .../picture-in-picture-control-demo.tsx | 2 +- .../examples/timeline-control-demo.tsx | 2 +- .../default/examples/timeline-labels-demo.tsx | 2 +- .../volume-slider-control-horizontal-demo.tsx | 2 +- .../volume-slider-control-vertical-demo.tsx | 2 +- apps/www/registry/default/hooks/use-asset.ts | 235 +++++----- .../default/hooks/use-controls-visibility.ts | 144 ++++++ .../default/hooks/use-playback-source.ts | 29 +- .../registry/default/hooks/use-timeline.ts | 9 +- apps/www/registry/default/lib/utils.ts | 7 +- .../www/registry/default/ui/player-layout.tsx | 48 +- .../registry/default/ui/root-container.tsx | 124 +---- .../www/registry/default/ui/seek-controls.tsx | 6 +- .../registry/default/ui/timeline-control.tsx | 2 +- .../registry/default/ui/timeline-labels.tsx | 2 +- .../registry/default/ui/volume-control.tsx | 1 + apps/www/scripts/test-registry-install.ts | 4 +- bun.lock | 5 +- 96 files changed, 2106 insertions(+), 1500 deletions(-) create mode 100644 apps/www/app/eslint.css delete mode 100644 apps/www/app/limeplay.css create mode 100644 apps/www/components/blocks/block-stream-sync.tsx rename apps/www/{registry/default/examples => components}/player-root-demo.tsx (100%) delete mode 100644 apps/www/components/stream-panel/content-catalog.ts create mode 100644 apps/www/content/docs/hooks/use-controls-visibility.mdx create mode 100644 apps/www/lib/catalogs/apple-music.ts create mode 100644 apps/www/lib/catalogs/blender-open-films.ts create mode 100644 apps/www/lib/catalogs/index.ts create mode 100644 apps/www/lib/catalogs/player-assets.ts create mode 100644 apps/www/lib/catalogs/playlists.ts create mode 100644 apps/www/lib/catalogs/types.ts create mode 100644 apps/www/lib/catalogs/utils.ts delete mode 100644 apps/www/registry/default/blocks/audio-player/audio-player.module.css rename apps/www/registry/default/blocks/audio-player/{components/media-player.tsx => player.tsx} (57%) create mode 100644 apps/www/registry/default/blocks/audio-player/styles.css delete mode 100644 apps/www/registry/default/blocks/video-player/components/asset-metadata-overlay.tsx delete mode 100644 apps/www/registry/default/blocks/video-player/components/media-player.tsx create mode 100644 apps/www/registry/default/blocks/video-player/components/player-error-screen.tsx create mode 100644 apps/www/registry/default/blocks/video-player/components/player-root-container.tsx create mode 100644 apps/www/registry/default/blocks/video-player/components/top-overlay-container.tsx delete mode 100644 apps/www/registry/default/blocks/video-player/page.tsx create mode 100644 apps/www/registry/default/blocks/video-player/player.tsx create mode 100644 apps/www/registry/default/blocks/video-player/styles.css create mode 100644 apps/www/registry/default/hooks/use-controls-visibility.ts diff --git a/README.md b/README.md index 9b464f57..722850a4 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ npx shadcn add @limeplay/video-player Use it anywhere in your app: ```tsx -import { VideoPlayer } from "@/components/limeplay/video-player/components/media-player" +import { VideoPlayer } from "@/components/limeplay/video-player/player" export function Player() { return ( diff --git a/apps/www/app/(home)/layout.tsx b/apps/www/app/(home)/layout.tsx index ee8cdee0..0cf03ffd 100644 --- a/apps/www/app/(home)/layout.tsx +++ b/apps/www/app/(home)/layout.tsx @@ -18,9 +18,10 @@ export default function RootLayout({ /> )}
diff --git a/apps/www/app/(home)/page.tsx b/apps/www/app/(home)/page.tsx index 6a894247..1383a27d 100644 --- a/apps/www/app/(home)/page.tsx +++ b/apps/www/app/(home)/page.tsx @@ -1,5 +1,3 @@ -import { Suspense } from "react" - import { FeatureTrailSection } from "@/components/feature-trail-section" import { FeatureGrid } from "@/components/features" import { Hero } from "@/components/hero" @@ -13,16 +11,7 @@ export default function Home() { <> - + diff --git a/apps/www/app/eslint.css b/apps/www/app/eslint.css new file mode 100644 index 00000000..96646935 --- /dev/null +++ b/apps/www/app/eslint.css @@ -0,0 +1,5 @@ +@import "./global.css"; +@import "./docs/docs.css"; +@import "./blocks/blocks.css"; +@import "../registry/default/blocks/video-player/styles.css"; +@import "../registry/default/blocks/audio-player/styles.css"; diff --git a/apps/www/app/global.css b/apps/www/app/global.css index cf9e965f..af397c30 100644 --- a/apps/www/app/global.css +++ b/apps/www/app/global.css @@ -2,7 +2,6 @@ @import "tw-animate-css"; @import "./shadcn.css"; -@import "./limeplay.css"; @custom-variant dark (&:is(.dark *)); @@ -46,3 +45,148 @@ @utility focus-ring { @apply focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary/50; } + +@utility hit-area-debug { + position: relative; + &::before { + content: ""; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + @apply border border-dashed border-blue-500 bg-blue-500/10; + } + &:hover::before { + @apply border border-dashed border-green-500 bg-green-500/10; + } +} + +@utility hit-area { + position: relative; + &::before { + content: ""; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-* { + position: relative; + --hit-area-t: --spacing(--value(number) * -1); + --hit-area-t: calc(--value([*]) * -1); + --hit-area-b: --spacing(--value(number) * -1); + --hit-area-b: calc(--value([*]) * -1); + --hit-area-l: --spacing(--value(number) * -1); + --hit-area-l: calc(--value([*]) * -1); + --hit-area-r: --spacing(--value(number) * -1); + --hit-area-r: calc(--value([*]) * -1); + &::before { + content: ""; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-l-* { + position: relative; + --hit-area-l: --spacing(--value(number) * -1); + --hit-area-l: calc(--value([*]) * -1); + &::before { + content: ""; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-r-* { + position: relative; + --hit-area-r: --spacing(--value(number) * -1); + --hit-area-r: calc(--value([*]) * -1); + &::before { + content: ""; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-t-* { + position: relative; + --hit-area-t: --spacing(--value(number) * -1); + --hit-area-t: calc(--value([*]) * -1); + &::before { + content: ""; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-b-* { + position: relative; + --hit-area-b: --spacing(--value(number) * -1); + --hit-area-b: calc(--value([*]) * -1); + &::before { + content: ""; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-x-* { + position: relative; + --hit-area-l: --spacing(--value(number) * -1); + --hit-area-l: calc(--value([*]) * -1); + --hit-area-r: --spacing(--value(number) * -1); + --hit-area-r: calc(--value([*]) * -1); + &::before { + content: ""; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-y-* { + position: relative; + --hit-area-t: --spacing(--value(number) * -1); + --hit-area-t: calc(--value([*]) * -1); + --hit-area-b: --spacing(--value(number) * -1); + --hit-area-b: calc(--value([*]) * -1); + &::before { + content: ""; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} diff --git a/apps/www/app/limeplay.css b/apps/www/app/limeplay.css deleted file mode 100644 index 61ec4526..00000000 --- a/apps/www/app/limeplay.css +++ /dev/null @@ -1,77 +0,0 @@ -:root { - --lp-timeline-track-height: 4px; - --lp-timeline-track-height-active: 7px; - --lp-transition-ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94); - --background-image-lp-controls-fade: linear-gradient( - to bottom, - hsla(0, 0%, 91%, 0) 0%, - hsla(0, 0%, 91%, 0.002) 10.6%, - hsla(0, 0%, 91%, 0.008) 19.7%, - hsla(0, 0%, 91%, 0.019) 27.6%, - hsla(0, 0%, 91%, 0.035) 34.4%, - hsla(0, 0%, 91%, 0.057) 40.4%, - hsla(0, 0%, 91%, 0.086) 45.7%, - hsla(0, 0%, 91%, 0.122) 50.6%, - hsla(0, 0%, 91%, 0.165) 55.3%, - hsla(0, 0%, 91%, 0.217) 60%, - hsla(0, 0%, 91%, 0.278) 65%, - hsla(0, 0%, 91%, 0.349) 70.3%, - hsla(0, 0%, 91%, 0.43) 76.3%, - hsla(0, 0%, 91%, 0.522) 83.1%, - hsla(0, 0%, 91%, 0.625) 90.9%, - hsla(0, 0%, 91%, 0.74) 100% - ); -} - -.dark { - --background-image-lp-controls-fade: linear-gradient( - to bottom, - hsla(0, 0%, 0%, 0) 0%, - hsla(0, 0%, 0%, 0.009) 8.1%, - hsla(0, 0%, 0%, 0.035) 15.5%, - hsla(0, 0%, 0%, 0.074) 22.5%, - hsla(0, 0%, 0%, 0.125) 29%, - hsla(0, 0%, 0%, 0.184) 35.3%, - hsla(0, 0%, 0%, 0.25) 41.2%, - hsla(0, 0%, 0%, 0.32) 47.1%, - hsla(0, 0%, 0%, 0.39) 52.9%, - hsla(0, 0%, 0%, 0.46) 58.8%, - hsla(0, 0%, 0%, 0.526) 64.7%, - hsla(0, 0%, 0%, 0.585) 71%, - hsla(0, 0%, 0%, 0.636) 77.5%, - hsla(0, 0%, 0%, 0.675) 84.5%, - hsla(0, 0%, 0%, 0.701) 91.9%, - hsla(0, 0%, 0%, 0.71) 100% - ); -} - -@theme inline { - --ease-out-quad: var(--lp-transition-ease-out-quad); - --background-image-lp-controls-fade: var(--background-image-lp-controls-fade); -} - -@utility focus-area { - position: relative; - - &:before { - position: absolute; - content: ""; - inset: calc(var(--y) * 1px) calc(var(--x) * 1px); - } -} - -@utility focus-area-x-* { - --x: --value(number); -} - -@utility focus-area-y-* { - --y: --value(number); -} - -@utility -focus-area-x-* { - --x: --value(number) * -1; -} - -@utility -focus-area-y-* { - --y: --value(number) * -1; -} diff --git a/apps/www/components/blocks/block-page-shell.tsx b/apps/www/components/blocks/block-page-shell.tsx index 1b9c5f63..0d1d8376 100644 --- a/apps/www/components/blocks/block-page-shell.tsx +++ b/apps/www/components/blocks/block-page-shell.tsx @@ -94,7 +94,7 @@ export function BlockPageShell({ info, preview, title }: BlockPageShellProps) { {preview} diff --git a/apps/www/components/blocks/block-showcase.tsx b/apps/www/components/blocks/block-showcase.tsx index 990d1582..85c44766 100644 --- a/apps/www/components/blocks/block-showcase.tsx +++ b/apps/www/components/blocks/block-showcase.tsx @@ -1,9 +1,9 @@ import type { ReactNode } from "react" import { AudioPlayerDemo } from "@/components/players/audio-player/demo-player" -import { VideoPlayer } from "@/registry/default/blocks/video-player/components/media-player" +import { VideoPlayer } from "@/registry/default/blocks/video-player/player" -import { BlockStreamSync } from "./block-toolbar" +import { BlockStreamSync } from "./block-stream-sync" import { BlockPreviewPane } from "./preview-background" import { BlockPreviewWithToolbar } from "./preview-pane" @@ -30,7 +30,7 @@ const blockShowcaseRegistry = {
- + diff --git a/apps/www/components/blocks/block-stream-sync.tsx b/apps/www/components/blocks/block-stream-sync.tsx new file mode 100644 index 00000000..3409dd1d --- /dev/null +++ b/apps/www/components/blocks/block-stream-sync.tsx @@ -0,0 +1,40 @@ +"use client" + +import { useEffect, useMemo } from "react" + +import type { StreamPanelPlayerType } from "@/components/stream-panel/use-stream-panel" + +import { useStreamPanel } from "@/components/stream-panel" +import { useStreamPanelSync } from "@/components/stream-panel/use-stream-panel-sync" + +export function BlockStreamSync({ + playerType = "video", +}: { + playerType?: StreamPanelPlayerType +}) { + const { registerController } = useStreamPanel() + const { handleLoadStream, handlePlaylistPresetChange, handlePresetChange } = + useStreamPanelSync({ playerType }) + + const controller = useMemo( + () => ({ + onLoadStream: handleLoadStream, + onPlaylistChange: handlePlaylistPresetChange, + onPresetChange: handlePresetChange, + playerType, + }), + [ + handleLoadStream, + handlePlaylistPresetChange, + handlePresetChange, + playerType, + ] + ) + + useEffect( + () => registerController(controller), + [controller, registerController] + ) + + return null +} diff --git a/apps/www/components/blocks/block-toolbar.tsx b/apps/www/components/blocks/block-toolbar.tsx index bf42dec8..02697092 100644 --- a/apps/www/components/blocks/block-toolbar.tsx +++ b/apps/www/components/blocks/block-toolbar.tsx @@ -27,7 +27,6 @@ import React, { } from "react" import { StreamPanel, useStreamPanel } from "@/components/stream-panel" -import { useStreamPanelSync } from "@/components/stream-panel/use-stream-panel-sync" import { cn } from "@/lib/utils" const PILL_TRANSITION = { bounce: 0, duration: 0.24, type: "spring" } as const @@ -48,38 +47,6 @@ interface BlockToolbarProps { theme: "dark" | "light" } -export function BlockStreamSync({ - playerType = "video", -}: { - playerType?: "audio" | "video" -}) { - const { registerController } = useStreamPanel() - const { handleLoadStream, handlePlaylistPresetChange, handlePresetChange } = - useStreamPanelSync({ playerType }) - - const controller = React.useMemo( - () => ({ - onLoadStream: handleLoadStream, - onPlaylistChange: handlePlaylistPresetChange, - onPresetChange: handlePresetChange, - playerType, - }), - [ - handleLoadStream, - handlePlaylistPresetChange, - handlePresetChange, - playerType, - ] - ) - - useEffect( - () => registerController(controller), - [controller, registerController] - ) - - return null -} - export function BlockToolbar({ codeUrl, expanded, diff --git a/apps/www/components/component-preview.tsx b/apps/www/components/component-preview.tsx index 37985baa..3d511fb0 100644 --- a/apps/www/components/component-preview.tsx +++ b/apps/www/components/component-preview.tsx @@ -8,10 +8,10 @@ import { TabsContent } from "@/components/ui/tabs" import { atomReader } from "@/hooks/use-config" import { cn } from "@/lib/utils" import { Index } from "@/registry/__index__" -import { PlayerLayoutDemo } from "@/registry/default/examples/player-root-demo" import { CodeBlock as CustomCodeBlock } from "./codeblock" import { ComponentPreviewControl } from "./component-preview-control" +import { PlayerLayoutDemo } from "./player-root-demo" import { PreviewTabComponent } from "./preview-tab-component" interface ComponentPreviewProps extends React.HTMLAttributes { diff --git a/apps/www/components/hero-buttons.tsx b/apps/www/components/hero-buttons.tsx index dedc6d2a..0cf17eb4 100644 --- a/apps/www/components/hero-buttons.tsx +++ b/apps/www/components/hero-buttons.tsx @@ -20,8 +20,8 @@ export default function HeroButtons() { > {command.split("/")[0]}/ - + {command.split("/")[1]} + <>
- + -
+ ) } diff --git a/apps/www/components/immersive-scroll-player.tsx b/apps/www/components/immersive-scroll-player.tsx index 4b0da1a5..26bd78cc 100644 --- a/apps/www/components/immersive-scroll-player.tsx +++ b/apps/www/components/immersive-scroll-player.tsx @@ -24,7 +24,7 @@ export function ImmersiveScrollPlayer({ const [initialWidthPx, _, finalWidthPx] = useMemo(() => { const fitByHeight = Math.round((vh * 16) / 9) - const fitWidth = Math.min(vw, fitByHeight) // contain: fit within viewport bounds + const fitWidth = Math.min(vw, fitByHeight) const initial = Math.min(1080, Math.max(320, Math.round(fitWidth * 0.6))) const mid = Math.min(1920, Math.round(fitWidth * 0.96)) const final = fitWidth @@ -56,9 +56,18 @@ export function ImmersiveScrollPlayer({ if (isMobile) { return ( -
+ {children} -
+ ) } @@ -76,9 +85,7 @@ export function ImmersiveScrollPlayer({ }, }} > -
+
(null) - const controlsVisibility = useVideoPlayerControlsVisibility({ - disabled: isMobilePortrait, - isMobile, - }) - return ( - {isMobilePortrait && } -
- - - -
+ + +
) } @@ -73,22 +48,37 @@ function HomeVideoStreamSelector() { <>
@@ -106,56 +96,6 @@ function HomeVideoStreamSelector() { ) } -function RotateMessage({ - playerRef, -}: { - playerRef: RefObject -}) { - const [show, toggle] = useToggle(false) - useFullscreen(playerRef as RefObject, show, { - onClose: () => { - toggle(false) - }, - }) - - const handleRotate = () => { - toggle(true) - } - - return ( -
-
-

- Rotate to Landscape -

- -

- For the best viewing experience, rotate your device to landscape mode. -

- - -
-
- ) -} - function useSelectedStreamName() { const selection = useStreamPanelStore((s) => s.contentSelections.video) if (!selection) return "Choose Stream" @@ -173,19 +113,3 @@ function useSelectedStreamName() { ?.title ?? "Custom Stream" ) } - -function useVideoPlayerControlsVisibility({ - disabled, - isMobile, -}: { - disabled: boolean - isMobile: boolean -}) { - return useMemo( - () => ({ - controlsHideDelay: disabled ? 0 : 1800, - hideCursorOnIdle: !disabled && !isMobile, - }), - [disabled, isMobile] - ) -} diff --git a/apps/www/components/stream-panel/content-catalog.ts b/apps/www/components/stream-panel/content-catalog.ts deleted file mode 100644 index f5280466..00000000 --- a/apps/www/components/stream-panel/content-catalog.ts +++ /dev/null @@ -1,432 +0,0 @@ -import type shaka from "shaka-player" - -import { z } from "zod" - -import type { StreamPanelPlayerType } from "@/components/stream-panel/use-stream-panel" -import type { Asset } from "@/registry/default/hooks/use-asset" - -export interface AppleMusicArtwork { - bgColor?: string - height?: number - templateUrl?: string - textColor1?: string - textColor2?: string - textColor3?: string - textColor4?: string - url?: string - width?: number -} - -export interface AppleMusicChartAsset extends Asset { - albumName?: string - artistName?: string - duration?: number - genre?: string - source: "apple-music-chart" - url?: string -} - -export interface AppleMusicChartPage { - assets: AppleMusicChartAsset[] - nextPage?: number -} - -export interface BlenderOpenFilmAsset extends Asset { - duration?: number - images?: BlenderOpenFilmImages - source: "blender-open-film" - subtitle?: string - year?: number -} -export interface BlenderOpenFilmImages { - backdrop?: string - logo?: string - poster?: string - thumbnail?: string -} - -export interface BlenderStreamResponse extends BlenderPlaylistItem { - captions?: BlenderStreamCaption[] - links?: Record - playback: { - hls: string - mimeType?: string - } -} - -export interface StreamPanelPlaylistPreset { - count?: number - description: string - id: string - name: string - type: StreamPanelPlayerType -} - -interface AppleMusicChartItem { - albumName?: string - artistName?: string - artwork?: AppleMusicArtwork - durationMs?: number - id: string - previewUrl?: string - title: string - url?: string -} - -interface BlenderPlaylistItem { - description?: string - duration?: number - id: string - images?: BlenderOpenFilmImages - subtitle?: string - title: string - year?: number -} - -interface BlenderPlaylistResponse { - count: number - items: BlenderPlaylistItem[] - title: string -} - -interface BlenderStreamCaption { - kind?: TextTrackKind - label: string - language: string - mimeType?: string - url: string -} - -export const APPLE_MUSIC_CHARTS_PLAYLIST_ID = "apple-music-charts" - -const BLENDER_OPEN_FILMS_PLAYLIST_ID = "blender-open-films" - -const BLENDER_API_BASE_URL = "https://limeplay.winoffrg.workers.dev/api/blender" -const APPLE_MUSIC_API_BASE_URL = - "https://limeplay.winoffrg.workers.dev/api/catalog/am" -const APPLE_MUSIC_CHARTS_TIMEOUT_MS = 10_000 -const BLENDER_STREAM_TIMEOUT_MS = 10_000 -const DEFAULT_APPLE_MUSIC_LOCALE = "en-US" -const DEFAULT_APPLE_MUSIC_STOREFRONT = "us" - -const AppleMusicArtworkSchema = z.object({ - bgColor: z.string().optional(), - height: z.number().optional(), - templateUrl: z.string().optional(), - textColor1: z.string().optional(), - textColor2: z.string().optional(), - textColor3: z.string().optional(), - textColor4: z.string().optional(), - url: z.string().optional(), - width: z.number().optional(), -}) - -const AppleMusicChartItemSchema = z.object({ - albumName: z.string().optional(), - artistName: z.string().optional(), - artwork: AppleMusicArtworkSchema.optional(), - durationMs: z.number().optional(), - id: z.string(), - previewUrl: z.string().optional(), - title: z.string(), - url: z.string().optional(), -}) - -const AppleMusicChartsResponseSchema = z.object({ - items: z.array(AppleMusicChartItemSchema), - nextPage: z.number().optional(), - page: z.number(), -}) - -const BlenderOpenFilmImagesSchema = z.object({ - backdrop: z.string().optional(), - logo: z.string().optional(), - poster: z.string().optional(), - thumbnail: z.string().optional(), -}) - -const BlenderStreamCaptionSchema = z.object({ - kind: z - .enum(["captions", "chapters", "descriptions", "metadata", "subtitles"]) - .optional(), - label: z.string(), - language: z.string(), - mimeType: z.string().optional(), - url: z.string(), -}) - -const BlenderStreamResponseSchema = z.object({ - captions: z.array(BlenderStreamCaptionSchema).optional(), - description: z.string().optional(), - duration: z.number().optional(), - id: z.string(), - images: BlenderOpenFilmImagesSchema.optional(), - links: z.record(z.string(), z.string().optional()).optional(), - playback: z.object({ - hls: z.string(), - mimeType: z.string().optional(), - }), - subtitle: z.string().optional(), - title: z.string(), - year: z.number().optional(), -}) - -const STREAM_PANEL_PLAYLIST_PRESETS: StreamPanelPlaylistPreset[] = [ - { - count: 17, - description: "Open movies from Blender Studio", - id: BLENDER_OPEN_FILMS_PLAYLIST_ID, - name: "Blender Open Films", - type: "video", - }, - { - description: "Top songs from Apple Music for your region", - id: APPLE_MUSIC_CHARTS_PLAYLIST_ID, - name: "Apple Music Charts", - type: "audio", - }, -] - -export async function addBlenderCaptions( - player: shaka.Player, - stream: BlenderStreamResponse -): Promise { - if (!stream.captions || stream.captions.length === 0) return - - const captionResults = await Promise.allSettled( - stream.captions.map((caption) => - player.addTextTrackAsync( - caption.url, - caption.language, - caption.kind ?? "subtitles", - caption.mimeType ?? "text/vtt", - undefined, - caption.label - ) - ) - ) - - captionResults.forEach((result, index) => { - if (result.status === "fulfilled") return - - const caption = stream.captions?.[index] - console.warn("Failed to add Blender caption track:", { - error: result.reason, - label: caption?.label, - url: caption?.url, - }) - }) -} - -export async function fetchAppleMusicChartAssetsPage( - page: number, - signal?: AbortSignal -): Promise { - const { locale, storefront } = getAppleMusicLocaleHeaders() - const combinedSignal = createTimeoutSignal( - signal, - APPLE_MUSIC_CHARTS_TIMEOUT_MS - ) - - const chart = await fetchAppleMusicChartPage({ - locale, - page, - signal: combinedSignal, - storefront, - }) - - return { - assets: chart.items - .filter((item) => Boolean(item.previewUrl)) - .map(toAppleMusicChartAsset), - nextPage: chart.nextPage, - } -} - -export async function fetchBlenderStream( - assetId: string, - signal?: AbortSignal -): Promise { - const combinedSignal = createTimeoutSignal(signal, BLENDER_STREAM_TIMEOUT_MS) - const response = await fetch(`${BLENDER_API_BASE_URL}/stream/${assetId}`, { - signal: combinedSignal, - }) - - if (!response.ok) { - throw new Error(`Failed to fetch Blender stream: ${response.statusText}`) - } - - return BlenderStreamResponseSchema.parse(await response.json()) -} - -export async function fetchPlaylistPresetAssets( - playlistId: string, - signal?: AbortSignal -): Promise { - if (playlistId === APPLE_MUSIC_CHARTS_PLAYLIST_ID) { - return (await fetchAppleMusicChartAssetsPage(1, signal)).assets - } - - if (playlistId !== BLENDER_OPEN_FILMS_PLAYLIST_ID) { - throw new Error(`Unknown playlist preset "${playlistId}".`) - } - const response = await fetch(`${BLENDER_API_BASE_URL}/playlist`, { signal }) - if (!response.ok) { - throw new Error(`Failed to fetch Blender playlist: ${response.statusText}`) - } - - const playlist = (await response.json()) as BlenderPlaylistResponse - - return playlist.items.map(toBlenderOpenFilmAsset) -} - -export function getPlaylistPresetsForType( - playerType: StreamPanelPlayerType -): StreamPanelPlaylistPreset[] { - return STREAM_PANEL_PLAYLIST_PRESETS.filter( - (playlist) => playlist.type === playerType - ) -} - -export function isBlenderOpenFilmAsset( - asset: Asset -): asset is BlenderOpenFilmAsset { - return "source" in asset && asset.source === "blender-open-film" -} - -function createTimeoutSignal( - signal: AbortSignal | undefined, - timeoutMs: number -): AbortSignal | undefined { - if (typeof AbortSignal.timeout !== "function") return signal - - const timeoutSignal = AbortSignal.timeout(timeoutMs) - if (!signal) return timeoutSignal - return AbortSignal.any([signal, timeoutSignal]) -} - -async function fetchAppleMusicChartPage({ - locale, - page, - signal, - storefront, -}: { - locale: string - page: number - signal?: AbortSignal - storefront: string -}) { - const response = await fetch( - `${APPLE_MUSIC_API_BASE_URL}/charts?page=${page}`, - { - headers: { - "x-locale": locale, - "x-storefront": storefront, - }, - signal, - } - ) - throwIfAborted(signal) - - if (!response.ok) { - throw new Error( - `Failed to fetch Apple Music charts page ${page}: ${response.statusText}` - ) - } - - const responseJson: unknown = await response.json() - throwIfAborted(signal) - - const chart = AppleMusicChartsResponseSchema.safeParse(responseJson) - if (!chart.success) { - throw new Error( - `Invalid Apple Music charts page ${page} response: ${chart.error}` - ) - } - - return chart.data -} - -function getAppleMusicLocaleHeaders(): { locale: string; storefront: string } { - const locale = getUserLocale() - const country = getLocaleCountry(locale) - - return { - locale, - storefront: country?.toLowerCase() ?? DEFAULT_APPLE_MUSIC_STOREFRONT, - } -} - -function getLocaleCountry(locale: string): string | undefined { - try { - const parsedLocale = new Intl.Locale(locale) - return parsedLocale.region ?? parsedLocale.maximize().region - } catch { - return locale.match(/[-_]([A-Za-z]{2})\b/)?.[1]?.toUpperCase() - } -} - -function getUserLocale(): string { - const browserLocale = - typeof navigator === "undefined" - ? undefined - : (navigator.languages.find(Boolean) ?? navigator.language) - - const resolvedLocale = - browserLocale ?? Intl.DateTimeFormat().resolvedOptions().locale - - return normalizeLocale(resolvedLocale) -} - -function normalizeLocale(locale: string | undefined): string { - if (!locale) return DEFAULT_APPLE_MUSIC_LOCALE - - const normalizedLocale = locale.replaceAll("_", "-") - try { - return ( - Intl.getCanonicalLocales(normalizedLocale)[0] ?? - DEFAULT_APPLE_MUSIC_LOCALE - ) - } catch { - return DEFAULT_APPLE_MUSIC_LOCALE - } -} - -function throwIfAborted(signal?: AbortSignal): void { - if (!signal?.aborted) return - - throw new DOMException("Aborted", "AbortError") -} - -function toAppleMusicChartAsset( - item: AppleMusicChartItem -): AppleMusicChartAsset { - return { - albumName: item.albumName, - artistName: item.artistName, - description: item.albumName, - duration: item.durationMs, - id: item.id, - poster: item.artwork?.url, - source: "apple-music-chart", - src: item.previewUrl, - title: item.title, - url: item.url, - } -} - -function toBlenderOpenFilmAsset( - item: BlenderPlaylistItem -): BlenderOpenFilmAsset { - return { - description: item.description, - duration: item.duration, - id: item.id, - images: item.images, - poster: item.images?.thumbnail ?? item.images?.poster, - source: "blender-open-film", - subtitle: item.subtitle, - title: item.title, - year: item.year, - } -} diff --git a/apps/www/components/stream-panel/panel-popover.config.ts b/apps/www/components/stream-panel/panel-popover.config.ts index dabaeff2..9b388c9d 100644 --- a/apps/www/components/stream-panel/panel-popover.config.ts +++ b/apps/www/components/stream-panel/panel-popover.config.ts @@ -1,8 +1,8 @@ -import type { StreamPanelPlaylistPreset } from "@/components/stream-panel/content-catalog" import type { StreamPanelContentKind, StreamPanelPlayerType, } from "@/components/stream-panel/use-stream-panel" +import type { CatalogPlaylistPreset } from "@/lib/catalogs" import type { StreamPreset } from "@/lib/stream-presets" export const STREAM_PANEL_OVERLAY = { @@ -45,7 +45,7 @@ export const STREAM_PANEL_EMPTY_CONTENT_LABEL = "Choose content" export interface StreamPanelProps { align?: "center" | "end" | "start" onLoadStream?: (src: string, config?: string) => void - onPlaylistChange?: (playlist: StreamPanelPlaylistPreset) => void + onPlaylistChange?: (playlist: CatalogPlaylistPreset) => void onPresetChange?: ( preset: StreamPreset, kind?: Extract diff --git a/apps/www/components/stream-panel/panel-popover.tsx b/apps/www/components/stream-panel/panel-popover.tsx index ed5bfc11..32f7c6db 100644 --- a/apps/www/components/stream-panel/panel-popover.tsx +++ b/apps/www/components/stream-panel/panel-popover.tsx @@ -9,12 +9,12 @@ import React, { useMemo, useState } from "react" import type { StreamPanelContentKind } from "@/components/stream-panel/use-stream-panel" -import { getPlaylistPresetsForType } from "@/components/stream-panel/content-catalog" import { useStreamPanelStore } from "@/components/stream-panel/use-stream-panel" import { Field, FieldLabel } from "@/components/ui/field" import { Popover, PopoverContent } from "@/components/ui/popover" import { Separator } from "@/components/ui/separator" import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group" +import { getPlaylistPresetsForType } from "@/lib/catalogs" import { getPresetsForType, type StreamPreset } from "@/lib/stream-presets" import { cn } from "@/lib/utils" diff --git a/apps/www/components/stream-panel/playlists-overlay.tsx b/apps/www/components/stream-panel/playlists-overlay.tsx index 97ac70fb..08ad4d38 100644 --- a/apps/www/components/stream-panel/playlists-overlay.tsx +++ b/apps/www/components/stream-panel/playlists-overlay.tsx @@ -3,8 +3,8 @@ import { Menu as MenuPrimitive } from "@base-ui/react/menu" import { CheckIcon } from "lucide-react" -import type { StreamPanelPlaylistPreset } from "@/components/stream-panel/content-catalog" import type { StreamPanelSelection } from "@/components/stream-panel/use-stream-panel" +import type { CatalogPlaylistPreset } from "@/lib/catalogs" import { cn } from "@/lib/utils" @@ -12,9 +12,9 @@ import { OverlayShell, type OverlayShellPlacement } from "./overlay-shell" interface PlaylistsOverlayProps { onBack: () => void - onSelect: (playlist: StreamPanelPlaylistPreset) => void + onSelect: (playlist: CatalogPlaylistPreset) => void placement?: OverlayShellPlacement - playlists: StreamPanelPlaylistPreset[] + playlists: CatalogPlaylistPreset[] selection?: StreamPanelSelection show: boolean } diff --git a/apps/www/components/stream-panel/provider.tsx b/apps/www/components/stream-panel/provider.tsx index 5a4b7dd0..af840041 100644 --- a/apps/www/components/stream-panel/provider.tsx +++ b/apps/www/components/stream-panel/provider.tsx @@ -9,13 +9,13 @@ import React, { useState, } from "react" -import type { StreamPanelPlaylistPreset } from "@/components/stream-panel/content-catalog" import type { StreamPanelPlayerType } from "@/components/stream-panel/use-stream-panel" +import type { CatalogPlaylistPreset } from "@/lib/catalogs" import type { StreamPreset } from "@/lib/stream-presets" export interface StreamPanelController { onLoadStream?: (src: string, config?: string) => void - onPlaylistChange?: (playlist: StreamPanelPlaylistPreset) => void + onPlaylistChange?: (playlist: CatalogPlaylistPreset) => void onPresetChange?: (preset: StreamPreset, kind?: "live" | "stream") => void playerType: StreamPanelPlayerType } diff --git a/apps/www/components/stream-panel/use-stream-panel-sync.ts b/apps/www/components/stream-panel/use-stream-panel-sync.ts index 1ce490f0..9f4fbcca 100644 --- a/apps/www/components/stream-panel/use-stream-panel-sync.ts +++ b/apps/www/components/stream-panel/use-stream-panel-sync.ts @@ -3,29 +3,31 @@ import { useCallback, useEffect, useMemo, useRef } from "react" import type { - Asset, AssetEvents, UseAssetOptions, } from "@/registry/default/hooks/use-asset" import type { PlaybackStore } from "@/registry/default/hooks/use-playback" +import { + type StreamPanelContentKind, + type StreamPanelPlayerType, + type StreamPanelSelection, + useStreamPanelStore, + useStreamPanelStoreHydrated, +} from "@/components/stream-panel/use-stream-panel" import { addBlenderCaptions, APPLE_MUSIC_CHARTS_PLAYLIST_ID, type BlenderStreamResponse, + type CatalogPlayerAsset, + type CatalogPlaylistPreset, fetchAppleMusicChartAssetsPage, fetchBlenderStream, fetchPlaylistPresetAssets, isBlenderOpenFilmAsset, - type StreamPanelPlaylistPreset, -} from "@/components/stream-panel/content-catalog" -import { - type StreamPanelContentKind, - type StreamPanelPlayerType, - type StreamPanelSelection, - useStreamPanelStore, - useStreamPanelStoreHydrated, -} from "@/components/stream-panel/use-stream-panel" + mapCatalogAssetsToPlayerAssets, + mapStreamPresetToPlayerAsset, +} from "@/lib/catalogs" import { getPresetsForType, type StreamPreset } from "@/lib/stream-presets" import { AssetRecoveryAction, @@ -86,7 +88,7 @@ export function useStreamPanelSync({ } const blenderStreamCache = blenderStreamCacheRef.current - const assetOptions = useMemo>( + const assetOptions = useMemo>( () => ({ getAssetId: (asset) => asset.id ?? asset.src, loader: { @@ -149,7 +151,7 @@ export function useStreamPanelSync({ [blenderStreamCache, playbackApi] ) - const { loadSource } = useAsset() + const { loadSource } = useAsset() const appendNextAppleMusicChartPage = useCallback( (currentIndex: number) => { @@ -184,7 +186,8 @@ export function useStreamPanelSync({ const existingIds = new Set( playlistApi.getState().playlist.queue.map((item) => item.id) ) - const newItems = assets + const playerAssets = mapCatalogAssetsToPlayerAssets(assets) + const newItems = playerAssets .filter((asset) => asset.id && !existingIds.has(asset.id)) .map((asset) => ({ id: asset.id!, @@ -282,7 +285,9 @@ export function useStreamPanelSync({ title: "Custom Stream", type: playerType, } - loadSource(asset as unknown as Asset, { loading: assetOptions }) + loadSource(mapStreamPresetToPlayerAsset(asset, "custom-stream"), { + loading: assetOptions, + }) return asset }, [assetOptions, loadSource, playbackApi, playerType] @@ -316,16 +321,17 @@ export function useStreamPanelSync({ .then((assets) => { if (abortController.signal.aborted) return + const playerAssets = mapCatalogAssetsToPlayerAssets(assets) const index = normalizePlaylistIndex( playlistId === APPLE_MUSIC_CHARTS_PLAYLIST_ID ? 0 : startIndex, - assets.length + playerAssets.length ) setContentSelection(playerType, { id: playlistId, index, kind: "playlist", }) - loadSource(assets, { + loadSource(playerAssets, { initialIndex: index, loading: assetOptions, }) @@ -363,7 +369,9 @@ export function useStreamPanelSync({ ) if (preset) { abortPlaylistRequest() - loadSource(preset as unknown as Asset, { loading: assetOptions }) + loadSource(mapStreamPresetToPlayerAsset(preset), { + loading: assetOptions, + }) return } @@ -415,7 +423,9 @@ export function useStreamPanelSync({ (preset: StreamPreset, kind: StreamPanelContentKind = "stream") => { abortPlaylistRequest() setContentSelection(playerType, { id: preset.id, index: 0, kind }) - loadSource(preset as unknown as Asset, { loading: assetOptions }) + loadSource(mapStreamPresetToPlayerAsset(preset), { + loading: assetOptions, + }) }, [ abortPlaylistRequest, @@ -427,7 +437,7 @@ export function useStreamPanelSync({ ) const handlePlaylistPresetChange = useCallback( - (playlist: StreamPanelPlaylistPreset) => { + (playlist: CatalogPlaylistPreset) => { loadPlaylistPreset(playlist.id) }, [loadPlaylistPreset] diff --git a/apps/www/content/docs/blocks/audio-player.mdx b/apps/www/content/docs/blocks/audio-player.mdx index 245f1675..9ddc5763 100644 --- a/apps/www/content/docs/blocks/audio-player.mdx +++ b/apps/www/content/docs/blocks/audio-player.mdx @@ -19,7 +19,7 @@ npx shadcn add @limeplay/audio-player import { AudioPlayer, type AudioPlayerAsset, -} from "@/components/limeplay/audio-player/components/media-player" +} from "@/components/limeplay/audio-player/player" const playlist: AudioPlayerAsset[] = [ { @@ -98,7 +98,7 @@ The audio block includes an opinionated default resolver for `src` and `playback ## API Reference diff --git a/apps/www/content/docs/blocks/video-player.mdx b/apps/www/content/docs/blocks/video-player.mdx index d9606258..dfa17666 100644 --- a/apps/www/content/docs/blocks/video-player.mdx +++ b/apps/www/content/docs/blocks/video-player.mdx @@ -16,7 +16,7 @@ npx shadcn add @limeplay/video-player ## Minimal Usage ```tsx -import { VideoPlayer } from "@/components/limeplay/video-player/components/media-player" +import { VideoPlayer } from "@/components/limeplay/video-player/player" const src = "https://ad391cc0d55b44c6a86d232548adc225.mediatailor.us-east-1.amazonaws.com/v1/master/d02fedbbc5a68596164208dd24e9b48aa60dadc7/singssai/master.m3u8" @@ -43,7 +43,7 @@ Pass an array to `source` when the player should manage a queue. import { VideoPlayer, type VideoPlayerAsset, -} from "@/components/limeplay/video-player/components/media-player" +} from "@/components/limeplay/video-player/player" const playlist: VideoPlayerAsset[] = [ { @@ -105,7 +105,7 @@ Use `loading.resolveSource` when your app needs signed URLs, token refresh, or s ## API Reference diff --git a/apps/www/content/docs/components/player-layout.mdx b/apps/www/content/docs/components/player-layout.mdx index a6652fa0..1137d59c 100644 --- a/apps/www/content/docs/components/player-layout.mdx +++ b/apps/www/content/docs/components/player-layout.mdx @@ -44,7 +44,7 @@ export function MediaPlayer() { ### RootContainer -Outermost wrapper. Manages idle state detection and focus management for accessibility. +Outermost wrapper. Provides the player region, aspect ratio, shared refs, and root data attributes for child layout styling. ### PlayerContainer diff --git a/apps/www/content/docs/components/timeline-control.mdx b/apps/www/content/docs/components/timeline-control.mdx index c3082626..f13d68c8 100644 --- a/apps/www/content/docs/components/timeline-control.mdx +++ b/apps/www/content/docs/components/timeline-control.mdx @@ -25,7 +25,7 @@ import { Duration, HoverTime } from "@/components/limeplay/timeline-labels" export function TimelineSliderControl() { return (
- + diff --git a/apps/www/content/docs/components/volume-control.mdx b/apps/www/content/docs/components/volume-control.mdx index e040cfbc..9f5b395f 100644 --- a/apps/www/content/docs/components/volume-control.mdx +++ b/apps/www/content/docs/components/volume-control.mdx @@ -25,7 +25,7 @@ import * as VolumeSlider from "@/components/limeplay/volume-control" export function VolumeSliderControl() { return ( diff --git a/apps/www/content/docs/concepts.mdx b/apps/www/content/docs/concepts.mdx index 4670d548..2536c567 100644 --- a/apps/www/content/docs/concepts.mdx +++ b/apps/www/content/docs/concepts.mdx @@ -96,7 +96,7 @@ Most Limeplay players follow this shape. Blocks package this structure for you; + Requires [`mediaFeature`](/docs/hooks/use-media) and + [`playbackFeature`](/docs/hooks/use-playback) registered in your + [`createMediaKit`](/docs/components/media-provider). + + +## Usage + +```tsx +import { + CONTROLS_FORCE_VISIBLE_ATTRIBUTE, + useControlsVisibility, +} from "@/hooks/limeplay/use-controls-visibility" + +export function PlayerShell() { + const controlsVisibility = useControlsVisibility() + + return ( +
+
+ ) +} +``` + +`rootProps` should be spread on the player root. Add +`CONTROLS_FORCE_VISIBLE_ATTRIBUTE` to any controls area that should keep controls +visible while hovered or scrubbed. + +## API Reference + + diff --git a/apps/www/content/docs/hooks/use-playback-source.mdx b/apps/www/content/docs/hooks/use-playback-source.mdx index 3838cf4f..ab95bae6 100644 --- a/apps/www/content/docs/hooks/use-playback-source.mdx +++ b/apps/www/content/docs/hooks/use-playback-source.mdx @@ -29,10 +29,10 @@ export function SourceController({ src }: { src?: string }) { For typed assets, pass one asset or an asset array to `source`, then pass source resolution and recovery policies through `loading`. ```tsx -import type { Asset } from "@/hooks/limeplay/use-asset" +import type { TAsset } from "@/hooks/limeplay/use-asset" import { PlaybackSourceController } from "@/hooks/limeplay/use-playback-source" -interface VideoAsset extends Asset { +interface VideoAsset extends TAsset { playbackUrl: string slug: string } @@ -83,12 +83,12 @@ export const media = createMediaKit({ ``` ```tsx -import type { Asset } from "@/hooks/limeplay/use-asset" +import type { TAsset } from "@/hooks/limeplay/use-asset" import type { UsePlaybackSourceOptions } from "@/hooks/limeplay/use-playback-source" import { PlaybackSourceController } from "@/hooks/limeplay/use-playback-source" -export function Source( - props: UsePlaybackSourceOptions +export function Source( + props: UsePlaybackSourceOptions ) { return } diff --git a/apps/www/content/docs/usage.mdx b/apps/www/content/docs/usage.mdx index 8c10e289..211d904a 100644 --- a/apps/www/content/docs/usage.mdx +++ b/apps/www/content/docs/usage.mdx @@ -14,7 +14,7 @@ npx shadcn add @limeplay/video-player ``` ```tsx title="components/player.tsx" -import { VideoPlayer } from "@/components/limeplay/video-player/components/media-player" +import { VideoPlayer } from "@/components/limeplay/video-player/player" export function Player() { return ( @@ -32,7 +32,7 @@ For playlist-style blocks, pass an array of assets to the same `source` prop. import { AudioPlayer, type AudioPlayerAsset, -} from "@/components/limeplay/audio-player/components/media-player" +} from "@/components/limeplay/audio-player/player" const tracks: AudioPlayerAsset[] = [ { @@ -247,13 +247,13 @@ When you build a new block, keep the public loading API consistent with the exis "use client" import type React from "react" -import type { Asset, PlayerSource, UseAssetOptions } from "@/hooks/limeplay/use-asset" +import type { PlayerSource, TAsset, UseAssetOptions } from "@/hooks/limeplay/use-asset" import { MediaProvider } from "@/components/limeplay/custom-video-player/lib/media-kit" import { PlaybackSourceController } from "@/hooks/limeplay/use-playback-source" import { Media } from "@/components/limeplay/media" -interface CustomVideoAsset extends Asset { +interface CustomVideoAsset extends TAsset { title?: string } diff --git a/apps/www/eslint.config.mjs b/apps/www/eslint.config.mjs index 668b3424..5a9f494a 100644 --- a/apps/www/eslint.config.mjs +++ b/apps/www/eslint.config.mjs @@ -1,9 +1,14 @@ +import path from "node:path" +import { fileURLToPath } from "node:url" + import nextPlugin from "@next/eslint-plugin-next" import pluginBetterTailwindcss from "eslint-plugin-better-tailwindcss" import tseslint from "typescript-eslint" import { baseConfig } from "../../eslint.config.mjs" +const appDir = path.dirname(fileURLToPath(import.meta.url)) + const eslintConfig = [ { files: ["**/*"], @@ -26,16 +31,13 @@ const eslintConfig = [ ], "better-tailwindcss/no-unknown-classes": [ "error", - { ignore: ["dark", "shiki", "not-prose", "light"] }, + { ignore: ["limeplay", "dark", "shiki", "not-prose", "light"] }, ], }, settings: { "better-tailwindcss": { - entryPoint: "app/global.css", - detectComponentClasses: true, - }, - "better-tailwindcss": { - entryPoint: "app/docs/docs.css", + cwd: appDir, + entryPoint: "app/eslint.css", detectComponentClasses: true, }, }, diff --git a/apps/www/lib/catalogs/apple-music.ts b/apps/www/lib/catalogs/apple-music.ts new file mode 100644 index 00000000..bc7cc12b --- /dev/null +++ b/apps/www/lib/catalogs/apple-music.ts @@ -0,0 +1,208 @@ +import { z } from "zod" + +import type { CatalogBaseAsset } from "@/lib/catalogs/types" + +import { createTimeoutSignal, throwIfAborted } from "@/lib/catalogs/utils" + +export const APPLE_MUSIC_CHARTS_PLAYLIST_ID = "apple-music-charts" + +const APPLE_MUSIC_API_BASE_URL = + "https://limeplay.winoffrg.workers.dev/api/catalog/am" +const APPLE_MUSIC_CHARTS_TIMEOUT_MS = 10_000 +const DEFAULT_APPLE_MUSIC_LOCALE = "en-US" +const DEFAULT_APPLE_MUSIC_STOREFRONT = "us" + +export interface AppleMusicArtwork { + bgColor?: string + height?: number + templateUrl?: string + textColor1?: string + textColor2?: string + textColor3?: string + textColor4?: string + url?: string + width?: number +} + +export interface AppleMusicChartAsset extends CatalogBaseAsset { + albumName?: string + artistName?: string + duration?: number + genre?: string + source: "apple-music-chart" + url?: string +} + +export interface AppleMusicChartPage { + assets: AppleMusicChartAsset[] + nextPage?: number +} + +interface AppleMusicChartItem { + albumName?: string + artistName?: string + artwork?: AppleMusicArtwork + durationMs?: number + id: string + previewUrl?: string + title: string + url?: string +} + +const AppleMusicArtworkSchema = z.object({ + bgColor: z.string().optional(), + height: z.number().optional(), + templateUrl: z.string().optional(), + textColor1: z.string().optional(), + textColor2: z.string().optional(), + textColor3: z.string().optional(), + textColor4: z.string().optional(), + url: z.string().optional(), + width: z.number().optional(), +}) + +const AppleMusicChartItemSchema = z.object({ + albumName: z.string().optional(), + artistName: z.string().optional(), + artwork: AppleMusicArtworkSchema.optional(), + durationMs: z.number().optional(), + id: z.string(), + previewUrl: z.string().optional(), + title: z.string(), + url: z.string().optional(), +}) + +const AppleMusicChartsResponseSchema = z.object({ + items: z.array(AppleMusicChartItemSchema), + nextPage: z.number().optional(), + page: z.number(), +}) + +export async function fetchAppleMusicChartAssetsPage( + page: number, + signal?: AbortSignal +): Promise { + const { locale, storefront } = getAppleMusicLocaleHeaders() + const combinedSignal = createTimeoutSignal( + signal, + APPLE_MUSIC_CHARTS_TIMEOUT_MS + ) + + const chart = await fetchAppleMusicChartPage({ + locale, + page, + signal: combinedSignal, + storefront, + }) + + return { + assets: chart.items + .filter((item) => Boolean(item.previewUrl)) + .map(toAppleMusicChartAsset), + nextPage: chart.nextPage, + } +} + +async function fetchAppleMusicChartPage({ + locale, + page, + signal, + storefront, +}: { + locale: string + page: number + signal?: AbortSignal + storefront: string +}) { + const response = await fetch( + `${APPLE_MUSIC_API_BASE_URL}/charts?page=${page}`, + { + headers: { + "x-locale": locale, + "x-storefront": storefront, + }, + signal, + } + ) + throwIfAborted(signal) + + if (!response.ok) { + throw new Error( + `Failed to fetch Apple Music charts page ${page}: ${response.statusText}` + ) + } + + const responseJson: unknown = await response.json() + throwIfAborted(signal) + + const chart = AppleMusicChartsResponseSchema.safeParse(responseJson) + if (!chart.success) { + throw new Error( + `Invalid Apple Music charts page ${page} response: ${chart.error}` + ) + } + + return chart.data +} + +function getAppleMusicLocaleHeaders(): { locale: string; storefront: string } { + const locale = getUserLocale() + const country = getLocaleCountry(locale) + + return { + locale, + storefront: country?.toLowerCase() ?? DEFAULT_APPLE_MUSIC_STOREFRONT, + } +} + +function getLocaleCountry(locale: string): string | undefined { + try { + const parsedLocale = new Intl.Locale(locale) + return parsedLocale.region ?? parsedLocale.maximize().region + } catch { + return locale.match(/[-_]([A-Za-z]{2})\b/)?.[1]?.toUpperCase() + } +} + +function getUserLocale(): string { + const browserLocale = + typeof navigator === "undefined" + ? undefined + : (navigator.languages.find(Boolean) ?? navigator.language) + + const resolvedLocale = + browserLocale ?? Intl.DateTimeFormat().resolvedOptions().locale + + return normalizeLocale(resolvedLocale) +} + +function normalizeLocale(locale: string | undefined): string { + if (!locale) return DEFAULT_APPLE_MUSIC_LOCALE + + const normalizedLocale = locale.replaceAll("_", "-") + try { + return ( + Intl.getCanonicalLocales(normalizedLocale)[0] ?? + DEFAULT_APPLE_MUSIC_LOCALE + ) + } catch { + return DEFAULT_APPLE_MUSIC_LOCALE + } +} + +function toAppleMusicChartAsset( + item: AppleMusicChartItem +): AppleMusicChartAsset { + return { + albumName: item.albumName, + artistName: item.artistName, + description: item.albumName, + duration: item.durationMs, + id: item.id, + poster: item.artwork?.url, + source: "apple-music-chart", + src: item.previewUrl, + title: item.title, + url: item.url, + } +} diff --git a/apps/www/lib/catalogs/blender-open-films.ts b/apps/www/lib/catalogs/blender-open-films.ts new file mode 100644 index 00000000..50c94d81 --- /dev/null +++ b/apps/www/lib/catalogs/blender-open-films.ts @@ -0,0 +1,187 @@ +import type shaka from "shaka-player" + +import { z } from "zod" + +import type { CatalogBaseAsset } from "@/lib/catalogs/types" + +import { createTimeoutSignal } from "@/lib/catalogs/utils" + +export const BLENDER_OPEN_FILMS_PLAYLIST_ID = "blender-open-films" + +const BLENDER_API_BASE_URL = "https://limeplay.winoffrg.workers.dev/api/blender" +const BLENDER_STREAM_TIMEOUT_MS = 10_000 + +export interface BlenderOpenFilmAsset extends CatalogBaseAsset { + duration?: number + images?: BlenderOpenFilmImages + source: "blender-open-film" + subtitle?: string + year?: number +} + +export interface BlenderOpenFilmImages { + backdrop?: string + logo?: string + poster?: string + thumbnail?: string +} + +export interface BlenderStreamResponse extends BlenderPlaylistItem { + captions?: BlenderStreamCaption[] + links?: Record + playback: { + hls: string + mimeType?: string + } +} + +interface BlenderPlaylistItem { + description?: string + duration?: number + id: string + images?: BlenderOpenFilmImages + subtitle?: string + title: string + year?: number +} + +interface BlenderStreamCaption { + kind?: TextTrackKind + label: string + language: string + mimeType?: string + url: string +} + +const BlenderOpenFilmImagesSchema = z.object({ + backdrop: z.string().optional(), + logo: z.string().optional(), + poster: z.string().optional(), + thumbnail: z.string().optional(), +}) + +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(), +}) + +const BlenderStreamCaptionSchema = z.object({ + kind: z + .enum(["captions", "chapters", "descriptions", "metadata", "subtitles"]) + .optional(), + label: z.string(), + language: z.string(), + mimeType: z.string().optional(), + url: z.string(), +}) + +const BlenderStreamResponseSchema = z.object({ + captions: z.array(BlenderStreamCaptionSchema).optional(), + description: z.string().optional(), + duration: z.number().optional(), + id: z.string(), + images: BlenderOpenFilmImagesSchema.optional(), + links: z.record(z.string(), z.string().optional()).optional(), + playback: z.object({ + hls: z.string(), + mimeType: z.string().optional(), + }), + subtitle: z.string().optional(), + title: z.string(), + year: z.number().optional(), +}) + +export async function addBlenderCaptions( + player: shaka.Player, + stream: BlenderStreamResponse +): Promise { + if (!stream.captions || stream.captions.length === 0) return + + const captionResults = await Promise.allSettled( + stream.captions.map((caption) => + player.addTextTrackAsync( + caption.url, + caption.language, + caption.kind ?? "subtitles", + caption.mimeType ?? "text/vtt", + undefined, + caption.label + ) + ) + ) + + captionResults.forEach((result, index) => { + if (result.status === "fulfilled") return + + const caption = stream.captions?.[index] + console.warn("Failed to add Blender caption track:", { + error: result.reason, + label: caption?.label, + url: caption?.url, + }) + }) +} + +export async function fetchBlenderOpenFilmAssets( + signal?: AbortSignal +): Promise { + const response = await fetch(`${BLENDER_API_BASE_URL}/playlist`, { signal }) + if (!response.ok) { + throw new Error(`Failed to fetch Blender playlist: ${response.statusText}`) + } + + const playlist = BlenderPlaylistResponseSchema.parse(await response.json()) + + return playlist.items.map(toBlenderOpenFilmAsset) +} + +export async function fetchBlenderStream( + assetId: string, + signal?: AbortSignal +): Promise { + const combinedSignal = createTimeoutSignal(signal, BLENDER_STREAM_TIMEOUT_MS) + const response = await fetch(`${BLENDER_API_BASE_URL}/stream/${assetId}`, { + signal: combinedSignal, + }) + + if (!response.ok) { + throw new Error(`Failed to fetch Blender stream: ${response.statusText}`) + } + + return BlenderStreamResponseSchema.parse(await response.json()) +} + +export function isBlenderOpenFilmAsset( + asset: unknown +): asset is BlenderOpenFilmAsset { + if (!asset || typeof asset !== "object") return false + + return (asset as { source?: unknown }).source === "blender-open-film" +} + +function toBlenderOpenFilmAsset( + item: BlenderPlaylistItem +): BlenderOpenFilmAsset { + return { + description: item.description, + duration: item.duration, + id: item.id, + images: item.images, + poster: item.images?.thumbnail ?? item.images?.poster, + source: "blender-open-film", + subtitle: item.subtitle, + title: item.title, + year: item.year, + } +} diff --git a/apps/www/lib/catalogs/index.ts b/apps/www/lib/catalogs/index.ts new file mode 100644 index 00000000..c91fb5a4 --- /dev/null +++ b/apps/www/lib/catalogs/index.ts @@ -0,0 +1,5 @@ +export * from "@/lib/catalogs/apple-music" +export * from "@/lib/catalogs/blender-open-films" +export * from "@/lib/catalogs/player-assets" +export * from "@/lib/catalogs/playlists" +export * from "@/lib/catalogs/types" diff --git a/apps/www/lib/catalogs/player-assets.ts b/apps/www/lib/catalogs/player-assets.ts new file mode 100644 index 00000000..ccbef267 --- /dev/null +++ b/apps/www/lib/catalogs/player-assets.ts @@ -0,0 +1,121 @@ +import type shaka from "shaka-player" + +import type { AppleMusicChartAsset } from "@/lib/catalogs/apple-music" +import type { + BlenderOpenFilmAsset, + BlenderOpenFilmImages, +} from "@/lib/catalogs/blender-open-films" +import type { CatalogAsset } from "@/lib/catalogs/playlists" +import type { StreamPreset } from "@/lib/stream-presets" + +export type CatalogPlayerAsset = + | CatalogPlayerAudioAsset + | CatalogPlayerVideoAsset + +export interface CatalogPlayerAudioAsset { + albumName?: string + artistName?: string + config?: shaka.extern.PlayerConfiguration + description?: string + duration?: number + features?: string[] + genre?: string + group?: string + id?: string + poster?: string + source?: "apple-music-chart" | "custom-stream" | "stream-preset" + src?: string + title?: string + url?: string +} + +export interface CatalogPlayerVideoAsset { + config?: shaka.extern.PlayerConfiguration + description?: string + duration?: number + features?: string[] + group?: string + id?: string + images?: BlenderOpenFilmImages + poster?: string + source?: "blender-open-film" | "custom-stream" | "stream-preset" + src?: string + subtitle?: string + title?: string + year?: string +} + +export function mapCatalogAssetsToPlayerAssets( + assets: readonly CatalogAsset[] +): CatalogPlayerAsset[] { + return assets.map(mapCatalogAssetToPlayerAsset) +} + +export function mapCatalogAssetToPlayerAsset( + asset: CatalogAsset +): CatalogPlayerAsset { + if (asset.source === "apple-music-chart") { + return mapAppleMusicChartAsset(asset) + } + + return mapBlenderOpenFilmAsset(asset) +} + +export function mapStreamPresetToPlayerAsset( + preset: StreamPreset, + source: "custom-stream" | "stream-preset" = "stream-preset" +): CatalogPlayerAsset { + return { + config: toPlayerConfig(preset.config), + description: preset.description, + features: preset.features, + group: preset.group, + id: preset.id, + poster: preset.poster ?? preset.thumbnail, + source, + src: preset.src, + title: preset.title, + } +} + +function mapAppleMusicChartAsset( + asset: AppleMusicChartAsset +): CatalogPlayerAudioAsset { + return { + albumName: asset.albumName, + artistName: asset.artistName, + config: asset.config, + description: asset.description, + duration: asset.duration, + genre: asset.genre, + id: asset.id, + poster: asset.poster, + source: asset.source, + src: asset.src, + title: asset.title, + url: asset.url, + } +} + +function mapBlenderOpenFilmAsset( + asset: BlenderOpenFilmAsset +): CatalogPlayerVideoAsset { + return { + config: asset.config, + description: asset.description, + duration: asset.duration, + id: asset.id, + images: asset.images, + poster: asset.poster ?? asset.images?.thumbnail ?? asset.images?.poster, + source: asset.source, + subtitle: asset.subtitle, + title: asset.title, + year: asset.year ? String(asset.year) : undefined, + } +} + +function toPlayerConfig( + config: StreamPreset["config"] +): shaka.extern.PlayerConfiguration | undefined { + return config as shaka.extern.PlayerConfiguration | undefined +} diff --git a/apps/www/lib/catalogs/playlists.ts b/apps/www/lib/catalogs/playlists.ts new file mode 100644 index 00000000..895d1b60 --- /dev/null +++ b/apps/www/lib/catalogs/playlists.ts @@ -0,0 +1,56 @@ +import type { AppleMusicChartAsset } from "@/lib/catalogs/apple-music" +import type { BlenderOpenFilmAsset } from "@/lib/catalogs/blender-open-films" +import type { + CatalogPlayerType, + CatalogPlaylistPreset, +} from "@/lib/catalogs/types" + +import { + APPLE_MUSIC_CHARTS_PLAYLIST_ID, + fetchAppleMusicChartAssetsPage, +} from "@/lib/catalogs/apple-music" +import { + BLENDER_OPEN_FILMS_PLAYLIST_ID, + fetchBlenderOpenFilmAssets, +} from "@/lib/catalogs/blender-open-films" + +export type CatalogAsset = AppleMusicChartAsset | BlenderOpenFilmAsset + +export const CATALOG_PLAYLIST_PRESETS: CatalogPlaylistPreset[] = [ + { + count: 17, + description: "Open movies from Blender Studio", + id: BLENDER_OPEN_FILMS_PLAYLIST_ID, + name: "Blender Open Films", + type: "video", + }, + { + description: "Top songs from Apple Music for your region", + id: APPLE_MUSIC_CHARTS_PLAYLIST_ID, + name: "Apple Music Charts", + type: "audio", + }, +] + +export async function fetchPlaylistPresetAssets( + playlistId: string, + signal?: AbortSignal +): Promise { + if (playlistId === APPLE_MUSIC_CHARTS_PLAYLIST_ID) { + return (await fetchAppleMusicChartAssetsPage(1, signal)).assets + } + + if (playlistId === BLENDER_OPEN_FILMS_PLAYLIST_ID) { + return fetchBlenderOpenFilmAssets(signal) + } + + throw new Error(`Unknown playlist preset "${playlistId}".`) +} + +export function getPlaylistPresetsForType( + playerType: CatalogPlayerType +): CatalogPlaylistPreset[] { + return CATALOG_PLAYLIST_PRESETS.filter( + (playlist) => playlist.type === playerType + ) +} diff --git a/apps/www/lib/catalogs/types.ts b/apps/www/lib/catalogs/types.ts new file mode 100644 index 00000000..429f6358 --- /dev/null +++ b/apps/www/lib/catalogs/types.ts @@ -0,0 +1,20 @@ +import type shaka from "shaka-player" + +export interface CatalogBaseAsset { + config?: shaka.extern.PlayerConfiguration + description?: string + id?: string + poster?: string + src?: string + title?: string +} + +export type CatalogPlayerType = "audio" | "video" + +export interface CatalogPlaylistPreset { + count?: number + description: string + id: string + name: string + type: CatalogPlayerType +} diff --git a/apps/www/lib/catalogs/utils.ts b/apps/www/lib/catalogs/utils.ts new file mode 100644 index 00000000..cc07c7c7 --- /dev/null +++ b/apps/www/lib/catalogs/utils.ts @@ -0,0 +1,17 @@ +export function createTimeoutSignal( + signal: AbortSignal | undefined, + timeoutMs: number +): AbortSignal | undefined { + if (typeof AbortSignal.timeout !== "function") return signal + + const timeoutSignal = AbortSignal.timeout(timeoutMs) + if (!signal) return timeoutSignal + + return AbortSignal.any([signal, timeoutSignal]) +} + +export function throwIfAborted(signal?: AbortSignal): void { + if (!signal?.aborted) return + + throw new DOMException("Aborted", "AbortError") +} diff --git a/apps/www/lib/stream-presets.ts b/apps/www/lib/stream-presets.ts index 7dbfb88b..87a47ba7 100644 --- a/apps/www/lib/stream-presets.ts +++ b/apps/www/lib/stream-presets.ts @@ -52,7 +52,6 @@ export const STREAM_PRESETS: StreamPreset[] = [ format: "hls", group: "HLS", id: "mux-big-buck-bunny", - poster: "https://files.vidstack.io/sprite-fight/poster.webp", src: "https://stream.mux.com/VZtzUzGRv02OhRnZCxcNg49OilvolTqdnFLEqBsTwaxU.m3u8", title: "Big Buck Bunny", type: "video", diff --git a/apps/www/lib/utils.ts b/apps/www/lib/utils.ts index 7fc86e31..730cc3b5 100644 --- a/apps/www/lib/utils.ts +++ b/apps/www/lib/utils.ts @@ -1,9 +1,4 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} +export { cn } from "cnfast" export function isActive( url: string, diff --git a/apps/www/next-env.d.ts b/apps/www/next-env.d.ts index c4b7818f..9edff1c7 100644 --- a/apps/www/next-env.d.ts +++ b/apps/www/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/www/package.json b/apps/www/package.json index 3a878fae..1f9f7cfd 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -44,7 +44,7 @@ "async-retry": "^1.3.3", "beautiful-mermaid": "^1.1.3", "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", + "cnfast": "^0.0.8", "date-fns": "^4.1.0", "fumadocs-core": "16.9.3", "fumadocs-mdx": "15.0.10", @@ -71,7 +71,6 @@ "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "shaka-player": "^4.16.34", - "tailwind-merge": "^3.6.0", "three": "^0.184.0", "ts-morph": "27.0.2", "tw-animate-css": "^1.4.0", diff --git a/apps/www/registry/collection/registry-blocks.ts b/apps/www/registry/collection/registry-blocks.ts index 6db69ffc..57d1943b 100644 --- a/apps/www/registry/collection/registry-blocks.ts +++ b/apps/www/registry/collection/registry-blocks.ts @@ -16,16 +16,24 @@ export const blocks: Registry["items"] = [ description: "Modern seamless flat video player", files: [ { - path: `${VIDEO_PLAYER_SRC_URL}/page.tsx`, - target: "app/player/page.tsx", - type: "registry:page", + path: `${VIDEO_PLAYER_SRC_URL}/player.tsx`, + type: "registry:component", + }, + { + path: `${VIDEO_PLAYER_SRC_URL}/styles.css`, + target: "components/video-player/styles.css", + type: "registry:style", + }, + { + path: `${VIDEO_PLAYER_SRC_URL}/components/player-root-container.tsx`, + type: "registry:component", }, { - path: `${VIDEO_PLAYER_SRC_URL}/components/media-player.tsx`, + path: `${VIDEO_PLAYER_SRC_URL}/components/player-error-screen.tsx`, type: "registry:component", }, { - path: `${VIDEO_PLAYER_SRC_URL}/components/asset-metadata-overlay.tsx`, + path: `${VIDEO_PLAYER_SRC_URL}/components/top-overlay-container.tsx`, type: "registry:component", }, { @@ -89,11 +97,13 @@ export const blocks: Registry["items"] = [ meta: { iframeHeight: "750px", props: { - src: "https://ad391cc0d55b44c6a86d232548adc225.mediatailor.us-east-1.amazonaws.com/v1/master/d02fedbbc5a68596164208dd24e9b48aa60dadc7/singssai/master.m3u8", + source: + "https://ad391cc0d55b44c6a86d232548adc225.mediatailor.us-east-1.amazonaws.com/v1/master/d02fedbbc5a68596164208dd24e9b48aa60dadc7/singssai/master.m3u8", }, }, name: "video-player", registryDependencies: [ + "https://bazza.dev/r/hit-area", "dropdown-menu", "player-layout", "media", @@ -122,6 +132,7 @@ export const blocks: Registry["items"] = [ "use-playlist", "use-asset", "use-media", + "use-controls-visibility", "use-playback-source", ], type: "registry:block", @@ -138,12 +149,12 @@ export const blocks: Registry["items"] = [ description: "Compact audio player with playlist support", files: [ { - path: "blocks/audio-player/lib/media-kit.ts", - type: "registry:lib", + path: "blocks/audio-player/player.tsx", + type: "registry:component", }, { - path: "blocks/audio-player/components/media-player.tsx", - type: "registry:component", + path: "blocks/audio-player/lib/media-kit.ts", + type: "registry:lib", }, { path: "blocks/audio-player/components/audio-source.tsx", @@ -194,8 +205,8 @@ export const blocks: Registry["items"] = [ type: "registry:component", }, { - path: "blocks/audio-player/audio-player.module.css", - target: "components/audio-player/audio-player.module.css", + path: "blocks/audio-player/styles.css", + target: "components/audio-player/styles.css", type: "registry:style", }, ], @@ -204,6 +215,7 @@ export const blocks: Registry["items"] = [ }, name: "audio-player", registryDependencies: [ + "https://bazza.dev/r/hit-area", "media-provider", "media", "root-container", diff --git a/apps/www/registry/collection/registry-examples.ts b/apps/www/registry/collection/registry-examples.ts index ad105aef..a901d12c 100644 --- a/apps/www/registry/collection/registry-examples.ts +++ b/apps/www/registry/collection/registry-examples.ts @@ -1,27 +1,6 @@ import { type Registry } from "shadcn/schema" export const examples: Registry["items"] = [ - { - files: [ - { - path: "examples/player-root-demo.tsx", - type: "registry:example", - }, - ], - name: "player-root-demo", - registryDependencies: [ - "player-layout", - "media-provider", - "media", - "error-screen", - "custom-demo-controls", - "limeplay-logo", - "fallback-poster", - "root-container", - "use-asset", - ], - type: "registry:example", - }, { dependencies: ["lucide-react"], files: [ @@ -75,7 +54,11 @@ export const examples: Registry["items"] = [ }, ], name: "volume-slider-control-horizontal-demo", - registryDependencies: ["volume-control", "volume-state-control-demo"], + registryDependencies: [ + "https://bazza.dev/r/hit-area", + "volume-control", + "volume-state-control-demo", + ], type: "registry:example", }, { @@ -86,7 +69,11 @@ export const examples: Registry["items"] = [ }, ], name: "volume-slider-control-vertical-demo", - registryDependencies: ["volume-control", "volume-state-control-demo"], + registryDependencies: [ + "https://bazza.dev/r/hit-area", + "volume-control", + "volume-state-control-demo", + ], type: "registry:example", }, { @@ -109,6 +96,7 @@ export const examples: Registry["items"] = [ ], name: "timeline-control-demo", registryDependencies: [ + "https://bazza.dev/r/hit-area", "media-provider", "timeline-control", "timeline-labels", @@ -124,6 +112,7 @@ export const examples: Registry["items"] = [ ], name: "timeline-labels-demo", registryDependencies: [ + "https://bazza.dev/r/hit-area", "media-provider", "timeline-control", "timeline-labels", diff --git a/apps/www/registry/collection/registry-hooks.ts b/apps/www/registry/collection/registry-hooks.ts index 82813084..4496d9c3 100644 --- a/apps/www/registry/collection/registry-hooks.ts +++ b/apps/www/registry/collection/registry-hooks.ts @@ -36,6 +36,18 @@ export const hooks: Registry["items"] = [ ], type: "registry:hook", }, + { + files: [ + { + path: "hooks/use-controls-visibility.ts", + target: `${TARGET_BASE_PATH}/use-controls-visibility.ts`, + type: "registry:hook", + }, + ], + name: "use-controls-visibility", + registryDependencies: ["use-media", "use-playback", "media-provider"], + type: "registry:hook", + }, { dependencies: ["lodash.clamp"], devDependencies: ["@types/lodash.clamp"], diff --git a/apps/www/registry/collection/registry-ui.ts b/apps/www/registry/collection/registry-ui.ts index ba69b5ed..bf87f663 100644 --- a/apps/www/registry/collection/registry-ui.ts +++ b/apps/www/registry/collection/registry-ui.ts @@ -42,29 +42,6 @@ export const ui: Registry["items"] = [ type: "registry:ui", }, { - css: { - "@utility -focus-area-x-*": { - "--x": "--value(number) * -1", - }, - "@utility -focus-area-y-*": { - "--y": "--value(number) * -1", - }, - "@utility focus-area": { - "&:before": { - content: '""', - inset: "calc(var(--y) * 1px) calc(var(--x) * 1px)", - position: "absolute", - }, - - position: "relative", - }, - "@utility focus-area-x-*": { - "--x": "--value(number)", - }, - "@utility focus-area-y-*": { - "--y": "--value(number)", - }, - }, cssVars: { dark: { "lp-accent": "oklch(0.97 0 0)", diff --git a/apps/www/registry/default/blocks/audio-player/audio-player.module.css b/apps/www/registry/default/blocks/audio-player/audio-player.module.css deleted file mode 100644 index bc7a3842..00000000 --- a/apps/www/registry/default/blocks/audio-player/audio-player.module.css +++ /dev/null @@ -1,35 +0,0 @@ -.dark { - --background: oklch(0.247 0 129.63); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.87 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.7889 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.4202 0 0); - --accent: oklch(0.371 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.809 0.105 251.813); - --chart-2: oklch(0.623 0.214 259.815); - --chart-3: oklch(0.546 0.245 262.881); - --chart-4: oklch(0.488 0.243 264.376); - --chart-5: oklch(0.424 0.199 265.638); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); - - --lp-timeline-track-height: 2px; -} diff --git a/apps/www/registry/default/blocks/audio-player/components/audio-source.tsx b/apps/www/registry/default/blocks/audio-player/components/audio-source.tsx index c78c3711..622a5d9d 100644 --- a/apps/www/registry/default/blocks/audio-player/components/audio-source.tsx +++ b/apps/www/registry/default/blocks/audio-player/components/audio-source.tsx @@ -2,8 +2,8 @@ import * as React from "react" +import type { AudioPlayerAsset } from "@/registry/default/blocks/audio-player/player" import type { - Asset, PlayerSource, UseAssetOptions, } from "@/registry/default/hooks/use-asset" @@ -17,31 +17,6 @@ export interface AudioAssetDisplayMetadata { title: string } -export interface AudioPlayerAsset extends Asset { - albumName?: string - artistName?: string - artwork?: { - templateUrl?: string - url?: string - } - description?: string - duration?: number - features?: string[] - genre?: string - group?: string - images?: { - backdrop?: string - poster?: string - } - name?: string - playbackUrls?: PlaybackUrls - poster?: string - releaseYear?: number | string - subtitle?: string - title?: string - year?: number | string -} - export interface AudioSourceProviderProps { autoLoad?: boolean children?: React.ReactNode @@ -51,11 +26,6 @@ export interface AudioSourceProviderProps { sourceKey?: string } -export interface PlaybackUrls { - primary: string - secondary?: string -} - interface RawPlaybackResponse { expires_at: string url: string diff --git a/apps/www/registry/default/blocks/audio-player/components/fixed-timeline-control.tsx b/apps/www/registry/default/blocks/audio-player/components/fixed-timeline-control.tsx index 17c25a67..06f30560 100644 --- a/apps/www/registry/default/blocks/audio-player/components/fixed-timeline-control.tsx +++ b/apps/www/registry/default/blocks/audio-player/components/fixed-timeline-control.tsx @@ -28,7 +28,7 @@ export function TimelineControl() { group-hover/timeline:data-[orientation=horizontal]:h-1 `, "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", - "focus-area -focus-area-x-0 -focus-area-y-12" + "hit-area-x-0 hit-area-y-[12px] hit-area" )} > diff --git a/apps/www/registry/default/blocks/audio-player/components/playlist.tsx b/apps/www/registry/default/blocks/audio-player/components/playlist.tsx index 4780584a..7e7fc5bf 100644 --- a/apps/www/registry/default/blocks/audio-player/components/playlist.tsx +++ b/apps/www/registry/default/blocks/audio-player/components/playlist.tsx @@ -4,16 +4,15 @@ import { CardsThreeIcon, PlayIcon } from "@phosphor-icons/react" import { XIcon } from "lucide-react" import { type ReactNode, useCallback, useMemo, useState } from "react" +import type { AudioPlayerAsset } from "@/registry/default/blocks/audio-player/player" + import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { cn } from "@/lib/utils" -import { - type AudioPlayerAsset, - getAudioAssetMetadata, -} from "@/registry/default/blocks/audio-player/components/audio-source" +import { getAudioAssetMetadata } from "@/registry/default/blocks/audio-player/components/audio-source" import { Button } from "@/registry/default/blocks/audio-player/components/button" import { usePlayerStore } from "@/registry/default/hooks/use-player" import { usePlaylistStore } from "@/registry/default/hooks/use-playlist" diff --git a/apps/www/registry/default/blocks/audio-player/components/track-info.tsx b/apps/www/registry/default/blocks/audio-player/components/track-info.tsx index 0dcb4055..b2c8d30a 100644 --- a/apps/www/registry/default/blocks/audio-player/components/track-info.tsx +++ b/apps/www/registry/default/blocks/audio-player/components/track-info.tsx @@ -1,9 +1,8 @@ "use client" -import { - type AudioPlayerAsset, - getAudioAssetMetadata, -} from "@/registry/default/blocks/audio-player/components/audio-source" +import type { AudioPlayerAsset } from "@/registry/default/blocks/audio-player/player" + +import { getAudioAssetMetadata } from "@/registry/default/blocks/audio-player/components/audio-source" import { useAsset } from "@/registry/default/hooks/use-asset" import { LimeplayLogo } from "@/registry/default/ui/limeplay-logo" @@ -14,7 +13,7 @@ export function TrackInfo() { const metadata = getAudioAssetMetadata(asset) return ( -
+
{metadata.poster ? (
{metadata.subtitle && ( -
+
{metadata.subtitle}
)} diff --git a/apps/www/registry/default/blocks/audio-player/hooks/use-playlist-asset.ts b/apps/www/registry/default/blocks/audio-player/hooks/use-playlist-asset.ts index 165a6f9a..b7438311 100644 --- a/apps/www/registry/default/blocks/audio-player/hooks/use-playlist-asset.ts +++ b/apps/www/registry/default/blocks/audio-player/hooks/use-playlist-asset.ts @@ -3,7 +3,7 @@ import type { AudioPlayerAsset, PlaybackUrls, -} from "@/registry/default/blocks/audio-player/components/audio-source" +} from "@/registry/default/blocks/audio-player/player" import type { UseAssetReturn } from "@/registry/default/hooks/use-asset" import { useAsset } from "@/registry/default/hooks/use-asset" diff --git a/apps/www/registry/default/blocks/audio-player/components/media-player.tsx b/apps/www/registry/default/blocks/audio-player/player.tsx similarity index 57% rename from apps/www/registry/default/blocks/audio-player/components/media-player.tsx rename to apps/www/registry/default/blocks/audio-player/player.tsx index c176155f..6cbc3b21 100644 --- a/apps/www/registry/default/blocks/audio-player/components/media-player.tsx +++ b/apps/www/registry/default/blocks/audio-player/player.tsx @@ -1,18 +1,9 @@ "use client" -import type { - AudioHTMLAttributes, - ComponentPropsWithoutRef, - ReactNode, -} from "react" - import React from "react" -import type { - AudioPlayerAsset, - AudioSourceProviderProps, - PlaybackUrls, -} from "@/registry/default/blocks/audio-player/components/audio-source" +import type { TAsset } from "@/registry/default/hooks/use-asset" +import type { PlaybackSourceControllerProps } from "@/registry/default/hooks/use-playback-source" import { cn } from "@/lib/utils" import { AudioSourceProvider } from "@/registry/default/blocks/audio-player/components/audio-source" @@ -22,27 +13,54 @@ import { MediaProvider } from "@/registry/default/blocks/audio-player/lib/media- import { Media } from "@/registry/default/ui/media" import { RootContainer } from "@/registry/default/ui/root-container" -import styles from "../audio-player.module.css" +import "./styles.css" -export type { AudioPlayerAsset, PlaybackUrls } +export interface AudioPlayerAsset extends TAsset { + albumName?: string + artistName?: string + artwork?: { + templateUrl?: string + url?: string + } + description?: string + duration?: number + features?: string[] + genre?: string + group?: string + images?: { + backdrop?: string + poster?: string + } + name?: string + playbackUrls?: PlaybackUrls + poster?: string + releaseYear?: number | string + subtitle?: string + title?: string + year?: number | string +} -export interface AudioPlayerProps { - autoLoad?: boolean - children?: ReactNode +export interface AudioPlayerProps extends PlaybackSourceControllerProps { + children?: React.ReactNode className?: string debug?: boolean - initialIndex?: number - loading?: AudioSourceProviderProps["loading"] /** * Props to pass to the underlying audio element. */ - mediaProps?: Omit, "as" | "src"> - source?: AudioSourceProviderProps["source"] - sourceKey?: string + mediaProps?: Omit, "as" | "src"> + /** + * @default dark + */ + theme?: "dark" | "light" +} + +export interface PlaybackUrls { + primary: string + secondary?: string } export const AudioPlayer = React.forwardRef( - ( + function AudioPlayer( { autoLoad, children, @@ -53,9 +71,10 @@ export const AudioPlayer = React.forwardRef( mediaProps, source, sourceKey, + theme = "dark", }, ref - ) => { + ) { const { className: mediaClassName, ...safeMediaProps } = mediaProps ?? {} return ( @@ -71,14 +90,16 @@ export const AudioPlayer = React.forwardRef( aria-label="Audio player" aspectRatio={false} className={cn( - styles.dark, - "relative z-50 h-18 w-full border-t border-border bg-background", + "limeplay relative z-50 h-18 w-full border-t border-border bg-background", + theme === "dark" && "dark", className )} ref={ref} > )} + {...(safeMediaProps as React.ComponentPropsWithoutRef< + typeof Media + >)} as="audio" className={mediaClassName} /> diff --git a/apps/www/registry/default/blocks/audio-player/styles.css b/apps/www/registry/default/blocks/audio-player/styles.css new file mode 100644 index 00000000..d9644fae --- /dev/null +++ b/apps/www/registry/default/blocks/audio-player/styles.css @@ -0,0 +1,74 @@ +@import "tailwindcss"; + +.limeplay { + --background: oklch(0.985 0 0); + --foreground: oklch(0.205 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.205 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.205 0 0); + --primary: oklch(0.577 0.245 27.325); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0% 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.94 0 0); + --muted-foreground: oklch(0.439 0 0); + --accent: oklch(0.94 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0 0 0 / 12%); + --input: oklch(0 0 0 / 14%); + --ring: oklch(0.577 0.245 27.325); + --chart-1: oklch(0.577 0.245 27.325); + --chart-2: oklch(0.704 0.191 22.216); + --chart-3: oklch(0.623 0.214 259.815); + --chart-4: oklch(0.809 0.105 251.813); + --chart-5: oklch(0.488 0.243 264.376); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.205 0 0); + --sidebar-primary: oklch(0.577 0.245 27.325); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.94 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0 0 0 / 12%); + --sidebar-ring: oklch(0.577 0.245 27.325); + + --lp-timeline-track-height: 2px; +} + +.dark .limeplay, +.limeplay.dark { + --background: oklch(0.247 0 129.63); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.87 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.7889 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.4202 0 0); + --accent: oklch(0.371 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.809 0.105 251.813); + --chart-2: oklch(0.623 0.214 259.815); + --chart-3: oklch(0.546 0.245 262.881); + --chart-4: oklch(0.488 0.243 264.376); + --chart-5: oklch(0.424 0.199 265.638); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); + + --lp-timeline-track-height: 2px; +} diff --git a/apps/www/registry/default/blocks/video-player/components/asset-metadata-overlay.tsx b/apps/www/registry/default/blocks/video-player/components/asset-metadata-overlay.tsx deleted file mode 100644 index 364a6265..00000000 --- a/apps/www/registry/default/blocks/video-player/components/asset-metadata-overlay.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client" - -import type { VideoPlayerAsset } from "@/registry/default/blocks/video-player/components/media-player" - -import { useAsset } from "@/registry/default/hooks/use-asset" -import { ControlsTopContainer } from "@/registry/default/ui/player-layout" - -export function AssetMetadataOverlay() { - const { currentItem } = useAsset() - const asset = currentItem?.properties - const description = asset?.description?.trim() - const title = asset?.title?.trim() - - if (!title && !description) return null - - return ( - <> -
- -
- {title && ( -

- {title} -

- )} - {description && ( -

- {description} -

- )} -
-
- - ) -} diff --git a/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx b/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx index c58f02e1..8c2d9d47 100644 --- a/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx +++ b/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx @@ -1,24 +1,60 @@ import { CaptionsStateControl } from "@/registry/default/blocks/video-player/components/captions-state-control" import { PictureInPictureControl } from "@/registry/default/blocks/video-player/components/pip-control" -import { PlaybackRateControl } from "@/registry/default/blocks/video-player/components/playback-rate-control" import { PlaybackStateControl } from "@/registry/default/blocks/video-player/components/playback-state-control" import { Playlist } from "@/registry/default/blocks/video-player/components/playlist" import { PlaylistNextControl } from "@/registry/default/blocks/video-player/components/playlist-navigation-controls" import { TimelineSliderControl } from "@/registry/default/blocks/video-player/components/timeline-slider-control" import { VolumeGroupControl } from "@/registry/default/blocks/video-player/components/volume-group-control" +import { CONTROLS_FORCE_VISIBLE_ATTRIBUTE } from "@/registry/default/hooks/use-controls-visibility" import * as Layout from "@/registry/default/ui/player-layout" -export function BottomControls() { +export interface BottomControlsProps { + className?: string +} + +export function BottomControls({ className }: BottomControlsProps) { return ( - - - - - - - - - + +
+
+ + + +
+ +
+ +
+ +
+ + + +
+
) } diff --git a/apps/www/registry/default/blocks/video-player/components/button.tsx b/apps/www/registry/default/blocks/video-player/components/button.tsx index 859f4a79..1338a9c5 100644 --- a/apps/www/registry/default/blocks/video-player/components/button.tsx +++ b/apps/www/registry/default/blocks/video-player/components/button.tsx @@ -6,11 +6,18 @@ import { cn } from "@/lib/utils" const buttonVariants = cva( ` - inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-colors + inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 rounded-md text-xs font-medium whitespace-nowrap + transition-[background-color,color,box-shadow,scale,backdrop-filter] duration-150 ease-out focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary/50 + active:scale-[0.96] disabled:pointer-events-none disabled:opacity-50 + motion-reduce:transition-none + motion-reduce:active:scale-100 + @md/root:gap-2 @md/root:text-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 - [&_svg:not([class*='size-'])]:size-4 + [&_svg:not([class*='size-'])]:size-3.5 + @md/root:[&_svg:not([class*='size-'])]:size-4 + @3xl/root:[&_svg:not([class*='size-'])]:size-4.5 `, { defaultVariants: { @@ -20,17 +27,33 @@ const buttonVariants = cva( variants: { size: { default: ` - h-9 px-4 py-2 - has-[>svg]:px-3 + h-8 px-3 py-1.5 + has-[>svg]:px-2.5 + @md/root:h-9 @md/root:px-4 @md/root:py-2 + @md/root:has-[>svg]:px-3 + @3xl/root:h-10 @3xl/root:px-5 + @3xl/root:has-[>svg]:px-4 + `, + icon: ` + size-7 rounded-lg p-1.5 + @md/root:size-8 @md/root:p-2 + @3xl/root:size-9 `, - icon: "size-8 rounded-md p-2", lg: ` - h-10 rounded-md px-6 - has-[>svg]:px-4 + h-9 rounded-md px-4 + has-[>svg]:px-3 + @md/root:h-10 @md/root:px-6 + @md/root:has-[>svg]:px-4 + @3xl/root:h-11 @3xl/root:px-7 + @3xl/root:has-[>svg]:px-5 `, sm: ` - h-8 gap-1.5 rounded-md px-3 - has-[>svg]:px-2.5 + h-7 gap-1.5 rounded-md px-2.5 text-xs + has-[>svg]:px-2 + @md/root:h-8 @md/root:px-3 @md/root:text-sm + @md/root:has-[>svg]:px-2.5 + @3xl/root:h-9 @3xl/root:px-3.5 + @3xl/root:has-[>svg]:px-3 `, }, variant: { @@ -40,9 +63,9 @@ const buttonVariants = cva( `, ghost: `hover:bg-accent hover:text-accent-foreground`, glass: ` - bg-transparent text-secondary-foreground - hover:bg-primary/10 - active:scale-[0.97] + bg-transparent text-foreground + hover:bg-foreground/10 + focus-visible:bg-background/20 `, link: ` text-primary underline-offset-4 diff --git a/apps/www/registry/default/blocks/video-player/components/captions-state-control.tsx b/apps/www/registry/default/blocks/video-player/components/captions-state-control.tsx index e1fe35e6..2fcf8b61 100644 --- a/apps/www/registry/default/blocks/video-player/components/captions-state-control.tsx +++ b/apps/www/registry/default/blocks/video-player/components/captions-state-control.tsx @@ -12,7 +12,7 @@ export function CaptionsStateControl() { return ( ) diff --git a/apps/www/registry/default/blocks/video-player/components/media-player.tsx b/apps/www/registry/default/blocks/video-player/components/media-player.tsx deleted file mode 100644 index 10b21df9..00000000 --- a/apps/www/registry/default/blocks/video-player/components/media-player.tsx +++ /dev/null @@ -1,134 +0,0 @@ -"use client" - -import { RotateCwIcon } from "lucide-react" -import React from "react" - -import type { - Asset, - PlayerSource, - UseAssetOptions, -} from "@/registry/default/hooks/use-asset" - -import { cn } from "@/lib/utils" -import { AssetMetadataOverlay } from "@/registry/default/blocks/video-player/components/asset-metadata-overlay" -import { BottomControls } from "@/registry/default/blocks/video-player/components/bottom-controls" -import { Button } from "@/registry/default/blocks/video-player/components/button" -import { MediaProvider } from "@/registry/default/blocks/video-player/lib/media-kit" -import { useAsset } from "@/registry/default/hooks/use-asset" -import { usePlaybackStore } from "@/registry/default/hooks/use-playback" -import { PlaybackSourceController } from "@/registry/default/hooks/use-playback-source" -import { CaptionsContainer } from "@/registry/default/ui/captions" -import { ErrorScreen } from "@/registry/default/ui/error-screen" -import { FallbackPoster } from "@/registry/default/ui/fallback-poster" -import { LimeplayLogo } from "@/registry/default/ui/limeplay-logo" -import { Media } from "@/registry/default/ui/media" -import * as Layout from "@/registry/default/ui/player-layout" -import { RootContainer } from "@/registry/default/ui/root-container" - -export interface VideoPlayerAsset extends Asset { - description?: string - poster?: string - title?: string - year?: string -} - -export interface VideoPlayerProps { - autoLoad?: boolean - children?: React.ReactNode - className?: string - controlsHideDelay?: number - debug?: boolean - hideCursorOnIdle?: boolean - initialIndex?: number - loading?: UseAssetOptions - /** - * Props to pass to the underlying video element. - */ - mediaProps?: Omit, "as" | "src"> - source?: PlayerSource - sourceKey?: string -} - -export const VideoPlayer = React.forwardRef( - ( - { - autoLoad, - children, - className, - controlsHideDelay, - debug, - hideCursorOnIdle, - initialIndex, - loading, - mediaProps, - source, - sourceKey, - }, - ref - ) => { - const { className: mediaClassName, ...safeMediaProps } = mediaProps ?? {} - - return ( - - - - - - - - - )} - as="video" - className={cn("size-full object-cover", mediaClassName)} - /> - {children} - - - - - - - - - - ) - } -) - -VideoPlayer.displayName = "VideoPlayer" - -function PlayerErrorScreen() { - const error = usePlaybackStore((s) => s.error) - const status = usePlaybackStore((s) => s.status) - const { currentItem, loadAsset } = useAsset() - - const retryStream = React.useCallback(() => { - if (!currentItem) return - void loadAsset(currentItem.properties) - }, [currentItem, loadAsset]) - - if (status !== "error") return null - - return ( - - - - ) -} diff --git a/apps/www/registry/default/blocks/video-player/components/pip-control.tsx b/apps/www/registry/default/blocks/video-player/components/pip-control.tsx index c8202d42..dcb38417 100644 --- a/apps/www/registry/default/blocks/video-player/components/pip-control.tsx +++ b/apps/www/registry/default/blocks/video-player/components/pip-control.tsx @@ -15,7 +15,7 @@ export function PictureInPictureControl() { diff --git a/apps/www/registry/default/blocks/video-player/components/playback-rate-control.tsx b/apps/www/registry/default/blocks/video-player/components/playback-rate-control.tsx index c877a935..eb7d7992 100644 --- a/apps/www/registry/default/blocks/video-player/components/playback-rate-control.tsx +++ b/apps/www/registry/default/blocks/video-player/components/playback-rate-control.tsx @@ -5,22 +5,27 @@ import React from "react" import * as Select from "@/components/ui/select" import * as PlaybackRate from "@/registry/default/ui/playback-rate" +import { Button } from "./button" + export function PlaybackRateControl() { return ( - - - + + + + + source?: PlayerSource + sourceKey?: string +} + +export function PlayerErrorScreen({ + initialIndex, + loading, + source, + sourceKey, +}: PlayerErrorScreenProps) { + const error = usePlaybackStore((s) => s.error) + const status = usePlaybackStore((s) => s.status) + const { currentItem, loadAsset, loadSource } = useAsset() + + const retryStream = React.useCallback(() => { + if (currentItem) { + void loadAsset(currentItem.properties) + return + } + + if (source === undefined) return + + loadSource(source, { + initialIndex, + loading, + sourceKey, + }) + }, [ + currentItem, + initialIndex, + loadAsset, + loading, + loadSource, + source, + sourceKey, + ]) + + if (status !== "error") return null + + return ( + + {currentItem && ( + + )} + + ) +} diff --git a/apps/www/registry/default/blocks/video-player/components/player-root-container.tsx b/apps/www/registry/default/blocks/video-player/components/player-root-container.tsx new file mode 100644 index 00000000..04eda406 --- /dev/null +++ b/apps/www/registry/default/blocks/video-player/components/player-root-container.tsx @@ -0,0 +1,47 @@ +"use client" + +import React from "react" + +import type { RootContainerProps } from "@/registry/default/ui/root-container" + +import { cn } from "@/lib/utils" +import { useControlsVisibility } from "@/registry/default/hooks/use-controls-visibility" +import { RootContainer } from "@/registry/default/ui/root-container" + +export type PlayerRootContainerLayout = "aspect" | "fill" + +export interface PlayerRootContainerProps extends RootContainerProps { + /** + * Controls how the player frame gets its size. + * + * `aspect` keeps the default aspect-ratio driven player box. + * `fill` lets a height-constrained parent own the player box. + */ + layout?: PlayerRootContainerLayout +} + +export const PlayerRootContainer = React.forwardRef< + HTMLDivElement, + PlayerRootContainerProps +>(function PlayerRootContainer(props, ref) { + const { aspectRatio, className, layout = "aspect", ...rootProps } = props + const controlsVisibility = useControlsVisibility() + + return ( + + ) +}) + +PlayerRootContainer.displayName = "PlayerRootContainer" diff --git a/apps/www/registry/default/blocks/video-player/components/playlist-navigation-controls.tsx b/apps/www/registry/default/blocks/video-player/components/playlist-navigation-controls.tsx index a9b0c0ef..0b6ecb1e 100644 --- a/apps/www/registry/default/blocks/video-player/components/playlist-navigation-controls.tsx +++ b/apps/www/registry/default/blocks/video-player/components/playlist-navigation-controls.tsx @@ -3,16 +3,22 @@ import { SkipForwardIcon } from "@phosphor-icons/react" import { Button } from "@/registry/default/blocks/video-player/components/button" +import { + AssetSourceType, + useAssetStore, +} from "@/registry/default/hooks/use-asset" import { usePlaylistStore } from "@/registry/default/hooks/use-playlist" export function PlaylistNextControl() { const next = usePlaylistStore((state) => state.next) + const hasPlaylist = useAssetStore( + (state) => state.sourceType === AssetSourceType.Playlist + ) const hasNext = usePlaylistStore((state) => { if (state.repeatMode === "all" && state.queue.length > 0) return true return state.getNextIndex() !== -1 }) - const hasPlaylist = usePlaylistStore((state) => state.queue.length > 0) if (!hasPlaylist) return null diff --git a/apps/www/registry/default/blocks/video-player/components/playlist.tsx b/apps/www/registry/default/blocks/video-player/components/playlist.tsx index e116d23a..d0a4673d 100644 --- a/apps/www/registry/default/blocks/video-player/components/playlist.tsx +++ b/apps/www/registry/default/blocks/video-player/components/playlist.tsx @@ -3,7 +3,7 @@ import { CardsThreeIcon, PlayIcon } from "@phosphor-icons/react" import { useEffect, useMemo } from "react" -import type { VideoPlayerAsset } from "@/registry/default/blocks/video-player/components/media-player" +import type { VideoPlayerAsset } from "@/registry/default/blocks/video-player/player" import { DropdownMenu, diff --git a/apps/www/registry/default/blocks/video-player/components/timeline-slider-control.tsx b/apps/www/registry/default/blocks/video-player/components/timeline-slider-control.tsx index 2dfc7668..be50b6ed 100644 --- a/apps/www/registry/default/blocks/video-player/components/timeline-slider-control.tsx +++ b/apps/www/registry/default/blocks/video-player/components/timeline-slider-control.tsx @@ -14,6 +14,8 @@ import { Remaining, } from "@/registry/default/ui/timeline-labels" +const LIVE_DELAY_VISIBLE_SEC = 3 + export function TimelineSliderControl() { const [showRemaining, setShowRemaining] = useState(false) const liveLatency = useTimelineStore((state) => state.liveLatency) @@ -21,12 +23,17 @@ export function TimelineSliderControl() { const player = usePlayerStore((state) => state.instance) return ( -
- {!isLive && } +
+ {!isLive && }
@@ -49,6 +56,7 @@ export function TimelineSliderControl() { `} showWithHover > + {isLive && <>−} {!isLive && ( <> @@ -60,49 +68,57 @@ export function TimelineSliderControl() {
{!isLive && ( - + )} - {liveLatency && player && liveLatency > 1 && ( + {isLive && ( <> - - + {liveLatency && player && liveLatency >= LIVE_DELAY_VISIBLE_SEC && ( + <> + + + + )} + {liveLatency && liveLatency < LIVE_DELAY_VISIBLE_SEC && ( +
+ LIVE +
+ )} )} - {liveLatency && liveLatency <= 1 && ( -
-
- LIVE -
- )}
) } diff --git a/apps/www/registry/default/blocks/video-player/components/top-overlay-container.tsx b/apps/www/registry/default/blocks/video-player/components/top-overlay-container.tsx new file mode 100644 index 00000000..91492812 --- /dev/null +++ b/apps/www/registry/default/blocks/video-player/components/top-overlay-container.tsx @@ -0,0 +1,51 @@ +"use client" + +import type { VideoPlayerAsset } from "@/registry/default/blocks/video-player/player" + +import { useAsset } from "@/registry/default/hooks/use-asset" +import { ControlsTopContainer } from "@/registry/default/ui/player-layout" + +export interface TopOverlayContainerProps { + className?: string +} + +export function TopOverlayContainer({ className }: TopOverlayContainerProps) { + const { currentItem } = useAsset() + const asset = currentItem?.properties + const description = asset?.description + const title = asset?.title?.trim() + + if (!title && !description) return null + + return ( + +
+ {title && ( +

+ {title} +

+ )} + {description && ( +

+ {description} +

+ )} +
+
+ ) +} diff --git a/apps/www/registry/default/blocks/video-player/components/volume-group-control.tsx b/apps/www/registry/default/blocks/video-player/components/volume-group-control.tsx index 6db1fb46..66306f73 100644 --- a/apps/www/registry/default/blocks/video-player/components/volume-group-control.tsx +++ b/apps/www/registry/default/blocks/video-player/components/volume-group-control.tsx @@ -1,17 +1,32 @@ -import { VolumeSliderControl } from "@/registry/default/blocks/video-player/components/volume-slider-control" +import { cn } from "@/lib/utils" +import { + HorizontalVolumeSliderControl, + VerticalVolumeSliderControl, +} from "@/registry/default/blocks/video-player/components/volume-slider-control" import { VolumeStateControl } from "@/registry/default/blocks/video-player/components/volume-state-control" export function VolumeGroupControl() { return (
- - +
+
+ +
+ +
) } diff --git a/apps/www/registry/default/blocks/video-player/components/volume-slider-control.tsx b/apps/www/registry/default/blocks/video-player/components/volume-slider-control.tsx index f4c29226..6bbeef56 100644 --- a/apps/www/registry/default/blocks/video-player/components/volume-slider-control.tsx +++ b/apps/www/registry/default/blocks/video-player/components/volume-slider-control.tsx @@ -1,23 +1,51 @@ import { cn } from "@/lib/utils" import * as VolumeSlider from "@/registry/default/ui/volume-control" -export function VolumeSliderControl() { +export function HorizontalVolumeSliderControl() { + return ( + + ) +} + +export function VerticalVolumeSliderControl() { + return ( + + ) +} + +function VolumeSliderControlRoot({ + className, + orientation, +}: { + className?: string + orientation: "horizontal" | "vertical" +}) { return ( - + ) } diff --git a/apps/www/registry/default/blocks/video-player/page.tsx b/apps/www/registry/default/blocks/video-player/page.tsx deleted file mode 100644 index 55fc2ce9..00000000 --- a/apps/www/registry/default/blocks/video-player/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { VideoPlayer } from "@/registry/default/blocks/video-player/components/media-player" - -const src = - "https://ad391cc0d55b44c6a86d232548adc225.mediatailor.us-east-1.amazonaws.com/v1/master/d02fedbbc5a68596164208dd24e9b48aa60dadc7/singssai/master.m3u8" - -export default function Page() { - return ( -
- -
- ) -} diff --git a/apps/www/registry/default/blocks/video-player/player.tsx b/apps/www/registry/default/blocks/video-player/player.tsx new file mode 100644 index 00000000..58027272 --- /dev/null +++ b/apps/www/registry/default/blocks/video-player/player.tsx @@ -0,0 +1,169 @@ +"use client" + +import React from "react" + +import type { PlaybackSourceControllerProps } from "@/registry/default/hooks/use-playback-source" + +import { cn } from "@/lib/utils" +import { BottomControls } from "@/registry/default/blocks/video-player/components/bottom-controls" +import { PlayerErrorScreen } from "@/registry/default/blocks/video-player/components/player-error-screen" +import { + PlayerRootContainer, + type PlayerRootContainerLayout, +} from "@/registry/default/blocks/video-player/components/player-root-container" +import { TopOverlayContainer } from "@/registry/default/blocks/video-player/components/top-overlay-container" +import { MediaProvider } from "@/registry/default/blocks/video-player/lib/media-kit" +import { type TAsset, useAsset } from "@/registry/default/hooks/use-asset" +import { PlaybackSourceController } from "@/registry/default/hooks/use-playback-source" +import { CaptionsContainer } from "@/registry/default/ui/captions" +import { FallbackPoster } from "@/registry/default/ui/fallback-poster" +import { LimeplayLogo } from "@/registry/default/ui/limeplay-logo" +import { Media } from "@/registry/default/ui/media" +import * as Layout from "@/registry/default/ui/player-layout" + +import "./styles.css" + +export interface VideoPlayerAsset extends TAsset { + description?: string + poster?: string + title?: string + year?: string +} + +export interface VideoPlayerProps extends PlaybackSourceControllerProps { + children?: React.ReactNode + className?: string + debug?: boolean + /** + * Controls how the player frame gets its size. + * + * `aspect` keeps the default 16:9 box. `fill` lets a height-constrained + * parent own the player size. + * + * @default "aspect" + */ + layout?: PlayerRootContainerLayout + /** + * Props to pass to the underlying video element. + */ + mediaProps?: Omit, "as" | "src"> +} + +export const VideoPlayer = React.forwardRef( + function VideoPlayer( + { + autoLoad, + children, + className, + debug, + initialIndex, + layout = "aspect", + loading, + mediaProps, + source, + sourceKey, + }, + ref + ) { + return ( + + + + + + + + + + {children} + + + + + + + + + + + ) + } +) + +VideoPlayer.displayName = "VideoPlayer" + +function CurrentAssetMedia({ + className, + poster, + ...mediaProps +}: NonNullable) { + const { currentItem } = useAsset() + const currentPoster = currentItem?.properties.poster + + return ( + )} + as="video" + className={cn("size-full object-contain", className)} + poster={poster ?? currentPoster} + /> + ) +} diff --git a/apps/www/registry/default/blocks/video-player/styles.css b/apps/www/registry/default/blocks/video-player/styles.css new file mode 100644 index 00000000..4353c831 --- /dev/null +++ b/apps/www/registry/default/blocks/video-player/styles.css @@ -0,0 +1,48 @@ +@import "tailwindcss"; + +:root { + --lp-timeline-track-height: 4px; + --lp-timeline-track-height-active: 7px; + + --lp-controls-fade-stops: + hsla(0, 0%, 91%, 0) 0%, hsla(0, 0%, 91%, 0.002) 10.6%, + hsla(0, 0%, 91%, 0.008) 19.7%, hsla(0, 0%, 91%, 0.019) 27.6%, + hsla(0, 0%, 91%, 0.035) 34.4%, hsla(0, 0%, 91%, 0.057) 40.4%, + hsla(0, 0%, 91%, 0.086) 45.7%, hsla(0, 0%, 91%, 0.122) 50.6%, + hsla(0, 0%, 91%, 0.165) 55.3%, hsla(0, 0%, 91%, 0.217) 60%, + hsla(0, 0%, 91%, 0.278) 65%, hsla(0, 0%, 91%, 0.349) 70.3%, + hsla(0, 0%, 91%, 0.43) 76.3%, hsla(0, 0%, 91%, 0.522) 83.1%, + hsla(0, 0%, 91%, 0.625) 90.9%, hsla(0, 0%, 91%, 0.74) 100%; + + --background-image-lp-controls-fade-bottom: linear-gradient( + to bottom, + var(--lp-controls-fade-stops) + ); + + --background-image-lp-controls-fade-top: linear-gradient( + to top, + var(--lp-controls-fade-stops) + ); +} + +.dark { + --lp-controls-fade-stops: + hsla(0, 0%, 0%, 0) 0%, hsla(0, 0%, 0%, 0.009) 8.1%, + hsla(0, 0%, 0%, 0.035) 15.5%, hsla(0, 0%, 0%, 0.074) 22.5%, + hsla(0, 0%, 0%, 0.125) 29%, hsla(0, 0%, 0%, 0.184) 35.3%, + hsla(0, 0%, 0%, 0.25) 41.2%, hsla(0, 0%, 0%, 0.32) 47.1%, + hsla(0, 0%, 0%, 0.39) 52.9%, hsla(0, 0%, 0%, 0.46) 58.8%, + hsla(0, 0%, 0%, 0.526) 64.7%, hsla(0, 0%, 0%, 0.585) 71%, + hsla(0, 0%, 0%, 0.636) 77.5%, hsla(0, 0%, 0%, 0.675) 84.5%, + hsla(0, 0%, 0%, 0.701) 91.9%, hsla(0, 0%, 0%, 0.71) 100%; +} + +@theme inline { + --ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94); + --background-image-lp-controls-fade-top: var( + --background-image-lp-controls-fade-top + ); + --background-image-lp-controls-fade-bottom: var( + --background-image-lp-controls-fade-bottom + ); +} diff --git a/apps/www/registry/default/examples/picture-in-picture-control-demo.tsx b/apps/www/registry/default/examples/picture-in-picture-control-demo.tsx index 43624c07..b0fcf1f1 100644 --- a/apps/www/registry/default/examples/picture-in-picture-control-demo.tsx +++ b/apps/www/registry/default/examples/picture-in-picture-control-demo.tsx @@ -2,7 +2,7 @@ import { PictureInPictureIcon } from "@phosphor-icons/react" -import { Button } from "@/registry/default/blocks/video-player/components/button" +import { Button } from "@/components/ui/button" import { usePictureInPictureStore } from "@/registry/default/hooks/use-picture-in-picture" import { PictureInPictureControl } from "@/registry/default/ui/picture-in-picture-control" diff --git a/apps/www/registry/default/examples/timeline-control-demo.tsx b/apps/www/registry/default/examples/timeline-control-demo.tsx index 81606a34..7d0c7023 100644 --- a/apps/www/registry/default/examples/timeline-control-demo.tsx +++ b/apps/www/registry/default/examples/timeline-control-demo.tsx @@ -18,7 +18,7 @@ export function TimelineControlDemo() {
diff --git a/apps/www/registry/default/examples/timeline-labels-demo.tsx b/apps/www/registry/default/examples/timeline-labels-demo.tsx index c7c6922e..d8a6835f 100644 --- a/apps/www/registry/default/examples/timeline-labels-demo.tsx +++ b/apps/www/registry/default/examples/timeline-labels-demo.tsx @@ -12,7 +12,7 @@ export function TimelineLabelsDemo() {
diff --git a/apps/www/registry/default/examples/volume-slider-control-horizontal-demo.tsx b/apps/www/registry/default/examples/volume-slider-control-horizontal-demo.tsx index 37e33830..e65b4cd2 100644 --- a/apps/www/registry/default/examples/volume-slider-control-horizontal-demo.tsx +++ b/apps/www/registry/default/examples/volume-slider-control-horizontal-demo.tsx @@ -6,7 +6,7 @@ export function VolumeSliderControlHorizontalDemo() {
diff --git a/apps/www/registry/default/examples/volume-slider-control-vertical-demo.tsx b/apps/www/registry/default/examples/volume-slider-control-vertical-demo.tsx index f425962d..49ab042e 100644 --- a/apps/www/registry/default/examples/volume-slider-control-vertical-demo.tsx +++ b/apps/www/registry/default/examples/volume-slider-control-vertical-demo.tsx @@ -5,7 +5,7 @@ export function VolumeSliderControlVerticalDemo() { return (
diff --git a/apps/www/registry/default/hooks/use-asset.ts b/apps/www/registry/default/hooks/use-asset.ts index d725f3f2..84c9ff96 100644 --- a/apps/www/registry/default/hooks/use-asset.ts +++ b/apps/www/registry/default/hooks/use-asset.ts @@ -32,26 +32,10 @@ import { useMediaFeatureStore, } from "@/registry/default/ui/media-provider" -/** Metadata and playback configuration for one playable media item. */ -export interface Asset { - /** Shaka Player configuration applied before this asset loads. */ - config?: shaka.extern.PlayerConfiguration - /** Optional description for player UI. */ - description?: string - /** Stable identifier used for playlist, preload, and recovery state. */ - id?: string - /** Poster artwork shown by player UI. */ - poster?: string - /** Direct playback URL. Use `resolveSource` when this is resolved lazily. */ - src?: string - /** Display title for player UI. */ - title?: string -} - /** Context passed to a custom asset load handler. */ -export interface AssetLoadContext { +export interface AssetLoadContext { /** Asset being loaded. */ - asset: TAsset + asset: TItem /** Run Limeplay's default Shaka load behavior. */ loadDefault: ( source?: null | PlaybackSource | shaka.media.PreloadManager | string, @@ -74,9 +58,9 @@ export interface AssetLoadContext { } /** Context passed to a custom asset preload handler. */ -export interface AssetPreloadContext { +export interface AssetPreloadContext { /** Asset being preloaded. */ - asset: TAsset + asset: TItem /** Active Shaka Player instance. */ player: shaka.Player /** Run Limeplay's default Shaka preload behavior. */ @@ -121,19 +105,19 @@ export const AssetRecoveryAction = { /** Asset lifecycle events emitted through `useMediaEvents`. */ export interface AssetEvents { /** Emitted when the active playlist asset changes. */ - assetchange: PlaylistChangeEvent + assetchange: PlaylistChangeEvent /** Emitted after an asset loads successfully. */ - assetloaded: { asset: Asset } + assetloaded: { asset: TAsset } /** Emitted when asset loading fails. */ - assetloaderror: { asset: Asset; error: Error } + assetloaderror: { asset: TAsset; error: Error } /** Emitted before asset loading starts. */ - assetloadstart: { asset: Asset; sourceType: AssetSourceType | null } + assetloadstart: { asset: TAsset; sourceType: AssetSourceType | null } /** Emitted after an asset preloads successfully. */ - assetpreloaded: { asset: Asset } + assetpreloaded: { asset: TAsset } /** Emitted when asset preloading fails. */ - assetpreloaderror: { asset: Asset; error: Error } + assetpreloaderror: { asset: TAsset; error: Error } /** Emitted before asset preloading starts. */ - assetpreloadstart: { asset: Asset } + assetpreloadstart: { asset: TAsset } } /** Decision returned from load-error recovery. */ @@ -143,11 +127,11 @@ export type AssetLoadDecision = | typeof AssetRecoveryAction.Stop /** Options for loading a source into the asset feature. */ -export interface AssetLoadSourceOptions { +export interface AssetLoadSourceOptions { /** Initial playlist index when the source is an array. */ initialIndex?: number /** Session-local loading configuration. */ - loading?: UseAssetOptions + loading?: UseAssetOptions /** Stable key used by source controllers to identify equivalent sources. */ sourceKey?: string /** Explicit source type override for UI and recovery behavior. */ @@ -155,10 +139,10 @@ export interface AssetLoadSourceOptions { } /** Decision returned from playback-error recovery. */ -export type AssetPlaybackRecoveryDecision = +export type AssetPlaybackRecoveryDecision = | { action: typeof AssetRecoveryAction.Reload - asset?: TAsset + asset?: TItem startTime?: number } | { action: typeof AssetRecoveryAction.Skip } @@ -168,11 +152,11 @@ export type AssetRecoveryAction = (typeof AssetRecoveryAction)[keyof typeof AssetRecoveryAction] /** Active source loading session. */ -export interface AssetSession { +export interface AssetSession { /** Unique session ID generated for each explicit load. */ id: string /** Loading configuration attached to this session. */ - loading?: UseAssetOptions + loading?: UseAssetOptions /** Stable source key, when provided by the caller. */ sourceKey?: string /** Source type for this session. */ @@ -180,8 +164,8 @@ export interface AssetSession { } /** Returns a stable ID for an asset when `asset.id` is not enough. */ -export type GetAssetId = ( - asset: TAsset, +export type GetAssetId = ( + asset: TItem, context: { index?: number origin: AssetSourceOrigin @@ -189,11 +173,11 @@ export type GetAssetId = ( ) => null | string | undefined /** Asset normalized for playlist storage. */ -export interface NormalizedAsset { +export interface NormalizedAsset { /** Stable playlist item ID. */ id: string /** Original asset object. */ - properties: TAsset + properties: TItem } /** Resolved playback source passed to Shaka Player. */ @@ -205,20 +189,20 @@ export interface PlaybackSource { } /** Public source input accepted by blocks and `loadSource`. */ -export type PlayerSource = - | readonly TAsset[] +export type PlayerSource = + | readonly TItem[] | string - | TAsset + | TItem /** Lazily resolves a playable source for an asset. */ -export type ResolveSource = ( - context: ResolveSourceContext +export type ResolveSource = ( + context: ResolveSourceContext ) => MaybePromise /** Context passed to `resolveSource`. */ -export interface ResolveSourceContext { +export interface ResolveSourceContext { /** Asset whose source should be resolved. */ - asset: TAsset + asset: TItem /** Previous error for retry-aware resolution. */ previousError?: unknown /** Number of retry attempts in the active session. */ @@ -229,86 +213,99 @@ export interface ResolveSourceContext { startTime?: number } +/** Minimal playback configuration for one playable media item. */ +export interface TAsset { + /** Shaka Player configuration applied before this asset loads. */ + config?: shaka.extern.PlayerConfiguration + /** Stable identifier used for playlist, preload, and recovery state. */ + id?: string + /** Direct playback URL. Use `resolveSource` when this is resolved lazily. */ + src?: string +} + /** Store actions exposed by the asset feature. */ export interface UseAssetActions { /** Current source loading session. */ activeSession: AssetSession | null /** Load a single asset immediately. */ - loadAsset: (asset: Asset, startTime?: number) => Promise + loadAsset: (asset: TAsset, startTime?: number) => Promise /** Load assets into the playlist and start from `startIndex`. */ loadPlaylist: ( - assets: Asset[], + assets: TAsset[], startIndex?: number, sourceType?: AssetSourceType, - options?: Omit, "initialIndex" | "sourceType"> + options?: Omit< + AssetLoadSourceOptions, + "initialIndex" | "sourceType" + > ) => void /** Normalize and load a URL, asset, or asset array. */ loadSource: ( - source: PlayerSource, - options?: AssetLoadSourceOptions + source: PlayerSource, + options?: AssetLoadSourceOptions ) => void /** Preload one asset for faster future playback. */ - preloadAsset: (asset: Asset) => Promise + preloadAsset: (asset: TAsset) => Promise /** Preload the next playlist item, when one exists. */ preloadNext: () => Promise } /** Alias for custom load handler context. */ -export type UseAssetLoadContext = AssetLoadContext +export type UseAssetLoadContext = AssetLoadContext /** Custom loader hooks for replacing or extending default Shaka behavior. */ -export interface UseAssetLoader { +export interface UseAssetLoader { /** Custom load implementation. Call `context.loadDefault()` to reuse defaults. */ - load?: (context: AssetLoadContext) => Promise + load?: (context: AssetLoadContext) => Promise /** Custom preload implementation. Call `context.preloadDefault()` to reuse defaults. */ preload?: ( - context: AssetPreloadContext + context: AssetPreloadContext ) => Promise } /** Session-local loading options for asset source loading. */ -export interface UseAssetOptions { +export interface UseAssetOptions { /** Allow autoplay on the first loaded asset when the media element requests autoplay. */ autoplayFirst?: boolean /** Resolve a stable asset ID when `asset.id` is missing or insufficient. */ - getAssetId?: GetAssetId + getAssetId?: GetAssetId /** Custom load and preload handlers. */ - loader?: UseAssetLoader + loader?: UseAssetLoader /** Maximum retry attempts used by `recover.loadError`. */ maxRetries?: number /** Recovery policies for load and playback errors. */ recover?: { /** Decide whether to retry, skip, or stop after a load error. */ loadError?: ( - asset: TAsset, + asset: TItem, error: unknown, context: { hasNext: boolean; retryCount: number } ) => AssetLoadDecision /** Decide whether to reload, skip, or stop after a playback error. */ playbackError?: ( - asset: TAsset, + asset: TItem, error: Error, context: { currentTime: number } - ) => Promise> + ) => Promise> } /** Lazily resolve the playback URL or source config for an asset. */ - resolveSource?: ResolveSource + resolveSource?: ResolveSource } /** Alias for custom preload handler context. */ -export type UseAssetPreloadContext = - AssetPreloadContext +export type UseAssetPreloadContext = + AssetPreloadContext /** Values and actions returned by `useAsset`. */ -export interface UseAssetReturn { +export interface UseAssetReturn { /** Cancel an active preload by asset ID. */ cancelPreload: (assetId: string) => void /** Current playlist index. */ currentIndex: number /** Current playlist item. */ - currentItem: null | { id: string; properties: TAsset } + currentItem: null | { id: string; properties: TItem } /** Return a playlist item by ID. */ - getItem: (id: string) => null | { id: string; properties: TAsset } + getItem: (id: string) => null | { id: string; properties: TItem } /** Whether a next playlist item is available. */ hasNext: boolean /** Whether a previous playlist item is available. */ @@ -316,32 +313,29 @@ export interface UseAssetReturn { /** Check whether an asset has a stored Shaka preload manager. */ isPreloaded: (assetId: string) => boolean /** Load one asset immediately. */ - loadAsset: (asset: TAsset, startTime?: number) => Promise + loadAsset: (asset: TItem, startTime?: number) => Promise /** Load a playlist and start from `startIndex`. */ loadPlaylist: ( - assets: TAsset[], + assets: TItem[], startIndex?: number, sourceType?: AssetSourceType, - options?: Omit< - AssetLoadSourceOptions, - "initialIndex" | "sourceType" - > + options?: Omit, "initialIndex" | "sourceType"> ) => void /** Load a URL string, one asset, or an asset array. */ loadSource: ( - source: PlayerSource, - options?: AssetLoadSourceOptions + source: PlayerSource, + options?: AssetLoadSourceOptions ) => void /** Next playlist item, when available. */ - nextItem: null | { id: string; properties: TAsset } + nextItem: null | { id: string; properties: TItem } /** Playlist items in playback order. */ - orderedItems: { id: string; properties: TAsset }[] + orderedItems: { id: string; properties: TItem }[] /** Preload one asset for faster future playback. */ - preloadAsset: (asset: TAsset) => Promise + preloadAsset: (asset: TItem) => Promise /** Preload the next playlist item, when one exists. */ preloadNext: () => Promise /** Previous playlist item, when available. */ - previousItem: null | { id: string; properties: TAsset } + previousItem: null | { id: string; properties: TItem } /** Current source mode, or `null` before a source is loaded. */ sourceType: AssetSourceType | null } @@ -373,26 +367,26 @@ export interface AssetStore extends MediaEventSlice { isFirstLoad: boolean loadAbortController: AbortController | null loadAsset: ( - asset: Asset, + asset: TAsset, startTime?: number, sourceType?: AssetSourceType ) => Promise loadGeneration: number loadPlaylist: ( - assets: Asset[], + assets: TAsset[], startIndex?: number, sourceType?: AssetSourceType, options?: Omit< - AssetLoadSourceOptions, + AssetLoadSourceOptions, "initialIndex" | "sourceType" > ) => void loadSource: ( - source: PlayerSource, - options?: AssetLoadSourceOptions + source: PlayerSource, + options?: AssetLoadSourceOptions ) => void preloadAbortControllers: Record - preloadAsset: (asset: Asset) => Promise + preloadAsset: (asset: TAsset) => Promise preloadNext: () => Promise previousError: unknown retryCount: number @@ -428,7 +422,9 @@ export function assetFeature(): MediaFeature< const player = get().player.instance as null | shaka.Player const media = get().media.mediaElement const session = get().asset.activeSession - const options = session?.loading as undefined | UseAssetOptions + const options = session?.loading as + | undefined + | UseAssetOptions if (sourceType) { set(({ asset }) => { @@ -455,6 +451,10 @@ export function assetFeature(): MediaFeature< }) get().playback.pause() + set(({ playback }) => { + playback.error = null + playback.status = "loading" + }) events.emit("assetloadstart", { asset, sourceType: sourceType ?? get().asset.sourceType, @@ -643,7 +643,9 @@ export function assetFeature(): MediaFeature< preloadAsset: async (asset) => { const player = get().player.instance as null | shaka.Player const session = get().asset.activeSession - const options = session?.loading as undefined | UseAssetOptions + const options = session?.loading as + | undefined + | UseAssetOptions if (!player) return const assetId = getNormalizedAssetId(asset, options, { @@ -764,7 +766,7 @@ export function assetFeature(): MediaFeature< // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- noUncheckedIndexedAccess is off; index can be out of bounds if (nextItem) { await get().asset.preloadAsset( - nextItem.properties as unknown as Asset + nextItem.properties as unknown as TAsset ) } }, @@ -791,41 +793,38 @@ export function assetFeature(): MediaFeature< * the active source session. */ export function useAsset< - TAsset extends Asset = Asset, ->(): UseAssetReturn { + TItem extends TAsset = TAsset, +>(): UseAssetReturn { const api = useMediaFeatureApi(ASSET_FEATURE_KEY) - const playlist = usePlaylist() + const playlist = usePlaylist() const loadAssetAction = useAssetStore((state) => state.loadAsset) as ( - asset: TAsset, + asset: TItem, startTime?: number, sourceType?: AssetSourceType ) => Promise const loadPlaylist = useAssetStore((state) => state.loadPlaylist) as ( - assets: TAsset[], + assets: TItem[], startIndex?: number, sourceType?: AssetSourceType, - options?: Omit< - AssetLoadSourceOptions, - "initialIndex" | "sourceType" - > + options?: Omit, "initialIndex" | "sourceType"> ) => void const loadSourceAction = useAssetStore((state) => state.loadSource) as ( - source: PlayerSource, - options?: AssetLoadSourceOptions + source: PlayerSource, + options?: AssetLoadSourceOptions ) => void const preloadAsset = useAssetStore((state) => state.preloadAsset) as ( - asset: TAsset + asset: TItem ) => Promise const preloadNext = useAssetStore((state) => state.preloadNext) const sourceType = useAssetStore((state) => state.sourceType) - const loadAsset = useCallback["loadAsset"]>( + const loadAsset = useCallback["loadAsset"]>( (asset, startTime) => - loadAssetAction(asset, startTime, AssetSourceType.Asset), - [loadAssetAction] + loadAssetAction(asset, startTime, sourceType ?? AssetSourceType.Asset), + [loadAssetAction, sourceType] ) - const loadSource = useCallback["loadSource"]>( + const loadSource = useCallback["loadSource"]>( (source, options) => loadSourceAction(source, options), [loadSourceAction] ) @@ -913,11 +912,11 @@ function AssetSetup() { const offPlaylistChange = events.on("playlistchange", (event) => { const item = event.currentItem if (!item) return - events.emit("assetchange", event as PlaylistChangeEvent) + events.emit("assetchange", event as PlaylistChangeEvent) void api .getState() .asset.loadAsset( - item.properties as unknown as Asset, + item.properties as unknown as TAsset, undefined, api.getState().asset.sourceType ?? AssetSourceType.Playlist ) @@ -930,7 +929,7 @@ function AssetSetup() { const sessionId = state.asset.activeSession?.id const options = state.asset.activeSession?.loading as | undefined - | UseAssetOptions + | UseAssetOptions if (!currentItem) return const currentItemId = currentItem.id @@ -949,7 +948,7 @@ function AssetSetup() { payload.currentTime ?? mediaElement?.currentTime ?? 0 const decision = options?.recover?.playbackError ? await options.recover.playbackError( - currentItem.properties as unknown as Asset, + currentItem.properties as unknown as TAsset, payload.error, { currentTime } ) @@ -961,7 +960,7 @@ function AssetSetup() { if (decision.action === AssetRecoveryAction.Reload) { const assetToLoad = (decision.asset ?? - freshCurrentItem.properties) as unknown as Asset + freshCurrentItem.properties) as unknown as TAsset const startTime = decision.startTime ?? currentTime api.setState(({ asset }) => { asset.previousError = payload.error @@ -1005,7 +1004,7 @@ function AssetSetup() { } function createAssetSession( sourceType: AssetSourceType, - options?: Omit, "initialIndex" | "sourceType"> + options?: Omit, "initialIndex" | "sourceType"> ): AssetSession { return { id: generateAssetSessionId(), @@ -1022,7 +1021,7 @@ function createDefaultLoad({ preloadManager, startTime, }: { - asset: Asset + asset: TAsset assetId: string player: shaka.Player preloadManager?: shaka.media.PreloadManager @@ -1069,8 +1068,8 @@ function generateAssetSessionId(): string { } function getNormalizedAssetId( - asset: Asset, - options: undefined | UseAssetOptions, + asset: TAsset, + options: undefined | UseAssetOptions, context: { index?: number; origin: AssetSourceOrigin } ): string { const id = asset.id ?? options?.getAssetId?.(asset, context) @@ -1124,8 +1123,8 @@ function mergePlayerConfiguration( } function normalizeAssets( - assets: Asset[], - options?: UseAssetOptions + assets: TAsset[], + options?: UseAssetOptions ): NormalizedAsset[] { const usedIds = new Set() @@ -1173,15 +1172,15 @@ function normalizePlaybackSource( return { source: source as shaka.media.PreloadManager } } -function normalizePlayerSource(source: PlayerSource): Asset[] { +function normalizePlayerSource(source: PlayerSource): TAsset[] { if (Array.isArray(source)) return [...source] if (typeof source === "string") return [{ src: source }] - return [source as Asset] + return [source as TAsset] } async function resolveSourceValue( - options: undefined | UseAssetOptions, - context: ResolveSourceContext + options: undefined | UseAssetOptions, + context: ResolveSourceContext ): Promise { return options?.resolveSource?.(context) } diff --git a/apps/www/registry/default/hooks/use-controls-visibility.ts b/apps/www/registry/default/hooks/use-controls-visibility.ts new file mode 100644 index 00000000..a95e87e7 --- /dev/null +++ b/apps/www/registry/default/hooks/use-controls-visibility.ts @@ -0,0 +1,144 @@ +"use client" + +import React from "react" + +import { useMediaStore } from "@/registry/default/hooks/use-media" +import { usePlaybackStore } from "@/registry/default/hooks/use-playback" + +export interface UseControlsVisibilityOptions { + controlsHideDelay?: number + hideCursorOnIdle?: boolean +} + +type RootInteractionProps = Pick< + React.ComponentPropsWithoutRef<"div">, + | "onBlur" + | "onFocus" + | "onPointerEnter" + | "onPointerLeave" + | "onPointerMove" + | "onPointerOver" + | "onPointerUp" +> + +export const CONTROLS_FORCE_VISIBLE_ATTRIBUTE = "data-controls-force-visible" + +const CONTROLS_KEEP_VISIBLE_SELECTOR = `[${CONTROLS_FORCE_VISIBLE_ATTRIBUTE}]` + +export function useControlsVisibility({ + controlsHideDelay = 2000, + hideCursorOnIdle = true, +}: UseControlsVisibilityOptions = {}) { + const debug = useMediaStore((state) => state.debug) + const forceIdle = useMediaStore((state) => state.forceIdle) + const idle = useMediaStore((state) => state.idle) + const setIdle = useMediaStore((state) => state.setIdle) + const status = usePlaybackStore((state) => state.status) + const hideTimerRef = React.useRef(null) + const autoHide = controlsHideDelay > 0 + const controlsHidden = + !debug && + !forceIdle && + idle && + status !== "buffering" && + status !== "error" && + status !== "paused" + + const clearHideTimer = React.useCallback(() => { + if (hideTimerRef.current === null) return + + window.clearTimeout(hideTimerRef.current) + hideTimerRef.current = null + }, []) + + const hideControls = React.useCallback(() => { + if (forceIdle) return + + clearHideTimer() + + if (autoHide) { + hideTimerRef.current = window.setTimeout(() => { + setIdle(true) + hideTimerRef.current = null + }, controlsHideDelay) + return + } + + setIdle(true) + }, [autoHide, clearHideTimer, controlsHideDelay, forceIdle, setIdle]) + + const showControls = React.useCallback( + (options?: { autoHide?: boolean }) => { + if (forceIdle) return + + clearHideTimer() + setIdle(false) + + if (options?.autoHide) { + hideControls() + } + }, + [clearHideTimer, forceIdle, hideControls, setIdle] + ) + + React.useEffect(() => clearHideTimer, [clearHideTimer]) + + React.useEffect(() => { + if (forceIdle) { + clearHideTimer() + } + }, [clearHideTimer, forceIdle]) + + const rootProps = React.useMemo( + () => ({ + onBlur: (event) => { + const relatedTarget = event.relatedTarget + if ( + relatedTarget instanceof Node && + event.currentTarget.contains(relatedTarget) + ) { + return + } + + hideControls() + }, + onFocus: () => { + showControls() + }, + onPointerEnter: () => { + showControls({ autoHide }) + }, + onPointerLeave: () => { + hideControls() + }, + onPointerMove: (event) => { + showControls({ + autoHide: autoHide && !isKeepVisibleTarget(event.target), + }) + }, + onPointerOver: (event) => { + if (isKeepVisibleTarget(event.target)) { + showControls() + } + }, + onPointerUp: (event) => { + showControls({ + autoHide: autoHide && !isKeepVisibleTarget(event.target), + }) + }, + }), + [autoHide, hideControls, showControls] + ) + + return { + className: hideCursorOnIdle && controlsHidden ? "cursor-none" : undefined, + rootProps, + } +} + +function isKeepVisibleTarget(target: EventTarget | null) { + return ( + target instanceof Element && + target.closest(CONTROLS_KEEP_VISIBLE_SELECTOR) !== null + ) +} diff --git a/apps/www/registry/default/hooks/use-playback-source.ts b/apps/www/registry/default/hooks/use-playback-source.ts index a142526c..e4be445b 100644 --- a/apps/www/registry/default/hooks/use-playback-source.ts +++ b/apps/www/registry/default/hooks/use-playback-source.ts @@ -3,9 +3,9 @@ import { useEffect, useMemo, useRef } from "react" import type { - Asset, GetAssetId, PlayerSource, + TAsset, UseAssetOptions, } from "@/registry/default/hooks/use-asset" @@ -16,24 +16,27 @@ import { } from "@/registry/default/hooks/use-asset" import { usePlayerStore } from "@/registry/default/hooks/use-player" -export interface UsePlaybackSourceOptions { +export interface PlaybackSourceControllerProps { autoLoad?: boolean initialIndex?: number - loading?: UseAssetOptions - source?: PlayerSource + loading?: UseAssetOptions + source?: PlayerSource sourceKey?: string } -export function PlaybackSourceController( - props: UsePlaybackSourceOptions +export type UsePlaybackSourceOptions = + PlaybackSourceControllerProps + +export function PlaybackSourceController( + props: PlaybackSourceControllerProps ) { usePlaybackSource(props) return null } -export function usePlaybackSource( - options: UsePlaybackSourceOptions +export function usePlaybackSource( + options: UsePlaybackSourceOptions ): void { const { autoLoad = true, @@ -45,13 +48,13 @@ export function usePlaybackSource( const assets = useMemo(() => { if (Array.isArray(source)) return [...source] - if (typeof source === "string") return [{ src: source } as TAsset] + if (typeof source === "string") return [{ src: source } as TItem] if (source) return [source] return [] }, [source]) const player = usePlayerStore((state) => state.instance) - const { loadSource } = useAsset() + const { loadSource } = useAsset() const loadedSourceKeyRef = useRef(null) useEffect(() => { @@ -117,9 +120,9 @@ export function usePlaybackSource( ]) } -function getSourceOrigin( - source: PlayerSource | undefined -): Parameters>[1]["origin"] { +function getSourceOrigin( + source: PlayerSource | undefined +): Parameters>[1]["origin"] { if (Array.isArray(source)) return AssetSourceOrigin.Playlist if (typeof source === "string") return AssetSourceOrigin.MediaProps return AssetSourceOrigin.Asset diff --git a/apps/www/registry/default/hooks/use-timeline.ts b/apps/www/registry/default/hooks/use-timeline.ts index 36c6c3dd..e2eef4f4 100644 --- a/apps/www/registry/default/hooks/use-timeline.ts +++ b/apps/www/registry/default/hooks/use-timeline.ts @@ -215,14 +215,19 @@ function TimelineSetup() { if (isLive) { const seekRange = player.seekRange() + const seekRangeSize = seekRange.end - seekRange.start + liveLatency = mediaElement.currentTime === 0 ? 0 : seekRange.end - mediaElement.currentTime liveLatency = toFixedNumber(clamp(liveLatency, 0, seekRange.end), 4) - - const seekRangeSize = seekRange.end - seekRange.start + currentTime = clamp( + mediaElement.currentTime - seekRange.start, + 0, + seekRangeSize + ) progress = seekRangeSize > 0 ? 1 - (seekRange.end - mediaElement.currentTime) / seekRangeSize diff --git a/apps/www/registry/default/lib/utils.ts b/apps/www/registry/default/lib/utils.ts index 915d2557..d1f8dca9 100644 --- a/apps/www/registry/default/lib/utils.ts +++ b/apps/www/registry/default/lib/utils.ts @@ -1,11 +1,6 @@ import type React from "react" -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} +export { cn } from "cnfast" /** * Type-safe event handler utility function diff --git a/apps/www/registry/default/ui/player-layout.tsx b/apps/www/registry/default/ui/player-layout.tsx index 1aedd6a9..716e2238 100644 --- a/apps/www/registry/default/ui/player-layout.tsx +++ b/apps/www/registry/default/ui/player-layout.tsx @@ -15,7 +15,7 @@ export const PlayerContainer = React.forwardRef< return (
{ + className?: string +} + +export const ControlsOverlayContainer = React.forwardRef< + HTMLDivElement, + ControlsOverlayContainerProps +>(({ className, ...props }, ref) => { return (
) -} +}) + +ControlsOverlayContainer.displayName = "ControlsOverlayContainer" export const ControlsBottomContainer = React.forwardRef< HTMLDivElement, @@ -78,16 +84,7 @@ export const ControlsBottomContainer = React.forwardRef< >(({ children, className, ...props }, ref) => { return (
(({ children, className, ...props }, ref) => { return (
export const RootContainer = React.forwardRef< @@ -56,125 +43,38 @@ export const RootContainer = React.forwardRef< aspectRatio: aspectRatioProp, children, className, - controlsHideDelay = 0, height = 1080, - hideCursorOnIdle = false, - onBlur, - onFocus, - onPointerEnter, - onPointerLeave, - onPointerMove, - onPointerUp, + render, style, width = 1920, ...etc } = props const idle = useMediaStore((state) => state.idle) const forceIdle = useMediaStore((state) => state.forceIdle) - const setIdle = useMediaStore((state) => state.setIdle) const status = usePlaybackStore((state) => state.status) const debug = useMediaStore((state) => state.debug) const setPlayerContainerRef = usePlayerStore((state) => state.setContainerRef) - const hideTimerRef = React.useRef(null) const aspectRatio = React.useMemo( () => resolveAspectRatio(aspectRatioProp, width, height), [aspectRatioProp, height, width] ) - const Component = asChild ? Slot : "div" - const controlsHidden = - !debug && - !forceIdle && - idle && - status !== "buffering" && - status !== "paused" - - const clearHideTimer = React.useCallback(() => { - if (hideTimerRef.current === null) return - - window.clearTimeout(hideTimerRef.current) - hideTimerRef.current = null - }, []) - - const hideControls = React.useCallback(() => { - if (forceIdle) return - - clearHideTimer() - - if (controlsHideDelay > 0) { - hideTimerRef.current = window.setTimeout(() => { - setIdle(true) - hideTimerRef.current = null - }, controlsHideDelay) - return - } - - setIdle(true) - }, [clearHideTimer, controlsHideDelay, forceIdle, setIdle]) - - const showControls = React.useCallback( - (options?: { autoHide?: boolean }) => { - if (forceIdle) return - - clearHideTimer() - setIdle(false) - - if (options?.autoHide) { - hideControls() - } - }, - [clearHideTimer, forceIdle, hideControls, setIdle] - ) - - React.useEffect(() => clearHideTimer, [clearHideTimer]) - - React.useEffect(() => { - if (forceIdle) { - clearHideTimer() - } - }, [clearHideTimer, forceIdle]) + const Component = render ? Slot : asChild ? Slot : "div" return ( { - const relatedTarget = event.relatedTarget - if ( - relatedTarget instanceof Node && - event.currentTarget.contains(relatedTarget) - ) { - return - } - - hideControls() - })} - onFocus={composeEventHandlers(onFocus, () => { - showControls() - })} - onPointerEnter={composeEventHandlers(onPointerEnter, () => { - showControls({ autoHide: controlsHideDelay > 0 }) - })} - onPointerLeave={composeEventHandlers(onPointerLeave, () => { - hideControls() - })} - onPointerMove={composeEventHandlers(onPointerMove, () => { - showControls({ autoHide: controlsHideDelay > 0 }) - })} - onPointerUp={composeEventHandlers(onPointerUp, () => { - showControls({ autoHide: controlsHideDelay > 0 }) - })} ref={composeRefs(forwardedRef, setPlayerContainerRef)} role="region" style={{ @@ -185,7 +85,7 @@ export const RootContainer = React.forwardRef< }} {...etc} > - {children} + {render ? React.cloneElement(render, undefined, children) : children} ) }) @@ -204,18 +104,6 @@ function calculateAspectRatio(width?: number, height?: number) { } } -function composeEventHandlers( - consumerHandler: ((event: E) => void) | undefined, - internalHandler: (event: E) => void -) { - return (event: E) => { - consumerHandler?.(event) - if (!event.defaultPrevented) { - internalHandler(event) - } - } -} - function resolveAspectRatio( aspectRatio: false | number | string | undefined, width?: number, diff --git a/apps/www/registry/default/ui/seek-controls.tsx b/apps/www/registry/default/ui/seek-controls.tsx index 1d1d442b..659cf05a 100644 --- a/apps/www/registry/default/ui/seek-controls.tsx +++ b/apps/www/registry/default/ui/seek-controls.tsx @@ -18,6 +18,7 @@ export interface SeekControlProps extends React.ComponentProps { * @example 10 for forward 10s, -10 for backward 10s */ offset: number + render?: React.ReactElement shortcut?: string } @@ -35,11 +36,12 @@ export const SeekControl = React.forwardRef< disabled: userDisabled, offset, onClick, + render, shortcut, ...restProps } = props - const Comp = asChild ? Slot : Button + const Comp = render ? Slot : asChild ? Slot : Button const handleClick = (event: React.MouseEvent) => { onClick?.(event) @@ -67,7 +69,7 @@ export const SeekControl = React.forwardRef< onClick={handleClick} ref={forwardedRef} > - {children} + {render ? React.cloneElement(render, undefined, children) : children} ) }) diff --git a/apps/www/registry/default/ui/timeline-control.tsx b/apps/www/registry/default/ui/timeline-control.tsx index 3e94808a..8d12ef74 100644 --- a/apps/www/registry/default/ui/timeline-control.tsx +++ b/apps/www/registry/default/ui/timeline-control.tsx @@ -85,7 +85,7 @@ export const Root = React.forwardRef< ) : newTime - setHoveringTime(liveSeekTime) + setHoveringTime(newTime) setIsHovering(true) if (isPointerDown) { diff --git a/apps/www/registry/default/ui/timeline-labels.tsx b/apps/www/registry/default/ui/timeline-labels.tsx index 0b891638..6d6fcad2 100644 --- a/apps/www/registry/default/ui/timeline-labels.tsx +++ b/apps/www/registry/default/ui/timeline-labels.tsx @@ -58,7 +58,7 @@ export const Remaining = React.forwardRef< ref={forwardedRef} > Remaining - + −  {formatTimestamp(duration - currentTime, duration > HOURS_IN_SECONDS)} ) diff --git a/apps/www/registry/default/ui/volume-control.tsx b/apps/www/registry/default/ui/volume-control.tsx index 6acf5d5f..9ef3fe8f 100644 --- a/apps/www/registry/default/ui/volume-control.tsx +++ b/apps/www/registry/default/ui/volume-control.tsx @@ -172,6 +172,7 @@ export const Thumb = React.forwardRef( ` block size-2 rounded-full bg-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary/50 + has-[input:focus-visible]:outline-2 has-[input:focus-visible]:outline-offset-2 has-[input:focus-visible]:outline-primary/50 data-disabled:bg-primary/85 `, className diff --git a/apps/www/scripts/test-registry-install.ts b/apps/www/scripts/test-registry-install.ts index 4ced27c2..599fdb29 100644 --- a/apps/www/scripts/test-registry-install.ts +++ b/apps/www/scripts/test-registry-install.ts @@ -50,12 +50,12 @@ interface BlockConfig { const BLOCK_CONFIGS: Record = { "audio-player": { component: "AudioPlayer", - importPath: "components/audio-player/components/media-player", + importPath: "components/audio-player/player", url: "/r/audio-player.json", }, "video-player": { component: "VideoPlayer", - importPath: "components/video-player/components/media-player", + importPath: "components/video-player/player", url: "/r/video-player.json", }, } diff --git a/bun.lock b/bun.lock index c8049217..b522d8f9 100644 --- a/bun.lock +++ b/bun.lock @@ -43,7 +43,7 @@ "async-retry": "^1.3.3", "beautiful-mermaid": "^1.1.3", "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", + "cnfast": "^0.0.8", "date-fns": "^4.1.0", "fumadocs-core": "16.9.3", "fumadocs-mdx": "15.0.10", @@ -70,7 +70,6 @@ "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "shaka-player": "^4.16.34", - "tailwind-merge": "^3.6.0", "three": "^0.184.0", "ts-morph": "27.0.2", "tw-animate-css": "^1.4.0", @@ -877,6 +876,8 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "cnfast": ["cnfast@0.0.8", "", { "bin": { "cnfast": "bin/cli.js" } }, "sha512-EjXKMfGfdwtV4AcNSQ6AwQaVzpC1B7IxeiwA3FlhTXz+YFlMKVi4c1JX9tgD2QOlahQXjB8KUXrBaYG+3v871Q=="], + "code-block-writer": ["code-block-writer@13.0.3", "", {}, "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg=="], "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], From 40b6c8a4a65ae30a02087a2d2585039d93c56f71 Mon Sep 17 00:00:00 2001 From: winoffrg Date: Mon, 29 Jun 2026 01:50:35 +0530 Subject: [PATCH 2/6] fix: build and tailwi nd override error --- apps/www/app/(home)/layout.tsx | 6 +- apps/www/app/docs/docs.css | 1 - apps/www/app/global.css | 6 - .../players/video-player/player-container.tsx | 2 +- apps/www/next-env.d.ts | 2 +- apps/www/package.json | 8 +- apps/www/registry/collection/registry-lib.ts | 1 + apps/www/registry/collection/registry-ui.ts | 44 -- .../default/blocks/audio-player/styles.css | 2 - .../components/bottom-controls.tsx | 2 +- .../default/blocks/video-player/player.tsx | 15 +- .../default/blocks/video-player/styles.css | 17 +- bun.lock | 724 ++++++++++++++++-- 13 files changed, 704 insertions(+), 126 deletions(-) diff --git a/apps/www/app/(home)/layout.tsx b/apps/www/app/(home)/layout.tsx index 0cf03ffd..19789b8a 100644 --- a/apps/www/app/(home)/layout.tsx +++ b/apps/www/app/(home)/layout.tsx @@ -19,9 +19,9 @@ export default function RootLayout({ )}
diff --git a/apps/www/app/docs/docs.css b/apps/www/app/docs/docs.css index 45d7e6c7..147f32a3 100644 --- a/apps/www/app/docs/docs.css +++ b/apps/www/app/docs/docs.css @@ -1,7 +1,6 @@ @import "tailwindcss"; @import "fumadocs-ui/css/neutral.css"; @import "fumadocs-ui/css/preset.css"; -@import "../global.css"; @theme { --spacing-fd-container: 1436px; diff --git a/apps/www/app/global.css b/apps/www/app/global.css index af397c30..0ae32d30 100644 --- a/apps/www/app/global.css +++ b/apps/www/app/global.css @@ -5,12 +5,6 @@ @custom-variant dark (&:is(.dark *)); -:root { - --page-padding-inline: 1rem; - --page-padding: max(env(safe-area-inset-left), var(--page-padding-inline)) - max(env(safe-area-inset-right), var(--page-padding-inline)); -} - @theme inline { --animate-blur-fade-slide-in: blur-fade-slide-in 0.6s ease-out forwards; --padding-page: var(--page-padding); diff --git a/apps/www/components/players/video-player/player-container.tsx b/apps/www/components/players/video-player/player-container.tsx index 9aef056b..f3c53045 100644 --- a/apps/www/components/players/video-player/player-container.tsx +++ b/apps/www/components/players/video-player/player-container.tsx @@ -22,7 +22,7 @@ export function VideoPlayerContainer() { /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/www/package.json b/apps/www/package.json index 1f9f7cfd..3912bcde 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -46,10 +46,10 @@ "class-variance-authority": "^0.7.1", "cnfast": "^0.0.8", "date-fns": "^4.1.0", - "fumadocs-core": "16.9.3", - "fumadocs-mdx": "15.0.10", + "fumadocs-core": "16.10.5", + "fumadocs-mdx": "15.0.12", "fumadocs-typescript": "^5.2.6", - "fumadocs-ui": "16.9.3", + "fumadocs-ui": "16.10.5", "hast-util-to-jsx-runtime": "^2.3.6", "immer": "^11.1.8", "jotai": "^2.20.0", @@ -59,7 +59,7 @@ "lodash.throttle": "^4.1.1", "lucide-react": "^1.16.0", "motion": "^12.38.0", - "next": "^16.2.6", + "next": "^16.2.9", "next-themes": "^0.4.6", "radix-ui": "^1.4.3", "react": "^19.2.6", diff --git a/apps/www/registry/collection/registry-lib.ts b/apps/www/registry/collection/registry-lib.ts index 23b81a13..2fa09415 100644 --- a/apps/www/registry/collection/registry-lib.ts +++ b/apps/www/registry/collection/registry-lib.ts @@ -2,6 +2,7 @@ import { type Registry } from "shadcn/schema" export const lib: Registry["items"] = [ { + dependencies: ["cnfast"], files: [ { path: "lib/utils.ts", diff --git a/apps/www/registry/collection/registry-ui.ts b/apps/www/registry/collection/registry-ui.ts index bf87f663..20953f2c 100644 --- a/apps/www/registry/collection/registry-ui.ts +++ b/apps/www/registry/collection/registry-ui.ts @@ -67,50 +67,6 @@ export const ui: Registry["items"] = [ type: "registry:ui", }, { - cssVars: { - dark: { - "background-image-lp-controls-fade": ` linear-gradient( - to bottom, - hsla(0, 0%, 0%, 0) 0%, - hsla(0, 0%, 0%, 0.009) 8.1%, - hsla(0, 0%, 0%, 0.035) 15.5%, - hsla(0, 0%, 0%, 0.074) 22.5%, - hsla(0, 0%, 0%, 0.125) 29%, - hsla(0, 0%, 0%, 0.184) 35.3%, - hsla(0, 0%, 0%, 0.25) 41.2%, - hsla(0, 0%, 0%, 0.32) 47.1%, - hsla(0, 0%, 0%, 0.39) 52.9%, - hsla(0, 0%, 0%, 0.46) 58.8%, - hsla(0, 0%, 0%, 0.526) 64.7%, - hsla(0, 0%, 0%, 0.585) 71%, - hsla(0, 0%, 0%, 0.636) 77.5%, - hsla(0, 0%, 0%, 0.675) 84.5%, - hsla(0, 0%, 0%, 0.701) 91.9%, - hsla(0, 0%, 0%, 0.71) 100% - )`, - }, - light: { - "background-image-lp-controls-fade": `linear-gradient( - to bottom, - hsla(0, 0%, 91%, 0) 0%, - hsla(0, 0%, 91%, 0.002) 10.6%, - hsla(0, 0%, 91%, 0.008) 19.7%, - hsla(0, 0%, 91%, 0.019) 27.6%, - hsla(0, 0%, 91%, 0.035) 34.4%, - hsla(0, 0%, 91%, 0.057) 40.4%, - hsla(0, 0%, 91%, 0.086) 45.7%, - hsla(0, 0%, 91%, 0.122) 50.6%, - hsla(0, 0%, 91%, 0.165) 55.3%, - hsla(0, 0%, 91%, 0.217) 60%, - hsla(0, 0%, 91%, 0.278) 65%, - hsla(0, 0%, 91%, 0.349) 70.3%, - hsla(0, 0%, 91%, 0.43) 76.3%, - hsla(0, 0%, 91%, 0.522) 83.1%, - hsla(0, 0%, 91%, 0.625) 90.9%, - hsla(0, 0%, 91%, 0.74) 100% - )`, - }, - }, dependencies: ["@radix-ui/react-compose-refs", "@radix-ui/react-slot"], files: [ { diff --git a/apps/www/registry/default/blocks/audio-player/styles.css b/apps/www/registry/default/blocks/audio-player/styles.css index d9644fae..9eba5bc3 100644 --- a/apps/www/registry/default/blocks/audio-player/styles.css +++ b/apps/www/registry/default/blocks/audio-player/styles.css @@ -1,5 +1,3 @@ -@import "tailwindcss"; - .limeplay { --background: oklch(0.985 0 0); --foreground: oklch(0.205 0 0); diff --git a/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx b/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx index 8c2d9d47..da49eca2 100644 --- a/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx +++ b/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx @@ -20,7 +20,7 @@ export function BottomControls({ className }: BottomControlsProps) { >
diff --git a/apps/www/registry/default/blocks/video-player/player.tsx b/apps/www/registry/default/blocks/video-player/player.tsx index 58027272..e971b6d8 100644 --- a/apps/www/registry/default/blocks/video-player/player.tsx +++ b/apps/www/registry/default/blocks/video-player/player.tsx @@ -47,6 +47,10 @@ export interface VideoPlayerProps extends PlaybackSourceControllerProps, "as" | "src"> + /** + * @default dark + */ + theme?: "dark" | "light" } export const VideoPlayer = React.forwardRef( @@ -62,12 +66,17 @@ export const VideoPlayer = React.forwardRef( mediaProps, source, sourceKey, + theme = "dark", }, ref ) { return ( - + ( > =0.134.0", "three": ">=0.134.0" } }, "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g=="], @@ -1660,7 +1662,7 @@ "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - "next": ["next@16.2.6", "", { "dependencies": { "@next/env": "16.2.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.6", "@next/swc-darwin-x64": "16.2.6", "@next/swc-linux-arm64-gnu": "16.2.6", "@next/swc-linux-arm64-musl": "16.2.6", "@next/swc-linux-x64-gnu": "16.2.6", "@next/swc-linux-x64-musl": "16.2.6", "@next/swc-win32-arm64-msvc": "16.2.6", "@next/swc-win32-x64-msvc": "16.2.6", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw=="], + "next": ["next@16.2.9", "", { "dependencies": { "@next/env": "16.2.9", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.9", "@next/swc-darwin-x64": "16.2.9", "@next/swc-linux-arm64-gnu": "16.2.9", "@next/swc-linux-arm64-musl": "16.2.9", "@next/swc-linux-x64-gnu": "16.2.9", "@next/swc-linux-x64-musl": "16.2.9", "@next/swc-win32-arm64-msvc": "16.2.9", "@next/swc-win32-x64-msvc": "16.2.9", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww=="], "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], @@ -1790,7 +1792,7 @@ "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], @@ -2230,28 +2232,368 @@ "@modelcontextprotocol/sdk/zod": ["zod@4.2.1", "", {}, "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw=="], + "@radix-ui/react-accessible-icon/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/react-alert-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-aspect-ratio/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-avatar/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-avatar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-avatar/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-avatar/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-checkbox/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-checkbox/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-checkbox/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-checkbox/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-checkbox/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-checkbox/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-checkbox/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-context-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-context-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-context-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-context-menu/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-context-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.2", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-form/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-form/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-form/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-form/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-form/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-hover-card/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-hover-card/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-hover-card/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-hover-card/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], - "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-hover-card/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-hover-card/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-hover-card/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-hover-card/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-menu/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-menu/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-menu/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-menu/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-menu/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-menu/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-menu/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-menu/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-menu/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-menu/react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + + "@radix-ui/react-menubar/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-menubar/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-menubar/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-menubar/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-menubar/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-menubar/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-menubar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-menubar/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-menubar/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-popover/@radix-ui/react-popper": ["@radix-ui/react-popper@1.3.1", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.10", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.6", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-rect": "1.1.2", "@radix-ui/react-use-size": "1.1.2", "@radix-ui/rect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bhnq/0DEPTi2lsOD3J5rTL65qUKHbKbhqHsmN9TMiclSXpipi651ooUKPPp6G5lF/WiHBdn1s0Wuqsn+myVAvw=="], "@radix-ui/react-popper/@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw=="], - "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-popper/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-popper/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-popper/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-progress/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-radio-group/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-radio-group/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-radio-group/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-radio-group/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-radio-group/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-radio-group/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-radio-group/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-radio-group/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-radio-group/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-scroll-area/@radix-ui/number": ["@radix-ui/number@1.1.2", "", {}, "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig=="], + + "@radix-ui/react-select/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-select/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-select/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-select/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-select/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-select/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-select/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-select/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-select/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-select/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-select/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-select/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-select/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-select/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-select/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-select/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/react-select/react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + "@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + "@radix-ui/react-slider/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-slider/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-slider/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-slider/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-slider/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-slider/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-slider/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-slider/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-slider/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-switch/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-switch/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-switch/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-switch/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-switch/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-switch/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-toast/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-toast/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-toast/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-toast/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-toast/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-toast/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-toast/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-toast/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toast/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-toast/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-toast/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-toast/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/react-toggle/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-toggle/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toggle/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-toggle-group/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-toolbar/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-toolbar/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-toolbar/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-toolbar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toolbar/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + "@radix-ui/react-toolbar/@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="], + "@radix-ui/react-tooltip/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-tooltip/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/react-use-controllable-state/@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.3", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA=="], + + "@radix-ui/react-use-effect-event/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-escape-keydown/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-use-size/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], @@ -2352,8 +2694,6 @@ "fumadocs-typescript/unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], - "fumadocs-ui/react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], - "fumadocs-ui/unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], "glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], @@ -2384,10 +2724,56 @@ "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + "radix-ui/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "radix-ui/@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + + "radix-ui/@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + + "radix-ui/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "radix-ui/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "radix-ui/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "radix-ui/@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "radix-ui/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "radix-ui/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "radix-ui/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "radix-ui/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "radix-ui/@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="], + + "radix-ui/@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "radix-ui/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "radix-ui/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "radix-ui/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "radix-ui/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "radix-ui/@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + "radix-ui/@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="], "radix-ui/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "radix-ui/@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + + "radix-ui/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "radix-ui/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "radix-ui/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "radix-ui/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + "remark-code-import/unist-util-visit": ["unist-util-visit@4.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.1.1" } }, "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg=="], "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], @@ -2448,8 +2834,172 @@ "@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "@radix-ui/react-accessible-icon/@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + + "@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-aspect-ratio/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-avatar/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-checkbox/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-checkbox/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-checkbox/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-context-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-context-menu/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-form/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-form/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-hover-card/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-hover-card/@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-hover-card/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-hover-card/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-hover-card/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-label/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-menu/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-menu/@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-menu/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-menu/@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-menubar/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-menubar/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-menubar/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-menubar/@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-menubar/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-one-time-password-field/@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-password-toggle-field/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.10", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-j2VTDz1vgCsmuG0k5lBfOcM8n5JPFqZBcMryasFjHYMhwxYL5SRUV5lMSUpRdNtw3D/Sv8pzJtrlAgkssYSsQQ=="], + + "@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.2", "", { "dependencies": { "@radix-ui/rect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw=="], + + "@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w=="], + + "@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/rect": ["@radix-ui/rect@1.1.2", "", {}, "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA=="], + "@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], + "@radix-ui/react-popper/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-progress/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-radio-group/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-radio-group/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-radio-group/@radix-ui/react-roving-focus/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-radio-group/@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-radio-group/@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-radio-group/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-separator/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + + "@radix-ui/react-slider/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-slider/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-switch/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-switch/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-toast/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-toast/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-roving-focus/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-roving-focus/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-toggle/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-toggle/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-toolbar/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-tooltip/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + "@ts-morph/common/tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.59.4", "", {}, "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q=="], @@ -2524,6 +3074,26 @@ "next/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "radix-ui/@radix-ui/react-accordion/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "radix-ui/@radix-ui/react-collapsible/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "radix-ui/@radix-ui/react-dialog/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "radix-ui/@radix-ui/react-dialog/react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + + "radix-ui/@radix-ui/react-navigation-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "radix-ui/@radix-ui/react-navigation-menu/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "radix-ui/@radix-ui/react-popover/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "radix-ui/@radix-ui/react-popover/react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + + "radix-ui/@radix-ui/react-roving-focus/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "radix-ui/@radix-ui/react-tabs/@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + "remark-code-import/unist-util-visit/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], "remark-code-import/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="], @@ -2552,10 +3122,60 @@ "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@radix-ui/react-accessible-icon/@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-aspect-ratio/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-avatar/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context-menu/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-label/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-menu/@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + "@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], "@radix-ui/react-popper/@floating-ui/react-dom/@floating-ui/dom/@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + "@radix-ui/react-progress/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-radio-group/@radix-ui/react-roving-focus/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-radio-group/@radix-ui/react-roving-focus/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-separator/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-roving-focus/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-roving-focus/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-toggle/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-toolbar/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-toolbar/@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], @@ -2578,6 +3198,8 @@ "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "@radix-ui/react-accessible-icon/@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + "@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "fumadocs-typescript/shiki/@shikijs/engine-javascript/oniguruma-to-es/oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], From a13d4688f04c43cc2ce9d698d9c2f7d3783ad403 Mon Sep 17 00:00:00 2001 From: winoffrg Date: Mon, 29 Jun 2026 01:58:50 +0530 Subject: [PATCH 3/6] fix: tailwind @theme extension broken --- .../video-player/components/bottom-controls.tsx | 2 +- .../registry/default/blocks/video-player/player.tsx | 4 ++-- .../registry/default/blocks/video-player/styles.css | 11 ++++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx b/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx index da49eca2..8c2d9d47 100644 --- a/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx +++ b/apps/www/registry/default/blocks/video-player/components/bottom-controls.tsx @@ -20,7 +20,7 @@ export function BottomControls({ className }: BottomControlsProps) { >
diff --git a/apps/www/registry/default/blocks/video-player/player.tsx b/apps/www/registry/default/blocks/video-player/player.tsx index e971b6d8..003b5110 100644 --- a/apps/www/registry/default/blocks/video-player/player.tsx +++ b/apps/www/registry/default/blocks/video-player/player.tsx @@ -123,7 +123,7 @@ export const VideoPlayer = React.forwardRef( > Date: Mon, 29 Jun 2026 11:50:10 +0530 Subject: [PATCH 4/6] fix: build error --- apps/www/components/codeblock.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/www/components/codeblock.tsx b/apps/www/components/codeblock.tsx index 3226112d..cff360b0 100644 --- a/apps/www/components/codeblock.tsx +++ b/apps/www/components/codeblock.tsx @@ -3,6 +3,7 @@ import { type ComponentProps, createContext, + type CSSProperties, type HTMLAttributes, type ReactNode, type RefObject, @@ -145,7 +146,7 @@ export function CodeBlock({ ? `line ${Number(props["data-line-numbers-start"] ?? 1) - 1}` : undefined, ...viewportProps.style, - } as object + } as CSSProperties } > {children} From 1c27ffebe57ea75102cca6f961c0b00e1ec692b8 Mon Sep 17 00:00:00 2001 From: winoffrg Date: Thu, 2 Jul 2026 20:19:23 +0530 Subject: [PATCH 5/6] fix: resolve AI comments and broken CSS --- .../players/video-player/player-container.tsx | 1 + apps/www/content/docs/hooks/use-asset.mdx | 12 ++-- .../docs/hooks/use-controls-visibility.mdx | 61 ++++++++++++++++++- .../docs/hooks/use-playback-source.mdx | 33 +++++----- apps/www/content/docs/usage.mdx | 2 +- apps/www/lib/catalogs/blender-open-films.ts | 5 +- .../registry/collection/registry-blocks.ts | 2 + .../components/action-controls.tsx | 4 +- .../blocks/audio-player/components/button.tsx | 2 +- .../components/fixed-timeline-control.tsx | 2 +- .../components/playback-mode-controls.tsx | 4 +- .../components/volume-group-control.tsx | 2 +- .../default/blocks/audio-player/player.tsx | 1 + .../default/blocks/audio-player/styles.css | 8 +-- .../components/bottom-controls.tsx | 2 +- .../components/captions-state-control.tsx | 2 +- .../components/player-error-screen.tsx | 2 +- .../components/player-root-container.tsx | 42 ++++++++++++- .../components/timeline-slider-control.tsx | 7 ++- .../default/blocks/video-player/player.tsx | 10 +-- .../default/blocks/video-player/styles.css | 30 --------- .../volume-slider-control-horizontal-demo.tsx | 2 +- .../default/hooks/use-playback-source.ts | 23 +++---- .../registry/default/hooks/use-timeline.ts | 2 +- .../www/registry/default/ui/player-layout.tsx | 2 +- 25 files changed, 169 insertions(+), 94 deletions(-) diff --git a/apps/www/components/players/video-player/player-container.tsx b/apps/www/components/players/video-player/player-container.tsx index f3c53045..fa76b54e 100644 --- a/apps/www/components/players/video-player/player-container.tsx +++ b/apps/www/components/players/video-player/player-container.tsx @@ -31,6 +31,7 @@ export function VideoPlayerContainer() { autoPlay: false, muted: true, }} + theme="dark" > diff --git a/apps/www/content/docs/hooks/use-asset.mdx b/apps/www/content/docs/hooks/use-asset.mdx index e7b46616..d1877e92 100644 --- a/apps/www/content/docs/hooks/use-asset.mdx +++ b/apps/www/content/docs/hooks/use-asset.mdx @@ -84,11 +84,11 @@ const showPlaylistControls = sourceType === "playlist" ### Base Asset Interface -| Field | Type | Required | Description | -| ------------- | ---------------------------------- | -------- | -------------------------------- | -| `id` | `string` | No | Stable identifier for the asset. | -| `src` | `string` | No | Media source URL. | -| `config` | `shaka.extern.PlayerConfiguration` | No | Per-asset Shaka config. | +| Field | Type | Required | Description | +| -------- | ---------------------------------- | -------- | -------------------------------- | +| `id` | `string` | No | Stable identifier for the asset. | +| `src` | `string` | No | Media source URL. | +| `config` | `shaka.extern.PlayerConfiguration` | No | Per-asset Shaka config. | `TAsset` is intentionally small. Blocks and apps should extend it with their own optional metadata, such as `title`, `description`, `poster`, or provider-specific fields. @@ -102,7 +102,7 @@ Use `resolveSource` when the playable source is not stored directly on `asset.sr const { loadSource } = useAsset() loadSource( - { id: "movie-123", title: "Movie" }, + { id: "movie-123" }, { loading: { resolveSource: async ({ asset, signal }) => { diff --git a/apps/www/content/docs/hooks/use-controls-visibility.mdx b/apps/www/content/docs/hooks/use-controls-visibility.mdx index 639a1524..6de13bb7 100644 --- a/apps/www/content/docs/hooks/use-controls-visibility.mdx +++ b/apps/www/content/docs/hooks/use-controls-visibility.mdx @@ -10,11 +10,49 @@ npx shadcn add @limeplay/use-controls-visibility ``` - Requires [`mediaFeature`](/docs/hooks/use-media) and + Requires [`mediaFeature`](/docs/concepts) and [`playbackFeature`](/docs/hooks/use-playback) registered in your [`createMediaKit`](/docs/components/media-provider). +## Feature Registration + +`use-controls-visibility` is a utility hook, not a media feature. Register the +store features it reads before using it inside a player tree: + +```tsx title="lib/media.ts" +"use client" + +import { mediaFeature } from "@/hooks/limeplay/use-media" +import { playbackFeature } from "@/hooks/limeplay/use-playback" +import { createMediaKit } from "@/components/limeplay/media-provider" + +createMediaKit({ + features: [mediaFeature(), playbackFeature()] as const, +}) +``` + +## Store + +`useControlsVisibility` does not create its own store slice. It reads state from +`mediaFeature` and `playbackFeature`, then returns root props and a class name +for the caller to apply. + +### State + +| Field | Owner | Description | +| ----------- | ----------------- | ----------------------------------------------------------- | +| `debug` | `mediaFeature` | Keeps controls visible while debug mode is enabled. | +| `forceIdle` | `mediaFeature` | Prevents the hook from changing idle state when forced. | +| `idle` | `mediaFeature` | Drives whether controls and cursor can hide. | +| `status` | `playbackFeature` | Keeps controls visible while buffering, paused, or errored. | + +### Actions + +| Method | Owner | Description | +| ---------------- | -------------- | -------------------------------------------- | +| `setIdle(value)` | `mediaFeature` | Updates idle state after interaction timers. | + ## Usage ```tsx @@ -45,6 +83,27 @@ export function PlayerShell() { `CONTROLS_FORCE_VISIBLE_ATTRIBUTE` to any controls area that should keep controls visible while hovered or scrubbed. +## Events + +`useControlsVisibility` does not emit custom media events. + +| Event | Emitted by | Description | +| ----- | ----------------------- | --------------------------------- | +| None | `useControlsVisibility` | The hook only handles DOM events. | + +The returned `rootProps` attach pointer and focus handlers to update +`mediaFeature` idle state. + +| DOM event | When it is handled | +| -------------- | ------------------------------------------ | +| `blur` | Hides controls after focus leaves the root | +| `focus` | Shows controls while focus is inside | +| `pointerenter` | Shows controls and may schedule auto-hide | +| `pointerleave` | Hides controls | +| `pointermove` | Shows controls and may schedule auto-hide | +| `pointerover` | Keeps controls visible over forced areas | +| `pointerup` | Shows controls and may schedule auto-hide | + ## API Reference ( - props: UsePlaybackSourceOptions + props: PlaybackSourceControllerProps ) { return } @@ -96,31 +96,30 @@ export function Source( ## Store -| State | Owner | Description | -| ----- | ----- | ----------- | -| `source` | `UsePlaybackSourceOptions` | Source string, one asset, or asset array normalized by `PlaybackSourceController`. | -| `sourceKey` | `UsePlaybackSourceOptions` | Optional stable key used to avoid duplicate playlist loads. | -| `player` | `use-player` | The current Shaka player instance required before loading. | +| State | Owner | Description | +| ----------- | ------------------------------- | ---------------------------------------------------------------------------------- | +| `source` | `PlaybackSourceControllerProps` | Source string, one asset, or asset array normalized by `PlaybackSourceController`. | +| `sourceKey` | `PlaybackSourceControllerProps` | Optional stable key used to avoid duplicate playlist loads. | +| `player` | `use-player` | The current Shaka player instance required before loading. | ## Actions -| Action | Symbol | Description | -| ------ | ------ | ----------- | -| Load source | `PlaybackSourceController` | Mounts a controller that calls `usePlaybackSource`. | -| Normalize options | `usePlaybackSource` | Converts a source string, one asset, or an asset array into a load session. | +| Action | Symbol | Description | +| ----------- | ---------------------------- | ---------------------------------------------------------------------------------- | +| Load source | `PlaybackSourceController` | Mounts a controller that normalizes source options into a load session. | | Load source | `loadSource` from `useAsset` | Receives the source plus `loading`, `initialIndex`, `sourceKey`, and `sourceType`. | ## Events -| Event | Emitted by | Description | -| ----- | ---------- | ----------- | -| `playlistchange` | `use-playlist` | Fired after the normalized source is loaded into the queue and the active item changes. | -| `playerready` | `use-player` | The controller waits for a player instance before calling `loadSource`. | -| None | `use-playback-source` | The hook itself does not emit custom events. | +| Event | Emitted by | Description | +| ---------------- | -------------------------- | --------------------------------------------------------------------------------------- | +| `playlistchange` | `use-playlist` | Fired after the normalized source is loaded into the queue and the active item changes. | +| `playerready` | `use-player` | The controller waits for a player instance before calling `loadSource`. | +| None | `PlaybackSourceController` | The controller itself does not emit custom events. | ## API Reference diff --git a/apps/www/content/docs/usage.mdx b/apps/www/content/docs/usage.mdx index 211d904a..d812b6f4 100644 --- a/apps/www/content/docs/usage.mdx +++ b/apps/www/content/docs/usage.mdx @@ -70,7 +70,7 @@ Limeplay has two source-loading layers. | Layer | Use it when | What it does | | --- | --- | --- | -| Opinionated blocks and `usePlaybackSource` | You are building reusable player blocks. | Normalizes `source`, waits for the player, deduplicates loads with `sourceKey`, and calls `useAsset().loadSource(...)`. | +| Opinionated blocks and `PlaybackSourceController` | You are building reusable player blocks. | Normalizes `source`, waits for the player, deduplicates loads with `sourceKey`, and calls `useAsset().loadSource(...)`. | | Unopinionated `useAsset` | You need app-specific orchestration. | Gives direct access to `loadSource`, `loadAsset`, `loadPlaylist`, `preloadAsset`, queue read state, and recovery behavior. | Blocks should usually wrap `PlaybackSourceController` from `use-playback-source`. App code that needs imperative control can call `useAsset` directly inside the same `MediaProvider` tree. diff --git a/apps/www/lib/catalogs/blender-open-films.ts b/apps/www/lib/catalogs/blender-open-films.ts index 50c94d81..1dd9a11e 100644 --- a/apps/www/lib/catalogs/blender-open-films.ts +++ b/apps/www/lib/catalogs/blender-open-films.ts @@ -136,7 +136,10 @@ export async function addBlenderCaptions( export async function fetchBlenderOpenFilmAssets( signal?: AbortSignal ): Promise { - 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, + }) if (!response.ok) { throw new Error(`Failed to fetch Blender playlist: ${response.statusText}`) } diff --git a/apps/www/registry/collection/registry-blocks.ts b/apps/www/registry/collection/registry-blocks.ts index 57d1943b..63daad35 100644 --- a/apps/www/registry/collection/registry-blocks.ts +++ b/apps/www/registry/collection/registry-blocks.ts @@ -17,6 +17,7 @@ export const blocks: Registry["items"] = [ files: [ { path: `${VIDEO_PLAYER_SRC_URL}/player.tsx`, + target: "components/video-player/player.tsx", type: "registry:component", }, { @@ -150,6 +151,7 @@ export const blocks: Registry["items"] = [ files: [ { path: "blocks/audio-player/player.tsx", + target: "components/audio-player/player.tsx", type: "registry:component", }, { diff --git a/apps/www/registry/default/blocks/audio-player/components/action-controls.tsx b/apps/www/registry/default/blocks/audio-player/components/action-controls.tsx index e3319097..4c714486 100644 --- a/apps/www/registry/default/blocks/audio-player/components/action-controls.tsx +++ b/apps/www/registry/default/blocks/audio-player/components/action-controls.tsx @@ -35,7 +35,7 @@ export function ActionControls() { aria-label="Dislike" className={` cursor-pointer rounded-full p-1.5 - hover:bg-muted-foreground + hover:bg-muted data-[state=on]:bg-transparent `} value="dislike" @@ -63,7 +63,7 @@ export function ActionControls() { aria-label="Like" className={` cursor-pointer rounded-full p-1.5 - hover:bg-muted-foreground + hover:bg-muted data-[state=on]:bg-transparent `} value="like" diff --git a/apps/www/registry/default/blocks/audio-player/components/button.tsx b/apps/www/registry/default/blocks/audio-player/components/button.tsx index e731e484..753f83eb 100644 --- a/apps/www/registry/default/blocks/audio-player/components/button.tsx +++ b/apps/www/registry/default/blocks/audio-player/components/button.tsx @@ -21,7 +21,7 @@ const Button = React.forwardRef( ` inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full bg-transparent text-foreground transition-[color,background-color,scale] duration-150 ease-out - hover:bg-muted-foreground + hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring active:scale-[0.96] disabled:cursor-not-allowed disabled:opacity-50 diff --git a/apps/www/registry/default/blocks/audio-player/components/fixed-timeline-control.tsx b/apps/www/registry/default/blocks/audio-player/components/fixed-timeline-control.tsx index 06f30560..487cf0d8 100644 --- a/apps/www/registry/default/blocks/audio-player/components/fixed-timeline-control.tsx +++ b/apps/www/registry/default/blocks/audio-player/components/fixed-timeline-control.tsx @@ -10,7 +10,7 @@ import { export function TimeLabels() { return ( -
+
/ diff --git a/apps/www/registry/default/blocks/audio-player/components/playback-mode-controls.tsx b/apps/www/registry/default/blocks/audio-player/components/playback-mode-controls.tsx index fa09dab3..fc61aee1 100644 --- a/apps/www/registry/default/blocks/audio-player/components/playback-mode-controls.tsx +++ b/apps/www/registry/default/blocks/audio-player/components/playback-mode-controls.tsx @@ -56,7 +56,7 @@ export function RepeatControl({ aria-label={`Repeat: ${normalizedRepeatMode}`} aria-pressed={isActive} className={cn({ - "text-secondary": !isActive, + "text-muted-foreground": !isActive, })} onClick={handleCycleRepeatMode} > @@ -95,7 +95,7 @@ export function ShuffleControl() { aria-label={shuffle ? "Shuffle on" : "Shuffle off"} aria-pressed={shuffle} className={cn({ - "text-secondary": !shuffle, + "text-muted-foreground": !shuffle, })} onClick={handleToggleShuffle} > diff --git a/apps/www/registry/default/blocks/audio-player/components/volume-group-control.tsx b/apps/www/registry/default/blocks/audio-player/components/volume-group-control.tsx index 83cf49b9..6d0b2281 100644 --- a/apps/www/registry/default/blocks/audio-player/components/volume-group-control.tsx +++ b/apps/www/registry/default/blocks/audio-player/components/volume-group-control.tsx @@ -46,7 +46,7 @@ export function VolumeControl() {