@lightbird/ui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +297 -4
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +299 -8
- package/dist/styles.css +1 -1
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,8 @@ var SelectPrimitive = require('@radix-ui/react-select');
|
|
|
21
21
|
var core = require('@lightbird/core');
|
|
22
22
|
var DialogPrimitive = require('@radix-ui/react-dialog');
|
|
23
23
|
var react = require('@lightbird/core/react');
|
|
24
|
+
var reactSdk = require('@openfeature/react-sdk');
|
|
25
|
+
var AlertDialogPrimitive = require('@radix-ui/react-alert-dialog');
|
|
24
26
|
var ToastPrimitives = require('@radix-ui/react-toast');
|
|
25
27
|
|
|
26
28
|
function _interopNamespace(e) {
|
|
@@ -50,6 +52,7 @@ var RadioGroupPrimitive__namespace = /*#__PURE__*/_interopNamespace(RadioGroupPr
|
|
|
50
52
|
var ScrollAreaPrimitive__namespace = /*#__PURE__*/_interopNamespace(ScrollAreaPrimitive);
|
|
51
53
|
var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
|
|
52
54
|
var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
|
|
55
|
+
var AlertDialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(AlertDialogPrimitive);
|
|
53
56
|
var ToastPrimitives__namespace = /*#__PURE__*/_interopNamespace(ToastPrimitives);
|
|
54
57
|
|
|
55
58
|
function cn(...inputs) {
|
|
@@ -658,7 +661,13 @@ function formatTime2(seconds) {
|
|
|
658
661
|
return `${m}:${String(s).padStart(2, "0")}`;
|
|
659
662
|
}
|
|
660
663
|
var VIDEO_EXTENSIONS_RE = /\.(mp4|mkv|webm|mov|avi|wmv|flv|m4v)$/i;
|
|
661
|
-
function
|
|
664
|
+
function formatBytes(bytes) {
|
|
665
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
666
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
667
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
668
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
669
|
+
}
|
|
670
|
+
function SortablePlaylistItem({ item, index, isActive, downloadReady, onSelect, onRemove }) {
|
|
662
671
|
const {
|
|
663
672
|
attributes,
|
|
664
673
|
listeners: listeners2,
|
|
@@ -702,12 +711,24 @@ function SortablePlaylistItem({ item, index, isActive, onSelect, onRemove }) {
|
|
|
702
711
|
className: "flex-1 flex items-center gap-1.5 min-w-0 text-left",
|
|
703
712
|
"aria-label": `Play ${item.name}`,
|
|
704
713
|
children: [
|
|
705
|
-
item.type === "video" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListVideo, { className: "w-3.5 h-3.5 shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Tv, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
714
|
+
item.source === "torrent" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { className: "w-3.5 h-3.5 shrink-0 text-emerald-500" }) : item.type === "video" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListVideo, { className: "w-3.5 h-3.5 shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Tv, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
706
715
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: item.name }),
|
|
707
716
|
duration && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground ml-auto shrink-0 pl-1", children: duration })
|
|
708
717
|
]
|
|
709
718
|
}
|
|
710
719
|
),
|
|
720
|
+
item.source === "torrent" && downloadReady && /* @__PURE__ */ jsxRuntime.jsx(
|
|
721
|
+
"a",
|
|
722
|
+
{
|
|
723
|
+
href: item.url,
|
|
724
|
+
download: item.name,
|
|
725
|
+
onClick: (e) => e.stopPropagation(),
|
|
726
|
+
className: "shrink-0 opacity-0 group-hover:opacity-100 transition-opacity p-0.5",
|
|
727
|
+
"aria-label": `Download ${item.name}`,
|
|
728
|
+
title: "Download",
|
|
729
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "w-3 h-3 text-emerald-500 hover:text-emerald-400" })
|
|
730
|
+
}
|
|
731
|
+
),
|
|
711
732
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
712
733
|
"button",
|
|
713
734
|
{
|
|
@@ -731,6 +752,9 @@ var PlaylistPanel = ({
|
|
|
731
752
|
onFilesAdded,
|
|
732
753
|
onFolderFilesAdded,
|
|
733
754
|
onAddStream,
|
|
755
|
+
onAddMagnet,
|
|
756
|
+
torrentStatus,
|
|
757
|
+
showMagnet = true,
|
|
734
758
|
onRemoveItem,
|
|
735
759
|
onReorder,
|
|
736
760
|
onImportM3U,
|
|
@@ -745,6 +769,9 @@ var PlaylistPanel = ({
|
|
|
745
769
|
const folderInputRef = React10.useRef(null);
|
|
746
770
|
const m3uInputRef = React10.useRef(null);
|
|
747
771
|
const [streamUrl, setStreamUrl] = React10.useState("");
|
|
772
|
+
const [magnetUri, setMagnetUri] = React10.useState("");
|
|
773
|
+
const [showMagnetInput, setShowMagnetInput] = React10.useState(false);
|
|
774
|
+
const [magnetError, setMagnetError] = React10.useState(null);
|
|
748
775
|
const [sortKey, setSortKey] = React10.useState("");
|
|
749
776
|
const handleStreamUrlSubmit = (e) => {
|
|
750
777
|
e.preventDefault();
|
|
@@ -753,6 +780,21 @@ var PlaylistPanel = ({
|
|
|
753
780
|
setStreamUrl("");
|
|
754
781
|
}
|
|
755
782
|
};
|
|
783
|
+
const handleMagnetSubmit = async (e) => {
|
|
784
|
+
e.preventDefault();
|
|
785
|
+
const uri = magnetUri.trim();
|
|
786
|
+
if (!uri) return;
|
|
787
|
+
setMagnetError(null);
|
|
788
|
+
try {
|
|
789
|
+
const added = await onAddMagnet(uri);
|
|
790
|
+
if (added) {
|
|
791
|
+
setMagnetUri("");
|
|
792
|
+
setShowMagnetInput(false);
|
|
793
|
+
}
|
|
794
|
+
} catch (err) {
|
|
795
|
+
setMagnetError(err instanceof Error ? err.message : "Failed to load magnet link");
|
|
796
|
+
}
|
|
797
|
+
};
|
|
756
798
|
const handleFolderSelect = (e) => {
|
|
757
799
|
const files = Array.from(e.target.files ?? []).filter((f) => VIDEO_EXTENSIONS_RE.test(f.name)).sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
|
|
758
800
|
if (files.length > 0) onFolderFilesAdded(files);
|
|
@@ -967,8 +1009,89 @@ var PlaylistPanel = ({
|
|
|
967
1009
|
className: "h-8 text-xs"
|
|
968
1010
|
}
|
|
969
1011
|
),
|
|
970
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", size: "icon", variant: "secondary", className: "h-8 w-8 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Link, { className: "h-3.5 w-3.5" }) })
|
|
1012
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", size: "icon", variant: "secondary", className: "h-8 w-8 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Link, { className: "h-3.5 w-3.5" }) }),
|
|
1013
|
+
showMagnet && /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1014
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1015
|
+
Button,
|
|
1016
|
+
{
|
|
1017
|
+
type: "button",
|
|
1018
|
+
size: "icon",
|
|
1019
|
+
variant: showMagnetInput ? "default" : "outline",
|
|
1020
|
+
className: "h-8 w-8 shrink-0",
|
|
1021
|
+
onClick: () => {
|
|
1022
|
+
setShowMagnetInput((v) => !v);
|
|
1023
|
+
setMagnetError(null);
|
|
1024
|
+
},
|
|
1025
|
+
"aria-label": "Add magnet link",
|
|
1026
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Link2, { className: "h-3.5 w-3.5" })
|
|
1027
|
+
}
|
|
1028
|
+
) }),
|
|
1029
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Add Magnet Link" }) })
|
|
1030
|
+
] })
|
|
971
1031
|
] }),
|
|
1032
|
+
showMagnet && showMagnetInput && /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleMagnetSubmit, className: "space-y-1.5", children: [
|
|
1033
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5", children: [
|
|
1034
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1035
|
+
Input,
|
|
1036
|
+
{
|
|
1037
|
+
placeholder: "magnet:?xt=urn:btih:\u2026",
|
|
1038
|
+
value: magnetUri,
|
|
1039
|
+
onChange: (e) => {
|
|
1040
|
+
setMagnetUri(e.target.value);
|
|
1041
|
+
setMagnetError(null);
|
|
1042
|
+
},
|
|
1043
|
+
className: "h-8 text-xs font-mono",
|
|
1044
|
+
disabled: torrentStatus.status === "loading-metadata"
|
|
1045
|
+
}
|
|
1046
|
+
),
|
|
1047
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1048
|
+
Button,
|
|
1049
|
+
{
|
|
1050
|
+
type: "submit",
|
|
1051
|
+
size: "icon",
|
|
1052
|
+
variant: "default",
|
|
1053
|
+
className: "h-8 w-8 shrink-0",
|
|
1054
|
+
disabled: !magnetUri.trim() || torrentStatus.status === "loading-metadata",
|
|
1055
|
+
"aria-label": "Load magnet link",
|
|
1056
|
+
children: torrentStatus.status === "loading-metadata" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { className: "h-3.5 w-3.5" })
|
|
1057
|
+
}
|
|
1058
|
+
)
|
|
1059
|
+
] }),
|
|
1060
|
+
torrentStatus.status === "loading-metadata" && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[11px] text-muted-foreground flex items-center gap-1", children: [
|
|
1061
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3 w-3 animate-spin" }),
|
|
1062
|
+
"Fetching torrent info\u2026"
|
|
1063
|
+
] }),
|
|
1064
|
+
magnetError && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[11px] text-destructive flex items-center gap-1", children: [
|
|
1065
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-3 w-3 shrink-0" }),
|
|
1066
|
+
magnetError
|
|
1067
|
+
] })
|
|
1068
|
+
] }),
|
|
1069
|
+
showMagnet && torrentStatus.status === "ready" && (torrentStatus.progress >= 1 ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[10px] text-emerald-500 flex items-center gap-1", children: [
|
|
1070
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-3 w-3 shrink-0" }),
|
|
1071
|
+
"Download ready \u2014 hover a file to save it"
|
|
1072
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
1073
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-muted rounded-full h-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1074
|
+
"div",
|
|
1075
|
+
{
|
|
1076
|
+
className: "bg-emerald-500 h-1 rounded-full transition-all duration-500",
|
|
1077
|
+
style: { width: `${Math.round(torrentStatus.progress * 100)}%` }
|
|
1078
|
+
}
|
|
1079
|
+
) }),
|
|
1080
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[10px] text-muted-foreground flex items-center justify-between", children: [
|
|
1081
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1082
|
+
"\u2193 ",
|
|
1083
|
+
formatBytes(torrentStatus.downloadSpeed),
|
|
1084
|
+
"/s \xB7 ",
|
|
1085
|
+
torrentStatus.numPeers,
|
|
1086
|
+
" peer",
|
|
1087
|
+
torrentStatus.numPeers !== 1 ? "s" : ""
|
|
1088
|
+
] }),
|
|
1089
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1090
|
+
Math.round(torrentStatus.progress * 100),
|
|
1091
|
+
"%"
|
|
1092
|
+
] })
|
|
1093
|
+
] })
|
|
1094
|
+
] })),
|
|
972
1095
|
playlist.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(Select, { value: sortKey, onValueChange: handleSort, children: [
|
|
973
1096
|
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "h-7 text-xs", "aria-label": "Sort playlist", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Sort by\u2026" }) }),
|
|
974
1097
|
/* @__PURE__ */ jsxRuntime.jsxs(SelectContent, { children: [
|
|
@@ -993,6 +1116,7 @@ var PlaylistPanel = ({
|
|
|
993
1116
|
item,
|
|
994
1117
|
index,
|
|
995
1118
|
isActive: index === currentVideoIndex,
|
|
1119
|
+
downloadReady: item.source === "torrent" && torrentStatus.progress >= 1,
|
|
996
1120
|
onSelect: onSelectVideo,
|
|
997
1121
|
onRemove: onRemoveItem
|
|
998
1122
|
},
|
|
@@ -1470,6 +1594,103 @@ function SubtitleOverlay({ videoRef, activeSubtitle }) {
|
|
|
1470
1594
|
}
|
|
1471
1595
|
) });
|
|
1472
1596
|
}
|
|
1597
|
+
var AlertDialog = AlertDialogPrimitive__namespace.Root;
|
|
1598
|
+
var AlertDialogPortal = AlertDialogPrimitive__namespace.Portal;
|
|
1599
|
+
var AlertDialogOverlay = React10__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1600
|
+
AlertDialogPrimitive__namespace.Overlay,
|
|
1601
|
+
{
|
|
1602
|
+
className: cn(
|
|
1603
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
1604
|
+
className
|
|
1605
|
+
),
|
|
1606
|
+
...props,
|
|
1607
|
+
ref
|
|
1608
|
+
}
|
|
1609
|
+
));
|
|
1610
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive__namespace.Overlay.displayName;
|
|
1611
|
+
var AlertDialogContent = React10__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogPortal, { children: [
|
|
1612
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogOverlay, {}),
|
|
1613
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1614
|
+
AlertDialogPrimitive__namespace.Content,
|
|
1615
|
+
{
|
|
1616
|
+
ref,
|
|
1617
|
+
className: cn(
|
|
1618
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
1619
|
+
className
|
|
1620
|
+
),
|
|
1621
|
+
...props
|
|
1622
|
+
}
|
|
1623
|
+
)
|
|
1624
|
+
] }));
|
|
1625
|
+
AlertDialogContent.displayName = AlertDialogPrimitive__namespace.Content.displayName;
|
|
1626
|
+
var AlertDialogHeader = ({
|
|
1627
|
+
className,
|
|
1628
|
+
...props
|
|
1629
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1630
|
+
"div",
|
|
1631
|
+
{
|
|
1632
|
+
className: cn(
|
|
1633
|
+
"flex flex-col space-y-2 text-center sm:text-left",
|
|
1634
|
+
className
|
|
1635
|
+
),
|
|
1636
|
+
...props
|
|
1637
|
+
}
|
|
1638
|
+
);
|
|
1639
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
1640
|
+
var AlertDialogFooter = ({
|
|
1641
|
+
className,
|
|
1642
|
+
...props
|
|
1643
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1644
|
+
"div",
|
|
1645
|
+
{
|
|
1646
|
+
className: cn(
|
|
1647
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
1648
|
+
className
|
|
1649
|
+
),
|
|
1650
|
+
...props
|
|
1651
|
+
}
|
|
1652
|
+
);
|
|
1653
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
1654
|
+
var AlertDialogTitle = React10__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1655
|
+
AlertDialogPrimitive__namespace.Title,
|
|
1656
|
+
{
|
|
1657
|
+
ref,
|
|
1658
|
+
className: cn("text-lg font-semibold", className),
|
|
1659
|
+
...props
|
|
1660
|
+
}
|
|
1661
|
+
));
|
|
1662
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive__namespace.Title.displayName;
|
|
1663
|
+
var AlertDialogDescription = React10__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1664
|
+
AlertDialogPrimitive__namespace.Description,
|
|
1665
|
+
{
|
|
1666
|
+
ref,
|
|
1667
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
1668
|
+
...props
|
|
1669
|
+
}
|
|
1670
|
+
));
|
|
1671
|
+
AlertDialogDescription.displayName = AlertDialogPrimitive__namespace.Description.displayName;
|
|
1672
|
+
var AlertDialogAction = React10__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1673
|
+
AlertDialogPrimitive__namespace.Action,
|
|
1674
|
+
{
|
|
1675
|
+
ref,
|
|
1676
|
+
className: cn(buttonVariants(), className),
|
|
1677
|
+
...props
|
|
1678
|
+
}
|
|
1679
|
+
));
|
|
1680
|
+
AlertDialogAction.displayName = AlertDialogPrimitive__namespace.Action.displayName;
|
|
1681
|
+
var AlertDialogCancel = React10__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1682
|
+
AlertDialogPrimitive__namespace.Cancel,
|
|
1683
|
+
{
|
|
1684
|
+
ref,
|
|
1685
|
+
className: cn(
|
|
1686
|
+
buttonVariants({ variant: "outline" }),
|
|
1687
|
+
"mt-2 sm:mt-0",
|
|
1688
|
+
className
|
|
1689
|
+
),
|
|
1690
|
+
...props
|
|
1691
|
+
}
|
|
1692
|
+
));
|
|
1693
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive__namespace.Cancel.displayName;
|
|
1473
1694
|
var MAX_RETRIES = 3;
|
|
1474
1695
|
var LightBirdPlayer = () => {
|
|
1475
1696
|
const videoRef = React10.useRef(null);
|
|
@@ -1495,6 +1716,9 @@ var LightBirdPlayer = () => {
|
|
|
1495
1716
|
const { metadata: videoMetadata } = react.useVideoInfo(videoRef, playlist.currentItem?.file ?? null);
|
|
1496
1717
|
react.useProgressPersistence(videoRef, playlist.currentItem?.name ?? null);
|
|
1497
1718
|
const { chapters, currentChapter, goToChapter } = react.useChapters(videoRef, playerRef);
|
|
1719
|
+
const magnetLinkEnabled = reactSdk.useBooleanFlagValue(core.FLAG_MAGNET_LINK, true);
|
|
1720
|
+
const magnet = react.useMagnet();
|
|
1721
|
+
const [disclaimerPendingUri, setDisclaimerPendingUri] = React10.useState(null);
|
|
1498
1722
|
const [shortcuts, setShortcuts] = React10.useState(() => core.loadShortcuts());
|
|
1499
1723
|
const [showShortcutsHelp, setShowShortcutsHelp] = React10.useState(false);
|
|
1500
1724
|
const [showShortcutsDialog, setShowShortcutsDialog] = React10.useState(false);
|
|
@@ -1884,6 +2108,40 @@ var LightBirdPlayer = () => {
|
|
|
1884
2108
|
startStallDetection();
|
|
1885
2109
|
}
|
|
1886
2110
|
}, [playlist, subtitles]);
|
|
2111
|
+
const handleAddMagnet = React10.useCallback(async (uri) => {
|
|
2112
|
+
if (!core.hasAcceptedDisclaimer()) {
|
|
2113
|
+
setDisclaimerPendingUri(uri);
|
|
2114
|
+
return false;
|
|
2115
|
+
}
|
|
2116
|
+
const items = await magnet.addMagnet(uri);
|
|
2117
|
+
const startIndex = playlist.playlist.length;
|
|
2118
|
+
items.forEach((item) => playlist.appendItem(item));
|
|
2119
|
+
if (playlist.currentIndex === null && items.length > 0) {
|
|
2120
|
+
playlist.selectItem(startIndex);
|
|
2121
|
+
if (videoRef.current) videoRef.current.src = items[0].url;
|
|
2122
|
+
subtitles.reset();
|
|
2123
|
+
setAudioTracks([]);
|
|
2124
|
+
setActiveAudioTrack("0");
|
|
2125
|
+
isStreamRef.current = true;
|
|
2126
|
+
startStallDetection();
|
|
2127
|
+
}
|
|
2128
|
+
if (items.length > 1) {
|
|
2129
|
+
toast2({ title: `${items.length} videos added from torrent`, description: magnet.torrentStatus.torrentName });
|
|
2130
|
+
}
|
|
2131
|
+
return true;
|
|
2132
|
+
}, [magnet, playlist, subtitles, toast2]);
|
|
2133
|
+
const handleDisclaimerAccepted = React10.useCallback(async () => {
|
|
2134
|
+
core.acceptDisclaimer();
|
|
2135
|
+
const uri = disclaimerPendingUri;
|
|
2136
|
+
setDisclaimerPendingUri(null);
|
|
2137
|
+
if (uri) {
|
|
2138
|
+
try {
|
|
2139
|
+
await handleAddMagnet(uri);
|
|
2140
|
+
} catch (err) {
|
|
2141
|
+
toast2({ title: "Magnet link failed", description: err instanceof Error ? err.message : "Unknown error", variant: "destructive" });
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
}, [disclaimerPendingUri, handleAddMagnet, toast2]);
|
|
1887
2145
|
const handleSubtitleChange = React10.useCallback(async (id) => {
|
|
1888
2146
|
subtitles.switchSubtitle(id);
|
|
1889
2147
|
if (playerRef.current) {
|
|
@@ -2132,6 +2390,9 @@ var LightBirdPlayer = () => {
|
|
|
2132
2390
|
onFilesAdded: handleFileChange,
|
|
2133
2391
|
onFolderFilesAdded: handleFolderFilesAdded,
|
|
2134
2392
|
onAddStream: handleAddStream,
|
|
2393
|
+
onAddMagnet: handleAddMagnet,
|
|
2394
|
+
torrentStatus: magnet.torrentStatus,
|
|
2395
|
+
showMagnet: magnetLinkEnabled,
|
|
2135
2396
|
onRemoveItem: handleRemoveItem,
|
|
2136
2397
|
onReorder: handleReorder,
|
|
2137
2398
|
onImportM3U: handleImportM3U,
|
|
@@ -2142,7 +2403,29 @@ var LightBirdPlayer = () => {
|
|
|
2142
2403
|
onTogglePin: () => setPlaylistPinned((v) => !v),
|
|
2143
2404
|
onSizeChange: setPlaylistSize
|
|
2144
2405
|
}
|
|
2145
|
-
)
|
|
2406
|
+
),
|
|
2407
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialog, { open: magnetLinkEnabled && disclaimerPendingUri !== null, onOpenChange: (open) => {
|
|
2408
|
+
if (!open) setDisclaimerPendingUri(null);
|
|
2409
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogContent, { children: [
|
|
2410
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AlertDialogHeader, { children: [
|
|
2411
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogTitle, { children: "Magnet Link Streaming" }),
|
|
2412
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogDescription, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 text-sm text-muted-foreground", children: [
|
|
2413
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
2414
|
+
"LightBird streams content via ",
|
|
2415
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-foreground", children: "BitTorrent" }),
|
|
2416
|
+
" (WebRTC/WebSocket) \u2014 the same technology used by applications like VLC and qBittorrent."
|
|
2417
|
+
] }),
|
|
2418
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
2419
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-foreground", children: "You are responsible" }),
|
|
2420
|
+
" for ensuring you have the legal right to access any content you stream. LightBird does not host, index, or endorse any content."
|
|
2421
|
+
] })
|
|
2422
|
+
] }) })
|
|
2423
|
+
] }),
|
|
2424
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { children: [
|
|
2425
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { onClick: () => setDisclaimerPendingUri(null), children: "Cancel" }),
|
|
2426
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogAction, { onClick: handleDisclaimerAccepted, children: "I Understand \u2014 Continue" })
|
|
2427
|
+
] })
|
|
2428
|
+
] }) })
|
|
2146
2429
|
] });
|
|
2147
2430
|
};
|
|
2148
2431
|
var lightbird_player_default = LightBirdPlayer;
|
|
@@ -2273,7 +2556,17 @@ function Toaster() {
|
|
|
2273
2556
|
/* @__PURE__ */ jsxRuntime.jsx(ToastViewport, {})
|
|
2274
2557
|
] });
|
|
2275
2558
|
}
|
|
2559
|
+
var started = false;
|
|
2560
|
+
function FeatureFlagsProvider({ children }) {
|
|
2561
|
+
React10.useEffect(() => {
|
|
2562
|
+
if (started) return;
|
|
2563
|
+
started = true;
|
|
2564
|
+
core.initFeatureFlags().catch(console.error);
|
|
2565
|
+
}, []);
|
|
2566
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactSdk.OpenFeatureProvider, { suspendUntilReady: false, suspendWhileReconciling: false, children });
|
|
2567
|
+
}
|
|
2276
2568
|
|
|
2569
|
+
exports.FeatureFlagsProvider = FeatureFlagsProvider;
|
|
2277
2570
|
exports.LightBirdPlayer = lightbird_player_default;
|
|
2278
2571
|
exports.PlayerControls = player_controls_default;
|
|
2279
2572
|
exports.PlayerErrorBoundary = PlayerErrorBoundary;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import React$1, { RefObject, Component, ReactNode } from 'react';
|
|
3
|
-
import { VideoFilters, Subtitle, AudioTrack, Chapter, PlaylistItem, ParsedMediaError, VideoMetadata, ShortcutBinding } from '@lightbird/core';
|
|
3
|
+
import { VideoFilters, Subtitle, AudioTrack, Chapter, PlaylistItem, TorrentStatus, ParsedMediaError, VideoMetadata, ShortcutBinding } from '@lightbird/core';
|
|
4
4
|
|
|
5
5
|
declare const LightBirdPlayer: () => react_jsx_runtime.JSX.Element;
|
|
6
6
|
|
|
@@ -56,6 +56,11 @@ interface PlaylistPanelProps {
|
|
|
56
56
|
onFilesAdded: (files: FileList) => void;
|
|
57
57
|
onFolderFilesAdded: (files: File[]) => void;
|
|
58
58
|
onAddStream: (url: string, name?: string) => void;
|
|
59
|
+
/** Returns true if items were added, false if a disclaimer prompt was triggered. Throws on error. */
|
|
60
|
+
onAddMagnet: (uri: string) => Promise<boolean>;
|
|
61
|
+
torrentStatus: TorrentStatus;
|
|
62
|
+
/** When false, hides the magnet link button and input entirely. Defaults to true. */
|
|
63
|
+
showMagnet?: boolean;
|
|
59
64
|
onRemoveItem: (index: number) => void;
|
|
60
65
|
onReorder: (newPlaylist: PlaylistItem[]) => void;
|
|
61
66
|
onImportM3U: (items: Omit<PlaylistItem, "id">[]) => void;
|
|
@@ -120,4 +125,8 @@ declare function ShortcutSettingsDialog({ shortcuts, onSave, onClose, }: Shortcu
|
|
|
120
125
|
|
|
121
126
|
declare function Toaster(): react_jsx_runtime.JSX.Element;
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
declare function FeatureFlagsProvider({ children }: {
|
|
129
|
+
children: React$1.ReactNode;
|
|
130
|
+
}): react_jsx_runtime.JSX.Element;
|
|
131
|
+
|
|
132
|
+
export { FeatureFlagsProvider, LightBirdPlayer, PlayerControls, PlayerErrorBoundary, PlayerErrorDisplay, PlaylistPanel, ShortcutSettingsDialog, SubtitleOverlay, Toaster, VideoInfoPanel, VideoOverlay };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import React$1, { RefObject, Component, ReactNode } from 'react';
|
|
3
|
-
import { VideoFilters, Subtitle, AudioTrack, Chapter, PlaylistItem, ParsedMediaError, VideoMetadata, ShortcutBinding } from '@lightbird/core';
|
|
3
|
+
import { VideoFilters, Subtitle, AudioTrack, Chapter, PlaylistItem, TorrentStatus, ParsedMediaError, VideoMetadata, ShortcutBinding } from '@lightbird/core';
|
|
4
4
|
|
|
5
5
|
declare const LightBirdPlayer: () => react_jsx_runtime.JSX.Element;
|
|
6
6
|
|
|
@@ -56,6 +56,11 @@ interface PlaylistPanelProps {
|
|
|
56
56
|
onFilesAdded: (files: FileList) => void;
|
|
57
57
|
onFolderFilesAdded: (files: File[]) => void;
|
|
58
58
|
onAddStream: (url: string, name?: string) => void;
|
|
59
|
+
/** Returns true if items were added, false if a disclaimer prompt was triggered. Throws on error. */
|
|
60
|
+
onAddMagnet: (uri: string) => Promise<boolean>;
|
|
61
|
+
torrentStatus: TorrentStatus;
|
|
62
|
+
/** When false, hides the magnet link button and input entirely. Defaults to true. */
|
|
63
|
+
showMagnet?: boolean;
|
|
59
64
|
onRemoveItem: (index: number) => void;
|
|
60
65
|
onReorder: (newPlaylist: PlaylistItem[]) => void;
|
|
61
66
|
onImportM3U: (items: Omit<PlaylistItem, "id">[]) => void;
|
|
@@ -120,4 +125,8 @@ declare function ShortcutSettingsDialog({ shortcuts, onSave, onClose, }: Shortcu
|
|
|
120
125
|
|
|
121
126
|
declare function Toaster(): react_jsx_runtime.JSX.Element;
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
declare function FeatureFlagsProvider({ children }: {
|
|
129
|
+
children: React$1.ReactNode;
|
|
130
|
+
}): react_jsx_runtime.JSX.Element;
|
|
131
|
+
|
|
132
|
+
export { FeatureFlagsProvider, LightBirdPlayer, PlayerControls, PlayerErrorBoundary, PlayerErrorDisplay, PlaylistPanel, ShortcutSettingsDialog, SubtitleOverlay, Toaster, VideoInfoPanel, VideoOverlay };
|
package/dist/index.js
CHANGED
|
@@ -10,16 +10,18 @@ import { cva } from 'class-variance-authority';
|
|
|
10
10
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
11
11
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
12
12
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
13
|
-
import { Circle, SkipBack, Pause, Play, SkipForward, VolumeX, Volume2, Rewind, FastForward, AudioLines, Loader2, Subtitles, Plus, X, List, Settings2, Info, Keyboard, Camera, RotateCcw, PictureInPicture2, Minimize, Maximize, ChevronDown, ChevronUp, Check, ChevronLeft, ListVideo, Download, Upload, Pin, PinOff, Minimize2, Maximize2, ChevronRight, FilePlus, FolderOpen, Link, AlertCircle, GripVertical, Tv } from 'lucide-react';
|
|
13
|
+
import { Circle, SkipBack, Pause, Play, SkipForward, VolumeX, Volume2, Rewind, FastForward, AudioLines, Loader2, Subtitles, Plus, X, List, Settings2, Info, Keyboard, Camera, RotateCcw, PictureInPicture2, Minimize, Maximize, ChevronDown, ChevronUp, Check, ChevronLeft, ListVideo, Download, Upload, Pin, PinOff, Minimize2, Maximize2, ChevronRight, FilePlus, FolderOpen, Link, Link2, Globe, AlertCircle, GripVertical, Tv } from 'lucide-react';
|
|
14
14
|
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
|
15
15
|
import { DndContext, closestCenter } from '@dnd-kit/core';
|
|
16
16
|
import { SortableContext, verticalListSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable';
|
|
17
17
|
import { CSS } from '@dnd-kit/utilities';
|
|
18
18
|
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
19
19
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
20
|
-
import { exportPlaylist, formatShortcutKey, loadShortcuts, ProgressEstimator, createVideoPlayer, CancellationError, parseMediaError, captureVideoThumbnail, validateFile, parseM3U8, matchesShortcut, saveShortcuts, DEFAULT_SHORTCUTS } from '@lightbird/core';
|
|
20
|
+
import { exportPlaylist, formatShortcutKey, FLAG_MAGNET_LINK, loadShortcuts, ProgressEstimator, createVideoPlayer, CancellationError, hasAcceptedDisclaimer, acceptDisclaimer, initFeatureFlags, parseMediaError, captureVideoThumbnail, validateFile, parseM3U8, matchesShortcut, saveShortcuts, DEFAULT_SHORTCUTS } from '@lightbird/core';
|
|
21
21
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
22
|
-
import { usePlaylist, useVideoPlayback, useVideoFilters, useSubtitles, useFullscreen, usePictureInPicture, useVideoInfo, useProgressPersistence, useChapters, useKeyboardShortcuts, useMediaSession } from '@lightbird/core/react';
|
|
22
|
+
import { usePlaylist, useVideoPlayback, useVideoFilters, useSubtitles, useFullscreen, usePictureInPicture, useVideoInfo, useProgressPersistence, useChapters, useMagnet, useKeyboardShortcuts, useMediaSession } from '@lightbird/core/react';
|
|
23
|
+
import { useBooleanFlagValue, OpenFeatureProvider } from '@openfeature/react-sdk';
|
|
24
|
+
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
|
23
25
|
import * as ToastPrimitives from '@radix-ui/react-toast';
|
|
24
26
|
|
|
25
27
|
function cn(...inputs) {
|
|
@@ -628,7 +630,13 @@ function formatTime2(seconds) {
|
|
|
628
630
|
return `${m}:${String(s).padStart(2, "0")}`;
|
|
629
631
|
}
|
|
630
632
|
var VIDEO_EXTENSIONS_RE = /\.(mp4|mkv|webm|mov|avi|wmv|flv|m4v)$/i;
|
|
631
|
-
function
|
|
633
|
+
function formatBytes(bytes) {
|
|
634
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
635
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
636
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
637
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
638
|
+
}
|
|
639
|
+
function SortablePlaylistItem({ item, index, isActive, downloadReady, onSelect, onRemove }) {
|
|
632
640
|
const {
|
|
633
641
|
attributes,
|
|
634
642
|
listeners: listeners2,
|
|
@@ -672,12 +680,24 @@ function SortablePlaylistItem({ item, index, isActive, onSelect, onRemove }) {
|
|
|
672
680
|
className: "flex-1 flex items-center gap-1.5 min-w-0 text-left",
|
|
673
681
|
"aria-label": `Play ${item.name}`,
|
|
674
682
|
children: [
|
|
675
|
-
item.type === "video" ? /* @__PURE__ */ jsx(ListVideo, { className: "w-3.5 h-3.5 shrink-0" }) : /* @__PURE__ */ jsx(Tv, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
683
|
+
item.source === "torrent" ? /* @__PURE__ */ jsx(Globe, { className: "w-3.5 h-3.5 shrink-0 text-emerald-500" }) : item.type === "video" ? /* @__PURE__ */ jsx(ListVideo, { className: "w-3.5 h-3.5 shrink-0" }) : /* @__PURE__ */ jsx(Tv, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
676
684
|
/* @__PURE__ */ jsx("span", { className: "truncate", children: item.name }),
|
|
677
685
|
duration && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground ml-auto shrink-0 pl-1", children: duration })
|
|
678
686
|
]
|
|
679
687
|
}
|
|
680
688
|
),
|
|
689
|
+
item.source === "torrent" && downloadReady && /* @__PURE__ */ jsx(
|
|
690
|
+
"a",
|
|
691
|
+
{
|
|
692
|
+
href: item.url,
|
|
693
|
+
download: item.name,
|
|
694
|
+
onClick: (e) => e.stopPropagation(),
|
|
695
|
+
className: "shrink-0 opacity-0 group-hover:opacity-100 transition-opacity p-0.5",
|
|
696
|
+
"aria-label": `Download ${item.name}`,
|
|
697
|
+
title: "Download",
|
|
698
|
+
children: /* @__PURE__ */ jsx(Download, { className: "w-3 h-3 text-emerald-500 hover:text-emerald-400" })
|
|
699
|
+
}
|
|
700
|
+
),
|
|
681
701
|
/* @__PURE__ */ jsx(
|
|
682
702
|
"button",
|
|
683
703
|
{
|
|
@@ -701,6 +721,9 @@ var PlaylistPanel = ({
|
|
|
701
721
|
onFilesAdded,
|
|
702
722
|
onFolderFilesAdded,
|
|
703
723
|
onAddStream,
|
|
724
|
+
onAddMagnet,
|
|
725
|
+
torrentStatus,
|
|
726
|
+
showMagnet = true,
|
|
704
727
|
onRemoveItem,
|
|
705
728
|
onReorder,
|
|
706
729
|
onImportM3U,
|
|
@@ -715,6 +738,9 @@ var PlaylistPanel = ({
|
|
|
715
738
|
const folderInputRef = useRef(null);
|
|
716
739
|
const m3uInputRef = useRef(null);
|
|
717
740
|
const [streamUrl, setStreamUrl] = useState("");
|
|
741
|
+
const [magnetUri, setMagnetUri] = useState("");
|
|
742
|
+
const [showMagnetInput, setShowMagnetInput] = useState(false);
|
|
743
|
+
const [magnetError, setMagnetError] = useState(null);
|
|
718
744
|
const [sortKey, setSortKey] = useState("");
|
|
719
745
|
const handleStreamUrlSubmit = (e) => {
|
|
720
746
|
e.preventDefault();
|
|
@@ -723,6 +749,21 @@ var PlaylistPanel = ({
|
|
|
723
749
|
setStreamUrl("");
|
|
724
750
|
}
|
|
725
751
|
};
|
|
752
|
+
const handleMagnetSubmit = async (e) => {
|
|
753
|
+
e.preventDefault();
|
|
754
|
+
const uri = magnetUri.trim();
|
|
755
|
+
if (!uri) return;
|
|
756
|
+
setMagnetError(null);
|
|
757
|
+
try {
|
|
758
|
+
const added = await onAddMagnet(uri);
|
|
759
|
+
if (added) {
|
|
760
|
+
setMagnetUri("");
|
|
761
|
+
setShowMagnetInput(false);
|
|
762
|
+
}
|
|
763
|
+
} catch (err) {
|
|
764
|
+
setMagnetError(err instanceof Error ? err.message : "Failed to load magnet link");
|
|
765
|
+
}
|
|
766
|
+
};
|
|
726
767
|
const handleFolderSelect = (e) => {
|
|
727
768
|
const files = Array.from(e.target.files ?? []).filter((f) => VIDEO_EXTENSIONS_RE.test(f.name)).sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
|
|
728
769
|
if (files.length > 0) onFolderFilesAdded(files);
|
|
@@ -937,8 +978,89 @@ var PlaylistPanel = ({
|
|
|
937
978
|
className: "h-8 text-xs"
|
|
938
979
|
}
|
|
939
980
|
),
|
|
940
|
-
/* @__PURE__ */ jsx(Button, { type: "submit", size: "icon", variant: "secondary", className: "h-8 w-8 shrink-0", children: /* @__PURE__ */ jsx(Link, { className: "h-3.5 w-3.5" }) })
|
|
981
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", size: "icon", variant: "secondary", className: "h-8 w-8 shrink-0", children: /* @__PURE__ */ jsx(Link, { className: "h-3.5 w-3.5" }) }),
|
|
982
|
+
showMagnet && /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
983
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
984
|
+
Button,
|
|
985
|
+
{
|
|
986
|
+
type: "button",
|
|
987
|
+
size: "icon",
|
|
988
|
+
variant: showMagnetInput ? "default" : "outline",
|
|
989
|
+
className: "h-8 w-8 shrink-0",
|
|
990
|
+
onClick: () => {
|
|
991
|
+
setShowMagnetInput((v) => !v);
|
|
992
|
+
setMagnetError(null);
|
|
993
|
+
},
|
|
994
|
+
"aria-label": "Add magnet link",
|
|
995
|
+
children: /* @__PURE__ */ jsx(Link2, { className: "h-3.5 w-3.5" })
|
|
996
|
+
}
|
|
997
|
+
) }),
|
|
998
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: /* @__PURE__ */ jsx("p", { children: "Add Magnet Link" }) })
|
|
999
|
+
] })
|
|
941
1000
|
] }),
|
|
1001
|
+
showMagnet && showMagnetInput && /* @__PURE__ */ jsxs("form", { onSubmit: handleMagnetSubmit, className: "space-y-1.5", children: [
|
|
1002
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
|
|
1003
|
+
/* @__PURE__ */ jsx(
|
|
1004
|
+
Input,
|
|
1005
|
+
{
|
|
1006
|
+
placeholder: "magnet:?xt=urn:btih:\u2026",
|
|
1007
|
+
value: magnetUri,
|
|
1008
|
+
onChange: (e) => {
|
|
1009
|
+
setMagnetUri(e.target.value);
|
|
1010
|
+
setMagnetError(null);
|
|
1011
|
+
},
|
|
1012
|
+
className: "h-8 text-xs font-mono",
|
|
1013
|
+
disabled: torrentStatus.status === "loading-metadata"
|
|
1014
|
+
}
|
|
1015
|
+
),
|
|
1016
|
+
/* @__PURE__ */ jsx(
|
|
1017
|
+
Button,
|
|
1018
|
+
{
|
|
1019
|
+
type: "submit",
|
|
1020
|
+
size: "icon",
|
|
1021
|
+
variant: "default",
|
|
1022
|
+
className: "h-8 w-8 shrink-0",
|
|
1023
|
+
disabled: !magnetUri.trim() || torrentStatus.status === "loading-metadata",
|
|
1024
|
+
"aria-label": "Load magnet link",
|
|
1025
|
+
children: torrentStatus.status === "loading-metadata" ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Globe, { className: "h-3.5 w-3.5" })
|
|
1026
|
+
}
|
|
1027
|
+
)
|
|
1028
|
+
] }),
|
|
1029
|
+
torrentStatus.status === "loading-metadata" && /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-muted-foreground flex items-center gap-1", children: [
|
|
1030
|
+
/* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin" }),
|
|
1031
|
+
"Fetching torrent info\u2026"
|
|
1032
|
+
] }),
|
|
1033
|
+
magnetError && /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-destructive flex items-center gap-1", children: [
|
|
1034
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "h-3 w-3 shrink-0" }),
|
|
1035
|
+
magnetError
|
|
1036
|
+
] })
|
|
1037
|
+
] }),
|
|
1038
|
+
showMagnet && torrentStatus.status === "ready" && (torrentStatus.progress >= 1 ? /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-emerald-500 flex items-center gap-1", children: [
|
|
1039
|
+
/* @__PURE__ */ jsx(Download, { className: "h-3 w-3 shrink-0" }),
|
|
1040
|
+
"Download ready \u2014 hover a file to save it"
|
|
1041
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1042
|
+
/* @__PURE__ */ jsx("div", { className: "w-full bg-muted rounded-full h-1 overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
1043
|
+
"div",
|
|
1044
|
+
{
|
|
1045
|
+
className: "bg-emerald-500 h-1 rounded-full transition-all duration-500",
|
|
1046
|
+
style: { width: `${Math.round(torrentStatus.progress * 100)}%` }
|
|
1047
|
+
}
|
|
1048
|
+
) }),
|
|
1049
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground flex items-center justify-between", children: [
|
|
1050
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1051
|
+
"\u2193 ",
|
|
1052
|
+
formatBytes(torrentStatus.downloadSpeed),
|
|
1053
|
+
"/s \xB7 ",
|
|
1054
|
+
torrentStatus.numPeers,
|
|
1055
|
+
" peer",
|
|
1056
|
+
torrentStatus.numPeers !== 1 ? "s" : ""
|
|
1057
|
+
] }),
|
|
1058
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1059
|
+
Math.round(torrentStatus.progress * 100),
|
|
1060
|
+
"%"
|
|
1061
|
+
] })
|
|
1062
|
+
] })
|
|
1063
|
+
] })),
|
|
942
1064
|
playlist.length > 1 && /* @__PURE__ */ jsxs(Select, { value: sortKey, onValueChange: handleSort, children: [
|
|
943
1065
|
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-7 text-xs", "aria-label": "Sort playlist", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Sort by\u2026" }) }),
|
|
944
1066
|
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
@@ -963,6 +1085,7 @@ var PlaylistPanel = ({
|
|
|
963
1085
|
item,
|
|
964
1086
|
index,
|
|
965
1087
|
isActive: index === currentVideoIndex,
|
|
1088
|
+
downloadReady: item.source === "torrent" && torrentStatus.progress >= 1,
|
|
966
1089
|
onSelect: onSelectVideo,
|
|
967
1090
|
onRemove: onRemoveItem
|
|
968
1091
|
},
|
|
@@ -1440,6 +1563,103 @@ function SubtitleOverlay({ videoRef, activeSubtitle }) {
|
|
|
1440
1563
|
}
|
|
1441
1564
|
) });
|
|
1442
1565
|
}
|
|
1566
|
+
var AlertDialog = AlertDialogPrimitive.Root;
|
|
1567
|
+
var AlertDialogPortal = AlertDialogPrimitive.Portal;
|
|
1568
|
+
var AlertDialogOverlay = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1569
|
+
AlertDialogPrimitive.Overlay,
|
|
1570
|
+
{
|
|
1571
|
+
className: cn(
|
|
1572
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
1573
|
+
className
|
|
1574
|
+
),
|
|
1575
|
+
...props,
|
|
1576
|
+
ref
|
|
1577
|
+
}
|
|
1578
|
+
));
|
|
1579
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
1580
|
+
var AlertDialogContent = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
|
|
1581
|
+
/* @__PURE__ */ jsx(AlertDialogOverlay, {}),
|
|
1582
|
+
/* @__PURE__ */ jsx(
|
|
1583
|
+
AlertDialogPrimitive.Content,
|
|
1584
|
+
{
|
|
1585
|
+
ref,
|
|
1586
|
+
className: cn(
|
|
1587
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
1588
|
+
className
|
|
1589
|
+
),
|
|
1590
|
+
...props
|
|
1591
|
+
}
|
|
1592
|
+
)
|
|
1593
|
+
] }));
|
|
1594
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
1595
|
+
var AlertDialogHeader = ({
|
|
1596
|
+
className,
|
|
1597
|
+
...props
|
|
1598
|
+
}) => /* @__PURE__ */ jsx(
|
|
1599
|
+
"div",
|
|
1600
|
+
{
|
|
1601
|
+
className: cn(
|
|
1602
|
+
"flex flex-col space-y-2 text-center sm:text-left",
|
|
1603
|
+
className
|
|
1604
|
+
),
|
|
1605
|
+
...props
|
|
1606
|
+
}
|
|
1607
|
+
);
|
|
1608
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
1609
|
+
var AlertDialogFooter = ({
|
|
1610
|
+
className,
|
|
1611
|
+
...props
|
|
1612
|
+
}) => /* @__PURE__ */ jsx(
|
|
1613
|
+
"div",
|
|
1614
|
+
{
|
|
1615
|
+
className: cn(
|
|
1616
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
1617
|
+
className
|
|
1618
|
+
),
|
|
1619
|
+
...props
|
|
1620
|
+
}
|
|
1621
|
+
);
|
|
1622
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
1623
|
+
var AlertDialogTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1624
|
+
AlertDialogPrimitive.Title,
|
|
1625
|
+
{
|
|
1626
|
+
ref,
|
|
1627
|
+
className: cn("text-lg font-semibold", className),
|
|
1628
|
+
...props
|
|
1629
|
+
}
|
|
1630
|
+
));
|
|
1631
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
1632
|
+
var AlertDialogDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1633
|
+
AlertDialogPrimitive.Description,
|
|
1634
|
+
{
|
|
1635
|
+
ref,
|
|
1636
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
1637
|
+
...props
|
|
1638
|
+
}
|
|
1639
|
+
));
|
|
1640
|
+
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
1641
|
+
var AlertDialogAction = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1642
|
+
AlertDialogPrimitive.Action,
|
|
1643
|
+
{
|
|
1644
|
+
ref,
|
|
1645
|
+
className: cn(buttonVariants(), className),
|
|
1646
|
+
...props
|
|
1647
|
+
}
|
|
1648
|
+
));
|
|
1649
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
1650
|
+
var AlertDialogCancel = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1651
|
+
AlertDialogPrimitive.Cancel,
|
|
1652
|
+
{
|
|
1653
|
+
ref,
|
|
1654
|
+
className: cn(
|
|
1655
|
+
buttonVariants({ variant: "outline" }),
|
|
1656
|
+
"mt-2 sm:mt-0",
|
|
1657
|
+
className
|
|
1658
|
+
),
|
|
1659
|
+
...props
|
|
1660
|
+
}
|
|
1661
|
+
));
|
|
1662
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|
1443
1663
|
var MAX_RETRIES = 3;
|
|
1444
1664
|
var LightBirdPlayer = () => {
|
|
1445
1665
|
const videoRef = useRef(null);
|
|
@@ -1465,6 +1685,9 @@ var LightBirdPlayer = () => {
|
|
|
1465
1685
|
const { metadata: videoMetadata } = useVideoInfo(videoRef, playlist.currentItem?.file ?? null);
|
|
1466
1686
|
useProgressPersistence(videoRef, playlist.currentItem?.name ?? null);
|
|
1467
1687
|
const { chapters, currentChapter, goToChapter } = useChapters(videoRef, playerRef);
|
|
1688
|
+
const magnetLinkEnabled = useBooleanFlagValue(FLAG_MAGNET_LINK, true);
|
|
1689
|
+
const magnet = useMagnet();
|
|
1690
|
+
const [disclaimerPendingUri, setDisclaimerPendingUri] = useState(null);
|
|
1468
1691
|
const [shortcuts, setShortcuts] = useState(() => loadShortcuts());
|
|
1469
1692
|
const [showShortcutsHelp, setShowShortcutsHelp] = useState(false);
|
|
1470
1693
|
const [showShortcutsDialog, setShowShortcutsDialog] = useState(false);
|
|
@@ -1854,6 +2077,40 @@ var LightBirdPlayer = () => {
|
|
|
1854
2077
|
startStallDetection();
|
|
1855
2078
|
}
|
|
1856
2079
|
}, [playlist, subtitles]);
|
|
2080
|
+
const handleAddMagnet = useCallback(async (uri) => {
|
|
2081
|
+
if (!hasAcceptedDisclaimer()) {
|
|
2082
|
+
setDisclaimerPendingUri(uri);
|
|
2083
|
+
return false;
|
|
2084
|
+
}
|
|
2085
|
+
const items = await magnet.addMagnet(uri);
|
|
2086
|
+
const startIndex = playlist.playlist.length;
|
|
2087
|
+
items.forEach((item) => playlist.appendItem(item));
|
|
2088
|
+
if (playlist.currentIndex === null && items.length > 0) {
|
|
2089
|
+
playlist.selectItem(startIndex);
|
|
2090
|
+
if (videoRef.current) videoRef.current.src = items[0].url;
|
|
2091
|
+
subtitles.reset();
|
|
2092
|
+
setAudioTracks([]);
|
|
2093
|
+
setActiveAudioTrack("0");
|
|
2094
|
+
isStreamRef.current = true;
|
|
2095
|
+
startStallDetection();
|
|
2096
|
+
}
|
|
2097
|
+
if (items.length > 1) {
|
|
2098
|
+
toast2({ title: `${items.length} videos added from torrent`, description: magnet.torrentStatus.torrentName });
|
|
2099
|
+
}
|
|
2100
|
+
return true;
|
|
2101
|
+
}, [magnet, playlist, subtitles, toast2]);
|
|
2102
|
+
const handleDisclaimerAccepted = useCallback(async () => {
|
|
2103
|
+
acceptDisclaimer();
|
|
2104
|
+
const uri = disclaimerPendingUri;
|
|
2105
|
+
setDisclaimerPendingUri(null);
|
|
2106
|
+
if (uri) {
|
|
2107
|
+
try {
|
|
2108
|
+
await handleAddMagnet(uri);
|
|
2109
|
+
} catch (err) {
|
|
2110
|
+
toast2({ title: "Magnet link failed", description: err instanceof Error ? err.message : "Unknown error", variant: "destructive" });
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
}, [disclaimerPendingUri, handleAddMagnet, toast2]);
|
|
1857
2114
|
const handleSubtitleChange = useCallback(async (id) => {
|
|
1858
2115
|
subtitles.switchSubtitle(id);
|
|
1859
2116
|
if (playerRef.current) {
|
|
@@ -2102,6 +2359,9 @@ var LightBirdPlayer = () => {
|
|
|
2102
2359
|
onFilesAdded: handleFileChange,
|
|
2103
2360
|
onFolderFilesAdded: handleFolderFilesAdded,
|
|
2104
2361
|
onAddStream: handleAddStream,
|
|
2362
|
+
onAddMagnet: handleAddMagnet,
|
|
2363
|
+
torrentStatus: magnet.torrentStatus,
|
|
2364
|
+
showMagnet: magnetLinkEnabled,
|
|
2105
2365
|
onRemoveItem: handleRemoveItem,
|
|
2106
2366
|
onReorder: handleReorder,
|
|
2107
2367
|
onImportM3U: handleImportM3U,
|
|
@@ -2112,7 +2372,29 @@ var LightBirdPlayer = () => {
|
|
|
2112
2372
|
onTogglePin: () => setPlaylistPinned((v) => !v),
|
|
2113
2373
|
onSizeChange: setPlaylistSize
|
|
2114
2374
|
}
|
|
2115
|
-
)
|
|
2375
|
+
),
|
|
2376
|
+
/* @__PURE__ */ jsx(AlertDialog, { open: magnetLinkEnabled && disclaimerPendingUri !== null, onOpenChange: (open) => {
|
|
2377
|
+
if (!open) setDisclaimerPendingUri(null);
|
|
2378
|
+
}, children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
|
|
2379
|
+
/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
|
|
2380
|
+
/* @__PURE__ */ jsx(AlertDialogTitle, { children: "Magnet Link Streaming" }),
|
|
2381
|
+
/* @__PURE__ */ jsx(AlertDialogDescription, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: "space-y-2 text-sm text-muted-foreground", children: [
|
|
2382
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
2383
|
+
"LightBird streams content via ",
|
|
2384
|
+
/* @__PURE__ */ jsx("strong", { className: "text-foreground", children: "BitTorrent" }),
|
|
2385
|
+
" (WebRTC/WebSocket) \u2014 the same technology used by applications like VLC and qBittorrent."
|
|
2386
|
+
] }),
|
|
2387
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
2388
|
+
/* @__PURE__ */ jsx("strong", { className: "text-foreground", children: "You are responsible" }),
|
|
2389
|
+
" for ensuring you have the legal right to access any content you stream. LightBird does not host, index, or endorse any content."
|
|
2390
|
+
] })
|
|
2391
|
+
] }) })
|
|
2392
|
+
] }),
|
|
2393
|
+
/* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
|
|
2394
|
+
/* @__PURE__ */ jsx(AlertDialogCancel, { onClick: () => setDisclaimerPendingUri(null), children: "Cancel" }),
|
|
2395
|
+
/* @__PURE__ */ jsx(AlertDialogAction, { onClick: handleDisclaimerAccepted, children: "I Understand \u2014 Continue" })
|
|
2396
|
+
] })
|
|
2397
|
+
] }) })
|
|
2116
2398
|
] });
|
|
2117
2399
|
};
|
|
2118
2400
|
var lightbird_player_default = LightBirdPlayer;
|
|
@@ -2243,5 +2525,14 @@ function Toaster() {
|
|
|
2243
2525
|
/* @__PURE__ */ jsx(ToastViewport, {})
|
|
2244
2526
|
] });
|
|
2245
2527
|
}
|
|
2528
|
+
var started = false;
|
|
2529
|
+
function FeatureFlagsProvider({ children }) {
|
|
2530
|
+
useEffect(() => {
|
|
2531
|
+
if (started) return;
|
|
2532
|
+
started = true;
|
|
2533
|
+
initFeatureFlags().catch(console.error);
|
|
2534
|
+
}, []);
|
|
2535
|
+
return /* @__PURE__ */ jsx(OpenFeatureProvider, { suspendUntilReady: false, suspendWhileReconciling: false, children });
|
|
2536
|
+
}
|
|
2246
2537
|
|
|
2247
|
-
export { lightbird_player_default as LightBirdPlayer, player_controls_default as PlayerControls, PlayerErrorBoundary, PlayerErrorDisplay, playlist_panel_default as PlaylistPanel, ShortcutSettingsDialog, SubtitleOverlay, Toaster, VideoInfoPanel, VideoOverlay };
|
|
2538
|
+
export { FeatureFlagsProvider, lightbird_player_default as LightBirdPlayer, player_controls_default as PlayerControls, PlayerErrorBoundary, PlayerErrorDisplay, playlist_panel_default as PlaylistPanel, ShortcutSettingsDialog, SubtitleOverlay, Toaster, VideoInfoPanel, VideoOverlay };
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.left-0{left:0}.left-2{left:.5rem}.left-\[50\%\]{left:50%}.right-0{right:0}.right-2{right:.5rem}.right-4{right:1rem}.top-0{top:0}.top-2{top:.5rem}.top-4{top:1rem}.top-\[50\%\]{top:50%}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1/1}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-80{max-height:20rem}.max-h-96{max-height:24rem}.max-h-screen{max-height:100vh}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-40{width:10rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-60{width:15rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-80{width:20rem}.w-9{width:2.25rem}.w-96{width:24rem}.w-full{width:100%}.min-w-0{min-width:0}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.translate-x-\[-50\%\]{--tw-translate-x:-50%}.translate-x-\[-50\%\],.translate-y-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.translate-y-\[-50\%\]{--tw-translate-y:-50%}.transform,.translate-y-\[-50\%\]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-grab{cursor:grab}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem*var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-border{border-color:hsl(var(--border))}.border-destructive{border-color:hsl(var(--destructive))}.border-input{border-color:hsl(var(--input))}.border-muted-foreground{border-color:hsl(var(--muted-foreground))}.border-primary{border-color:hsl(var(--primary))}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.border-white\/30{border-color:hsla(0,0%,100%,.3)}.border-l-transparent{border-left-color:transparent}.border-t-transparent{border-top-color:transparent}.bg-accent{background-color:hsl(var(--accent))}.bg-background{background-color:hsl(var(--background))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-black\/85{background-color:rgba(0,0,0,.85)}.bg-black\/90{background-color:rgba(0,0,0,.9)}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.bg-muted{background-color:hsl(var(--muted))}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary)/.1)}.bg-primary\/20{background-color:hsl(var(--primary)/.2)}.bg-secondary{background-color:hsl(var(--secondary))}.bg-transparent{background-color:transparent}.bg-opacity-70{--tw-bg-opacity:0.7}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-black\/70{--tw-gradient-from:rgba(0,0,0,.7) var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.fill-current{fill:currentColor}.object-contain{-o-object-fit:contain;object-fit:contain}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-\[1px\]{padding:1px}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pl-1{padding-left:.25rem}.pl-8{padding-left:2rem}.pr-1{padding-right:.25rem}.pr-2{padding-right:.5rem}.pr-3{padding-right:.75rem}.pr-8{padding-right:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.tracking-widest{letter-spacing:.1em}.text-current{color:currentColor}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-foreground{color:hsl(var(--foreground))}.text-foreground\/50{color:hsl(var(--foreground)/.5)}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/60{color:hsla(0,0%,100%,.6)}.text-white\/80{color:hsla(0,0%,100%,.8)}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-90{opacity:.9}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-background{--tw-ring-color:hsl(var(--background))}.ring-offset-background{--tw-ring-offset-color:hsl(var(--background))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[width\]{transition-property:width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0) scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1)) rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0) scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1)) rotate(var(--tw-exit-rotate,0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity:initial;--tw-enter-scale:initial;--tw-enter-rotate:initial;--tw-enter-translate-x:initial;--tw-enter-translate-y:initial}.fade-in-0{--tw-enter-opacity:0}.zoom-in-95{--tw-enter-scale:.95}.duration-200{animation-duration:.2s}.duration-300{animation-duration:.3s}.duration-500{animation-duration:.5s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.paused{animation-play-state:paused}:root{--background:0 0% 13%;--foreground:0 0% 87%;--card:0 0% 10%;--card-foreground:0 0% 87%;--popover:0 0% 8%;--popover-foreground:0 0% 87%;--primary:207 100% 40%;--primary-foreground:0 0% 100%;--secondary:0 0% 20%;--secondary-foreground:0 0% 98%;--muted:0 0% 20%;--muted-foreground:0 0% 64%;--accent:207 100% 50%;--accent-foreground:0 0% 100%;--destructive:0 63% 31%;--destructive-foreground:0 0% 98%;--border:0 0% 20%;--input:0 0% 20%;--ring:207 100% 40%;--radius:0.5rem}.file\:border-0::file-selector-button{border-width:0}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.file\:text-foreground::file-selector-button{color:hsl(var(--foreground))}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.last\:border-0:last-child{border-width:0}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive)/.9)}.hover\:bg-muted:hover{background-color:hsl(var(--muted))}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary)/.9)}.hover\:bg-secondary:hover{background-color:hsl(var(--secondary))}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary)/.8)}.hover\:bg-white\/10:hover{background-color:hsla(0,0%,100%,.1)}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-destructive:hover{color:hsl(var(--destructive))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-gray-300:hover{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:opacity-100:focus{opacity:1}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-color:hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:-translate-y-24{--tw-translate-y:-6rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:opacity-100{opacity:1}.group.destructive .group-\[\.destructive\]\:border-muted\/40{border-color:hsl(var(--muted)/.4)}.group.destructive .group-\[\.destructive\]\:text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.group.destructive .group-\[\.destructive\]\:hover\:border-destructive\/30:hover{border-color:hsl(var(--destructive)/.3)}.group.destructive .group-\[\.destructive\]\:hover\:bg-destructive:hover{background-color:hsl(var(--destructive))}.group.destructive .group-\[\.destructive\]\:hover\:text-destructive-foreground:hover{color:hsl(var(--destructive-foreground))}.group.destructive .group-\[\.destructive\]\:hover\:text-red-50:hover{--tw-text-opacity:1;color:rgb(254 242 242/var(--tw-text-opacity,1))}.group.destructive .group-\[\.destructive\]\:focus\:ring-destructive:focus{--tw-ring-color:hsl(var(--destructive))}.group.destructive .group-\[\.destructive\]\:focus\:ring-red-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(248 113 113/var(--tw-ring-opacity,1))}.group.destructive .group-\[\.destructive\]\:focus\:ring-offset-red-600:focus{--tw-ring-offset-color:#dc2626}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:0.25rem}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom],.data-\[side\=left\]\:-translate-x-1[data-side=left]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:-0.25rem}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:0.25rem}.data-\[side\=right\]\:translate-x-1[data-side=right],.data-\[side\=top\]\:-translate-y-1[data-side=top]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:-0.25rem}.data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel]{--tw-translate-x:0px}.data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel],.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end]{--tw-translate-x:var(--radix-toast-swipe-end-x)}.data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move]{--tw-translate-x:var(--radix-toast-swipe-move-x);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:hsl(var(--accent))}.data-\[active\=true\]\:text-primary[data-active=true]{color:hsl(var(--primary))}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:hsl(var(--muted-foreground))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[swipe\=move\]\:transition-none[data-swipe=move]{transition-property:none}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity:initial;--tw-enter-scale:initial;--tw-enter-rotate:initial;--tw-enter-translate-x:initial;--tw-enter-translate-y:initial}.data-\[state\=closed\]\:animate-out[data-state=closed],.data-\[swipe\=end\]\:animate-out[data-swipe=end]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity:initial;--tw-exit-scale:initial;--tw-exit-rotate:initial;--tw-exit-translate-x:initial;--tw-exit-translate-y:initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:fade-out-80[data-state=closed]{--tw-exit-opacity:0.8}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:-0.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:0.5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:-0.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:0.5rem}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x:-50%}.data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y:-48%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x:-50%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y:-48%}.data-\[state\=open\]\:slide-in-from-top-full[data-state=open]{--tw-enter-translate-y:-100%}@media (min-width:640px){.sm\:bottom-0{bottom:0}.sm\:right-0{right:0}.sm\:top-auto{top:auto}.sm\:flex-row{flex-direction:row}.sm\:flex-col{flex-direction:column}.sm\:justify-end{justify-content:flex-end}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}.data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open]{--tw-enter-translate-y:100%}}@media (min-width:768px){.md\:max-w-\[420px\]{max-width:420px}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}
|
|
1
|
+
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.left-0{left:0}.left-2{left:.5rem}.left-\[50\%\]{left:50%}.right-0{right:0}.right-2{right:.5rem}.right-4{right:1rem}.top-0{top:0}.top-2{top:.5rem}.top-4{top:1rem}.top-\[50\%\]{top:50%}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1/1}.h-1{height:.25rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-80{max-height:20rem}.max-h-96{max-height:24rem}.max-h-screen{max-height:100vh}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-40{width:10rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-60{width:15rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-80{width:20rem}.w-9{width:2.25rem}.w-96{width:24rem}.w-full{width:100%}.min-w-0{min-width:0}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.translate-x-\[-50\%\]{--tw-translate-x:-50%}.translate-x-\[-50\%\],.translate-y-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y:0px}.translate-y-\[-50\%\]{--tw-translate-y:-50%}.transform,.translate-y-\[-50\%\]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-grab{cursor:grab}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem*var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-border{border-color:hsl(var(--border))}.border-destructive{border-color:hsl(var(--destructive))}.border-input{border-color:hsl(var(--input))}.border-muted-foreground{border-color:hsl(var(--muted-foreground))}.border-primary{border-color:hsl(var(--primary))}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.border-white\/30{border-color:hsla(0,0%,100%,.3)}.border-l-transparent{border-left-color:transparent}.border-t-transparent{border-top-color:transparent}.bg-accent{background-color:hsl(var(--accent))}.bg-background{background-color:hsl(var(--background))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-black\/85{background-color:rgba(0,0,0,.85)}.bg-black\/90{background-color:rgba(0,0,0,.9)}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.bg-muted{background-color:hsl(var(--muted))}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary)/.1)}.bg-primary\/20{background-color:hsl(var(--primary)/.2)}.bg-secondary{background-color:hsl(var(--secondary))}.bg-transparent{background-color:transparent}.bg-opacity-70{--tw-bg-opacity:0.7}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-black\/70{--tw-gradient-from:rgba(0,0,0,.7) var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.fill-current{fill:currentColor}.object-contain{-o-object-fit:contain;object-fit:contain}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-\[1px\]{padding:1px}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pl-1{padding-left:.25rem}.pl-8{padding-left:2rem}.pr-1{padding-right:.25rem}.pr-2{padding-right:.5rem}.pr-3{padding-right:.75rem}.pr-8{padding-right:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.tracking-widest{letter-spacing:.1em}.text-current{color:currentColor}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-emerald-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.text-foreground{color:hsl(var(--foreground))}.text-foreground\/50{color:hsl(var(--foreground)/.5)}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/60{color:hsla(0,0%,100%,.6)}.text-white\/80{color:hsla(0,0%,100%,.8)}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-90{opacity:.9}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-background{--tw-ring-color:hsl(var(--background))}.ring-offset-background{--tw-ring-offset-color:hsl(var(--background))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[width\]{transition-property:width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0) scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1)) rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0) scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1)) rotate(var(--tw-exit-rotate,0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity:initial;--tw-enter-scale:initial;--tw-enter-rotate:initial;--tw-enter-translate-x:initial;--tw-enter-translate-y:initial}.fade-in-0{--tw-enter-opacity:0}.zoom-in-95{--tw-enter-scale:.95}.duration-200{animation-duration:.2s}.duration-300{animation-duration:.3s}.duration-500{animation-duration:.5s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.paused{animation-play-state:paused}:root{--background:0 0% 13%;--foreground:0 0% 87%;--card:0 0% 10%;--card-foreground:0 0% 87%;--popover:0 0% 8%;--popover-foreground:0 0% 87%;--primary:207 100% 40%;--primary-foreground:0 0% 100%;--secondary:0 0% 20%;--secondary-foreground:0 0% 98%;--muted:0 0% 20%;--muted-foreground:0 0% 64%;--accent:207 100% 50%;--accent-foreground:0 0% 100%;--destructive:0 63% 31%;--destructive-foreground:0 0% 98%;--border:0 0% 20%;--input:0 0% 20%;--ring:207 100% 40%;--radius:0.5rem}.file\:border-0::file-selector-button{border-width:0}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.file\:text-foreground::file-selector-button{color:hsl(var(--foreground))}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.last\:border-0:last-child{border-width:0}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive)/.9)}.hover\:bg-muted:hover{background-color:hsl(var(--muted))}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary)/.9)}.hover\:bg-secondary:hover{background-color:hsl(var(--secondary))}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary)/.8)}.hover\:bg-white\/10:hover{background-color:hsla(0,0%,100%,.1)}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-destructive:hover{color:hsl(var(--destructive))}.hover\:text-emerald-400:hover{--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-gray-300:hover{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:opacity-100:focus{opacity:1}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-color:hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:-translate-y-24{--tw-translate-y:-6rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:opacity-100{opacity:1}.group.destructive .group-\[\.destructive\]\:border-muted\/40{border-color:hsl(var(--muted)/.4)}.group.destructive .group-\[\.destructive\]\:text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.group.destructive .group-\[\.destructive\]\:hover\:border-destructive\/30:hover{border-color:hsl(var(--destructive)/.3)}.group.destructive .group-\[\.destructive\]\:hover\:bg-destructive:hover{background-color:hsl(var(--destructive))}.group.destructive .group-\[\.destructive\]\:hover\:text-destructive-foreground:hover{color:hsl(var(--destructive-foreground))}.group.destructive .group-\[\.destructive\]\:hover\:text-red-50:hover{--tw-text-opacity:1;color:rgb(254 242 242/var(--tw-text-opacity,1))}.group.destructive .group-\[\.destructive\]\:focus\:ring-destructive:focus{--tw-ring-color:hsl(var(--destructive))}.group.destructive .group-\[\.destructive\]\:focus\:ring-red-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(248 113 113/var(--tw-ring-opacity,1))}.group.destructive .group-\[\.destructive\]\:focus\:ring-offset-red-600:focus{--tw-ring-offset-color:#dc2626}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:0.25rem}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom],.data-\[side\=left\]\:-translate-x-1[data-side=left]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:-0.25rem}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:0.25rem}.data-\[side\=right\]\:translate-x-1[data-side=right],.data-\[side\=top\]\:-translate-y-1[data-side=top]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:-0.25rem}.data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel]{--tw-translate-x:0px}.data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel],.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end]{--tw-translate-x:var(--radix-toast-swipe-end-x)}.data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move]{--tw-translate-x:var(--radix-toast-swipe-move-x);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:hsl(var(--accent))}.data-\[active\=true\]\:text-primary[data-active=true]{color:hsl(var(--primary))}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:hsl(var(--muted-foreground))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[swipe\=move\]\:transition-none[data-swipe=move]{transition-property:none}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity:initial;--tw-enter-scale:initial;--tw-enter-rotate:initial;--tw-enter-translate-x:initial;--tw-enter-translate-y:initial}.data-\[state\=closed\]\:animate-out[data-state=closed],.data-\[swipe\=end\]\:animate-out[data-swipe=end]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity:initial;--tw-exit-scale:initial;--tw-exit-rotate:initial;--tw-exit-translate-x:initial;--tw-exit-translate-y:initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:fade-out-80[data-state=closed]{--tw-exit-opacity:0.8}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:-0.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:0.5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:-0.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:0.5rem}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x:-50%}.data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y:-48%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x:-50%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y:-48%}.data-\[state\=open\]\:slide-in-from-top-full[data-state=open]{--tw-enter-translate-y:-100%}@media (min-width:640px){.sm\:bottom-0{bottom:0}.sm\:right-0{right:0}.sm\:top-auto{top:auto}.sm\:mt-0{margin-top:0}.sm\:flex-row{flex-direction:row}.sm\:flex-col{flex-direction:column}.sm\:justify-end{justify-content:flex-end}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}.data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open]{--tw-enter-translate-y:100%}}@media (min-width:768px){.md\:max-w-\[420px\]{max-width:420px}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightbird/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Drop-in React video player component powered by LightBird. Full controls, playlist, subtitles, chapters — one import.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Punyam Singh",
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"@dnd-kit/core": "^6.3.1",
|
|
49
49
|
"@dnd-kit/sortable": "^10.0.0",
|
|
50
50
|
"@dnd-kit/utilities": "^3.2.2",
|
|
51
|
+
"@openfeature/react-sdk": "^1.2.1",
|
|
52
|
+
"@radix-ui/react-alert-dialog": "^1.1.6",
|
|
51
53
|
"@radix-ui/react-dialog": "^1.1.6",
|
|
52
54
|
"@radix-ui/react-label": "^2.1.2",
|
|
53
55
|
"@radix-ui/react-popover": "^1.1.6",
|
|
@@ -63,7 +65,7 @@
|
|
|
63
65
|
"clsx": "^2.1.1",
|
|
64
66
|
"lucide-react": "^0.475.0",
|
|
65
67
|
"tailwind-merge": "^3.0.1",
|
|
66
|
-
"@lightbird/core": "0.
|
|
68
|
+
"@lightbird/core": "0.3.0"
|
|
67
69
|
},
|
|
68
70
|
"peerDependencies": {
|
|
69
71
|
"react": "^18.0.0 || ^19.0.0",
|