diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 00000000..474eef39
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,39 @@
+name: Handle Stale Issues
+on:
+ schedule:
+ - cron: "30 1 * * *" # Runs at 1:30 UTC every day
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+
+ steps:
+ - uses: actions/stale@v9
+ with:
+ # Issue specific settings
+ days-before-issue-stale: 90
+ days-before-issue-close: 7
+ stale-issue-label: "stale"
+ stale-issue-message: |
+ This issue has been automatically marked as stale because it has had no activity in the last 30 days.
+
+ If this issue is still relevant, please leave a comment to keep it open.
+ Otherwise, it will be closed in 7 days if no further activity occurs.
+
+ Thank you for your contributions!
+ close-issue-message: |
+ This issue has been automatically closed because it has been inactive for 7 days since being marked as stale.
+
+ If you believe this issue is still relevant, please feel free to reopen it and add a comment explaining the current status.
+
+ # Pull request settings (disabled)
+ days-before-pr-stale: -1
+ days-before-pr-close: -1
+
+ # Other settings
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ operations-per-run: 100
+ exempt-issue-labels: "Roadmap v1,help needed,enhancement"
diff --git a/.gitignore b/.gitignore
index 2b3bd6ed..2a0ce8db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,16 +1,30 @@
-# Dependencies and Package Managers
node_modules/
-bun.lock
-bun.lockb
-package-lock.json
-
-# Expo and React Native Build Artifacts
.expo/
dist/
+npm-debug.*
+*.jks
+*.p8
+*.p12
+*.key
+*.mobileprovision
+*.orig.*
web-build/
-.tsbuildinfo
+modules/vlc-player/android/build
+bun.lockb
+
+# macOS
+.DS_Store
+
+expo-env.d.ts
+
+Streamyfin.app
+
+build-*
+*.mp4
+build-*
+Streamyfin.app
+package-lock.json
-# Platform-specific Build Directories
/ios
/android
/iostv
@@ -18,58 +32,15 @@ web-build/
/androidmobile
/androidtv
-# Module-specific Builds
-modules/mpv-player/android/build
modules/player/android
-modules/hls-downloader/android/build
-# Generated Applications
-Streamyfin.app
+pc-api-7079014811501811218-719-3b9f15aeccf8.json
+credentials.json
*.apk
*.ipa
-*.aab
+.continuerc.json
-# Certificates and Keys
-*.jks
-*.p8
-*.p12
-*.key
-*.mobileprovision
-
-# Debug and Temporary Files
-npm-debug.*
-*.orig.*
-*.mp4
-
-# OS-specific Files
-# macOS
-.DS_Store
-
-# IDE and Editor Files
.vscode/
.idea/
.ruby-lsp
-.cursor/
-.claude/
-CLAUDE.md
-
-# Environment and Configuration
-expo-env.d.ts
-.continuerc.json
-.env
-.env.local
-
-# Secrets and Credentials
-pc-api-7079014811501811218-719-3b9f15aeccf8.json
-credentials.json
-streamyfin-4fec1-firebase-adminsdk.json
-
-# Version and Backup Files
-/version-backup-*
-/modules/sf-player/android/build
-/modules/music-controls/android/build
-modules/background-downloader/android/build/*
-/modules/mpv-player/android/build
-
-# ios:unsigned-build Artifacts
-build/
\ No newline at end of file
+modules/hls-downloader/android/build
diff --git a/README.md b/README.md
index b5b418d2..da4f1eb5 100644
--- a/README.md
+++ b/README.md
@@ -1,256 +1,99 @@
+# 📺 Streamyfin
+
+Welcome to Streamyfin, a simple and user-friendly Jellyfin client built with Expo. If you're looking for an alternative to other Jellyfin clients, we hope you'll find Streamyfin to be a useful addition to your media streaming toolbox.
-
-
-
-
-
-
-
-
-
-
-**Streamyfin is a user-friendly Jellyfin video streaming client built with Expo. Designed as an alternative to other Jellyfin clients, it aims to offer a smooth and reliable streaming experience. We hope you'll find it a valuable addition to your media streaming toolbox.**
-
----
-
-
-
-
-
-
-
-
-
-
-
+
## 🌟 Features
-- 🚀 **Skip Intro / Credits Support**: Lets you quickly skip intros and credits during playback
-- 🖼️ **Trickplay images**: The new golden standard for chapter previews when seeking
-- 📥 **Download media**: Save your media locally and watch it offline
-- ⚙️ **Settings management**: Manage app configurations for all users through our plugin
-- 🤖 **Seerr (formerly Jellyseerr) integration**: Request media directly in the app
-- 👁️ **Sessions view:** View all active sessions currently streaming on your server
-- 📡 **Chromecast**: Cast your media to any Chromecast-enabled device
+- 🚀 **Skip Intro / Credits Support**
+- 🖼️ **Trickplay images**: The new golden standard for chapter previews when seeking.
+- 🔊 **Background audio**: Stream music in the background, even when locking the phone.
+- 📥 **Download media** (Experimental): Save your media locally and watch it offline.
+- 📡 **Chromecast** (Experimental): Cast your media to any Chromecast-enabled device.
+- 📡 **Settings management** (Experimental): Manage app settings for all your users with a JF plugin.
+- 🤖 **Jellyseerr integration**: Request media directly in the app.
## 🧪 Experimental Features
-Streamyfin offers exciting experimental features such as media downloading and Chromecast support. These features are under active development, and your feedback and patience help us make them even better.
+Streamyfin includes some exciting experimental features like media downloading and Chromecast support. These are still in development, and we appreciate your patience and feedback as we work to improve them.
-### 📥 Downloading
+### Downloading
-Downloading works by using FFmpeg to convert an HLS stream into a video file on your device. This lets you download and watch any content that you can stream. The conversion is handled in real time by Jellyfin on the server during the download. While this may take a bit longer, it ensures compatibility with any file your server can transcode.
+Downloading works by using ffmpeg to convert an HLS stream into a video file on the device. This means that you can download and view any file you can stream! The file is converted by Jellyfin on the server in real time as it is downloaded. This means a **bit longer download times** but supports any file that your server can transcode.
-### 🧩 Streamyfin Plugin
+### Chromecast
-The Jellyfin Plugin for Streamyfin is a plugin you install into Jellyfin that holds all settings for the client Streamyfin. This allows you to synchronize settings across all your users, like for example:
+Chromecast support is still in development, and we're working on improving it. Currently, it supports casting videos and audio, but we're working on adding support for subtitles and other features.
-- Automatic Seerr login with no user input required
-- Set your preferred default languages
-- Configure download method and search provider
-- Personalize your home screen
-- And much more
+### Streamyfin Plugin
+
+The Jellyfin Plugin for Streamyfin is a plugin you install into Jellyfin that hold all settings for the client Streamyfin. This allows you to syncronize settings accross all your users, like:
+
+- Auto log in to Jellyseerr without the user having to do anythin
+- Choose the default languages
+- Set download method and search provider
+- Customize homescreen
+- And more...
[Streamyfin Plugin](https://github.com/streamyfin/jellyfin-plugin-streamyfin)
-### 📡 Chromecast
+### Jellysearch
-Chromecast support is currently under development. Video casting is already available, and we're actively working on adding subtitle support and additional features.
-
-### 🎬 MPV Player
-
-Streamyfin uses [MPV](https://mpv.io/) as its primary video player on all platforms, powered by [MPVKit](https://github.com/mpvkit/MPVKit). MPV is a powerful, open-source media player known for its wide format support and high-quality playback.
-Thanks to [@Alexk2309](https://github.com/Alexk2309) for the hard work building the native MPV module in Streamyfin.
-
-### 🔍 Jellysearch
-
-[Jellysearch](https://gitlab.com/DomiStyle/jellysearch) works with Streamyfin
+[Jellysearch](https://gitlab.com/DomiStyle/jellysearch) now works with Streamyfin! 🚀
> A fast full-text search proxy for Jellyfin. Integrates seamlessly with most Jellyfin clients.
-## 🛣️ Roadmap
+## Roadmap for V1
-Check out our [Roadmap](https://github.com/users/fredrikburmester/projects/5) To see what we're working on next, we are always open to feedback and suggestions. Please let us know if you have any ideas or feature requests.
+Check out our [Roadmap](https://github.com/users/fredrikburmester/projects/5) to see what we're working on next. We are always open for feedback and suggestions, so please let us know if you have any ideas or feature requests.
-## 📥 Download Streamyfin
+## Get it now
-### 🧪 Beta Testing
+Or download the APKs [here on GitHub](https://github.com/streamyfin/streamyfin/releases) for Android.
-To access the Streamyfin beta, you need to subscribe to the Member tier (or higher) on [Patreon](https://www.patreon.com/streamyfin). This grants you immediate access to the 🧪-beta-releases channel on Discord and lets me know you’ve subscribed. This is where I share APKs and IPAs. It does not provide automatic TestFlight access, so please send me a DM (Cagemaster) with the email you use for Apple so we can add you manually.
+### Beta testing
-**Note**: Anyone actively contributing to Streamyfin’s source code will receive automatic access to beta releases.
+To access the Streamyfin beta, you need to subscribe to the Member tier (or higher) on [Patreon](https://www.patreon.com/streamyfin). This will give you immediate access to the 🧪-public-beta channel on Discord and i'll know that you have subscribed. This is where I post APKs and IPAs. This won't give automatic access to the TestFlight, however, so you need to send me a DM with the email you use for Apple so that i can manually add you.
+
+**Note**: Everyone who is actively contributing to the source code of Streamyfin will have automatic access to the betas.
## 🚀 Getting Started
-### ⚙️ Prerequisites
+### Prerequisites
-- Your device is on the same network as the Jellyfin server (for local connections)
-- Your Jellyfin server is up and running with remote access enabled if you plan to connect from outside your local network
-- Your server version is up to date (older versions may cause compatibility issues)
-- You have a valid Jellyfin user account with access to the media libraries you want to view
-- If using features such as **downloads** or **Seerr integration**, confirm the required plugins are installed and configured on your Jellyfin server
+- Ensure you have an active Jellyfin server.
+- Make sure your device is connected to the same network as your Jellyfin server.
## 🙌 Contributing
-We welcome contributions that improve Streamyfin. Start by forking the repository and submitting a pull request. For major changes or new features, please open an issue first to discuss your ideas and ensure alignment with the project.
+We welcome any help to make Streamyfin better. If you'd like to contribute, please fork the repository and submit a pull request. For major changes, it's best to open an issue first to discuss your ideas.
-## 🌍 Translations
-
-[](https://crowdin.com/project/streamyfin)
-
-Streamyfin is available in multiple languages, and we’re always looking for contributors to help make the app accessible worldwide.
-You can contribute translations directly on our [Crowdin project page](https://crowdin.com/project/streamyfin).
-
-### 👨💻 Development Info
+### Development info
1. Use node `>20`
2. Install dependencies `bun i && bun run submodule-reload`
3. Make sure you have xcode and/or android studio installed. (follow the guides for expo: https://docs.expo.dev/workflow/android-studio-emulator/)
- - If iOS builds fail with `missing Metal Toolchain` (KSPlayer shaders), run `npm run ios:install-metal-toolchain` once
-4. Install BiomeJS extension in VSCode/Your IDE (https://biomejs.dev/)
4. run `npm run prebuild`
-5. Create an expo dev build by running `npm run ios` or `npm run android`. This will open a simulator on your computer and run the app
+5. Create an expo dev build by running `npm run ios` or `npm run android`. This will open a simulator on your computer and run the app.
For the TV version suffix the npm commands with `:tv`.
`npm run prebuild:tv`
`npm run ios:tv or npm run android:tv`
-## 👋 Get in Touch with Us
-
-Need assistance or have any questions?
-
-- **Discord:** [Join our server](https://discord.gg/BuGG9ZNhaE)
-- **GitHub Issues:** [Report bugs or request features](https://github.com/streamyfin/streamyfin/issues)
-- **Email:** [developer@streamyfin.app](mailto:developer@streamyfin.app)
-
-
-## ❓ FAQ
-
-1. Q: Why can't I see my libraries in Streamyfin?
- A: Make sure your server is running one of the latest versions and that you have at least one library that isn't audio only
-2. Q: Why can't I see my music library?
- A: We don't currently support music and are unlikely to support music in the near future
-
-## 📝 Credits
-
-Streamyfin is developed by [Fredrik Burmester](https://github.com/fredrikburmester) and is not affiliated with Jellyfin. The app is built using Expo, React Native, and other open-source libraries.
-
-## 🎖️ Core Developers
-
-Thanks to the following contributors for their significant contributions:
-
-
-
-## ✨ Acknowledgements
-
-We would like to thank the Jellyfin team for their excellent software and support on Discord.
-
-Special thanks to the official Jellyfin clients, which have served as an inspiration for Streamyfin.
-
-We also thank all other developers who have contributed to Streamyfin, your efforts are greatly appreciated.
-
-A special mention to the following people and projects for their contributions:
-
-- [@Alexk2309](https://github.com/Alexk2309) for building the native MPV module that integrates [MPVKit](https://github.com/mpvkit/MPVKit) with React Native
-- [Reiverr](https://github.com/aleksilassila/reiverr) for invaluable help with understanding the Jellyfin API
-- [Jellyfin TS SDK](https://github.com/jellyfin/jellyfin-sdk-typescript) for providing the TypeScript SDK
-- [Seerr](https://github.com/seerr-team/seerr) for enabling API integration with their project
-
-
-## ⭐ Star History
-
-[](https://star-history.com/#streamyfin/streamyfin&Date)
-
## 📄 License
Streamyfin is licensed under the Mozilla Public License 2.0 (MPL-2.0).
@@ -263,10 +106,108 @@ Key points of the MPL-2.0:
- You must disclose your source code for any modifications to the covered files
- Larger works may combine MPL code with code under other licenses
- MPL-licensed components must remain under the MPL, but the larger work can be under a different license
-- For the full text of the license, please see the LICENSE file in this repository
+- For the full text of the license, please see the LICENSE file in this repository.
-## ⚠️ Disclaimer
-Streamyfin does not promote, support, or condone piracy in any form. The app is intended solely for streaming media that you personally own and control. It does not provide or include any media content. Any discussions, support requests, or references to piracy, as well as any tools, software, or websites related to piracy, are strictly prohibited across all our channels.
+## 🌐 Connect with Us
-## 🤝 Sponsorship
-VPS hosting generously provided by [Hexabyte](https://hexabyte.se/en/vps/?currency=eur) and [SweHosting](https://swehosting.se/en/#tj%C3%A4nster)
+Join our Discord: [https://discord.gg/aJvAYeycyY](https://discord.gg/aJvAYeycyY)
+
+If you have questions or need support, feel free to reach out:
+
+- GitHub Issues: Report bugs or request features here.
+- Email: [fredrik.burmester@gmail.com](mailto:fredrik.burmester@gmail.com)
+
+## 📝 Credits
+
+Streamyfin is developed by [Fredrik Burmester](https://github.com/fredrikburmester) and is not affiliated with Jellyfin. The app is built with Expo, React Native, and other open-source libraries.
+
+## ✨ Acknowledgements
+
+### Core Developers
+
+Thanks to the following contributors for their significant contributions:
+
+
+
+And all other developers who have contributed to Streamyfin, thank you for your contributions.
+
+I'd also like to thank the following people and projects for their contributions to Streamyfin:
+
+- [Reiverr](https://github.com/aleksilassila/reiverr) for great help with understanding the Jellyfin API.
+- [Jellyfin TS SDK](https://github.com/jellyfin/jellyfin-sdk-typescript) for the TypeScript SDK.
+- [Jellyseerr](https://github.com/Fallenbagel/jellyseerr) for enabling API integration with their project.
+- The Jellyfin devs for always being helpful in the Discord.
+
+## Star History
+
+[](https://star-history.com/#streamyfin/streamyfin&Date)
diff --git a/app.json b/app.json
index 288f3e3a..140a7f9a 100644
--- a/app.json
+++ b/app.json
@@ -2,13 +2,12 @@
"expo": {
"name": "Streamyfin",
"slug": "streamyfin",
- "version": "0.52.0",
+ "version": "0.27.0",
"orientation": "default",
"icon": "./assets/images/icon.png",
"scheme": "streamyfin",
"userInterfaceStyle": "dark",
"jsEngine": "hermes",
- "newArchEnabled": true,
"assetBundlePatterns": ["**/*"],
"ios": {
"requireFullScreen": true,
@@ -17,7 +16,6 @@
"NSMicrophoneUsageDescription": "The app needs access to your microphone.",
"UIBackgroundModes": ["audio", "fetch"],
"NSLocalNetworkUsageDescription": "The app needs access to your local network to connect to your Jellyfin server.",
- "NSLocationWhenInUseUsageDescription": "Streamyfin uses your location to detect your home WiFi network for automatic local server switching.",
"NSAppTransportSecurity": {
"NSAllowsArbitraryLoads": true
},
@@ -29,36 +27,39 @@
"usesNonExemptEncryption": false
},
"supportsTablet": true,
- "entitlements": {
- "com.apple.developer.networking.wifi-info": true
- },
- "bundleIdentifier": "com.fredrikburmester.streamyfin",
- "icon": "./assets/images/icon-ios-liquid-glass.icon",
- "appleTeamId": "MWD5K362T8"
+ "bundleIdentifier": "com.fredrikburmester.streamyfin"
},
"android": {
"jsEngine": "hermes",
- "versionCode": 92,
+ "versionCode": 53,
"adaptiveIcon": {
- "foregroundImage": "./assets/images/icon-android-plain.png",
- "monochromeImage": "./assets/images/icon-android-themed.png",
- "backgroundColor": "#2E2E2E"
+ "foregroundImage": "./assets/images/adaptive_icon.png"
},
"package": "com.fredrikburmester.streamyfin",
"permissions": [
"android.permission.FOREGROUND_SERVICE",
"android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK",
- "android.permission.WRITE_SETTINGS",
- "android.permission.ACCESS_FINE_LOCATION"
- ],
- "blockedPermissions": ["android.permission.ACTIVITY_RECOGNITION"],
- "googleServicesFile": "./google-services.json"
+ "android.permission.WRITE_SETTINGS"
+ ]
},
"plugins": [
"@react-native-tvos/config-tv",
"expo-router",
"expo-font",
- "./plugins/withExcludeMedia3Dash.js",
+ "@config-plugins/ffmpeg-kit-react-native",
+ [
+ "react-native-video",
+ {
+ "enableNotificationControls": true,
+ "enableBackgroundAudio": true,
+ "androidExtensions": {
+ "useExoplayerRtsp": false,
+ "useExoplayerSmoothStreaming": false,
+ "useExoplayerHls": true,
+ "useExoplayerDash": false
+ }
+ }
+ ],
[
"expo-build-properties",
{
@@ -67,12 +68,11 @@
"useFrameworks": "static"
},
"android": {
- "buildArchs": ["arm64-v8a", "x86_64"],
- "compileSdkVersion": 36,
+ "compileSdkVersion": 35,
"targetSdkVersion": 35,
"buildToolsVersion": "35.0.0",
"kotlinVersion": "2.0.21",
- "minSdkVersion": 26,
+ "minSdkVersion": 24,
"usesCleartextTraffic": true,
"packagingOptions": {
"jniLibs": {
@@ -90,6 +90,12 @@
"initialOrientation": "DEFAULT"
}
],
+ [
+ "expo-sensors",
+ {
+ "motionPermission": "Allow Streamyfin to access your device motion for landscape video watching."
+ }
+ ],
"expo-localization",
"expo-asset",
[
@@ -100,32 +106,17 @@
}
}
],
- [
- "expo-splash-screen",
- {
- "backgroundColor": "#010101",
- "image": "./assets/images/icon-ios-plain.png",
- "imageWidth": 100
- }
- ],
- [
- "expo-notifications",
- {
- "icon": "./assets/images/notification.png",
- "color": "#9333EA"
- }
- ],
- "expo-web-browser",
- ["./plugins/with-runtime-framework-headers.js"],
+ ["react-native-bottom-tabs"],
["./plugins/withChangeNativeAndroidTextToWhite.js"],
["./plugins/withAndroidManifest.js"],
["./plugins/withTrustLocalCerts.js"],
["./plugins/withGradleProperties.js"],
[
- "./plugins/withGitPod.js",
+ "expo-splash-screen",
{
- "podName": "MPVKit-GPL",
- "podspecUrl": "https://raw.githubusercontent.com/streamyfin/MPVKit/0.40.0-av/MPVKit-GPL.podspec"
+ "backgroundColor": "#2e2e2e",
+ "image": "./assets/images/StreamyFinFinal.png",
+ "imageWidth": 100
}
]
],
@@ -140,12 +131,13 @@
"projectId": "e79219d1-797f-4fbe-9fa1-cfd360690a68"
}
},
- "owner": "streamyfin",
+ "owner": "fredrikburmester",
"runtimeVersion": {
"policy": "appVersion"
},
"updates": {
"url": "https://u.expo.dev/e79219d1-797f-4fbe-9fa1-cfd360690a68"
- }
+ },
+ "newArchEnabled": false
}
}
diff --git a/app/(auth)/(tabs)/(home)/_layout.tsx b/app/(auth)/(tabs)/(home)/_layout.tsx
index a7e059ed..ddcd6f7b 100644
--- a/app/(auth)/(tabs)/(home)/_layout.tsx
+++ b/app/(auth)/(tabs)/(home)/_layout.tsx
@@ -1,38 +1,40 @@
-import { Feather, Ionicons } from "@expo/vector-icons";
-import { Stack } from "expo-router";
-import { useTranslation } from "react-i18next";
-import { Platform, View } from "react-native";
-import { Pressable } from "react-native-gesture-handler";
import { nestedTabPageScreenOptions } from "@/components/stacks/NestedTabPageStack";
-import useRouter from "@/hooks/useAppRouter";
-
-const Chromecast = Platform.isTV ? null : require("@/components/Chromecast");
-
+import { Ionicons, Feather } from "@expo/vector-icons";
+import { Stack, useRouter } from "expo-router";
+import { Platform, TouchableOpacity, View } from "react-native";
+import { useTranslation } from "react-i18next";
+const Chromecast = !Platform.isTV ? require("@/components/Chromecast") : null;
import { useAtom } from "jotai";
-import { useSessions, type useSessionsProps } from "@/hooks/useSessions";
import { userAtom } from "@/providers/JellyfinProvider";
+import { useSessions, useSessionsProps } from "@/hooks/useSessions";
export default function IndexLayout() {
- const _router = useRouter();
+ const router = useRouter();
const [user] = useAtom(userAtom);
const { t } = useTranslation();
return (
(
-
+
{!Platform.isTV && (
<>
-
- {user?.Policy?.IsAdministrator && }
+
+ {user && user.Policy?.IsAdministrator && (
+
+ )}
>
)}
@@ -41,309 +43,83 @@ export default function IndexLayout() {
}}
/>
(
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
}}
/>
+ (
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
}}
/>
(
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
}}
/>
(
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
+ title: "",
}}
/>
(
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
+ title: "",
}}
/>
(
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
+ title: t("home.settings.dashboard.sessions_title"),
}}
/>
(
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
+ title: "",
}}
/>
(
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
+ title: "",
}}
/>
(
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
+ title: "",
}}
/>
(
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
- }}
- />
- (
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
- }}
- />
- (
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
- }}
- />
- (
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
- }}
- />
- (
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
- }}
- />
- (
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
- }}
- />
- (
- _router.back()}
- className='pl-0.5'
- style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
- >
-
-
- ),
+ headerShown: false,
+ title: "",
+ presentation: "modal",
}}
/>
{Object.entries(nestedTabPageScreenOptions).map(([name, options]) => (
))}
(
- _router.back()} className='pl-0.5'>
-
-
- ),
headerShown: true,
headerBlurEffect: "prominent",
- headerTransparent: Platform.OS === "ios",
+ headerTransparent: Platform.OS === "ios" ? true : false,
headerShadowVisible: false,
}}
/>
@@ -355,32 +131,33 @@ const SettingsButton = () => {
const router = useRouter();
return (
- {
router.push("/(auth)/settings");
}}
>
-
-
+
+
);
};
const SessionsButton = () => {
const router = useRouter();
- const { sessions = [] } = useSessions({} as useSessionsProps);
+ const { sessions = [], _ } = useSessions({} as useSessionsProps);
return (
- {
router.push("/(auth)/sessions");
}}
- className='mr-4'
>
-
-
+
+
+
+
);
};
diff --git a/app/(auth)/(tabs)/(home)/index.tsx b/app/(auth)/(tabs)/(home)/index.tsx
index ad951c36..dc04e43b 100644
--- a/app/(auth)/(tabs)/(home)/index.tsx
+++ b/app/(auth)/(tabs)/(home)/index.tsx
@@ -1,16 +1,5 @@
-import { useSettings } from "@/utils/atoms/settings";
-import { Home } from "../../../../components/home/Home";
-import { HomeWithCarousel } from "../../../../components/home/HomeWithCarousel";
+import { HomeIndex } from "@/components/settings/HomeIndex";
-const Index = () => {
- const { settings } = useSettings();
- const showLargeHomeCarousel = settings.showLargeHomeCarousel ?? false;
-
- if (showLargeHomeCarousel) {
- return ;
- }
-
- return ;
-};
-
-export default Index;
+export default function page() {
+ return ;
+}
diff --git a/app/(auth)/(tabs)/(home)/sessions/index.tsx b/app/(auth)/(tabs)/(home)/sessions/index.tsx
index 0ed8fc94..8da32bce 100644
--- a/app/(auth)/(tabs)/(home)/sessions/index.tsx
+++ b/app/(auth)/(tabs)/(home)/sessions/index.tsx
@@ -1,27 +1,25 @@
-import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
-import { HardwareAccelerationType } from "@jellyfin/sdk/lib/generated-client";
-import {
- GeneralCommandType,
- PlaystateCommand,
- SessionInfoDto,
-} from "@jellyfin/sdk/lib/generated-client/models";
-import { getSessionApi } from "@jellyfin/sdk/lib/utils/api/session-api";
-import { FlashList } from "@shopify/flash-list";
-import { useQuery } from "@tanstack/react-query";
-import { useAtomValue } from "jotai";
-import { useEffect, useMemo, useState } from "react";
-import { useTranslation } from "react-i18next";
-import { Platform, TouchableOpacity, View } from "react-native";
-import { Badge } from "@/components/Badge";
import { Text } from "@/components/common/Text";
+import { useSessions, useSessionsProps } from "@/hooks/useSessions";
+import { FlashList } from "@shopify/flash-list";
+import { useTranslation } from "react-i18next";
+import { View } from "react-native";
import { Loader } from "@/components/Loader";
-import Poster from "@/components/posters/Poster";
-import { useInterval } from "@/hooks/useInterval";
-import { useSessions, type useSessionsProps } from "@/hooks/useSessions";
+import { SessionInfoDto } from "@jellyfin/sdk/lib/generated-client";
+import { useAtomValue } from "jotai";
import { apiAtom } from "@/providers/JellyfinProvider";
-import { formatBitrate } from "@/utils/bitrate";
+import Poster from "@/components/posters/Poster";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
+import { useInterval } from "@/hooks/useInterval";
+import React, { useEffect, useMemo, useState } from "react";
import { formatTimeString } from "@/utils/time";
+import { formatBitrate } from "@/utils/bitrate";
+import {
+ Ionicons,
+ Entypo,
+ AntDesign,
+ MaterialCommunityIcons,
+} from "@expo/vector-icons";
+import { Badge } from "@/components/Badge";
export default function page() {
const { sessions, isLoading } = useSessions({} as useSessionsProps);
@@ -29,15 +27,15 @@ export default function page() {
if (isLoading)
return (
-
+
);
- if (!sessions || sessions.length === 0)
+ if (!sessions || sessions.length == 0)
return (
-
-
+
+
{t("home.sessions.no_active_sessions")}
@@ -45,15 +43,16 @@ export default function page() {
return (
}
keyExtractor={(item) => item.Id || ""}
+ estimatedItemSize={200}
/>
);
}
@@ -78,7 +77,7 @@ const SessionCard = ({ session }: SessionCardProps) => {
return Math.round(
(100 / session.NowPlayingItem?.RunTimeTicks) *
- (session.NowPlayingItem?.RunTimeTicks - remainingTicks),
+ (session.NowPlayingItem?.RunTimeTicks - remainingTicks)
);
};
@@ -96,112 +95,26 @@ const SessionCard = ({ session }: SessionCardProps) => {
}
}, [session]);
- const { data: ipInfo } = useQuery<{
- cityName?: string;
- countryCode?: string;
- }>({
- queryKey: ["ipinfo", session.RemoteEndPoint],
- staleTime: Number.POSITIVE_INFINITY,
- queryFn: async () => {
- const resp = await api!.axiosInstance.get(
- `https://freeipapi.com/api/json/${session.RemoteEndPoint}`,
- );
- return resp.data;
- },
- enabled: !!api,
- });
-
- // Handle session controls
- const [isControlLoading, setIsControlLoading] = useState<
- Record
- >({});
-
- const handleSystemCommand = async (command: GeneralCommandType) => {
- if (!api || !session.Id) return false;
-
- setIsControlLoading({ ...isControlLoading, [command]: true });
-
- try {
- getSessionApi(api).sendSystemCommand({
- sessionId: session.Id,
- command,
- });
- return true;
- } catch (error) {
- console.error(`Error sending ${command} command:`, error);
- return false;
- } finally {
- setIsControlLoading({ ...isControlLoading, [command]: false });
- }
- };
-
- const handlePlaystateCommand = async (command: PlaystateCommand) => {
- if (!api || !session.Id) return false;
-
- setIsControlLoading({ ...isControlLoading, [command]: true });
-
- try {
- getSessionApi(api).sendPlaystateCommand({
- sessionId: session.Id,
- command,
- });
-
- return true;
- } catch (error) {
- console.error(`Error sending playstate ${command} command:`, error);
- return false;
- } finally {
- setIsControlLoading({ ...isControlLoading, [command]: false });
- }
- };
-
- const handlePlayPause = async () => {
- console.log("handlePlayPause");
- await handlePlaystateCommand(PlaystateCommand.PlayPause);
- };
-
- const handleStop = async () => {
- await handlePlaystateCommand(PlaystateCommand.Stop);
- };
-
- const handlePrevious = async () => {
- await handlePlaystateCommand(PlaystateCommand.PreviousTrack);
- };
-
- const handleNext = async () => {
- await handlePlaystateCommand(PlaystateCommand.NextTrack);
- };
-
- const handleToggleMute = async () => {
- await handleSystemCommand(GeneralCommandType.ToggleMute);
- };
- const handleVolumeUp = async () => {
- await handleSystemCommand(GeneralCommandType.VolumeUp);
- };
- const handleVolumeDown = async () => {
- await handleSystemCommand(GeneralCommandType.VolumeDown);
- };
-
useInterval(tick, 1000);
return (
-
-
-
+
+
+
-
-
-
+
+
+
{session.NowPlayingItem?.Type === "Episode" ? (
<>
-
+
{session.NowPlayingItem?.Name}
-
+
{`S${session.NowPlayingItem.ParentIndexNumber?.toString()}:E${session.NowPlayingItem.IndexNumber?.toString()}`}
{" - "}
{session.NowPlayingItem.SeriesName}
@@ -209,151 +122,48 @@ const SessionCard = ({ session }: SessionCardProps) => {
>
) : (
<>
-
+
{session.NowPlayingItem?.Name}
-
+
{session.NowPlayingItem?.ProductionYear}
-
+
{session.NowPlayingItem?.SeriesName}
>
)}
-
+
{session.UserName}
{"\n"}
{session.Client}
{"\n"}
{session.DeviceName}
- {"\n"}
- {ipInfo?.cityName} {ipInfo?.countryCode}
-
-
-
-
+
+
+
+
{!session.PlayState?.IsPaused ? (
-
+
) : (
-
+
)}
-
+
{formatTimeString(remainingTicks, "tick")} left
-
+
-
- {/* Session controls */}
-
-
-
-
-
-
- {session.PlayState?.IsPaused ? (
-
- ) : (
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -368,21 +178,20 @@ interface TranscodingBadgesProps {
const TranscodingBadges = ({ properties }: TranscodingBadgesProps) => {
const iconMap = {
- bitrate: ,
- codec: ,
+ bitrate: ,
+ codec: ,
videoRange: (
-
+
),
- resolution: ,
- language: ,
- audioChannels: ,
- hwType: ,
+ resolution: ,
+ language: ,
+ audioChannels: ,
} as const;
const icon = (val: string) => {
return (
iconMap[val as keyof typeof iconMap] ?? (
-
+
)
);
};
@@ -391,8 +200,6 @@ const TranscodingBadges = ({ properties }: TranscodingBadgesProps) => {
switch (key) {
case "bitrate":
return formatBitrate(val);
- case "hwType":
- return val === HardwareAccelerationType.None ? "sw" : "hw";
default:
return val;
}
@@ -403,8 +210,8 @@ const TranscodingBadges = ({ properties }: TranscodingBadgesProps) => {
.map(([key]) => (
@@ -412,7 +219,6 @@ const TranscodingBadges = ({ properties }: TranscodingBadgesProps) => {
};
interface StreamProps {
- hwType?: HardwareAccelerationType | null | undefined;
resolution?: string | null | undefined;
language?: string | null | undefined;
codec?: string | null | undefined;
@@ -424,7 +230,7 @@ interface StreamProps {
interface TranscodingStreamViewProps {
title: string | undefined;
value?: string;
- isTranscoding: boolean;
+ isTranscoding: Boolean;
transcodeValue?: string | undefined | null;
properties: StreamProps;
transcodeProperties?: StreamProps;
@@ -435,40 +241,43 @@ const TranscodingStreamView = ({
isTranscoding,
properties,
transcodeProperties,
+ value,
+ transcodeValue,
}: TranscodingStreamViewProps) => {
return (
-
-
-
+
+
+
{title}
-
+
{isTranscoding && transcodeProperties ? (
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+
+ >
) : null}
);
};
const TranscodingView = ({ session }: SessionCardProps) => {
- const { t } = useTranslation();
const videoStream = useMemo(() => {
return session.NowPlayingItem?.MediaStreams?.filter(
- (s) => s.Type === "Video",
+ (s) => s.Type == "Video"
)[0];
}, [session]);
@@ -487,36 +296,35 @@ const TranscodingView = ({ session }: SessionCardProps) => {
}, [session.PlayState?.SubtitleStreamIndex]);
const isTranscoding = useMemo(() => {
- return (
- session.PlayState?.PlayMethod === "Transcode" && session.TranscodingInfo
- );
- }, [session.PlayState?.PlayMethod, session.TranscodingInfo]);
+ return session.PlayState?.PlayMethod == "Transcode";
+ }, [session.PlayState?.PlayMethod]);
const videoStreamTitle = () => {
return videoStream?.DisplayTitle?.split(" ")[0];
};
return (
-
+
{
audioChannels: audioStream?.ChannelLayout,
}}
transcodeProperties={{
+ bitrate: session.TranscodingInfo?.Bitrate,
codec: session.TranscodingInfo?.AudioCodec,
audioChannels: session.TranscodingInfo?.AudioChannels?.toString(),
}}
isTranscoding={
- !!(isTranscoding && !session.TranscodingInfo?.IsVideoDirect)
+ isTranscoding && !session.TranscodingInfo?.IsVideoDirect
+ ? true
+ : false
}
/>
{subtitleStream && (
-
+ <>
+
+ >
)}
);
diff --git a/app/(auth)/(tabs)/(home)/settings.tsx b/app/(auth)/(tabs)/(home)/settings.tsx
index 76675ae8..4fe3b0cb 100644
--- a/app/(auth)/(tabs)/(home)/settings.tsx
+++ b/app/(auth)/(tabs)/(home)/settings.tsx
@@ -1,24 +1,41 @@
-import { useNavigation } from "expo-router";
-import { t } from "i18next";
-import { useAtom } from "jotai";
-import { useEffect } from "react";
-import { Platform, ScrollView, TouchableOpacity, View } from "react-native";
-import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Text } from "@/components/common/Text";
import { ListGroup } from "@/components/list/ListGroup";
import { ListItem } from "@/components/list/ListItem";
import { AppLanguageSelector } from "@/components/settings/AppLanguageSelector";
+import { AudioToggles } from "@/components/settings/AudioToggles";
+import DownloadSettings from "@/components/settings/DownloadSettings";
+import { MediaProvider } from "@/components/settings/MediaContext";
+import { MediaToggles } from "@/components/settings/MediaToggles";
+import { OtherSettings } from "@/components/settings/OtherSettings";
+import { PluginSettings } from "@/components/settings/PluginSettings";
import { QuickConnect } from "@/components/settings/QuickConnect";
import { StorageSettings } from "@/components/settings/StorageSettings";
+import { SubtitleToggles } from "@/components/settings/SubtitleToggles";
import { UserInfo } from "@/components/settings/UserInfo";
-import useRouter from "@/hooks/useAppRouter";
-import { useJellyfin, userAtom } from "@/providers/JellyfinProvider";
+import { useHaptic } from "@/hooks/useHaptic";
+import { useJellyfin } from "@/providers/JellyfinProvider";
+import { clearLogs } from "@/utils/log";
+import { storage } from "@/utils/mmkv";
+import { useNavigation, useRouter } from "expo-router";
+import { t } from "i18next";
+import React, { useEffect } from "react";
+import { ScrollView, Switch, TouchableOpacity, View } from "react-native";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+import { useAtom } from "jotai";
+import { userAtom } from "@/providers/JellyfinProvider";
+import { ChromecastSettings } from "@/components/settings/ChromecastSettings";
export default function settings() {
const router = useRouter();
const insets = useSafeAreaInsets();
- const [_user] = useAtom(userAtom);
+ const [user] = useAtom(userAtom);
const { logout } = useJellyfin();
+ const successHapticFeedback = useHaptic("success");
+
+ const onClearLogsClicked = async () => {
+ clearLogs();
+ successHapticFeedback();
+ };
const navigation = useNavigation();
useEffect(() => {
@@ -29,7 +46,7 @@ export default function settings() {
logout();
}}
>
-
+
{t("home.settings.log_out_button")}
@@ -39,72 +56,64 @@ export default function settings() {
return (
-
-
-
-
+
+
-
+
-
-
-
+
+
+
+
+
-
-
- router.push("/settings/playback-controls/page")}
- showArrow
- title={t("home.settings.playback_controls.title")}
- />
- router.push("/settings/audio-subtitles/page")}
- showArrow
- title={t("home.settings.audio_subtitles.title")}
- />
- router.push("/settings/music/page")}
- showArrow
- title={t("home.settings.music.title")}
- />
- router.push("/settings/appearance/page")}
- showArrow
- title={t("home.settings.appearance.title")}
- />
- router.push("/settings/plugins/page")}
- showArrow
- title={t("home.settings.plugins.plugins_title")}
- />
- router.push("/settings/intro/page")}
- showArrow
- title={t("home.settings.intro.title")}
- />
- router.push("/settings/network/page")}
- showArrow
- title={t("home.settings.network.title")}
- />
+
+
+
+
+
+
+
+
+
+
+
+ {
+ router.push("/intro/page");
+ }}
+ title={t("home.settings.intro.show_intro")}
+ />
+ {
+ storage.set("hasShownIntro", false);
+ }}
+ title={t("home.settings.intro.reset_intro")}
+ />
+
+
+
+
router.push("/settings/logs/page")}
showArrow
title={t("home.settings.logs.logs_title")}
/>
+
- {!Platform.isTV && }
+
);
diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/jellyseerr/person/[personId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/jellyseerr/person/[personId].tsx
index a29e1280..bd0fc216 100644
--- a/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/jellyseerr/person/[personId].tsx
+++ b/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/jellyseerr/person/[personId].tsx
@@ -1,29 +1,29 @@
+import {
+ useLocalSearchParams,
+ useSegments,
+} from "expo-router";
+import React, { useMemo } from "react";
import { useQuery } from "@tanstack/react-query";
-import { Image } from "expo-image";
-import { useLocalSearchParams } from "expo-router";
-import { orderBy, uniqBy } from "lodash";
-import { useMemo } from "react";
-import { useTranslation } from "react-i18next";
-import { Text } from "@/components/common/Text";
-import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow";
-import { OverviewText } from "@/components/OverviewText";
-import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
import { useJellyseerr } from "@/hooks/useJellyseerr";
-import type { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person";
+import { Text } from "@/components/common/Text";
+import { Image } from "expo-image";
+import { OverviewText } from "@/components/OverviewText";
+import {orderBy, uniqBy} from "lodash";
+import { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person";
+import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow";
+import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
+import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search";
+import { useTranslation } from "react-i18next";
export default function page() {
const local = useLocalSearchParams();
const { t } = useTranslation();
- const {
- jellyseerrApi,
- jellyseerrRegion: region,
- jellyseerrLocale: locale,
- } = useJellyseerr();
+ const { jellyseerrApi, jellyseerrUser, jellyseerrRegion: region, jellyseerrLocale: locale } = useJellyseerr();
const { personId } = local as { personId: string };
- const { data } = useQuery({
+ const { data, isLoading, isFetching } = useQuery({
queryKey: ["jellyseerr", "person", personId],
queryFn: async () => ({
details: await jellyseerrApi?.personDetails(personId),
@@ -34,27 +34,18 @@ export default function page() {
const castedRoles: PersonCreditCast[] = useMemo(
() =>
- uniqBy(
- orderBy(
- data?.combinedCredits?.cast,
- ["voteCount", "voteAverage"],
- "desc",
- ),
- "id",
- ),
- [data?.combinedCredits],
+ uniqBy(orderBy(
+ data?.combinedCredits?.cast,
+ ["voteCount", "voteAverage"],
+ "desc"
+ ), 'id'),
+ [data?.combinedCredits]
);
const backdrops = useMemo(
- () =>
- jellyseerrApi
- ? castedRoles.map((c) =>
- jellyseerrApi.imageProxy(
- c.backdropPath,
- "w1920_and_h800_multi_faces",
- ),
- )
- : [],
- [jellyseerrApi, data?.combinedCredits],
+ () => jellyseerrApi
+ ? castedRoles.map((c) => jellyseerrApi.imageProxy(c.backdropPath, "w1920_and_h800_multi_faces"))
+ : [],
+ [jellyseerrApi, data?.combinedCredits]
);
return (
@@ -67,15 +58,15 @@ export default function page() {
(
<>
- {data?.details?.name}
-
+
+ {data?.details?.name}
+
+
{t("jellyseerr.born")}{" "}
- {data?.details?.birthday &&
- new Date(data.details.birthday).toLocaleDateString(
- `${locale}-${region}`,
- {
- year: "numeric",
- month: "long",
- day: "numeric",
- },
- )}{" "}
+ {new Date(data?.details?.birthday!!).toLocaleDateString(
+ `${locale}-${region}`,
+ {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ }
+ )}{" "}
| {data?.details?.placeOfBirth}
>
)}
MainContent={() => (
-
+
)}
- renderItem={(item, _index) => }
+ renderItem={(item, index) => }
/>
);
}
diff --git a/app/(auth)/(tabs)/(search)/_layout.tsx b/app/(auth)/(tabs)/(search)/_layout.tsx
index f4d34978..ce787316 100644
--- a/app/(auth)/(tabs)/(search)/_layout.tsx
+++ b/app/(auth)/(tabs)/(search)/_layout.tsx
@@ -1,22 +1,26 @@
-import { Stack } from "expo-router";
-import { useTranslation } from "react-i18next";
-import { Platform } from "react-native";
import {
commonScreenOptions,
nestedTabPageScreenOptions,
} from "@/components/stacks/NestedTabPageStack";
+import { Stack } from "expo-router";
+import { Platform } from "react-native";
+import { useTranslation } from "react-i18next";
export default function SearchLayout() {
const { t } = useTranslation();
return (
@@ -24,26 +28,26 @@ export default function SearchLayout() {
))}
-
+
diff --git a/app/(auth)/(tabs)/(search)/index.tsx b/app/(auth)/(tabs)/(search)/index.tsx
index 751b1df1..6d1ac344 100644
--- a/app/(auth)/(tabs)/(search)/index.tsx
+++ b/app/(auth)/(tabs)/(search)/index.tsx
@@ -1,48 +1,39 @@
-import type {
+import { Input } from "@/components/common/Input";
+import { Text } from "@/components/common/Text";
+import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
+import ContinueWatchingPoster from "@/components/ContinueWatchingPoster";
+import { Tag } from "@/components/GenreTags";
+import { ItemCardText } from "@/components/ItemCardText";
+import { JellyserrIndexPage } from "@/components/jellyseerr/JellyseerrIndexPage";
+import MoviePoster from "@/components/posters/MoviePoster";
+import SeriesPoster from "@/components/posters/SeriesPoster";
+import { LoadingSkeleton } from "@/components/search/LoadingSkeleton";
+import { SearchItemWrapper } from "@/components/search/SearchItemWrapper";
+import { useJellyseerr } from "@/hooks/useJellyseerr";
+import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
+import { useSettings } from "@/utils/atoms/settings";
+import {
BaseItemDto,
BaseItemKind,
} from "@jellyfin/sdk/lib/generated-client/models";
-import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
-import { useAsyncDebouncer } from "@tanstack/react-pacer";
+import { getItemsApi, getSearchApi } from "@jellyfin/sdk/lib/utils/api";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
-import { Image } from "expo-image";
-import { useLocalSearchParams, useNavigation } from "expo-router";
+import { Href, router, useLocalSearchParams, useNavigation } from "expo-router";
import { useAtom } from "jotai";
-import {
+import React, {
useCallback,
useEffect,
- useId,
useLayoutEffect,
useMemo,
useRef,
useState,
} from "react";
-import { useTranslation } from "react-i18next";
import { Platform, ScrollView, TouchableOpacity, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
-import ContinueWatchingPoster from "@/components/ContinueWatchingPoster";
-import { Input } from "@/components/common/Input";
-import { Text } from "@/components/common/Text";
-import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
-import { ItemCardText } from "@/components/ItemCardText";
-import {
- JellyseerrSearchSort,
- JellyserrIndexPage,
-} from "@/components/jellyseerr/JellyseerrIndexPage";
-import MoviePoster from "@/components/posters/MoviePoster";
-import SeriesPoster from "@/components/posters/SeriesPoster";
-import { DiscoverFilters } from "@/components/search/DiscoverFilters";
-import { LoadingSkeleton } from "@/components/search/LoadingSkeleton";
-import { SearchItemWrapper } from "@/components/search/SearchItemWrapper";
-import { SearchTabButtons } from "@/components/search/SearchTabButtons";
-import useRouter from "@/hooks/useAppRouter";
-import { useJellyseerr } from "@/hooks/useJellyseerr";
-import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
-import { useSettings } from "@/utils/atoms/settings";
+import { useDebounce } from "use-debounce";
+import { useTranslation } from "react-i18next";
import { eventBus } from "@/utils/eventBus";
-import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
-import { createStreamystatsApi } from "@/utils/streamystats";
type SearchType = "Library" | "Discover";
@@ -58,218 +49,77 @@ const exampleSearches = [
export default function search() {
const params = useLocalSearchParams();
const insets = useSafeAreaInsets();
- const router = useRouter();
-
- const [user] = useAtom(userAtom);
const { t } = useTranslation();
- const searchFilterId = useId();
- const orderFilterId = useId();
-
const { q } = params as { q: string };
const [searchType, setSearchType] = useState("Library");
const [search, setSearch] = useState("");
- const [debouncedSearch, setDebouncedSearch] = useState("");
- const abortControllerRef = useRef(null);
-
- const searchDebouncer = useAsyncDebouncer(
- async (query: string) => {
- // Cancel previous in-flight requests
- abortControllerRef.current?.abort();
- abortControllerRef.current = new AbortController();
- setDebouncedSearch(query);
- return query;
- },
- { wait: 200 },
- );
-
- useEffect(() => {
- searchDebouncer.maybeExecute(search);
- }, [search]);
+ const [debouncedSearch] = useDebounce(search, 500);
const [api] = useAtom(apiAtom);
+ const [user] = useAtom(userAtom);
- const { settings } = useSettings();
+ const [settings] = useSettings();
const { jellyseerrApi } = useJellyseerr();
- const [jellyseerrOrderBy, setJellyseerrOrderBy] =
- useState(
- JellyseerrSearchSort[
- JellyseerrSearchSort.DEFAULT
- ] as unknown as JellyseerrSearchSort,
- );
- const [jellyseerrSortOrder, setJellyseerrSortOrder] = useState<
- "asc" | "desc"
- >("desc");
const searchEngine = useMemo(() => {
return settings?.searchEngine || "Jellyfin";
}, [settings]);
useEffect(() => {
- if (q && q.length > 0) {
- setSearch(q);
- }
+ if (q && q.length > 0) setSearch(q);
}, [q]);
const searchFn = useCallback(
async ({
types,
query,
- signal,
}: {
types: BaseItemKind[];
query: string;
- signal?: AbortSignal;
}): Promise => {
- if (!api || !query) {
- return [];
- }
+ if (!api || !query) return [];
try {
if (searchEngine === "Jellyfin") {
- const searchApi = await getItemsApi(api).getItems(
- {
- searchTerm: query,
- limit: 10,
- includeItemTypes: types,
- recursive: true,
- userId: user?.Id,
- },
- { signal },
- );
-
- return (searchApi.data.Items as BaseItemDto[]) || [];
- }
-
- if (searchEngine === "Streamystats") {
- if (!settings?.streamyStatsServerUrl || !api.accessToken) {
- return [];
- }
-
- const streamyStatsApi = createStreamystatsApi({
- serverUrl: settings.streamyStatsServerUrl,
- jellyfinToken: api.accessToken,
- });
-
- const typeMap: Record = {
- Movie: "movies",
- Series: "series",
- Episode: "episodes",
- Person: "actors",
- BoxSet: "movies",
- Audio: "audio",
- } as Record;
-
- const searchType = types.length === 1 ? typeMap[types[0]] : "media";
- const response = await streamyStatsApi.searchIds(
- query,
- searchType as "movies" | "series" | "episodes" | "actors" | "media",
- 10,
- signal,
- );
-
- const allIds: string[] = [
- ...(response.data.movies || []),
- ...(response.data.series || []),
- ...(response.data.episodes || []),
- ...(response.data.actors || []),
- ...(response.data.audio || []),
- ];
-
- if (!allIds.length) {
- return [];
- }
-
- const itemsResponse = await getItemsApi(api).getItems(
- {
- ids: allIds,
- enableImageTypes: ["Primary", "Backdrop", "Thumb"],
- },
- { signal },
- );
-
- return (itemsResponse.data.Items as BaseItemDto[]) || [];
- }
-
- // Marlin search
- if (!settings?.marlinServerUrl) {
- return [];
- }
-
- const url = `${
- settings.marlinServerUrl
- }/search?q=${encodeURIComponent(query)}&includeItemTypes=${types
- .map((type) => encodeURIComponent(type))
- .join("&includeItemTypes=")}`;
-
- const response1 = await axios.get(url, { signal });
-
- const ids = response1.data.ids;
-
- if (!ids || !ids.length) {
- return [];
- }
-
- const response2 = await getItemsApi(api).getItems(
- {
- ids,
- enableImageTypes: ["Primary", "Backdrop", "Thumb"],
- },
- { signal },
- );
-
- return (response2.data.Items as BaseItemDto[]) || [];
- } catch (error) {
- // Silently handle aborted requests
- if (error instanceof Error && error.name === "AbortError") {
- return [];
- }
- return [];
- }
- },
- [api, searchEngine, settings, user?.Id],
- );
-
- // Separate search function for music types - always uses Jellyfin since Streamystats doesn't support music
- const jellyfinSearchFn = useCallback(
- async ({
- types,
- query,
- signal,
- }: {
- types: BaseItemKind[];
- query: string;
- signal?: AbortSignal;
- }): Promise => {
- if (!api || !query) {
- return [];
- }
-
- try {
- const searchApi = await getItemsApi(api).getItems(
- {
+ const searchApi = await getSearchApi(api).getSearchHints({
searchTerm: query,
limit: 10,
includeItemTypes: types,
- recursive: true,
- userId: user?.Id,
- },
- { signal },
- );
+ });
- return (searchApi.data.Items as BaseItemDto[]) || [];
- } catch (error) {
- // Silently handle aborted requests
- if (error instanceof Error && error.name === "AbortError") {
- return [];
+ return (searchApi.data.SearchHints as BaseItemDto[]) || [];
+ } else {
+ if (!settings?.marlinServerUrl) return [];
+
+ const url = `${
+ settings.marlinServerUrl
+ }/search?q=${encodeURIComponent(query)}&includeItemTypes=${types
+ .map((type) => encodeURIComponent(type))
+ .join("&includeItemTypes=")}`;
+
+ const response1 = await axios.get(url);
+
+ const ids = response1.data.ids;
+
+ if (!ids || !ids.length) return [];
+
+ const response2 = await getItemsApi(api).getItems({
+ ids,
+ enableImageTypes: ["Primary", "Backdrop", "Thumb"],
+ });
+
+ return (response2.data.Items as BaseItemDto[]) || [];
}
- return [];
+ } catch (error) {
+ console.error("Error during search:", error);
+ return []; // Ensure an empty array is returned in case of an error
}
},
- [api, user?.Id],
+ [api, searchEngine, settings]
);
type HeaderSearchBarRef = {
@@ -299,10 +149,8 @@ export default function search() {
useEffect(() => {
const unsubscribe = eventBus.on("searchTabPressed", () => {
- // Screen not active
- if (!searchBarRef.current) {
- return;
- }
+ // Screen not actuve
+ if (!searchBarRef.current) return;
// Screen is active, focus search bar
searchBarRef.current?.focus();
});
@@ -318,7 +166,6 @@ export default function search() {
searchFn({
query: debouncedSearch,
types: ["Movie"],
- signal: abortControllerRef.current?.signal,
}),
enabled: searchType === "Library" && debouncedSearch.length > 0,
});
@@ -329,7 +176,6 @@ export default function search() {
searchFn({
query: debouncedSearch,
types: ["Series"],
- signal: abortControllerRef.current?.signal,
}),
enabled: searchType === "Library" && debouncedSearch.length > 0,
});
@@ -340,7 +186,6 @@ export default function search() {
searchFn({
query: debouncedSearch,
types: ["Episode"],
- signal: abortControllerRef.current?.signal,
}),
enabled: searchType === "Library" && debouncedSearch.length > 0,
});
@@ -351,7 +196,6 @@ export default function search() {
searchFn({
query: debouncedSearch,
types: ["BoxSet"],
- signal: abortControllerRef.current?.signal,
}),
enabled: searchType === "Library" && debouncedSearch.length > 0,
});
@@ -362,52 +206,6 @@ export default function search() {
searchFn({
query: debouncedSearch,
types: ["Person"],
- signal: abortControllerRef.current?.signal,
- }),
- enabled: searchType === "Library" && debouncedSearch.length > 0,
- });
-
- // Music search queries - always use Jellyfin since Streamystats doesn't support music
- const { data: artists, isFetching: l9 } = useQuery({
- queryKey: ["search", "artists", debouncedSearch],
- queryFn: () =>
- jellyfinSearchFn({
- query: debouncedSearch,
- types: ["MusicArtist"],
- signal: abortControllerRef.current?.signal,
- }),
- enabled: searchType === "Library" && debouncedSearch.length > 0,
- });
-
- const { data: albums, isFetching: l10 } = useQuery({
- queryKey: ["search", "albums", debouncedSearch],
- queryFn: () =>
- jellyfinSearchFn({
- query: debouncedSearch,
- types: ["MusicAlbum"],
- signal: abortControllerRef.current?.signal,
- }),
- enabled: searchType === "Library" && debouncedSearch.length > 0,
- });
-
- const { data: songs, isFetching: l11 } = useQuery({
- queryKey: ["search", "songs", debouncedSearch],
- queryFn: () =>
- jellyfinSearchFn({
- query: debouncedSearch,
- types: ["Audio"],
- signal: abortControllerRef.current?.signal,
- }),
- enabled: searchType === "Library" && debouncedSearch.length > 0,
- });
-
- const { data: playlists, isFetching: l12 } = useQuery({
- queryKey: ["search", "playlists", debouncedSearch],
- queryFn: () =>
- jellyfinSearchFn({
- query: debouncedSearch,
- types: ["Playlist"],
- signal: abortControllerRef.current?.signal,
}),
enabled: searchType === "Library" && debouncedSearch.length > 0,
});
@@ -418,376 +216,174 @@ export default function search() {
episodes?.length ||
series?.length ||
collections?.length ||
- actors?.length ||
- artists?.length ||
- albums?.length ||
- songs?.length ||
- playlists?.length
+ actors?.length
);
- }, [
- episodes,
- movies,
- series,
- collections,
- actors,
- artists,
- albums,
- songs,
- playlists,
- ]);
+ }, [episodes, movies, series, collections, actors]);
const loading = useMemo(() => {
- return l1 || l2 || l3 || l7 || l8 || l9 || l10 || l11 || l12;
- }, [l1, l2, l3, l7, l8, l9, l10, l11, l12]);
+ return l1 || l2 || l3 || l7 || l8;
+ }, [l1, l2, l3, l7, l8]);
return (
-
- {/*
+ */}
- {Platform.isTV && (
- {
- router.setParams({ q: "" });
- setSearch(text);
- }}
- keyboardType='default'
- returnKeyType='done'
- autoCapitalize='none'
- clearButtonMode='while-editing'
- maxLength={500}
- />
- )}
-
- {jellyseerrApi && (
-
-
- {searchType === "Discover" &&
- !loading &&
- noResults &&
- debouncedSearch.length > 0 && (
-
+ {jellyseerrApi && (
+
+ setSearchType("Library")}>
+
- )}
-
- )}
+
+ setSearchType("Discover")}>
+
+
+
+ )}
-
-
+
+
+
+
+ {searchType === "Library" ? (
+
+ m.Id!)}
+ renderItem={(item: BaseItemDto) => (
+
+
+
+ {item.Name}
+
+
+ {item.ProductionYear}
+
+
+ )}
+ />
+ m.Id!)}
+ header={t("search.series")}
+ renderItem={(item: BaseItemDto) => (
+
+
+
+ {item.Name}
+
+
+ {item.ProductionYear}
+
+
+ )}
+ />
+ m.Id!)}
+ header={t("search.episodes")}
+ renderItem={(item: BaseItemDto) => (
+
+
+
+
+ )}
+ />
+ m.Id!)}
+ header={t("search.collections")}
+ renderItem={(item: BaseItemDto) => (
+
+
+
+ {item.Name}
+
+
+ )}
+ />
+ m.Id!)}
+ header={t("search.actors")}
+ renderItem={(item: BaseItemDto) => (
+
+
+
+
+ )}
+ />
+
+ ) : (
+
+ )}
+
+ {searchType === "Library" && (
+ <>
+ {!loading && noResults && debouncedSearch.length > 0 ? (
+
+
+ {t("search.no_results_found_for")}
+
+
+ "{debouncedSearch}"
+
+
+ ) : debouncedSearch.length === 0 ? (
+
+ {exampleSearches.map((e) => (
+ setSearch(e)}
+ key={e}
+ className="mb-2"
+ >
+ {e}
+
+ ))}
+
+ ) : null}
+ >
+ )}
-
- {searchType === "Library" ? (
-
- (
-
-
-
- {item.Name}
-
-
- {item.ProductionYear}
-
-
- )}
- />
- (
-
-
-
- {item.Name}
-
-
- {item.ProductionYear}
-
-
- )}
- />
- (
-
-
-
-
- )}
- />
- (
-
-
-
- {item.Name}
-
-
- )}
- />
- (
-
-
-
-
- )}
- />
- {/* Music search results */}
- {
- const imageUrl = getPrimaryImageUrl({ api, item });
- return (
-
-
- {imageUrl ? (
-
- ) : (
-
- 👤
-
- )}
-
-
- {item.Name}
-
-
- );
- }}
- />
- {
- const imageUrl = getPrimaryImageUrl({ api, item });
- return (
-
-
- {imageUrl ? (
-
- ) : (
-
- 🎵
-
- )}
-
-
- {item.Name}
-
-
- {item.AlbumArtist || item.Artists?.join(", ")}
-
-
- );
- }}
- />
- {
- const imageUrl = getPrimaryImageUrl({ api, item });
- return (
-
-
- {imageUrl ? (
-
- ) : (
-
- 🎵
-
- )}
-
-
- {item.Name}
-
-
- {item.Artists?.join(", ") || item.AlbumArtist}
-
-
- );
- }}
- />
- {
- const imageUrl = getPrimaryImageUrl({ api, item });
- return (
-
-
- {imageUrl ? (
-
- ) : (
-
- 🎶
-
- )}
-
-
- {item.Name}
-
-
- {item.ChildCount} tracks
-
-
- );
- }}
- />
-
- ) : (
-
- )}
-
- {searchType === "Library" &&
- (!loading && noResults && debouncedSearch.length > 0 ? (
-
-
- {t("search.no_results_found_for")}
-
-
- "{debouncedSearch}"
-
-
- ) : debouncedSearch.length === 0 ? (
-
- {exampleSearches.map((e) => (
- {
- setSearch(e);
- searchBarRef.current?.setText(e);
- }}
- key={e}
- className='mb-2'
- >
- {e}
-
- ))}
-
- ) : null)}
-
-
+
+ >
);
}
diff --git a/app/(auth)/(tabs)/_layout.tsx b/app/(auth)/(tabs)/_layout.tsx
index df1ed986..6f581ae0 100644
--- a/app/(auth)/(tabs)/_layout.tsx
+++ b/app/(auth)/(tabs)/_layout.tsx
@@ -1,125 +1,151 @@
+import React, { useCallback, useRef } from "react";
+import { Platform } from "react-native";
+import { useTranslation } from "react-i18next";
+
+import { useFocusEffect, useRouter, withLayoutContext } from "expo-router";
+
import {
createNativeBottomTabNavigator,
- type NativeBottomTabNavigationEventMap,
- type NativeBottomTabNavigationOptions,
+ NativeBottomTabNavigationEventMap,
} from "@bottom-tabs/react-navigation";
+
+const { Navigator } = createNativeBottomTabNavigator();
+import { BottomTabNavigationOptions } from "@react-navigation/bottom-tabs";
+
+import { Colors } from "@/constants/Colors";
+import { useSettings } from "@/utils/atoms/settings";
+import { storage } from "@/utils/mmkv";
import type {
ParamListBase,
TabNavigationState,
} from "@react-navigation/native";
-import { withLayoutContext } from "expo-router";
-import { useTranslation } from "react-i18next";
-import { Platform, View } from "react-native";
import { SystemBars } from "react-native-edge-to-edge";
-import { MiniPlayerBar } from "@/components/music/MiniPlayerBar";
-import { MusicPlaybackEngine } from "@/components/music/MusicPlaybackEngine";
-import { Colors } from "@/constants/Colors";
-import { useSettings } from "@/utils/atoms/settings";
import { eventBus } from "@/utils/eventBus";
-const { Navigator } = createNativeBottomTabNavigator();
-
export const NativeTabs = withLayoutContext<
- NativeBottomTabNavigationOptions,
+ BottomTabNavigationOptions,
typeof Navigator,
TabNavigationState,
NativeBottomTabNavigationEventMap
>(Navigator);
export default function TabLayout() {
- const { settings } = useSettings();
+ const [settings] = useSettings();
const { t } = useTranslation();
+ const router = useRouter();
+
+ useFocusEffect(
+ useCallback(() => {
+ const hasShownIntro = storage.getBoolean("hasShownIntro");
+ if (!hasShownIntro) {
+ const timer = setTimeout(() => {
+ router.push("/intro/page");
+ }, 1000);
+
+ return () => {
+ clearTimeout(timer);
+ };
+ }
+ }, [])
+ );
return (
-
-
+ <>
+
-
+
({
- tabPress: (_e) => {
+ listeners={({ navigation }) => ({
+ tabPress: (e) => {
eventBus.emit("scrollToTop");
},
})}
- name='(home)'
+ name="(home)"
options={{
title: t("tabs.home"),
tabBarIcon:
- Platform.OS === "android"
- ? (_e) => require("@/assets/icons/house.fill.png")
- : (_e) => ({ sfSymbol: "house.fill" }),
+ Platform.OS == "android"
+ ? ({ color, focused, size }) =>
+ require("@/assets/icons/house.fill.png")
+ : ({ focused }) =>
+ focused
+ ? { sfSymbol: "house.fill" }
+ : { sfSymbol: "house" },
}}
/>
({
- tabPress: (_e) => {
+ listeners={({ navigation }) => ({
+ tabPress: (e) => {
eventBus.emit("searchTabPressed");
},
})}
- name='(search)'
+ name="(search)"
options={{
- role: "search",
title: t("tabs.search"),
tabBarIcon:
- Platform.OS === "android"
- ? (_e) => require("@/assets/icons/magnifyingglass.png")
- : (_e) => ({ sfSymbol: "magnifyingglass" }),
+ Platform.OS == "android"
+ ? ({ color, focused, size }) =>
+ require("@/assets/icons/magnifyingglass.png")
+ : ({ focused }) =>
+ focused
+ ? { sfSymbol: "magnifyingglass" }
+ : { sfSymbol: "magnifyingglass" },
}}
/>
require("@/assets/icons/heart.fill.png")
- : (_e) => ({ sfSymbol: "heart.fill" }),
+ Platform.OS == "android"
+ ? ({ color, focused, size }) =>
+ focused
+ ? require("@/assets/icons/heart.fill.png")
+ : require("@/assets/icons/heart.png")
+ : ({ focused }) =>
+ focused
+ ? { sfSymbol: "heart.fill" }
+ : { sfSymbol: "heart" },
}}
/>
require("@/assets/icons/list.png")
- : (_e) => ({ sfSymbol: "list.bullet.rectangle" }),
- }}
- />
- require("@/assets/icons/server.rack.png")
- : (_e) => ({ sfSymbol: "rectangle.stack.fill" }),
+ Platform.OS == "android"
+ ? ({ color, focused, size }) =>
+ require("@/assets/icons/server.rack.png")
+ : ({ focused }) =>
+ focused
+ ? { sfSymbol: "rectangle.stack.fill" }
+ : { sfSymbol: "rectangle.stack" },
}}
/>
require("@/assets/icons/list.png")
- : (_e) => ({ sfSymbol: "list.dash.fill" }),
+ Platform.OS == "android"
+ ? ({ focused }) => require("@/assets/icons/list.png")
+ : ({ focused }) =>
+ focused
+ ? { sfSymbol: "list.dash.fill" }
+ : { sfSymbol: "list.dash" },
}}
/>
-
-
-
+ >
);
}
diff --git a/app/(auth)/player/_layout.tsx b/app/(auth)/player/_layout.tsx
index 5604160e..318322ec 100644
--- a/app/(auth)/player/_layout.tsx
+++ b/app/(auth)/player/_layout.tsx
@@ -1,39 +1,57 @@
import { Stack } from "expo-router";
-import { useEffect } from "react";
-import { AppState } from "react-native";
+import React, { useEffect } from "react";
import { SystemBars } from "react-native-edge-to-edge";
-
-import { useOrientation } from "@/hooks/useOrientation";
+import * as ScreenOrientation from "@/packages/expo-screen-orientation";
import { useSettings } from "@/utils/atoms/settings";
+import { Platform } from "react-native";
export default function Layout() {
- const { settings } = useSettings();
- const { lockOrientation, unlockOrientation } = useOrientation();
+ const [settings] = useSettings();
useEffect(() => {
- if (settings?.defaultVideoOrientation) {
- lockOrientation(settings.defaultVideoOrientation);
+ if (Platform.isTV) return;
+
+ if (settings.defaultVideoOrientation) {
+ ScreenOrientation.lockAsync(settings.defaultVideoOrientation);
}
- // Re-apply orientation lock when app returns to foreground (iOS resets it)
- const subscription = AppState.addEventListener("change", (nextAppState) => {
- if (nextAppState === "active" && settings?.defaultVideoOrientation) {
- lockOrientation(settings.defaultVideoOrientation);
- }
- });
-
return () => {
- subscription.remove();
- unlockOrientation();
+ if (Platform.isTV) return;
+
+ if (settings.autoRotate === true) {
+ ScreenOrientation.unlockAsync();
+ } else {
+ ScreenOrientation.lockAsync(
+ ScreenOrientation.OrientationLock.PORTRAIT_UP
+ );
+ }
};
- }, [settings?.defaultVideoOrientation, lockOrientation, unlockOrientation]);
+ }, [settings]);
return (
<>
+
+ (null);
+ console.log("Direct Player");
+ const videoRef = useRef(null);
const user = useAtomValue(userAtom);
const api = useAtomValue(apiAtom);
const { t } = useTranslation();
const navigation = useNavigation();
- const router = useRouter();
- const { settings, updateSettings } = useSettings();
-
- const { width: screenWidth, height: screenHeight } = useWindowDimensions();
const [isPlaybackStopped, setIsPlaybackStopped] = useState(false);
const [showControls, _setShowControls] = useState(true);
- const [isPipMode, setIsPipMode] = useState(false);
- const [aspectRatio] = useState<"default" | "16:9" | "4:3" | "1:1" | "21:9">(
- "default",
- );
- const [isZoomedToFill, setIsZoomedToFill] = useState(false);
+ const [ignoreSafeAreas, setIgnoreSafeAreas] = useState(false);
const [isPlaying, setIsPlaying] = useState(false);
- const [isMuted, setIsMuted] = useState(false);
const [isBuffering, setIsBuffering] = useState(true);
const [isVideoLoaded, setIsVideoLoaded] = useState(false);
- const [tracksReady, setTracksReady] = useState(false);
- const [hasPlaybackStarted, setHasPlaybackStarted] = useState(false);
- const [currentPlaybackSpeed, setCurrentPlaybackSpeed] = useState(1.0);
- const [showTechnicalInfo, setShowTechnicalInfo] = useState(false);
+ const [isPipStarted, setIsPipStarted] = useState(false);
const progress = useSharedValue(0);
const isSeeking = useSharedValue(false);
const cacheProgress = useSharedValue(0);
- const VolumeManager = Platform.isTV
- ? null
- : require("react-native-volume-manager");
-
- const downloadUtils = useDownload();
- // Call directly instead of useMemo - the function reference doesn't change
- // when data updates, only when the provider initializes
- const downloadedFiles = downloadUtils.getDownloadedItems();
+ let getDownloadedItem = null;
+ if (!Platform.isTV) {
+ getDownloadedItem = downloadProvider.useDownload();
+ }
const revalidateProgressCache = useInvalidatePlaybackProgressCache();
@@ -112,346 +84,182 @@ export default function page() {
mediaSourceId,
bitrateValue: bitrateValueStr,
offline: offlineStr,
- playbackPosition: playbackPositionFromUrl,
- } = useLocalSearchParams<{
+ } = useGlobalSearchParams<{
itemId: string;
audioIndex: string;
subtitleIndex: string;
mediaSourceId: string;
bitrateValue: string;
offline: string;
- /** Playback position in ticks. */
- playbackPosition?: string;
}>();
- const { lockOrientation, unlockOrientation } = useOrientation();
-
+ const [settings] = useSettings();
const offline = offlineStr === "true";
- const playbackManager = usePlaybackManager({ isOffline: offline });
- // Audio index: use URL param if provided, otherwise use stored index for offline playback
- // This is computed after downloadedItem is available, see audioIndexResolved below
- const audioIndexFromUrl = audioIndexStr
- ? Number.parseInt(audioIndexStr, 10)
- : undefined;
- const subtitleIndex = subtitleIndexStr
- ? Number.parseInt(subtitleIndexStr, 10)
- : -1;
+ const audioIndex = audioIndexStr ? parseInt(audioIndexStr, 10) : undefined;
+ const subtitleIndex = subtitleIndexStr ? parseInt(subtitleIndexStr, 10) : -1;
const bitrateValue = bitrateValueStr
- ? Number.parseInt(bitrateValueStr, 10)
+ ? parseInt(bitrateValueStr, 10)
: BITRATES[0].value;
- const [item, setItem] = useState(null);
- const [downloadedItem, setDownloadedItem] = useState(
- null,
- );
- const [itemStatus, setItemStatus] = useState({
- isLoading: true,
- isError: false,
- });
-
- // Resolve audio index: use URL param if provided, otherwise use stored index for offline playback
- const audioIndex = useMemo(() => {
- if (audioIndexFromUrl !== undefined) {
- return audioIndexFromUrl;
- }
- if (offline && downloadedItem?.userData?.audioStreamIndex !== undefined) {
- return downloadedItem.userData.audioStreamIndex;
- }
- return undefined;
- }, [audioIndexFromUrl, offline, downloadedItem?.userData?.audioStreamIndex]);
-
- // Get the playback speed for this item based on settings
- const { playbackSpeed: initialPlaybackSpeed } = usePlaybackSpeed(
- item,
- settings,
- );
-
- // Handler for changing playback speed
- const handleSetPlaybackSpeed = useCallback(
- async (speed: number, scope: PlaybackSpeedScope) => {
- // Update settings based on scope
- updatePlaybackSpeedSettings(
- speed,
- scope,
- item ?? undefined,
- settings,
- updateSettings,
- );
-
- // Apply speed to the current player (MPV)
- setCurrentPlaybackSpeed(speed);
- await videoRef.current?.setSpeed?.(speed);
- },
- [item, settings, updateSettings],
- );
-
- /** Gets the initial playback position from the URL. */
- const getInitialPlaybackTicks = useCallback((): number => {
- if (playbackPositionFromUrl) {
- return Number.parseInt(playbackPositionFromUrl, 10);
- }
- return item?.UserData?.PlaybackPositionTicks ?? 0;
- }, [playbackPositionFromUrl, item?.UserData?.PlaybackPositionTicks]);
-
- useEffect(() => {
- const fetchItemData = async () => {
- setItemStatus({ isLoading: true, isError: false });
- try {
- let fetchedItem: BaseItemDto | null = null;
- if (offline && !Platform.isTV) {
- const data = downloadUtils.getDownloadedItemById(itemId);
- if (data) {
- fetchedItem = data.item as BaseItemDto;
- setDownloadedItem(data);
- }
- } else {
- const res = await getUserLibraryApi(api!).getItem({
- itemId,
- userId: user?.Id,
- });
- fetchedItem = res.data;
- }
- setItem(fetchedItem);
- setItemStatus({ isLoading: false, isError: false });
- } catch (error) {
- console.error("Failed to fetch item:", error);
- setItemStatus({ isLoading: false, isError: true });
+ const {
+ data: item,
+ isLoading: isLoadingItem,
+ isError: isErrorItem,
+ } = useQuery({
+ queryKey: ["item", itemId],
+ queryFn: async () => {
+ if (offline && !Platform.isTV) {
+ const item = await getDownloadedItem.getDownloadedItem(itemId);
+ if (item) return item.item;
}
- };
- if (itemId) {
- fetchItemData();
- }
- }, [itemId, offline, api, user?.Id]);
+ const res = await getUserLibraryApi(api!).getItem({
+ itemId,
+ userId: user?.Id,
+ });
- // Lock orientation based on user settings
- useEffect(() => {
- if (settings?.defaultVideoOrientation) {
- lockOrientation(settings.defaultVideoOrientation);
- }
-
- return () => {
- unlockOrientation();
- };
- }, [settings?.defaultVideoOrientation, lockOrientation, unlockOrientation]);
-
- interface Stream {
- mediaSource: MediaSourceInfo;
- sessionId: string;
- url: string;
- }
-
- const [stream, setStream] = useState(null);
- const [streamStatus, setStreamStatus] = useState({
- isLoading: true,
- isError: false,
+ return res.data;
+ },
+ enabled: !!itemId,
+ staleTime: 0,
});
+ const [stream, setStream] = useState<{
+ mediaSource: MediaSourceInfo;
+ url: string;
+ sessionId: string | undefined;
+ } | null>(null);
+ const [isLoadingStream, setIsLoadingStream] = useState(true);
+ const [isErrorStream, setIsErrorStream] = useState(false);
+
useEffect(() => {
- const fetchStreamData = async () => {
- setStreamStatus({ isLoading: true, isError: false });
+ const fetchStream = async () => {
+ setIsLoadingStream(true);
+ setIsErrorStream(false);
+
try {
- // Don't attempt to fetch stream data if item is not available
- if (!item?.Id) {
- console.log("Item not loaded yet, skipping stream data fetch");
- setStreamStatus({ isLoading: false, isError: false });
+ if (offline && !Platform.isTV) {
+ const data = await getDownloadedItem.getDownloadedItem(itemId);
+ if (!data?.mediaSource) {
+ setStream(null);
+ return;
+ }
+
+ const url = await getDownloadedFileUrl(data.item.Id!);
+
+ if (item) {
+ setStream({
+ mediaSource: data.mediaSource as MediaSourceInfo,
+ url,
+ sessionId: undefined,
+ });
+ return;
+ }
+ }
+
+ const res = await getStreamUrl({
+ api,
+ item,
+ startTimeTicks: item?.UserData?.PlaybackPositionTicks!,
+ userId: user?.Id,
+ audioStreamIndex: audioIndex,
+ maxStreamingBitrate: bitrateValue,
+ mediaSourceId: mediaSourceId,
+ subtitleStreamIndex: subtitleIndex,
+ deviceProfile: native,
+ });
+
+ if (!res) {
+ setStream(null);
return;
}
- let result: Stream | null = null;
- if (offline && downloadedItem && downloadedItem.mediaSource) {
- const url = downloadedItem.videoFilePath;
- if (item) {
- result = {
- mediaSource: downloadedItem.mediaSource,
- sessionId: "",
- url: url,
- };
- }
- } else {
- // Validate required parameters before calling getStreamUrl
- if (!api) {
- console.warn("API not available for streaming");
- setStreamStatus({ isLoading: false, isError: true });
- return;
- }
- if (!user?.Id) {
- console.warn("User not authenticated for streaming");
- setStreamStatus({ isLoading: false, isError: true });
- return;
- }
+ const { mediaSource, sessionId, url } = res;
- // Calculate start ticks directly from item to avoid stale closure
- const startTicks = playbackPositionFromUrl
- ? Number.parseInt(playbackPositionFromUrl, 10)
- : (item?.UserData?.PlaybackPositionTicks ?? 0);
-
- const res = await getStreamUrl({
- api,
- item,
- startTimeTicks: startTicks,
- userId: user.Id,
- audioStreamIndex: audioIndex,
- maxStreamingBitrate: bitrateValue,
- mediaSourceId: mediaSourceId,
- subtitleStreamIndex: subtitleIndex,
- deviceProfile: generateDeviceProfile(),
- });
- if (!res) return;
- const { mediaSource, sessionId, url } = res;
-
- if (!sessionId || !mediaSource || !url) {
- Alert.alert(
- t("player.error"),
- t("player.failed_to_get_stream_url"),
- );
- return;
- }
- result = { mediaSource, sessionId, url };
+ if (!sessionId || !mediaSource || !url) {
+ Alert.alert(t("player.error"), t("player.failed_to_get_stream_url"));
+ setStream(null);
+ return;
}
- setStream(result);
- setStreamStatus({ isLoading: false, isError: false });
- } catch (error) {
- console.error("Failed to fetch stream:", error);
- setStreamStatus({ isLoading: false, isError: true });
- }
- };
- fetchStreamData();
- }, [
- itemId,
- mediaSourceId,
- bitrateValue,
- api,
- item,
- user?.Id,
- downloadedItem,
- ]);
- useEffect(() => {
- if (!stream || !api || offline) return;
- const reportPlaybackStart = async () => {
- const progressInfo = currentPlayStateInfo();
- if (progressInfo) {
- await getPlaystateApi(api).reportPlaybackStart({
- playbackStartInfo: progressInfo,
+ setStream({
+ mediaSource,
+ sessionId,
+ url,
});
+ } catch (error) {
+ console.error("Error fetching stream:", error);
+ setIsErrorStream(true);
+ setStream(null);
+ } finally {
+ setIsLoadingStream(false);
}
};
- reportPlaybackStart();
- }, [stream, api, offline]);
- const togglePlay = async () => {
+ fetchStream();
+ }, [itemId, mediaSourceId]);
+
+ const togglePlay = useCallback(async () => {
+ if (!api) return;
+
lightHapticFeedback();
- setIsPlaying(!isPlaying);
if (isPlaying) {
await videoRef.current?.pause();
- const progressInfo = currentPlayStateInfo();
- if (progressInfo) {
- playbackManager.reportPlaybackProgress(progressInfo);
- }
} else {
videoRef.current?.play();
- const progressInfo = currentPlayStateInfo();
- if (!offline && api) {
- await getPlaystateApi(api).reportPlaybackStart({
- playbackStartInfo: progressInfo,
- });
- }
}
- };
- const reportPlaybackStopped = useCallback(async () => {
- if (!item?.Id || !stream?.sessionId || offline || !api) return;
-
- const currentTimeInTicks = msToTicks(progress.get());
- await getPlaystateApi(api).onPlaybackStopped({
- itemId: item.Id,
- mediaSourceId: mediaSourceId,
- positionTicks: currentTimeInTicks,
- playSessionId: stream.sessionId,
- });
+ if (!offline && stream) {
+ await getPlaystateApi(api).onPlaybackProgress({
+ itemId: item?.Id!,
+ audioStreamIndex: audioIndex ? audioIndex : undefined,
+ subtitleStreamIndex: subtitleIndex ? subtitleIndex : undefined,
+ mediaSourceId: mediaSourceId,
+ positionTicks: msToTicks(progress.get()),
+ isPaused: !isPlaying,
+ playMethod: stream?.url.includes("m3u8") ? "Transcode" : "DirectStream",
+ playSessionId: stream.sessionId,
+ });
+ }
}, [
+ isPlaying,
api,
item,
- mediaSourceId,
stream,
- progress,
- offline,
- revalidateProgressCache,
- ]);
-
- const stop = useCallback(() => {
- // Update URL with final playback position before stopping
- router.setParams({
- playbackPosition: msToTicks(progress.get()).toString(),
- });
- reportPlaybackStopped();
- setIsPlaybackStopped(true);
- videoRef.current?.pause();
- revalidateProgressCache();
- }, [videoRef, reportPlaybackStopped, progress]);
-
- useEffect(() => {
- const beforeRemoveListener = navigation.addListener("beforeRemove", stop);
- return () => {
- beforeRemoveListener();
- };
- }, [navigation, stop]);
-
- const currentPlayStateInfo = useCallback(():
- | PlaybackProgressInfo
- | undefined => {
- if (!stream || !item?.Id) return;
-
- return {
- ItemId: item.Id,
- AudioStreamIndex: audioIndex ? audioIndex : undefined,
- SubtitleStreamIndex: subtitleIndex ? subtitleIndex : undefined,
- MediaSourceId: mediaSourceId,
- PositionTicks: msToTicks(progress.get()),
- IsPaused: !isPlaying,
- PlayMethod: stream?.url.includes("m3u8") ? "Transcode" : "DirectStream",
- PlaySessionId: stream.sessionId,
- IsMuted: isMuted,
- CanSeek: true,
- RepeatMode: RepeatMode.RepeatNone,
- PlaybackOrder: PlaybackOrder.Default,
- };
- }, [
- stream,
- item?.Id,
+ videoRef,
audioIndex,
subtitleIndex,
mediaSourceId,
+ offline,
progress,
- isPlaying,
- isMuted,
]);
- const lastUrlUpdateTime = useSharedValue(0);
- const wasJustSeeking = useSharedValue(false);
- const URL_UPDATE_INTERVAL = 30000; // Update URL every 30 seconds instead of every second
+ const reportPlaybackStopped = useCallback(async () => {
+ if (offline) return;
- // Track when seeking ends to update URL immediately
- useAnimatedReaction(
- () => isSeeking.get(),
- (currentSeeking, previousSeeking) => {
- if (previousSeeking && !currentSeeking) {
- // Seeking just ended
- wasJustSeeking.value = true;
- }
- },
- [],
- );
+ const currentTimeInTicks = msToTicks(progress.get());
+
+ await getPlaystateApi(api!).onPlaybackStopped({
+ itemId: item?.Id!,
+ mediaSourceId: mediaSourceId,
+ positionTicks: currentTimeInTicks,
+ playSessionId: stream?.sessionId!,
+ });
+
+ revalidateProgressCache();
+ }, [api, item, mediaSourceId, stream]);
+
+ const stop = useCallback(() => {
+ reportPlaybackStopped();
+ setIsPlaybackStopped(true);
+ videoRef.current?.stop();
+ }, [videoRef, reportPlaybackStopped]);
- /** Progress handler for MPV - position in seconds */
const onProgress = useCallback(
- async (data: { nativeEvent: MpvOnProgressEventPayload }) => {
+ async (data: ProgressUpdatePayload) => {
if (isSeeking.get() || isPlaybackStopped) return;
- const { position } = data.nativeEvent;
- // MPV reports position in seconds, convert to ms
- const currentTime = position * 1000;
+ const { currentTime } = data.nativeEvent;
if (isBuffering) {
setIsBuffering(false);
@@ -459,251 +267,109 @@ export default function page() {
progress.set(currentTime);
- // Update URL immediately after seeking, or every 30 seconds during normal playback
- const now = Date.now();
- const shouldUpdateUrl = wasJustSeeking.get();
- wasJustSeeking.value = false;
+ if (offline) return;
- if (
- shouldUpdateUrl ||
- now - lastUrlUpdateTime.get() > URL_UPDATE_INTERVAL
- ) {
- router.setParams({
- playbackPosition: msToTicks(currentTime).toString(),
- });
- lastUrlUpdateTime.value = now;
- }
+ const currentTimeInTicks = msToTicks(currentTime);
- if (!item?.Id) return;
+ if (!item?.Id || !stream) return;
- playbackManager.reportPlaybackProgress(
- currentPlayStateInfo() as PlaybackProgressInfo,
- );
+ await getPlaystateApi(api!).onPlaybackProgress({
+ itemId: item.Id,
+ audioStreamIndex: audioIndex ? audioIndex : undefined,
+ subtitleStreamIndex: subtitleIndex ? subtitleIndex : undefined,
+ mediaSourceId: mediaSourceId,
+ positionTicks: Math.floor(currentTimeInTicks),
+ isPaused: !isPlaying,
+ playMethod: stream?.url.includes("m3u8") ? "Transcode" : "DirectStream",
+ playSessionId: stream.sessionId,
+ });
},
- [
- item?.Id,
- audioIndex,
- subtitleIndex,
- mediaSourceId,
- isPlaying,
- stream,
- isSeeking,
- isPlaybackStopped,
- isBuffering,
- ],
+ [item?.Id, isSeeking, api, isPlaybackStopped, audioIndex, subtitleIndex]
);
- /** Gets the initial playback position in seconds. */
- const _startPosition = useMemo(() => {
- return ticksToSeconds(getInitialPlaybackTicks());
- }, [getInitialPlaybackTicks]);
-
- /** Build video source config for MPV */
- const videoSource = useMemo(() => {
- if (!stream?.url) return undefined;
-
- const mediaSource = stream.mediaSource;
- const isTranscoding = Boolean(mediaSource?.TranscodingUrl);
-
- // Get external subtitle URLs
- // - Online: prepend API base path to server URLs
- // - Offline: use local file paths (stored in DeliveryUrl during download)
- let externalSubs: string[] | undefined;
- if (!offline && api?.basePath) {
- externalSubs = mediaSource?.MediaStreams?.filter(
- (s) =>
- s.Type === "Subtitle" &&
- s.DeliveryMethod === "External" &&
- s.DeliveryUrl,
- ).map((s) => `${api.basePath}${s.DeliveryUrl}`);
- } else if (offline) {
- externalSubs = mediaSource?.MediaStreams?.filter(
- (s) =>
- s.Type === "Subtitle" &&
- s.DeliveryMethod === "External" &&
- s.DeliveryUrl,
- ).map((s) => s.DeliveryUrl!);
- }
-
- // Calculate track IDs for initial selection
- const initialSubtitleId = getMpvSubtitleId(
- mediaSource,
- subtitleIndex,
- isTranscoding,
- );
- const initialAudioId = getMpvAudioId(
- mediaSource,
- audioIndex,
- isTranscoding,
- );
-
- // Calculate start position directly here to avoid timing issues
- const startTicks = playbackPositionFromUrl
- ? Number.parseInt(playbackPositionFromUrl, 10)
- : (item?.UserData?.PlaybackPositionTicks ?? 0);
- const startPos = ticksToSeconds(startTicks);
-
- // Build source config - headers only needed for online streaming
- const source: MpvVideoSource = {
- url: stream.url,
- startPosition: startPos,
- autoplay: true,
- initialSubtitleId,
- initialAudioId,
- };
-
- // Add external subtitles only for online playback
- if (externalSubs && externalSubs.length > 0) {
- source.externalSubtitles = externalSubs;
- }
-
- // Add auth headers only for online streaming (not for local file:// URLs)
- if (!offline && api?.accessToken) {
- source.headers = {
- Authorization: `MediaBrowser Token="${api.accessToken}"`,
- };
- }
-
- return source;
- }, [
- stream?.url,
- stream?.mediaSource,
- item?.UserData?.PlaybackPositionTicks,
- playbackPositionFromUrl,
- api?.basePath,
- api?.accessToken,
- subtitleIndex,
- audioIndex,
- offline,
- ]);
-
- const volumeUpCb = useCallback(async () => {
- if (Platform.isTV) return;
-
- try {
- const { volume: currentVolume } = await VolumeManager.getVolume();
- const newVolume = Math.min(currentVolume + 0.1, 1.0);
-
- await VolumeManager.setVolume(newVolume);
- } catch (error) {
- console.error("Error adjusting volume:", error);
- }
- }, []);
- const [previousVolume, setPreviousVolume] = useState(null);
-
- const toggleMuteCb = useCallback(async () => {
- if (Platform.isTV) return;
-
- try {
- const { volume: currentVolume } = await VolumeManager.getVolume();
- const currentVolumePercent = currentVolume * 100;
-
- if (currentVolumePercent > 0) {
- // Currently not muted, so mute
- setPreviousVolume(currentVolumePercent);
- await VolumeManager.setVolume(0);
- setIsMuted(true);
- } else {
- // Currently muted, so restore previous volume
- const volumeToRestore = previousVolume || 50; // Default to 50% if no previous volume
- await VolumeManager.setVolume(volumeToRestore / 100);
- setPreviousVolume(null);
- setIsMuted(false);
- }
- } catch (error) {
- console.error("Error toggling mute:", error);
- }
- }, [previousVolume]);
-
- const volumeDownCb = useCallback(async () => {
- if (Platform.isTV) return;
-
- try {
- const { volume: currentVolume } = await VolumeManager.getVolume();
- const newVolume = Math.max(currentVolume - 0.1, 0); // Decrease by 10%
- console.log(
- "Volume Down",
- Math.round(currentVolume * 100),
- "→",
- Math.round(newVolume * 100),
- );
- await VolumeManager.setVolume(newVolume);
- } catch (error) {
- console.error("Error adjusting volume:", error);
- }
- }, []);
-
- const setVolumeCb = useCallback(async (newVolume: number) => {
- if (Platform.isTV) return;
-
- try {
- const clampedVolume = Math.max(0, Math.min(newVolume, 100));
- console.log("Setting volume to", clampedVolume);
- await VolumeManager.setVolume(clampedVolume / 100);
- } catch (error) {
- console.error("Error setting volume:", error);
- }
- }, []);
-
useWebSocket({
isPlaying: isPlaying,
togglePlay: togglePlay,
stopPlayback: stop,
offline,
- toggleMute: toggleMuteCb,
- volumeUp: volumeUpCb,
- volumeDown: volumeDownCb,
- setVolume: setVolumeCb,
});
- /** Playback state handler for MPV */
- const onPlaybackStateChanged = useCallback(
- async (e: { nativeEvent: MpvOnPlaybackStateChangePayload }) => {
- const { isPaused, isPlaying: playing, isLoading } = e.nativeEvent;
+ const onPipStarted = useCallback((e: PipStartedPayload) => {
+ const { pipStarted } = e.nativeEvent;
+ setIsPipStarted(pipStarted);
+ }, []);
- if (playing) {
- setIsPlaying(true);
- setIsBuffering(false);
- setHasPlaybackStarted(true);
- if (item?.Id) {
- playbackManager.reportPlaybackProgress(
- currentPlayStateInfo() as PlaybackProgressInfo,
- );
- }
- if (!Platform.isTV) await activateKeepAwakeAsync();
- return;
- }
+ const onPlaybackStateChanged = useCallback(async (e: PlaybackStatePayload) => {
+ const { state, isBuffering, isPlaying } = e.nativeEvent;
- if (isPaused) {
- setIsPlaying(false);
- if (item?.Id) {
- playbackManager.reportPlaybackProgress(
- currentPlayStateInfo() as PlaybackProgressInfo,
- );
- }
- if (!Platform.isTV) await deactivateKeepAwake();
- return;
- }
+ if (state === "Playing") {
+ setIsPlaying(true);
+ if (!Platform.isTV) await activateKeepAwakeAsync()
+ return;
+ }
- if (isLoading !== undefined) {
- setIsBuffering(isLoading);
- }
- },
- [playbackManager, item?.Id, progress],
+ if (state === "Paused") {
+ setIsPlaying(false);
+ if (!Platform.isTV) await deactivateKeepAwake();
+ return;
+ }
+
+ if (isPlaying) {
+ setIsPlaying(true);
+ setIsBuffering(false);
+ } else if (isBuffering) {
+ setIsBuffering(true);
+ }
+ }, []);
+
+ const startPosition = useMemo(() => {
+ if (offline) return 0;
+
+ return item?.UserData?.PlaybackPositionTicks
+ ? ticksToSeconds(item.UserData.PlaybackPositionTicks)
+ : 0;
+ }, [item]);
+
+ // Preselection of audio and subtitle tracks.
+ if (!settings) return null;
+ let initOptions = [`--sub-text-scale=${settings.subtitleSize}`];
+
+ const allAudio =
+ stream?.mediaSource.MediaStreams?.filter(
+ (audio) => audio.Type === "Audio"
+ ) || [];
+ const allSubs =
+ stream?.mediaSource.MediaStreams?.filter(
+ (sub) => sub.Type === "Subtitle"
+ ) || [];
+ const textSubs = allSubs.filter((sub) => sub.IsTextSubtitleStream);
+
+ const chosenSubtitleTrack = allSubs.find(
+ (sub) => sub.Index === subtitleIndex
);
+ const chosenAudioTrack = allAudio.find((audio) => audio.Index === audioIndex);
- /** PiP handler for MPV */
- const _onPictureInPictureChange = useCallback(
- (e: { nativeEvent: { isActive: boolean } }) => {
- const { isActive } = e.nativeEvent;
- setIsPipMode(isActive);
- // Hide controls when entering PiP
- if (isActive) {
- _setShowControls(false);
- }
- },
- [],
- );
+ const notTranscoding = !stream?.mediaSource.TranscodingUrl;
+ if (
+ chosenSubtitleTrack &&
+ (notTranscoding || chosenSubtitleTrack.IsTextSubtitleStream)
+ ) {
+ const finalIndex = notTranscoding
+ ? allSubs.indexOf(chosenSubtitleTrack)
+ : textSubs.indexOf(chosenSubtitleTrack);
+ initOptions.push(`--sub-track=${finalIndex}`);
+ }
+
+ if (notTranscoding && chosenAudioTrack) {
+ initOptions.push(`--audio-track=${allAudio.indexOf(chosenAudioTrack)}`);
+ }
+
+ const externalSubtitles = allSubs
+ .filter((sub: any) => sub.DeliveryMethod === "External")
+ .map((sub: any) => ({
+ name: sub.DisplayTitle,
+ DeliveryUrl: api?.basePath + sub.DeliveryUrl,
+ }));
const [isMounted, setIsMounted] = useState(false);
@@ -713,285 +379,101 @@ export default function page() {
return () => setIsMounted(false);
}, []);
- // Memoize video ref functions to prevent unnecessary re-renders
- const startPictureInPicture = useCallback(async () => {
- return videoRef.current?.startPictureInPicture?.();
- }, []);
-
- const play = useCallback(() => {
- videoRef.current?.play?.();
- }, []);
-
- const pause = useCallback(() => {
- videoRef.current?.pause?.();
- }, []);
-
- const seek = useCallback((position: number) => {
- // MPV expects seconds, convert from ms
- videoRef.current?.seekTo?.(position / 1000);
- }, []);
-
- // Technical info toggle handler
- const handleToggleTechnicalInfo = useCallback(() => {
- setShowTechnicalInfo((prev) => !prev);
- }, []);
-
- // Get technical info from the player
- const getTechnicalInfo = useCallback(async () => {
- return (await videoRef.current?.getTechnicalInfo?.()) ?? {};
- }, []);
-
- // Determine play method based on stream URL and media source
- const playMethod = useMemo<
- "DirectPlay" | "DirectStream" | "Transcode" | undefined
- >(() => {
- if (!stream?.url) return undefined;
-
- // Check if transcoding (m3u8 playlist or TranscodingUrl present)
- if (stream.url.includes("m3u8") || stream.mediaSource?.TranscodingUrl) {
- return "Transcode";
- }
-
- // Check if direct play (no container remuxing needed)
- // Direct play means the file is being served as-is
- if (stream.url.includes("/Videos/") && stream.url.includes("/stream")) {
- return "DirectStream";
- }
-
- // Default to direct play if we're not transcoding
- return "DirectPlay";
- }, [stream?.url, stream?.mediaSource?.TranscodingUrl]);
-
- // Extract transcode reasons from the TranscodingUrl
- const transcodeReasons = useMemo(() => {
- const transcodingUrl = stream?.mediaSource?.TranscodingUrl;
- if (!transcodingUrl) return [];
-
- try {
- // Parse the TranscodeReasons parameter from the URL
- const url = new URL(transcodingUrl, "http://localhost");
- const reasons = url.searchParams.get("TranscodeReasons");
- if (reasons) {
- return reasons.split(",").filter(Boolean);
- }
- } catch {
- // If URL parsing fails, try regex fallback
- const match = transcodingUrl.match(/TranscodeReasons=([^&]+)/);
- if (match) {
- return match[1].split(",").filter(Boolean);
- }
- }
- return [];
- }, [stream?.mediaSource?.TranscodingUrl]);
-
- const handleZoomToggle = useCallback(async () => {
- const newZoomState = !isZoomedToFill;
- await videoRef.current?.setZoomedToFill?.(newZoomState);
- setIsZoomedToFill(newZoomState);
-
- // Adjust subtitle position to compensate for video cropping when zoomed
- if (newZoomState) {
- // Get video dimensions from mediaSource
- const videoStream = stream?.mediaSource?.MediaStreams?.find(
- (s) => s.Type === "Video",
- );
- const videoWidth = videoStream?.Width ?? 1920;
- const videoHeight = videoStream?.Height ?? 1080;
-
- const videoAR = videoWidth / videoHeight;
- const screenAR = screenWidth / screenHeight;
-
- if (screenAR > videoAR) {
- // Screen is wider than video - video height extends beyond screen
- // Calculate how much of the video is cropped at the bottom (as % of video height)
- const bottomCropPercent = 50 * (1 - videoAR / screenAR);
- // Only adjust by 70% of the crop to keep a comfortable margin from the edge
- // (subtitles already have some built-in padding from the bottom)
- const adjustmentFactor = 0.7;
- const newSubPos = Math.round(
- 100 - bottomCropPercent * adjustmentFactor,
- );
- await videoRef.current?.setSubtitlePosition?.(newSubPos);
- }
- // If videoAR >= screenAR, sides are cropped but bottom is visible, no adjustment needed
- } else {
- // Restore to default position (bottom of video frame)
- await videoRef.current?.setSubtitlePosition?.(100);
- }
- }, [isZoomedToFill, stream?.mediaSource, screenWidth, screenHeight]);
-
- // Apply subtitle settings when video loads
+ const insets = useSafeAreaInsets();
useEffect(() => {
- if (!isVideoLoaded || !videoRef.current) return;
-
- const applySubtitleSettings = async () => {
- if (settings.mpvSubtitleScale !== undefined) {
- await videoRef.current?.setSubtitleScale?.(settings.mpvSubtitleScale);
- }
- if (settings.mpvSubtitleMarginY !== undefined) {
- await videoRef.current?.setSubtitleMarginY?.(
- settings.mpvSubtitleMarginY,
- );
- }
- if (settings.mpvSubtitleAlignX !== undefined) {
- await videoRef.current?.setSubtitleAlignX?.(settings.mpvSubtitleAlignX);
- }
- if (settings.mpvSubtitleAlignY !== undefined) {
- await videoRef.current?.setSubtitleAlignY?.(settings.mpvSubtitleAlignY);
- }
- if (settings.mpvSubtitleFontSize !== undefined) {
- await videoRef.current?.setSubtitleFontSize?.(
- settings.mpvSubtitleFontSize,
- );
- }
- // Apply subtitle size from general settings
- if (settings.subtitleSize) {
- await videoRef.current?.setSubtitleFontSize?.(settings.subtitleSize);
- }
+ const beforeRemoveListener = navigation.addListener("beforeRemove", stop);
+ return () => {
+ beforeRemoveListener();
};
+ }, [navigation]);
- applySubtitleSettings();
- }, [isVideoLoaded, settings]);
-
- // Apply initial playback speed when video loads
- useEffect(() => {
- if (!isVideoLoaded || !videoRef.current) return;
-
- const applyInitialPlaybackSpeed = async () => {
- if (initialPlaybackSpeed !== 1.0) {
- setCurrentPlaybackSpeed(initialPlaybackSpeed);
- await videoRef.current?.setSpeed?.(initialPlaybackSpeed);
- }
- };
-
- applyInitialPlaybackSpeed();
- }, [isVideoLoaded, initialPlaybackSpeed]);
-
- // Show error UI first, before checking loading/missing‐data
- if (itemStatus.isError || streamStatus.isError) {
+ if (!item || isLoadingItem || !stream)
return (
-
- {t("player.error")}
-
- );
- }
-
- // Then show loader while either side is still fetching or data isn't present
- if (itemStatus.isLoading || streamStatus.isLoading || !item || !stream) {
- // …loader UI…
- return (
-
+
);
- }
- if (itemStatus.isError || streamStatus.isError)
+ if (isErrorItem || isErrorStream)
return (
-
- {t("player.error")}
+
+ {t("player.error")}
);
return (
-
-
+
-
-
-
- setIsVideoLoaded(true)}
- onError={(e: { nativeEvent: MpvOnErrorEventPayload }) => {
- console.error("Video Error:", e.nativeEvent);
- Alert.alert(
- t("player.error"),
- t("player.an_error_occured_while_playing_the_video"),
- );
- writeToLog("ERROR", "Video Error", e.nativeEvent);
- }}
- onTracksReady={() => {
- setTracksReady(true);
- }}
- />
- {!hasPlaybackStarted && (
-
-
-
- )}
-
- {isMounted === true && item && !isPipMode && (
-
- )}
-
-
-
-
+ {
+ setIsVideoLoaded(true);
+ }}
+ onVideoError={(e) => {
+ console.error("Video Error:", e.nativeEvent);
+ Alert.alert(
+ t("player.error"),
+ t("player.an_error_occured_while_playing_the_video")
+ );
+ writeToLog("ERROR", "Video Error", e.nativeEvent);
+ }}
+ />
+
+ {videoRef.current && !isPipStarted && isMounted === true ? (
+
+ ) : null}
+
);
}
diff --git a/app/(auth)/player/google-cast-player.tsx b/app/(auth)/player/google-cast-player.tsx
new file mode 100644
index 00000000..dd9cc448
--- /dev/null
+++ b/app/(auth)/player/google-cast-player.tsx
@@ -0,0 +1,184 @@
+import React, { useMemo, useRef, useState } from "react";
+import { View } from "react-native";
+import { Text } from "@/components/common/Text";
+import { Loader } from "@/components/Loader";
+import { Button } from "@/components/Button";
+import { Feather } from "@expo/vector-icons";
+import { RoundButton } from "@/components/RoundButton";
+
+import GoogleCast, {
+ CastButton,
+ CastContext,
+ CastState,
+ useCastDevice,
+ useCastState,
+ useDevices,
+ useMediaStatus,
+ useRemoteMediaClient,
+} from "react-native-google-cast";
+import { useCallback, useEffect } from "react";
+import { Platform } from "react-native";
+import { useRouter } from "expo-router";
+import { useHaptic } from "@/hooks/useHaptic";
+import ChromecastControls from "@/components/ChromecastControls";
+import { useTranslation } from "react-i18next";
+
+export default function Player() {
+ const castState = useCastState();
+
+ const client = useRemoteMediaClient();
+ const castDevice = useCastDevice();
+ const devices = useDevices();
+ const sessionManager = GoogleCast.getSessionManager();
+ const discoveryManager = GoogleCast.getDiscoveryManager();
+ const mediaStatus = useMediaStatus();
+
+ const [wasMediaPlaying, setWasMediaPlaying] = useState(false);
+ const reportPlaybackStopedRef = useRef(() => {});
+
+ useEffect(() => {
+ if (mediaStatus) return; // media currently playing
+
+ // media was just playing, report playback stopped
+ if (wasMediaPlaying) {
+ reportPlaybackStopedRef.current();
+ setWasMediaPlaying(false);
+ }
+ }, [mediaStatus, wasMediaPlaying]);
+
+ const router = useRouter();
+
+ const lightHapticFeedback = useHaptic("light");
+
+ const { t } = useTranslation();
+
+ useEffect(() => {
+ (async () => {
+ if (!discoveryManager) {
+ console.warn("DiscoveryManager is not initialized");
+ return;
+ }
+
+ await discoveryManager.startDiscovery();
+ })();
+ }, [client, devices, castDevice, sessionManager, discoveryManager]);
+
+ // Android requires the cast button to be present for startDiscovery to work
+ const AndroidCastButton = useCallback(
+ () =>
+ Platform.OS === "android" ? (
+
+ ) : (
+ <>>
+ ),
+ [Platform.OS]
+ );
+
+ const GoHomeButton = useCallback(
+ () => (
+ {
+ router.push("/(auth)/(home)/");
+ }}
+ >
+ {t("chromecast.go_home")}
+
+ ),
+ [router]
+ );
+
+ const ChromecastControlsMemoized = useMemo(() => {
+ if (!mediaStatus || !client) return undefined;
+ return (
+
+ );
+ }, [mediaStatus, client, setWasMediaPlaying]);
+
+ if (
+ castState === CastState.NO_DEVICES_AVAILABLE ||
+ castState === CastState.NOT_CONNECTED
+ ) {
+ // no devices to connect to
+ if (devices.length === 0) {
+ return (
+
+
+
+
+ {t("chromecast.no_devices_available")}
+
+
+ {t("chromecast.are_you_on_same_network")}
+
+
+
+
+
+
+ );
+ }
+ // no device selected
+ return (
+
+
+
+ {
+ lightHapticFeedback();
+ CastContext.showCastDialog();
+ }}
+ >
+
+
+
+
+ {t("chromecast.no_device_selected")}
+
+
+ {t("chromecast.click_icon_to_connect")}
+
+
+
+
+
+
+ );
+ }
+
+ if (castState === CastState.CONNECTING) {
+ return (
+
+
+ {t("chromecast.establishing_connection")}
+
+
+
+ );
+ }
+
+ // connected, but no media playing
+ if (!mediaStatus) {
+ return (
+
+
+
+ {t("chromecast.no_media_selected")}
+
+ {t("chromecast.start_playing")}
+
+
+
+
+
+ );
+ }
+
+ return ChromecastControlsMemoized;
+}
diff --git a/app/_layout.tsx b/app/_layout.tsx
index 79278b70..ad84ba0c 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -1,65 +1,44 @@
import "@/augmentations";
-import { ActionSheetProvider } from "@expo/react-native-action-sheet";
-import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
-import NetInfo from "@react-native-community/netinfo";
-import { DarkTheme, ThemeProvider } from "@react-navigation/native";
-import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
-import { onlineManager, QueryClient } from "@tanstack/react-query";
-import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
-import * as BackgroundTask from "expo-background-task";
-import * as Device from "expo-device";
import { Platform } from "react-native";
-import { GlobalModal } from "@/components/GlobalModal";
import i18n from "@/i18n";
import { DownloadProvider } from "@/providers/DownloadProvider";
-import { GlobalModalProvider } from "@/providers/GlobalModalProvider";
-import { IntroSheetProvider } from "@/providers/IntroSheetProvider";
import {
- apiAtom,
getOrSetDeviceId,
+ getTokenFromStorage,
JellyfinProvider,
} from "@/providers/JellyfinProvider";
-import { MusicPlayerProvider } from "@/providers/MusicPlayerProvider";
-import { NetworkStatusProvider } from "@/providers/NetworkStatusProvider";
+import { JobQueueProvider } from "@/providers/JobQueueProvider";
import { PlaySettingsProvider } from "@/providers/PlaySettingsProvider";
-import { ServerUrlProvider } from "@/providers/ServerUrlProvider";
import { WebSocketProvider } from "@/providers/WebSocketProvider";
-import { useSettings } from "@/utils/atoms/settings";
-import {
- BACKGROUND_FETCH_TASK,
- BACKGROUND_FETCH_TASK_SESSIONS,
- registerBackgroundFetchAsyncSessions,
-} from "@/utils/background-tasks";
-import {
- LogProvider,
- writeErrorLog,
- writeInfoLog,
- writeToLog,
-} from "@/utils/log";
+import { Settings, useSettings } from "@/utils/atoms/settings";
+import { BACKGROUND_FETCH_TASK } from "@/utils/background-tasks";
+import { LogProvider, writeToLog } from "@/utils/log";
import { storage } from "@/utils/mmkv";
-
+import { cancelJobById, getAllJobsByDeviceId } from "@/utils/optimize-server";
+import { ActionSheetProvider } from "@expo/react-native-action-sheet";
+import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
+const BackGroundDownloader = !Platform.isTV
+ ? require("@kesha-antonov/react-native-background-downloader")
+ : null;
+import { DarkTheme, ThemeProvider } from "@react-navigation/native";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+const BackgroundFetch = !Platform.isTV
+ ? require("expo-background-fetch")
+ : null;
+import * as FileSystem from "expo-file-system";
const Notifications = !Platform.isTV ? require("expo-notifications") : null;
-
-import { getSessionApi } from "@jellyfin/sdk/lib/utils/api/session-api";
-import { getLocales } from "expo-localization";
-import type { EventSubscription } from "expo-modules-core";
-import type {
- Notification,
- NotificationResponse,
-} from "expo-notifications/build/Notifications.types";
-import type { ExpoPushToken } from "expo-notifications/build/Tokens.types";
-import { Stack, useSegments } from "expo-router";
+import { router, Stack } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
-import * as TaskManager from "expo-task-manager";
-import { Provider as JotaiProvider, useAtom } from "jotai";
-import { useCallback, useEffect, useRef, useState } from "react";
+import * as ScreenOrientation from "@/packages/expo-screen-orientation";
+const TaskManager = !Platform.isTV ? require("expo-task-manager") : null;
+import { getLocales } from "expo-localization";
+import { Provider as JotaiProvider } from "jotai";
+import { useEffect, useRef } from "react";
import { I18nextProvider } from "react-i18next";
-import { Appearance } from "react-native";
+import { Appearance, AppState } from "react-native";
import { SystemBars } from "react-native-edge-to-edge";
import { GestureHandlerRootView } from "react-native-gesture-handler";
-import useRouter from "@/hooks/useAppRouter";
-import { userAtom } from "@/providers/JellyfinProvider";
-import { store } from "@/utils/store";
import "react-native-reanimated";
import { Toaster } from "sonner-native";
@@ -83,93 +62,164 @@ SplashScreen.setOptions({
});
function useNotificationObserver() {
- const router = useRouter();
+ if (Platform.isTV) return;
useEffect(() => {
- if (Platform.isTV) return;
-
let isMounted = true;
+ function redirect(notification: typeof Notifications.Notification) {
+ const url = notification.request.content.data?.url;
+ if (url) {
+ router.push(url);
+ }
+ }
+
Notifications.getLastNotificationResponseAsync().then(
(response: { notification: any }) => {
if (!isMounted || !response?.notification) {
return;
}
- const url = response?.notification.request.content.data?.url;
- if (url) {
- router.push(url);
- }
- },
+ redirect(response?.notification);
+ }
+ );
+
+ const subscription = Notifications.addNotificationResponseReceivedListener(
+ (response: { notification: any }) => {
+ redirect(response.notification);
+ }
);
return () => {
isMounted = false;
+ subscription.remove();
};
- }, [router]);
+ }, []);
}
if (!Platform.isTV) {
- TaskManager.defineTask(BACKGROUND_FETCH_TASK_SESSIONS, async () => {
- console.log("TaskManager ~ sessions trigger");
-
- const api = store.get(apiAtom);
- if (api === null || api === undefined) return;
-
- const response = await getSessionApi(api).getSessions({
- activeWithinSeconds: 360,
- });
-
- const result = response.data.filter((s) => s.NowPlayingItem);
- Notifications.setBadgeCountAsync(result.length);
-
- return BackgroundTask.BackgroundTaskResult.Success;
- });
-
TaskManager.defineTask(BACKGROUND_FETCH_TASK, async () => {
console.log("TaskManager ~ trigger");
- // Background fetch task placeholder - currently unused
- return BackgroundTask.BackgroundTaskResult.Success;
+
+ const now = Date.now();
+
+ const settingsData = storage.getString("settings");
+
+ if (!settingsData) return BackgroundFetch.BackgroundFetchResult.NoData;
+
+ const settings: Partial = JSON.parse(settingsData);
+ const url = settings?.optimizedVersionsServerUrl;
+
+ if (!settings?.autoDownload || !url)
+ return BackgroundFetch.BackgroundFetchResult.NoData;
+
+ const token = getTokenFromStorage();
+ const deviceId = getOrSetDeviceId();
+ const baseDirectory = FileSystem.documentDirectory;
+
+ if (!token || !deviceId || !baseDirectory)
+ return BackgroundFetch.BackgroundFetchResult.NoData;
+
+ const jobs = await getAllJobsByDeviceId({
+ deviceId,
+ authHeader: token,
+ url,
+ });
+
+ console.log("TaskManager ~ Active jobs: ", jobs.length);
+
+ for (let job of jobs) {
+ if (job.status === "completed") {
+ const downloadUrl = url + "download/" + job.id;
+ const tasks = await BackGroundDownloader.checkForExistingDownloads();
+
+ if (tasks.find((task: { id: string }) => task.id === job.id)) {
+ console.log("TaskManager ~ Download already in progress: ", job.id);
+ continue;
+ }
+
+ BackGroundDownloader.download({
+ id: job.id,
+ url: downloadUrl,
+ destination: `${baseDirectory}${job.item.Id}.mp4`,
+ headers: {
+ Authorization: token,
+ },
+ })
+ .begin(() => {
+ console.log("TaskManager ~ Download started: ", job.id);
+ })
+ .done(() => {
+ console.log("TaskManager ~ Download completed: ", job.id);
+ saveDownloadedItemInfo(job.item);
+ BackGroundDownloader.completeHandler(job.id);
+ cancelJobById({
+ authHeader: token,
+ id: job.id,
+ url: url,
+ });
+ Notifications.scheduleNotificationAsync({
+ content: {
+ title: job.item.Name,
+ body: "Download completed",
+ data: {
+ url: `/downloads`,
+ },
+ },
+ trigger: null,
+ });
+ })
+ .error((error: any) => {
+ console.log("TaskManager ~ Download error: ", job.id, error);
+ BackGroundDownloader.completeHandler(job.id);
+ Notifications.scheduleNotificationAsync({
+ content: {
+ title: job.item.Name,
+ body: "Download failed",
+ data: {
+ url: `/downloads`,
+ },
+ },
+ trigger: null,
+ });
+ });
+ }
+ }
+
+ console.log(`Auto download started: ${new Date(now).toISOString()}`);
+
+ // Be sure to return the successful result type!
+ return BackgroundFetch.BackgroundFetchResult.NewData;
});
}
const checkAndRequestPermissions = async () => {
try {
const hasAskedBefore = storage.getString(
- "hasAskedForNotificationPermission",
+ "hasAskedForNotificationPermission"
);
- let granted = false;
+
if (hasAskedBefore !== "true") {
const { status } = await Notifications.requestPermissionsAsync();
- granted = status === "granted";
- if (granted) {
+
+ if (status === "granted") {
writeToLog("INFO", "Notification permissions granted.");
console.log("Notification permissions granted.");
} else {
writeToLog("ERROR", "Notification permissions denied.");
console.log("Notification permissions denied.");
}
+
storage.set("hasAskedForNotificationPermission", "true");
} else {
- // Already asked before, check current status
- const { status } = await Notifications.getPermissionsAsync();
- granted = status === "granted";
- if (!granted) {
- writeToLog(
- "ERROR",
- "Notification permissions denied (already asked before).",
- );
- console.log("Notification permissions denied (already asked before).");
- }
+ console.log("Already asked for notification permissions before.");
}
- return granted;
} catch (error) {
writeToLog(
"ERROR",
"Error checking/requesting notification permissions:",
- error,
+ error
);
console.error("Error checking/requesting notification permissions:", error);
- return false;
}
};
@@ -189,273 +239,150 @@ export default function RootLayout() {
);
}
-// Set up online manager for network-aware query behavior
-onlineManager.setEventListener((setOnline) => {
- return NetInfo.addEventListener((state) => {
- setOnline(!!state.isConnected);
- });
-});
-
const queryClient = new QueryClient({
defaultOptions: {
queries: {
- staleTime: 0, // Always stale - triggers background refetch on mount
- gcTime: 1000 * 60 * 60 * 24, // 24 hours - keep in cache for offline
- networkMode: "offlineFirst", // Return cache first, refetch if online
- refetchOnMount: true, // Refetch when component mounts
- refetchOnReconnect: true, // Refetch when network reconnects
- refetchOnWindowFocus: false, // Not needed for mobile
- retry: (failureCount) => {
- if (!onlineManager.isOnline()) return false;
- return failureCount < 3;
- },
+ staleTime: 0,
+ refetchOnMount: true,
+ refetchOnReconnect: true,
+ refetchOnWindowFocus: true,
+ retryOnMount: true,
},
- mutations: {
- networkMode: "online", // Only run mutations when online
- },
- },
-});
-
-// Create MMKV-based persister for offline support
-const mmkvPersister = createSyncStoragePersister({
- storage: {
- getItem: (key) => storage.getString(key) ?? null,
- setItem: (key, value) => storage.set(key, value),
- removeItem: (key) => storage.remove(key),
},
});
function Layout() {
- const { settings } = useSettings();
- const [user] = useAtom(userAtom);
- const [api] = useAtom(apiAtom);
- const _segments = useSegments();
- const router = useRouter();
+ const [settings] = useSettings();
+ const appState = useRef(AppState.currentState);
useEffect(() => {
i18n.changeLanguage(
- settings?.preferedLanguage ?? getLocales()[0].languageCode ?? "en",
+ settings?.preferedLanguage ?? getLocales()[0].languageCode ?? "en"
);
}, [settings?.preferedLanguage, i18n]);
- useNotificationObserver();
+ if (!Platform.isTV) {
+ useNotificationObserver();
- const [expoPushToken, setExpoPushToken] = useState();
- const notificationListener = useRef(null);
- const responseListener = useRef(null);
+ useEffect(() => {
+ checkAndRequestPermissions();
+ }, []);
- useEffect(() => {
- if (!Platform.isTV && expoPushToken && api && user) {
- api
- ?.post("/Streamyfin/device", {
- token: expoPushToken.data,
- deviceId: getOrSetDeviceId(),
- userId: user.Id,
- })
- .then((_) => console.log("Posted expo push token"))
- .catch((_) =>
- writeErrorLog("Failed to push expo push token to plugin"),
+ useEffect(() => {
+ // If the user has auto rotate enabled, unlock the orientation
+ if (Platform.isTV) return;
+ if (settings.autoRotate === true) {
+ ScreenOrientation.unlockAsync();
+ } else {
+ // If the user has auto rotate disabled, lock the orientation to portrait
+ ScreenOrientation.lockAsync(
+ ScreenOrientation.OrientationLock.PORTRAIT_UP
);
- } else console.log("No token available");
- }, [api, expoPushToken, user]);
+ }
+ }, [settings]);
- const registerNotifications = useCallback(async () => {
- if (Platform.OS === "android") {
- console.log("Setting android notification channel 'default'");
- await Notifications?.setNotificationChannelAsync("default", {
- name: "default",
- });
-
- // Create dedicated channel for download notifications
- console.log("Setting android notification channel 'downloads'");
- await Notifications?.setNotificationChannelAsync("downloads", {
- name: "Downloads",
- importance: Notifications.AndroidImportance.DEFAULT,
- vibrationPattern: [0, 250, 250, 250],
- lightColor: "#FF231F7C",
- });
- }
-
- const granted = await checkAndRequestPermissions();
- if (!granted) {
- console.log(
- "Notification permissions not granted, skipping background fetch and push token registration.",
- );
- return;
- }
-
- if (!Platform.isTV && user && user.Policy?.IsAdministrator) {
- await registerBackgroundFetchAsyncSessions();
- }
-
- // only create push token for real devices (pointless for emulators)
- if (Device.isDevice) {
- Notifications?.getExpoPushTokenAsync({
- projectId: "e79219d1-797f-4fbe-9fa1-cfd360690a68",
- })
- .then((token: ExpoPushToken) => {
- if (token) {
- console.log("Expo push token obtained:", token.data);
- setExpoPushToken(token);
+ useEffect(() => {
+ const subscription = AppState.addEventListener(
+ "change",
+ (nextAppState) => {
+ if (
+ appState.current.match(/inactive|background/) &&
+ nextAppState === "active"
+ ) {
+ BackGroundDownloader.checkForExistingDownloads();
}
- })
- .catch((reason: any) => {
- console.error("Failed to get push token:", reason);
- writeErrorLog("Failed to get Expo push token", reason);
- });
- }
- }, [user]);
+ }
+ );
- useEffect(() => {
- if (!Platform.isTV) {
- void registerNotifications();
-
- notificationListener.current =
- Notifications?.addNotificationReceivedListener(
- (notification: Notification) => {
- console.log(
- "Notification received while app running",
- notification,
- );
- },
- );
-
- responseListener.current =
- Notifications?.addNotificationResponseReceivedListener(
- (response: NotificationResponse) => {
- // Currently the notifications supported by the plugin will send data for deep links.
- const { title, data } = response.notification.request.content;
- writeInfoLog(`Notification ${title} opened`, data);
-
- let url: any;
- const type = (data?.type ?? "").toString().toLowerCase();
- const itemId = data?.id;
-
- switch (type) {
- case "movie":
- url = `/(auth)/(tabs)/home/items/page?id=${itemId}`;
- break;
- case "episode":
- // `/(auth)/(tabs)/${from}/items/page?id=${item.Id}`;
- // We just clicked a notification for an individual episode.
- if (itemId) {
- url = `/(auth)/(tabs)/home/items/page?id=${itemId}`;
- // summarized season notification for multiple episodes. Bring them to series season
- } else {
- const seriesId = data.seriesId;
- const seasonIndex = data.seasonIndex;
- if (seasonIndex) {
- url = `/(auth)/(tabs)/home/series/${seriesId}?seasonIndex=${seasonIndex}`;
- } else {
- url = `/(auth)/(tabs)/home/series/${seriesId}`;
- }
- }
- break;
- }
-
- writeInfoLog(`Notification attempting to redirect to ${url}`);
- if (url) {
- router.push(url);
- }
- },
- );
+ BackGroundDownloader.checkForExistingDownloads();
return () => {
- notificationListener.current?.remove();
- responseListener.current?.remove();
+ subscription.remove();
};
- }
- }, [user]);
+ }, []);
+ }
return (
- {
- // Only persist successful queries
- return query.state.status === "success";
- },
- },
- }}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- null,
- }}
- />
- null,
- }}
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ null,
+ }}
+ />
+ null,
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
+
+function saveDownloadedItemInfo(item: BaseItemDto) {
+ try {
+ const downloadedItems = storage.getString("downloadedItems");
+ let items: BaseItemDto[] = downloadedItems
+ ? JSON.parse(downloadedItems)
+ : [];
+
+ const existingItemIndex = items.findIndex((i) => i.Id === item.Id);
+ if (existingItemIndex !== -1) {
+ items[existingItemIndex] = item;
+ } else {
+ items.push(item);
+ }
+
+ storage.set("downloadedItems", JSON.stringify(items));
+ } catch (error) {
+ writeToLog("ERROR", "Failed to save downloaded item information:", error);
+ console.error("Failed to save downloaded item information:", error);
+ }
+}
diff --git a/app/login.tsx b/app/login.tsx
index 33d06d41..220c6b97 100644
--- a/app/login.tsx
+++ b/app/login.tsx
@@ -1,34 +1,28 @@
-import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
-import type { PublicSystemInfo } from "@jellyfin/sdk/lib/generated-client";
-import { Image } from "expo-image";
-import { useLocalSearchParams, useNavigation } from "expo-router";
-import { t } from "i18next";
-import { useAtomValue } from "jotai";
-import { useCallback, useEffect, useState } from "react";
-import {
- Alert,
- Keyboard,
- KeyboardAvoidingView,
- Platform,
- Switch,
- TouchableOpacity,
- View,
-} from "react-native";
-import { SafeAreaView } from "react-native-safe-area-context";
-import { z } from "zod";
import { Button } from "@/components/Button";
import { Input } from "@/components/common/Input";
import { Text } from "@/components/common/Text";
import JellyfinServerDiscovery from "@/components/JellyfinServerDiscovery";
import { PreviousServersList } from "@/components/PreviousServersList";
-import { SaveAccountModal } from "@/components/SaveAccountModal";
import { Colors } from "@/constants/Colors";
import { apiAtom, useJellyfin } from "@/providers/JellyfinProvider";
-import type {
- AccountSecurityType,
- SavedServer,
-} from "@/utils/secureCredentials";
+import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
+import { PublicSystemInfo } from "@jellyfin/sdk/lib/generated-client";
+import { Image } from "expo-image";
+import { useLocalSearchParams, useNavigation } from "expo-router";
+import { useAtom, useAtomValue } from "jotai";
+import React, { useCallback, useEffect, useState } from "react";
+import {
+ Alert,
+ KeyboardAvoidingView,
+ Platform,
+ SafeAreaView,
+ TouchableOpacity,
+ View,
+} from "react-native";
+import { Keyboard } from "react-native";
+import { z } from "zod";
+import { t } from "i18next";
const CredentialsSchema = z.object({
username: z.string().min(1, t("login.username_required")),
});
@@ -37,14 +31,8 @@ const Login: React.FC = () => {
const api = useAtomValue(apiAtom);
const navigation = useNavigation();
const params = useLocalSearchParams();
- const {
- setServer,
- login,
- removeServer,
- initiateQuickConnect,
- loginWithSavedCredential,
- loginWithPassword,
- } = useJellyfin();
+ const { setServer, login, removeServer, initiateQuickConnect } =
+ useJellyfin();
const {
apiUrl: _apiUrl,
@@ -54,41 +42,32 @@ const Login: React.FC = () => {
const [loadingServerCheck, setLoadingServerCheck] = useState(false);
const [loading, setLoading] = useState(false);
- const [serverURL, setServerURL] = useState(_apiUrl || "");
+ const [serverURL, setServerURL] = useState(_apiUrl);
const [serverName, setServerName] = useState("");
const [credentials, setCredentials] = useState<{
username: string;
password: string;
}>({
- username: _username || "",
- password: _password || "",
+ username: _username,
+ password: _password,
});
- // Save account state
- const [saveAccount, setSaveAccount] = useState(false);
- const [showSaveModal, setShowSaveModal] = useState(false);
- const [pendingLogin, setPendingLogin] = useState<{
- username: string;
- password: string;
- } | null>(null);
-
/**
* A way to auto login based on a link
*/
useEffect(() => {
(async () => {
if (_apiUrl) {
- await setServer({
+ setServer({
address: _apiUrl,
});
- // Wait for server setup and state updates to complete
setTimeout(() => {
if (_username && _password) {
setCredentials({ username: _username, password: _password });
login(_username, _password);
}
- }, 0);
+ }, 300);
}
})();
}, [_apiUrl, _username, _password]);
@@ -102,10 +81,10 @@ const Login: React.FC = () => {
onPress={() => {
removeServer();
}}
- className='flex flex-row items-center pr-2 pl-1'
+ className="flex flex-row items-center"
>
-
-
+
+
{t("login.change_server")}
@@ -116,83 +95,23 @@ const Login: React.FC = () => {
const handleLogin = async () => {
Keyboard.dismiss();
- const result = CredentialsSchema.safeParse(credentials);
- if (!result.success) return;
-
- if (saveAccount) {
- // Show save account modal to choose security type
- setPendingLogin({
- username: credentials.username,
- password: credentials.password,
- });
- setShowSaveModal(true);
- } else {
- // Login without saving
- await performLogin(credentials.username, credentials.password);
- }
- };
-
- const performLogin = async (
- username: string,
- password: string,
- options?: {
- saveAccount?: boolean;
- securityType?: AccountSecurityType;
- pinCode?: string;
- },
- ) => {
setLoading(true);
try {
- await login(username, password, serverName, options);
+ const result = CredentialsSchema.safeParse(credentials);
+ if (result.success) {
+ await login(credentials.username, credentials.password);
+ }
} catch (error) {
if (error instanceof Error) {
Alert.alert(t("login.connection_failed"), error.message);
} else {
Alert.alert(
t("login.connection_failed"),
- t("login.an_unexpected_error_occured"),
+ t("login.an_unexpected_error_occured")
);
}
} finally {
setLoading(false);
- setPendingLogin(null);
- }
- };
-
- const handleSaveAccountConfirm = async (
- securityType: AccountSecurityType,
- pinCode?: string,
- ) => {
- setShowSaveModal(false);
- if (pendingLogin) {
- await performLogin(pendingLogin.username, pendingLogin.password, {
- saveAccount: true,
- securityType,
- pinCode,
- });
- }
- };
-
- const handleQuickLoginWithSavedCredential = async (
- serverUrl: string,
- userId: string,
- ) => {
- await loginWithSavedCredential(serverUrl, userId);
- };
-
- const handlePasswordLogin = async (
- serverUrl: string,
- username: string,
- password: string,
- ) => {
- await loginWithPassword(serverUrl, username, password);
- };
-
- const handleAddAccount = (server: SavedServer) => {
- // Server is already selected, go to credential entry
- setServer({ address: server.address });
- if (server.name) {
- setServerName(server.name);
}
};
@@ -213,52 +132,27 @@ const Login: React.FC = () => {
*/
const checkUrl = useCallback(async (url: string) => {
setLoadingServerCheck(true);
- const baseUrl = url.replace(/^https?:\/\//i, "");
- const protocols = ["https", "http"];
+
try {
- return checkHttp(baseUrl, protocols);
- } catch (e) {
- if (e instanceof Error && e.message === "Server too old") {
- throw e;
+ const response = await fetch(`${url}/System/Info/Public`, {
+ mode: "cors",
+ });
+
+ if (response.ok) {
+ const data = (await response.json()) as PublicSystemInfo;
+
+ setServerName(data.ServerName || "");
+ return url;
}
+
+ return undefined;
+ } catch {
return undefined;
} finally {
setLoadingServerCheck(false);
}
}, []);
- async function checkHttp(baseUrl: string, protocols: string[]) {
- for (const protocol of protocols) {
- try {
- const response = await fetch(
- `${protocol}://${baseUrl}/System/Info/Public`,
- {
- mode: "cors",
- },
- );
- if (response.ok) {
- const data = (await response.json()) as PublicSystemInfo;
- const serverVersion = data.Version?.split(".");
- if (serverVersion && +serverVersion[0] <= 10) {
- if (+serverVersion[1] < 10) {
- Alert.alert(
- t("login.too_old_server_text"),
- t("login.too_old_server_description"),
- );
- throw new Error("Server too old");
- }
- }
- setServerName(data.ServerName || "");
- return `${protocol}://${baseUrl}`;
- }
- } catch (e) {
- if (e instanceof Error && e.message === "Server too old") {
- throw e;
- }
- }
- }
- return undefined;
- }
/**
* Handles the connection attempt to a Jellyfin server.
*
@@ -277,17 +171,17 @@ const Login: React.FC = () => {
*/
const handleConnect = useCallback(async (url: string) => {
url = url.trim().replace(/\/$/, "");
- try {
- const result = await checkUrl(url);
- if (result === undefined) {
- Alert.alert(
- t("login.connection_failed"),
- t("login.could_not_connect_to_server"),
- );
- return;
- }
- await setServer({ address: result });
- } catch {}
+ const result = await checkUrl(url);
+
+ if (result === undefined) {
+ Alert.alert(
+ t("login.connection_failed"),
+ t("login.could_not_connect_to_server")
+ );
+ return;
+ }
+
+ setServer({ address: url });
}, []);
const handleQuickConnect = async () => {
@@ -301,358 +195,154 @@ const Login: React.FC = () => {
{
text: t("login.got_it"),
},
- ],
+ ]
);
}
- } catch (_error) {
+ } catch (error) {
Alert.alert(
t("login.error_title"),
- t("login.failed_to_initiate_quick_connect"),
+ t("login.failed_to_initiate_quick_connect")
);
}
};
- return Platform.isTV ? (
- // TV layout
-
+ return (
+
{api?.basePath ? (
- // ------------ Username/Password view ------------
-
- {/* Safe centered column with max width so TV doesn’t stretch too far */}
-
-
- {serverName ? (
- <>
- {`${t("login.login_to_title")} `}
- {serverName}
- >
- ) : (
- t("login.login_title")
- )}
-
-
- {api.basePath}
-
+ <>
+
+
+
+
+ <>
+ {serverName ? (
+ <>
+ {t("login.login_to_title") + " "}
+ {serverName}
+ >
+ ) : (
+ t("login.login_title")
+ )}
+ >
+
+
+ {api.basePath}
+
+
+ setCredentials({ ...credentials, username: text })
+ }
+ value={credentials.username}
+ secureTextEntry={false}
+ keyboardType="default"
+ returnKeyType="done"
+ autoCapitalize="none"
+ textContentType="username"
+ clearButtonMode="while-editing"
+ maxLength={500}
+ />
- {/* Username */}
-
- setCredentials((prev) => ({ ...prev, username: text }))
- }
- onEndEditing={(e) => {
- const newValue = e.nativeEvent.text;
- if (newValue && newValue !== credentials.username) {
- setCredentials((prev) => ({ ...prev, username: newValue }));
- }
- }}
- value={credentials.username}
- keyboardType='default'
- returnKeyType='done'
- autoCapitalize='none'
- autoCorrect={false}
- textContentType='username'
- clearButtonMode='while-editing'
- maxLength={500}
- extraClassName='mb-4'
- autoFocus={false}
- blurOnSubmit={true}
- />
-
- {/* Password */}
-
- setCredentials((prev) => ({ ...prev, password: text }))
- }
- onEndEditing={(e) => {
- const newValue = e.nativeEvent.text;
- if (newValue && newValue !== credentials.password) {
- setCredentials((prev) => ({ ...prev, password: newValue }));
- }
- }}
- value={credentials.password}
- secureTextEntry
- keyboardType='default'
- returnKeyType='done'
- autoCapitalize='none'
- textContentType='password'
- clearButtonMode='while-editing'
- maxLength={500}
- extraClassName='mb-4'
- autoFocus={false}
- blurOnSubmit={true}
- />
-
-
-
- {t("login.login_button")}
-
-
-
-
- {t("login.quick_connect")}
-
+
+ setCredentials({ ...credentials, password: text })
+ }
+ value={credentials.password}
+ secureTextEntry
+ keyboardType="default"
+ returnKeyType="done"
+ autoCapitalize="none"
+ textContentType="password"
+ clearButtonMode="while-editing"
+ maxLength={500}
+ />
+
+
+ {t("login.login_button")}
+
+
+
+
+
+
+
+
-
+ >
) : (
- // ------------ Server connect view ------------
-
-
-
+ <>
+
+
+ Streamyfin
+
+ {t("server.enter_url_to_jellyfin_server")}
+
+
-
-
-
- Streamyfin
-
-
- {t("server.enter_url_to_jellyfin_server")}
-
-
- {/* Full-width Input with clear focus ring */}
-
-
- {/* Full-width primary button */}
-
{
await handleConnect(serverURL);
}}
+ className="w-full grow"
>
{t("server.connect_button")}
-
-
- {/* Lists stay full width but inside max width container */}
-
{
+ onServerSelect={(server) => {
setServerURL(server.address);
- if (server.serverName) setServerName(server.serverName);
- await handleConnect(server.address);
+ if (server.serverName) {
+ setServerName(server.serverName);
+ }
+ handleConnect(server.address);
}}
/>
{
- await handleConnect(s.address);
+ onServerSelect={(s) => {
+ handleConnect(s.address);
}}
- onQuickLogin={handleQuickLoginWithSavedCredential}
- onPasswordLogin={handlePasswordLogin}
- onAddAccount={handleAddAccount}
/>
-
+ >
)}
- ) : (
- // Mobile layout
-
-
- {api?.basePath ? (
-
-
-
-
- {serverName ? (
- <>
- {`${t("login.login_to_title")} `}
- {serverName}
- >
- ) : (
- t("login.login_title")
- )}
-
- {api.basePath}
-
- setCredentials((prev) => ({ ...prev, username: text }))
- }
- onEndEditing={(e) => {
- const newValue = e.nativeEvent.text;
- if (newValue && newValue !== credentials.username) {
- setCredentials((prev) => ({
- ...prev,
- username: newValue,
- }));
- }
- }}
- value={credentials.username}
- keyboardType='default'
- returnKeyType='done'
- autoCapitalize='none'
- autoCorrect={false}
- textContentType='username'
- clearButtonMode='while-editing'
- maxLength={500}
- />
-
-
- setCredentials((prev) => ({ ...prev, password: text }))
- }
- onEndEditing={(e) => {
- const newValue = e.nativeEvent.text;
- if (newValue && newValue !== credentials.password) {
- setCredentials((prev) => ({
- ...prev,
- password: newValue,
- }));
- }
- }}
- value={credentials.password}
- secureTextEntry
- keyboardType='default'
- returnKeyType='done'
- autoCapitalize='none'
- textContentType='password'
- clearButtonMode='while-editing'
- maxLength={500}
- />
- setSaveAccount(!saveAccount)}
- className='flex flex-row items-center py-2'
- activeOpacity={0.7}
- >
-
-
- {t("save_account.save_for_later")}
-
-
-
-
- {t("login.login_button")}
-
-
-
-
-
-
-
-
-
-
- ) : (
-
-
-
- Streamyfin
-
- {t("server.enter_url_to_jellyfin_server")}
-
-
- {
- await handleConnect(serverURL);
- }}
- className='w-full grow'
- >
- {t("server.connect_button")}
-
- {
- setServerURL(server.address);
- if (server.serverName) {
- setServerName(server.serverName);
- }
- await handleConnect(server.address);
- }}
- />
- {
- await handleConnect(s.address);
- }}
- onQuickLogin={handleQuickLoginWithSavedCredential}
- onPasswordLogin={handlePasswordLogin}
- onAddAccount={handleAddAccount}
- />
-
-
- )}
-
-
- {/* Save Account Modal */}
- {
- setShowSaveModal(false);
- setPendingLogin(null);
- }}
- onSave={handleSaveAccountConfirm}
- username={pendingLogin?.username || credentials.username}
- />
-
);
};
diff --git a/assets/images/adaptive_icon.png b/assets/images/adaptive_icon.png
new file mode 100644
index 00000000..34a6f5d7
Binary files /dev/null and b/assets/images/adaptive_icon.png differ
diff --git a/bun.lock b/bun.lock
index bdcea301..b3c75406 100644
--- a/bun.lock
+++ b/bun.lock
@@ -1,178 +1,194 @@
{
"lockfileVersion": 1,
- "configVersion": 0,
"workspaces": {
"": {
"name": "streamyfin",
"dependencies": {
- "@bottom-tabs/react-navigation": "1.1.0",
- "@douglowder/expo-av-route-picker-view": "^0.0.5",
- "@expo/metro-runtime": "~6.1.1",
- "@expo/react-native-action-sheet": "^4.1.1",
- "@expo/ui": "0.2.0-beta.9",
- "@expo/vector-icons": "^15.0.3",
- "@gorhom/bottom-sheet": "5.2.8",
- "@jellyfin/sdk": "^0.13.0",
- "@react-native-community/netinfo": "^11.4.1",
- "@react-navigation/material-top-tabs": "7.4.9",
+ "@bottom-tabs/react-navigation": "0.8.6",
+ "@config-plugins/ffmpeg-kit-react-native": "^9.0.0",
+ "@expo/config-plugins": "~9.0.15",
+ "@expo/react-native-action-sheet": "^4.1.0",
+ "@expo/vector-icons": "^14.0.4",
+ "@futurejj/react-native-visibility-sensor": "^1.3.10",
+ "@gorhom/bottom-sheet": "^5.1.0",
+ "@jellyfin/sdk": "^0.11.0",
+ "@kesha-antonov/react-native-background-downloader": "3.2.6",
+ "@react-native-community/netinfo": "11.4.1",
+ "@react-native-menu/menu": "^1.2.2",
+ "@react-navigation/bottom-tabs": "^7.2.0",
+ "@react-navigation/material-top-tabs": "^7.1.0",
"@react-navigation/native": "^7.0.14",
- "@shopify/flash-list": "2.0.2",
- "@tanstack/query-sync-storage-persister": "^5.90.18",
- "@tanstack/react-pacer": "^0.19.1",
- "@tanstack/react-query": "5.90.17",
- "@tanstack/react-query-persist-client": "^5.90.18",
+ "@shopify/flash-list": "1.7.3",
+ "@tanstack/react-query": "^5.66.0",
+ "add": "^2.0.6",
"axios": "^1.7.9",
- "expo": "~54.0.31",
- "expo-application": "~7.0.8",
- "expo-asset": "~12.0.12",
- "expo-background-task": "~1.0.10",
- "expo-blur": "~15.0.8",
- "expo-brightness": "~14.0.8",
- "expo-build-properties": "~1.0.10",
- "expo-constants": "18.0.13",
- "expo-crypto": "^15.0.8",
- "expo-dev-client": "~6.0.20",
- "expo-device": "~8.0.10",
- "expo-font": "~14.0.10",
- "expo-haptics": "~15.0.8",
- "expo-image": "~3.0.11",
- "expo-linear-gradient": "~15.0.8",
- "expo-linking": "~8.0.11",
- "expo-localization": "~17.0.8",
- "expo-location": "^19.0.8",
- "expo-notifications": "~0.32.16",
- "expo-router": "~6.0.21",
- "expo-screen-orientation": "~9.0.8",
- "expo-secure-store": "^15.0.8",
- "expo-sharing": "~14.0.8",
- "expo-splash-screen": "~31.0.13",
- "expo-status-bar": "~3.0.9",
- "expo-system-ui": "~6.0.9",
- "expo-task-manager": "14.0.9",
- "expo-web-browser": "~15.0.10",
- "i18next": "^25.0.0",
- "jotai": "2.16.2",
- "lodash": "4.17.21",
+ "expo": "^52.0.31",
+ "expo-asset": "~11.0.3",
+ "expo-background-fetch": "~13.0.5",
+ "expo-blur": "~14.0.3",
+ "expo-brightness": "~13.0.3",
+ "expo-build-properties": "~0.13.2",
+ "expo-constants": "~17.0.5",
+ "expo-crypto": "~14.0.2",
+ "expo-dev-client": "~5.0.11",
+ "expo-device": "~7.0.2",
+ "expo-font": "~13.0.3",
+ "expo-haptics": "~14.0.1",
+ "expo-image": "~2.0.4",
+ "expo-keep-awake": "~14.0.2",
+ "expo-linear-gradient": "~14.0.2",
+ "expo-linking": "~7.0.5",
+ "expo-localization": "~16.0.1",
+ "expo-network": "~7.0.5",
+ "expo-notifications": "~0.29.13",
+ "expo-router": "~4.0.17",
+ "expo-screen-orientation": "~8.0.4",
+ "expo-sensors": "~14.0.2",
+ "expo-splash-screen": "~0.29.22",
+ "expo-status-bar": "~2.0.1",
+ "expo-system-ui": "~4.0.8",
+ "expo-task-manager": "~12.0.5",
+ "expo-updates": "~0.26.17",
+ "expo-web-browser": "~14.0.2",
+ "ffmpeg-kit-react-native": "^6.0.2",
+ "i18next": "^24.2.2",
+ "jotai": "^2.11.3",
+ "lodash": "^4.17.21",
"nativewind": "^2.0.11",
- "patch-package": "^8.0.0",
- "react": "19.1.0",
- "react-dom": "19.1.0",
- "react-i18next": "16.5.3",
- "react-native": "0.81.5",
+ "react": "18.3.1",
+ "react-dom": "18.3.1",
+ "react-i18next": "^15.4.0",
+ "react-native": "npm:react-native-tvos@~0.77.0-0",
"react-native-awesome-slider": "^2.9.0",
- "react-native-bottom-tabs": "1.1.0",
+ "react-native-bottom-tabs": "0.8.6",
"react-native-circular-progress": "^1.4.1",
- "react-native-collapsible": "^1.6.2",
+ "react-native-compressor": "^1.10.3",
"react-native-country-flag": "^2.0.2",
- "react-native-device-info": "^15.0.0",
- "react-native-draggable-flatlist": "^4.0.3",
- "react-native-edge-to-edge": "^1.7.0",
- "react-native-gesture-handler": "2.28.0",
- "react-native-glass-effect-view": "^1.0.0",
- "react-native-google-cast": "^4.9.1",
+ "react-native-device-info": "^14.0.4",
+ "react-native-edge-to-edge": "^1.4.3",
+ "react-native-gesture-handler": "2.22.0",
+ "react-native-get-random-values": "^1.11.0",
+ "react-native-google-cast": "^4.8.3",
"react-native-image-colors": "^2.4.0",
- "react-native-ios-context-menu": "^3.2.1",
- "react-native-ios-utilities": "5.2.0",
- "react-native-mmkv": "4.1.1",
- "react-native-nitro-modules": "0.33.1",
- "react-native-pager-view": "^6.9.1",
- "react-native-reanimated": "~4.1.1",
- "react-native-reanimated-carousel": "4.0.3",
- "react-native-safe-area-context": "~5.6.0",
- "react-native-screens": "~4.18.0",
- "react-native-svg": "15.12.1",
- "react-native-text-ticker": "^1.15.0",
- "react-native-track-player": "github:lovegaoshi/react-native-track-player#APM",
+ "react-native-ios-context-menu": "^3.1.0",
+ "react-native-ios-utilities": "5.1.1",
+ "react-native-mmkv": "^2.12.2",
+ "react-native-pager-view": "6.5.1",
+ "react-native-progress": "^5.0.1",
+ "react-native-reanimated": "~3.16.7",
+ "react-native-reanimated-carousel": "3.5.1",
+ "react-native-safe-area-context": "5.1.0",
+ "react-native-screens": "~4.5.0",
+ "react-native-svg": "15.11.1",
+ "react-native-tab-view": "^4.0.5",
"react-native-udp": "^4.1.7",
+ "react-native-uitextview": "^1.4.0",
"react-native-url-polyfill": "^2.0.0",
"react-native-uuid": "^2.0.3",
+ "react-native-video": "6.10.0",
"react-native-volume-manager": "^2.0.8",
- "react-native-web": "^0.21.0",
- "react-native-worklets": "0.5.1",
- "sonner-native": "0.21.2",
+ "react-native-web": "~0.19.13",
+ "react-native-webview": "13.13.2",
+ "sonner-native": "^0.17.0",
"tailwindcss": "3.3.2",
"use-debounce": "^10.0.4",
- "zod": "4.1.13",
+ "uuid": "^11.0.5",
+ "zeego": "^2.0.4",
+ "zod": "^3.24.1",
},
"devDependencies": {
- "@babel/core": "7.28.6",
- "@biomejs/biome": "2.3.11",
- "@react-native-community/cli": "20.1.0",
- "@react-native-tvos/config-tv": "0.1.4",
- "@types/jest": "29.5.14",
- "@types/lodash": "4.17.23",
- "@types/react": "19.1.17",
- "@types/react-test-renderer": "19.1.0",
- "cross-env": "10.1.0",
- "expo-doctor": "1.17.14",
- "husky": "9.1.7",
- "lint-staged": "16.2.7",
- "react-test-renderer": "19.2.3",
- "typescript": "5.9.3",
+ "@babel/core": "^7.26.8",
+ "@react-native-community/cli": "15.1.3",
+ "@react-native-tvos/config-tv": "^0.1.1",
+ "@types/jest": "^29.5.14",
+ "@types/lodash": "^4.17.15",
+ "@types/react": "~18.3.12",
+ "@types/react-native-vector-icons": "^6.4.18",
+ "@types/react-test-renderer": "^19.0.0",
+ "@types/uuid": "^10.0.0",
+ "patch-package": "^8.0.0",
+ "postinstall-postinstall": "^2.1.0",
+ "react-test-renderer": "19.0.0",
+ "typescript": "~5.7.3",
},
},
},
- "overrides": {
- "expo-constants": "18.0.13",
- },
"packages": {
- "@0no-co/graphql.web": ["@0no-co/graphql.web@1.2.0", "", { "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" }, "optionalPeers": ["graphql"] }, "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw=="],
+ "@0no-co/graphql.web": ["@0no-co/graphql.web@1.1.1", "", { "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" }, "optionalPeers": ["graphql"] }, "sha512-F2i3xdycesw78QCOBHmpTn7eaD2iNXGwB2gkfwxcOfBbeauYpr8RBSyJOkDrFtKtVRMclg8Sg3n1ip0ACyUuag=="],
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
- "@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="],
+ "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
- "@babel/compat-data": ["@babel/compat-data@7.28.6", "", {}, "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg=="],
+ "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
- "@babel/core": ["@babel/core@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw=="],
+ "@babel/compat-data": ["@babel/compat-data@7.26.8", "", {}, "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ=="],
- "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="],
+ "@babel/core": ["@babel/core@7.26.9", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.9", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", "@babel/helpers": "^7.26.9", "@babel/parser": "^7.26.9", "@babel/template": "^7.26.9", "@babel/traverse": "^7.26.9", "@babel/types": "^7.26.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw=="],
- "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
+ "@babel/generator": ["@babel/generator@7.26.9", "", { "dependencies": { "@babel/parser": "^7.26.9", "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg=="],
- "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
+ "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="],
- "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="],
+ "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.26.5", "", { "dependencies": { "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA=="],
- "@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="],
+ "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.26.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/traverse": "^7.26.9", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg=="],
- "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="],
+ "@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.26.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong=="],
- "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
+ "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg=="],
- "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="],
+ "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ=="],
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
- "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="],
+ "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.26.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw=="],
- "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="],
+ "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ=="],
- "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="],
+ "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.26.5", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="],
- "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="],
+ "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw=="],
- "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
+ "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.26.5", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/traverse": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg=="],
- "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="],
+ "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA=="],
- "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="],
- "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
- "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
+ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.25.9", "", {}, "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw=="],
- "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="],
+ "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.25.9", "", { "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g=="],
- "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="],
+ "@babel/helpers": ["@babel/helpers@7.26.9", "", { "dependencies": { "@babel/template": "^7.26.9", "@babel/types": "^7.26.9" } }, "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA=="],
"@babel/highlight": ["@babel/highlight@7.25.9", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw=="],
- "@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="],
+ "@babel/parser": ["@babel/parser@7.26.9", "", { "dependencies": { "@babel/types": "^7.26.9" }, "bin": "./bin/babel-parser.js" }, "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A=="],
- "@babel/plugin-proposal-decorators": ["@babel/plugin-proposal-decorators@7.28.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-decorators": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg=="],
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g=="],
- "@babel/plugin-proposal-export-default-from": ["@babel/plugin-proposal-export-default-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw=="],
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw=="],
+
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug=="],
+
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g=="],
+
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg=="],
+
+ "@babel/plugin-proposal-class-properties": ["@babel/plugin-proposal-class-properties@7.18.6", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ=="],
+
+ "@babel/plugin-proposal-decorators": ["@babel/plugin-proposal-decorators@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-syntax-decorators": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g=="],
+
+ "@babel/plugin-proposal-export-default-from": ["@babel/plugin-proposal-export-default-from@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykqgwNfSnNOB+C8fV5X4mG3AVmvu+WVxcaU9xHHtBb7PCrPeweMmPjGsn8eMaeJg6SJuoUuZENeeSWaarWqonQ=="],
+
+ "@babel/plugin-proposal-nullish-coalescing-operator": ["@babel/plugin-proposal-nullish-coalescing-operator@7.18.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA=="],
+
+ "@babel/plugin-proposal-optional-chaining": ["@babel/plugin-proposal-optional-chaining@7.21.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA=="],
+
+ "@babel/plugin-proposal-private-property-in-object": ["@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w=="],
"@babel/plugin-syntax-async-generators": ["@babel/plugin-syntax-async-generators@7.8.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw=="],
@@ -182,21 +198,23 @@
"@babel/plugin-syntax-class-static-block": ["@babel/plugin-syntax-class-static-block@7.14.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw=="],
- "@babel/plugin-syntax-decorators": ["@babel/plugin-syntax-decorators@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A=="],
+ "@babel/plugin-syntax-decorators": ["@babel/plugin-syntax-decorators@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg=="],
"@babel/plugin-syntax-dynamic-import": ["@babel/plugin-syntax-dynamic-import@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ=="],
- "@babel/plugin-syntax-export-default-from": ["@babel/plugin-syntax-export-default-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-eBC/3KSekshx19+N40MzjWqJd7KTEdOoLesAfa4IDFI8eRz5a47i5Oszus6zG/cwIXN63YhgLOMSSNJx49sENg=="],
+ "@babel/plugin-syntax-export-default-from": ["@babel/plugin-syntax-export-default-from@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9MhJ/SMTsVqsd69GyQg89lYR4o9T+oDGv5F6IsigxxqFVOyR/IflDLYP8WDI1l8fkhNGGktqkvL5qwNCtGEpgQ=="],
- "@babel/plugin-syntax-flow": ["@babel/plugin-syntax-flow@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA=="],
+ "@babel/plugin-syntax-flow": ["@babel/plugin-syntax-flow@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg=="],
- "@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww=="],
+ "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg=="],
+
+ "@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A=="],
"@babel/plugin-syntax-import-meta": ["@babel/plugin-syntax-import-meta@7.10.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g=="],
"@babel/plugin-syntax-json-strings": ["@babel/plugin-syntax-json-strings@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA=="],
- "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w=="],
+ "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA=="],
"@babel/plugin-syntax-logical-assignment-operators": ["@babel/plugin-syntax-logical-assignment-operators@7.10.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig=="],
@@ -214,183 +232,219 @@
"@babel/plugin-syntax-top-level-await": ["@babel/plugin-syntax-top-level-await@7.14.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw=="],
- "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ=="],
+ "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ=="],
- "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="],
+ "@babel/plugin-syntax-unicode-sets-regex": ["@babel/plugin-syntax-unicode-sets-regex@7.18.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg=="],
- "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="],
+ "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg=="],
- "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="],
+ "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.26.8", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", "@babel/traverse": "^7.26.8" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg=="],
- "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g=="],
+ "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-remap-async-to-generator": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ=="],
- "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="],
+ "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.26.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ=="],
- "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="],
+ "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg=="],
- "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="],
+ "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q=="],
- "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="],
+ "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.26.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ=="],
- "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="],
+ "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9", "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg=="],
- "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="],
+ "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA=="],
- "@babel/plugin-transform-flow-strip-types": ["@babel/plugin-transform-flow-strip-types@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-flow": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg=="],
+ "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ=="],
- "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="],
+ "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA=="],
- "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="],
+ "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw=="],
- "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="],
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog=="],
- "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA=="],
+ "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg=="],
- "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
+ "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.26.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ=="],
- "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="],
+ "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww=="],
- "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="],
+ "@babel/plugin-transform-flow-strip-types": ["@babel/plugin-transform-flow-strip-types@7.26.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/plugin-syntax-flow": "^7.26.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-eGK26RsbIkYUns3Y8qKl362juDDYK+wEdPGHGrhzUl6CewZFo55VZ7hg+CyMFU4dd5QQakBN86nBMpRsFpRvbQ=="],
- "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="],
+ "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.26.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg=="],
- "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="],
+ "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA=="],
- "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="],
+ "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw=="],
- "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ=="],
+ "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ=="],
- "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="],
+ "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q=="],
- "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="],
+ "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA=="],
- "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="],
+ "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw=="],
- "@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="],
+ "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.26.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.26.0", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ=="],
- "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="],
+ "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA=="],
- "@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="],
+ "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw=="],
- "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
+ "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA=="],
- "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
+ "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ=="],
- "@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="],
+ "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.26.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw=="],
- "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="],
+ "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q=="],
- "@babel/plugin-transform-runtime": ["@babel/plugin-transform-runtime@7.28.5", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w=="],
+ "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg=="],
- "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="],
+ "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A=="],
- "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="],
+ "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g=="],
- "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="],
+ "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A=="],
- "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="],
+ "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g=="],
- "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="],
+ "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw=="],
- "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="],
+ "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw=="],
- "@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="],
+ "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA=="],
- "@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="],
+ "@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ=="],
- "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="],
+ "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/types": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw=="],
- "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
+ "@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.25.9", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw=="],
- "@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="],
+ "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg=="],
- "@babel/traverse--for-generate-function-map": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+ "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg=="],
- "@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="],
+ "@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg=="],
- "@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="],
+ "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg=="],
- "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA=="],
+ "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.26.0", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw=="],
- "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-fh7nnvbweDPm2xEmFjfmq7zSUiox88plgdHF9OIW4i99WnXrAC3o2P3ag9judoUMv8FCSUnlwJCM1B64nO5Fbg=="],
+ "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg=="],
- "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-l4xkGa9E7Uc0/05qU2lMYfN1H+fzzkHgaJoy98wO+b/7Gl78srbCRRgwYSW+BTLixTBrM6Ede5NSBwt7rd/i6g=="],
+ "@babel/plugin-transform-runtime": ["@babel/plugin-transform-runtime@7.26.9", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.26.5", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Jf+8y9wXQbbxvVYTM8gO5oEF2POdNji0NMltEkG7FtmzD9PVz7/lxpqSdTvwsjTMU5HIHuDVNf2SOxLkWi+wPQ=="],
- "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-XPSQ+XIPZMLaZ6zveQdwNjbX+QdROEd1zPgMwD47zvHV+tCGB88VH+aynyGxAHdzL+Tm/+DtKST5SECs4iwCLg=="],
+ "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng=="],
- "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-/1s9V/H3cSe0r0Mv/Z8JryF5x9ywRxywomqZVLHAoa/uN0eY7F8gEngWKNS5vbbN/BsfpCG5yeBT5ENh50Frxg=="],
+ "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A=="],
- "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw=="],
+ "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA=="],
- "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-PZQ6ElCOnkYapSsysiTy0+fYX+agXPlWugh6+eQ6uPKI3vKAqNp6TnMhoM3oY2NltSB89hz59o8xIfOdyhi9Iw=="],
+ "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.26.8", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q=="],
- "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg=="],
+ "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.26.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw=="],
- "@bottom-tabs/react-navigation": ["@bottom-tabs/react-navigation@1.1.0", "", { "dependencies": { "color": "^5.0.0" }, "peerDependencies": { "@react-navigation/native": ">=7", "react": "*", "react-native": "*", "react-native-bottom-tabs": "*" } }, "sha512-+4YppCodABcSNIgJiq95QUQ+3ClVBG+rLG3WmYI0+/nbxqKbCz6luFBep4KFOj98Iplj1JY2Ki6ix8CcOZVQ/Q=="],
+ "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.26.8", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw=="],
+
+ "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q=="],
+
+ "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg=="],
+
+ "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA=="],
+
+ "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ=="],
+
+ "@babel/preset-env": ["@babel/preset-env@7.26.9", "", { "dependencies": { "@babel/compat-data": "^7.26.8", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.26.0", "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.26.8", "@babel/plugin-transform-async-to-generator": "^7.25.9", "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", "@babel/plugin-transform-classes": "^7.25.9", "@babel/plugin-transform-computed-properties": "^7.25.9", "@babel/plugin-transform-destructuring": "^7.25.9", "@babel/plugin-transform-dotall-regex": "^7.25.9", "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.26.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", "@babel/plugin-transform-optional-catch-binding": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9", "@babel/plugin-transform-private-methods": "^7.25.9", "@babel/plugin-transform-private-property-in-object": "^7.25.9", "@babel/plugin-transform-property-literals": "^7.25.9", "@babel/plugin-transform-regenerator": "^7.25.9", "@babel/plugin-transform-regexp-modifiers": "^7.26.0", "@babel/plugin-transform-reserved-words": "^7.25.9", "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.26.8", "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ=="],
+
+ "@babel/preset-flow": ["@babel/preset-flow@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-transform-flow-strip-types": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-EASHsAhE+SSlEzJ4bzfusnXSHiU+JfAYzj+jbw2vgQKgq5HrUr8qs+vgtiEL5dOH6sEweI+PNt2D7AqrDSHyqQ=="],
+
+ "@babel/preset-modules": ["@babel/preset-modules@0.1.6-no-external-plugins", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA=="],
+
+ "@babel/preset-react": ["@babel/preset-react@7.26.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-transform-react-display-name": "^7.25.9", "@babel/plugin-transform-react-jsx": "^7.25.9", "@babel/plugin-transform-react-jsx-development": "^7.25.9", "@babel/plugin-transform-react-pure-annotations": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw=="],
+
+ "@babel/preset-typescript": ["@babel/preset-typescript@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-transform-modules-commonjs": "^7.25.9", "@babel/plugin-transform-typescript": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg=="],
+
+ "@babel/register": ["@babel/register@7.25.9", "", { "dependencies": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", "make-dir": "^2.1.0", "pirates": "^4.0.6", "source-map-support": "^0.5.16" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8D43jXtGsYmEeDvm4MWHYUpWf8iiXgWYx3fW7E7Wb7Oe6FWqJPl5K6TuFW0dOwNZzEE5rjlaSJYH9JjrUKJszA=="],
+
+ "@babel/runtime": ["@babel/runtime@7.26.9", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg=="],
+
+ "@babel/template": ["@babel/template@7.26.9", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.26.9", "@babel/types": "^7.26.9" } }, "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA=="],
+
+ "@babel/traverse": ["@babel/traverse@7.26.9", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.9", "@babel/parser": "^7.26.9", "@babel/template": "^7.26.9", "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg=="],
+
+ "@babel/traverse--for-generate-function-map": ["@babel/traverse@7.26.9", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.9", "@babel/parser": "^7.26.9", "@babel/template": "^7.26.9", "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg=="],
+
+ "@babel/types": ["@babel/types@7.26.9", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw=="],
+
+ "@bottom-tabs/react-navigation": ["@bottom-tabs/react-navigation@0.8.6", "", { "dependencies": { "color": "^4.2.3" }, "peerDependencies": { "@react-navigation/native": ">=7", "react": "*", "react-native": "*", "react-native-bottom-tabs": "*" } }, "sha512-hLlyBAUz4ahaVK2Op2VcJeAkCSpm3KKho4IojkPyXsos4WEHtO44EYWC71TDbVGeOP5HQ9k7FSwAW3IiZs0wHw=="],
+
+ "@config-plugins/ffmpeg-kit-react-native": ["@config-plugins/ffmpeg-kit-react-native@9.0.0", "", { "dependencies": { "semver": "^7.3.5" }, "peerDependencies": { "expo": "^52" } }, "sha512-04bXwdq7pmUPoGqYV0YGsrW/8Db+TNicn2Hznb5t+Dl740z9QkNGP4A38y1Mdz7mCU2EW0riASwl/JTH+6rBvw=="],
"@dominicstop/ts-event-emitter": ["@dominicstop/ts-event-emitter@1.1.0", "", {}, "sha512-CcxmJIvUb1vsFheuGGVSQf4KdPZC44XolpUT34+vlal+LyQoBUOn31pjFET5M9ctOxEpt8xa0M3/2M7uUiAoJw=="],
- "@douglowder/expo-av-route-picker-view": ["@douglowder/expo-av-route-picker-view@0.0.5", "", { "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-oT4wf8aYYNfLEuZEkwZIH7CtEHKnEHWnjs6/hNwbFGEC0FnfjjWBNrQEt4fo5/gkafqa2G5ILkxndMyBZvk5dg=="],
-
"@egjs/hammerjs": ["@egjs/hammerjs@2.0.17", "", { "dependencies": { "@types/hammerjs": "^2.0.36" } }, "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A=="],
- "@epic-web/invariant": ["@epic-web/invariant@1.0.0", "", {}, "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA=="],
+ "@expo/bunyan": ["@expo/bunyan@4.0.1", "", { "dependencies": { "uuid": "^8.0.0" } }, "sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg=="],
- "@expo/cli": ["@expo/cli@54.0.21", "", { "dependencies": { "@0no-co/graphql.web": "^1.0.8", "@expo/code-signing-certificates": "^0.0.6", "@expo/config": "~12.0.13", "@expo/config-plugins": "~54.0.4", "@expo/devcert": "^1.2.1", "@expo/env": "~2.0.8", "@expo/image-utils": "^0.8.8", "@expo/json-file": "^10.0.8", "@expo/metro": "~54.2.0", "@expo/metro-config": "~54.0.13", "@expo/osascript": "^2.3.8", "@expo/package-manager": "^1.9.9", "@expo/plist": "^0.4.8", "@expo/prebuild-config": "^54.0.8", "@expo/schema-utils": "^0.1.8", "@expo/spawn-async": "^1.7.2", "@expo/ws-tunnel": "^1.0.1", "@expo/xcpretty": "^4.3.0", "@react-native/dev-middleware": "0.81.5", "@urql/core": "^5.0.6", "@urql/exchange-retry": "^1.3.0", "accepts": "^1.3.8", "arg": "^5.0.2", "better-opn": "~3.0.2", "bplist-creator": "0.1.0", "bplist-parser": "^0.3.1", "chalk": "^4.0.0", "ci-info": "^3.3.0", "compression": "^1.7.4", "connect": "^3.7.0", "debug": "^4.3.4", "env-editor": "^0.4.1", "expo-server": "^1.0.5", "freeport-async": "^2.0.0", "getenv": "^2.0.0", "glob": "^13.0.0", "lan-network": "^0.1.6", "minimatch": "^9.0.0", "node-forge": "^1.3.3", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "picomatch": "^3.0.1", "pretty-bytes": "^5.6.0", "pretty-format": "^29.7.0", "progress": "^2.0.3", "prompts": "^2.3.2", "qrcode-terminal": "0.11.0", "require-from-string": "^2.0.2", "requireg": "^0.2.2", "resolve": "^1.22.2", "resolve-from": "^5.0.0", "resolve.exports": "^2.0.3", "semver": "^7.6.0", "send": "^0.19.0", "slugify": "^1.3.4", "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", "structured-headers": "^0.4.1", "tar": "^7.5.2", "terminal-link": "^2.1.1", "undici": "^6.18.2", "wrap-ansi": "^7.0.0", "ws": "^8.12.1" }, "peerDependencies": { "expo": "*", "expo-router": "*", "react-native": "*" }, "optionalPeers": ["expo-router", "react-native"], "bin": { "expo-internal": "build/bin/cli" } }, "sha512-L/FdpyZDsg/Nq6xW6kfiyF9DUzKfLZCKFXEVZcDqCNar6bXxQVotQyvgexRvtUF5nLinuT/UafLOdC3FUALUmA=="],
+ "@expo/cli": ["@expo/cli@0.22.18", "", { "dependencies": { "@0no-co/graphql.web": "^1.0.8", "@babel/runtime": "^7.20.0", "@expo/code-signing-certificates": "^0.0.5", "@expo/config": "~10.0.10", "@expo/config-plugins": "~9.0.15", "@expo/devcert": "^1.1.2", "@expo/env": "~0.4.2", "@expo/image-utils": "^0.6.5", "@expo/json-file": "^9.0.2", "@expo/metro-config": "~0.19.11", "@expo/osascript": "^2.1.6", "@expo/package-manager": "^1.7.2", "@expo/plist": "^0.2.2", "@expo/prebuild-config": "^8.0.28", "@expo/rudder-sdk-node": "^1.1.1", "@expo/spawn-async": "^1.7.2", "@expo/ws-tunnel": "^1.0.1", "@expo/xcpretty": "^4.3.0", "@react-native/dev-middleware": "0.76.7", "@urql/core": "^5.0.6", "@urql/exchange-retry": "^1.3.0", "accepts": "^1.3.8", "arg": "^5.0.2", "better-opn": "~3.0.2", "bplist-creator": "0.0.7", "bplist-parser": "^0.3.1", "cacache": "^18.0.2", "chalk": "^4.0.0", "ci-info": "^3.3.0", "compression": "^1.7.4", "connect": "^3.7.0", "debug": "^4.3.4", "env-editor": "^0.4.1", "fast-glob": "^3.3.2", "form-data": "^3.0.1", "freeport-async": "^2.0.0", "fs-extra": "~8.1.0", "getenv": "^1.0.0", "glob": "^10.4.2", "internal-ip": "^4.3.0", "is-docker": "^2.0.0", "is-wsl": "^2.1.1", "lodash.debounce": "^4.0.8", "minimatch": "^3.0.4", "node-forge": "^1.3.1", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "picomatch": "^3.0.1", "pretty-bytes": "^5.6.0", "pretty-format": "^29.7.0", "progress": "^2.0.3", "prompts": "^2.3.2", "qrcode-terminal": "0.11.0", "require-from-string": "^2.0.2", "requireg": "^0.2.2", "resolve": "^1.22.2", "resolve-from": "^5.0.0", "resolve.exports": "^2.0.3", "semver": "^7.6.0", "send": "^0.19.0", "slugify": "^1.3.4", "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", "structured-headers": "^0.4.1", "tar": "^6.2.1", "temp-dir": "^2.0.0", "tempy": "^0.7.1", "terminal-link": "^2.1.1", "undici": "^6.18.2", "unique-string": "~2.0.0", "wrap-ansi": "^7.0.0", "ws": "^8.12.1" }, "bin": { "expo-internal": "build/bin/cli" } }, "sha512-TWGKHWTYU9xE7YETPk2zQzLPl+bldpzZCa0Cqg0QeENpu03ZEnMxUqrgHwrbWGTf7ONTYC1tODBkFCFw/qgPGA=="],
- "@expo/code-signing-certificates": ["@expo/code-signing-certificates@0.0.6", "", { "dependencies": { "node-forge": "^1.3.3" } }, "sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w=="],
+ "@expo/code-signing-certificates": ["@expo/code-signing-certificates@0.0.5", "", { "dependencies": { "node-forge": "^1.2.1", "nullthrows": "^1.1.1" } }, "sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw=="],
- "@expo/config": ["@expo/config@12.0.13", "", { "dependencies": { "@babel/code-frame": "~7.10.4", "@expo/config-plugins": "~54.0.4", "@expo/config-types": "^54.0.10", "@expo/json-file": "^10.0.8", "deepmerge": "^4.3.1", "getenv": "^2.0.0", "glob": "^13.0.0", "require-from-string": "^2.0.2", "resolve-from": "^5.0.0", "resolve-workspace-root": "^2.0.0", "semver": "^7.6.0", "slugify": "^1.3.4", "sucrase": "~3.35.1" } }, "sha512-Cu52arBa4vSaupIWsF0h7F/Cg//N374nYb7HAxV0I4KceKA7x2UXpYaHOL7EEYYvp7tZdThBjvGpVmr8ScIvaQ=="],
+ "@expo/config": ["@expo/config@10.0.10", "", { "dependencies": { "@babel/code-frame": "~7.10.4", "@expo/config-plugins": "~9.0.15", "@expo/config-types": "^52.0.4", "@expo/json-file": "^9.0.2", "deepmerge": "^4.3.1", "getenv": "^1.0.0", "glob": "^10.4.2", "require-from-string": "^2.0.2", "resolve-from": "^5.0.0", "resolve-workspace-root": "^2.0.0", "semver": "^7.6.0", "slugify": "^1.3.4", "sucrase": "3.35.0" } }, "sha512-wI9/iam3Irk99ADGM/FyD7YrrEibIZXR4huSZiU5zt9o3dASOKhqepiNJex4YPiktLfKhYrpSEJtwno1g0SrgA=="],
- "@expo/config-plugins": ["@expo/config-plugins@54.0.4", "", { "dependencies": { "@expo/config-types": "^54.0.10", "@expo/json-file": "~10.0.8", "@expo/plist": "^0.4.8", "@expo/sdk-runtime-versions": "^1.0.0", "chalk": "^4.1.2", "debug": "^4.3.5", "getenv": "^2.0.0", "glob": "^13.0.0", "resolve-from": "^5.0.0", "semver": "^7.5.4", "slash": "^3.0.0", "slugify": "^1.6.6", "xcode": "^3.0.1", "xml2js": "0.6.0" } }, "sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q=="],
+ "@expo/config-plugins": ["@expo/config-plugins@9.0.15", "", { "dependencies": { "@expo/config-types": "^52.0.4", "@expo/json-file": "~9.0.1", "@expo/plist": "^0.2.1", "@expo/sdk-runtime-versions": "^1.0.0", "chalk": "^4.1.2", "debug": "^4.3.5", "getenv": "^1.0.0", "glob": "^10.4.2", "resolve-from": "^5.0.0", "semver": "^7.5.4", "slash": "^3.0.0", "slugify": "^1.6.6", "xcode": "^3.0.1", "xml2js": "0.6.0" } }, "sha512-elKY/zIpAJ40RH26iwfyp+hwgeyPgIXX0SrCSOcjeJLsMsCmMac9ewvb+AN8y4k+N7m5lD/dMZupsaateKTFwA=="],
- "@expo/config-types": ["@expo/config-types@54.0.10", "", {}, "sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA=="],
+ "@expo/config-types": ["@expo/config-types@52.0.4", "", {}, "sha512-oMGrb2o3niVCIfjnIHFrOoiDA9jGb0lc3G4RI1UiO//KjULBaQr3QTBoKDzZQwMqDV1AgYgSr9mgEcnX3LqhIg=="],
- "@expo/devcert": ["@expo/devcert@1.2.1", "", { "dependencies": { "@expo/sudo-prompt": "^9.3.1", "debug": "^3.1.0" } }, "sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA=="],
+ "@expo/devcert": ["@expo/devcert@1.1.4", "", { "dependencies": { "application-config-path": "^0.1.0", "command-exists": "^1.2.4", "debug": "^3.1.0", "eol": "^0.9.1", "get-port": "^3.2.0", "glob": "^10.4.2", "lodash": "^4.17.21", "mkdirp": "^0.5.1", "password-prompt": "^1.0.4", "sudo-prompt": "^8.2.0", "tmp": "^0.0.33", "tslib": "^2.4.0" } }, "sha512-fqBODr8c72+gBSX5Ty3SIzaY4bXainlpab78+vEYEKL3fXmsOswMLf0+KE36mUEAa36BYabX7K3EiXOXX5OPMw=="],
- "@expo/devtools": ["@expo/devtools@0.1.8", "", { "dependencies": { "chalk": "^4.1.2" }, "peerDependencies": { "react": "*", "react-native": "*" }, "optionalPeers": ["react", "react-native"] }, "sha512-SVLxbuanDjJPgc0sy3EfXUMLb/tXzp6XIHkhtPVmTWJAp+FOr6+5SeiCfJrCzZFet0Ifyke2vX3sFcKwEvCXwQ=="],
+ "@expo/env": ["@expo/env@0.4.2", "", { "dependencies": { "chalk": "^4.0.0", "debug": "^4.3.4", "dotenv": "~16.4.5", "dotenv-expand": "~11.0.6", "getenv": "^1.0.0" } }, "sha512-TgbCgvSk0Kq0e2fLoqHwEBL4M0ztFjnBEz0YCDm5boc1nvkV1VMuIMteVdeBwnTh8Z0oPJTwHCD49vhMEt1I6A=="],
- "@expo/env": ["@expo/env@2.0.8", "", { "dependencies": { "chalk": "^4.0.0", "debug": "^4.3.4", "dotenv": "~16.4.5", "dotenv-expand": "~11.0.6", "getenv": "^2.0.0" } }, "sha512-5VQD6GT8HIMRaSaB5JFtOXuvfDVU80YtZIuUT/GDhUF782usIXY13Tn3IdDz1Tm/lqA9qnRZQ1BF4t7LlvdJPA=="],
+ "@expo/fingerprint": ["@expo/fingerprint@0.11.11", "", { "dependencies": { "@expo/spawn-async": "^1.7.2", "arg": "^5.0.2", "chalk": "^4.1.2", "debug": "^4.3.4", "find-up": "^5.0.0", "getenv": "^1.0.0", "minimatch": "^3.0.4", "p-limit": "^3.1.0", "resolve-from": "^5.0.0", "semver": "^7.6.0" }, "bin": { "fingerprint": "bin/cli.js" } }, "sha512-gNyn1KnAOpEa8gSNsYqXMTcq0fSwqU/vit6fP5863vLSKxHm/dNt/gm/uZJxrRZxKq71KUJWF6I7d3z8qIfq5g=="],
- "@expo/fingerprint": ["@expo/fingerprint@0.15.4", "", { "dependencies": { "@expo/spawn-async": "^1.7.2", "arg": "^5.0.2", "chalk": "^4.1.2", "debug": "^4.3.4", "getenv": "^2.0.0", "glob": "^13.0.0", "ignore": "^5.3.1", "minimatch": "^9.0.0", "p-limit": "^3.1.0", "resolve-from": "^5.0.0", "semver": "^7.6.0" }, "bin": { "fingerprint": "bin/cli.js" } }, "sha512-eYlxcrGdR2/j2M6pEDXo9zU9KXXF1vhP+V+Tl+lyY+bU8lnzrN6c637mz6Ye3em2ANy8hhUR03Raf8VsT9Ogng=="],
+ "@expo/image-utils": ["@expo/image-utils@0.6.5", "", { "dependencies": { "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", "fs-extra": "9.0.0", "getenv": "^1.0.0", "jimp-compact": "0.16.1", "parse-png": "^2.1.0", "resolve-from": "^5.0.0", "semver": "^7.6.0", "temp-dir": "~2.0.0", "unique-string": "~2.0.0" } }, "sha512-RsS/1CwJYzccvlprYktD42KjyfWZECH6PPIEowvoSmXfGLfdViwcUEI4RvBfKX5Jli6P67H+6YmHvPTbGOboew=="],
- "@expo/image-utils": ["@expo/image-utils@0.8.8", "", { "dependencies": { "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", "getenv": "^2.0.0", "jimp-compact": "0.16.1", "parse-png": "^2.1.0", "resolve-from": "^5.0.0", "resolve-global": "^1.0.0", "semver": "^7.6.0", "temp-dir": "~2.0.0", "unique-string": "~2.0.0" } }, "sha512-HHHaG4J4nKjTtVa1GG9PCh763xlETScfEyNxxOvfTRr8IKPJckjTyqSLEtdJoFNJ1vqiABEjW7tqGhqGibZLeA=="],
+ "@expo/json-file": ["@expo/json-file@9.0.2", "", { "dependencies": { "@babel/code-frame": "~7.10.4", "json5": "^2.2.3", "write-file-atomic": "^2.3.0" } }, "sha512-yAznIUrybOIWp3Uax7yRflB0xsEpvIwIEqIjao9SGi2Gaa+N0OamWfe0fnXBSWF+2zzF4VvqwT4W5zwelchfgw=="],
- "@expo/json-file": ["@expo/json-file@10.0.8", "", { "dependencies": { "@babel/code-frame": "~7.10.4", "json5": "^2.2.3" } }, "sha512-9LOTh1PgKizD1VXfGQ88LtDH0lRwq9lsTb4aichWTWSWqy3Ugfkhfm3BhzBIkJJfQQ5iJu3m/BoRlEIjoCGcnQ=="],
+ "@expo/metro-config": ["@expo/metro-config@0.19.11", "", { "dependencies": { "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", "@babel/parser": "^7.20.0", "@babel/types": "^7.20.0", "@expo/config": "~10.0.10", "@expo/env": "~0.4.2", "@expo/json-file": "~9.0.2", "@expo/spawn-async": "^1.7.2", "chalk": "^4.1.0", "debug": "^4.3.2", "fs-extra": "^9.1.0", "getenv": "^1.0.0", "glob": "^10.4.2", "jsc-safe-url": "^0.2.4", "lightningcss": "~1.27.0", "minimatch": "^3.0.4", "postcss": "~8.4.32", "resolve-from": "^5.0.0" } }, "sha512-XaobHTcsoHQdKEH7PI/DIpr2QiugkQmPYolbfzkpSJMplNWfSh+cTRjrm4//mS2Sb78qohtu0u2CGJnFqFUGag=="],
- "@expo/metro": ["@expo/metro@54.2.0", "", { "dependencies": { "metro": "0.83.3", "metro-babel-transformer": "0.83.3", "metro-cache": "0.83.3", "metro-cache-key": "0.83.3", "metro-config": "0.83.3", "metro-core": "0.83.3", "metro-file-map": "0.83.3", "metro-minify-terser": "0.83.3", "metro-resolver": "0.83.3", "metro-runtime": "0.83.3", "metro-source-map": "0.83.3", "metro-symbolicate": "0.83.3", "metro-transform-plugins": "0.83.3", "metro-transform-worker": "0.83.3" } }, "sha512-h68TNZPGsk6swMmLm9nRSnE2UXm48rWwgcbtAHVMikXvbxdS41NDHHeqg1rcQ9AbznDRp6SQVC2MVpDnsRKU1w=="],
+ "@expo/metro-runtime": ["@expo/metro-runtime@4.0.1", "", { "peerDependencies": { "react-native": "*" } }, "sha512-CRpbLvdJ1T42S+lrYa1iZp1KfDeBp4oeZOK3hdpiS5n0vR0nhD6sC1gGF0sTboCTp64tLteikz5Y3j53dvgOIw=="],
- "@expo/metro-config": ["@expo/metro-config@54.0.13", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", "@expo/config": "~12.0.13", "@expo/env": "~2.0.8", "@expo/json-file": "~10.0.8", "@expo/metro": "~54.2.0", "@expo/spawn-async": "^1.7.2", "browserslist": "^4.25.0", "chalk": "^4.1.0", "debug": "^4.3.2", "dotenv": "~16.4.5", "dotenv-expand": "~11.0.6", "getenv": "^2.0.0", "glob": "^13.0.0", "hermes-parser": "^0.29.1", "jsc-safe-url": "^0.2.4", "lightningcss": "^1.30.1", "minimatch": "^9.0.0", "postcss": "~8.4.32", "resolve-from": "^5.0.0" }, "peerDependencies": { "expo": "*" }, "optionalPeers": ["expo"] }, "sha512-RRufMCgLR2Za1WGsh02OatIJo5qZFt31yCnIOSfoubNc3Qqe92Z41pVsbrFnmw5CIaisv1NgdBy05DHe7pEyuw=="],
+ "@expo/osascript": ["@expo/osascript@2.1.6", "", { "dependencies": { "@expo/spawn-async": "^1.7.2", "exec-async": "^2.2.0" } }, "sha512-SbMp4BUwDAKiFF4zZEJf32rRYMeNnLK9u4FaPo0lQRer60F+SKd20NTSys0wgssiVeQyQz2OhGLRx3cxYowAGw=="],
- "@expo/metro-runtime": ["@expo/metro-runtime@6.1.2", "", { "dependencies": { "anser": "^1.4.9", "pretty-format": "^29.7.0", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-dom": "*", "react-native": "*" }, "optionalPeers": ["react-dom"] }, "sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g=="],
+ "@expo/package-manager": ["@expo/package-manager@1.7.2", "", { "dependencies": { "@expo/json-file": "^9.0.2", "@expo/spawn-async": "^1.7.2", "ansi-regex": "^5.0.0", "chalk": "^4.0.0", "find-up": "^5.0.0", "js-yaml": "^3.13.1", "micromatch": "^4.0.8", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "resolve-workspace-root": "^2.0.0", "split": "^1.0.1", "sudo-prompt": "9.1.1" } }, "sha512-wT/qh9ebNjl6xr00bYkSh93b6E/78J3JPlT6WzGbxbsnv5FIZKB/nr522oWqVe1E+ML7BpXs8WugErWDN9kOFg=="],
- "@expo/osascript": ["@expo/osascript@2.3.8", "", { "dependencies": { "@expo/spawn-async": "^1.7.2", "exec-async": "^2.2.0" } }, "sha512-/TuOZvSG7Nn0I8c+FcEaoHeBO07yu6vwDgk7rZVvAXoeAK5rkA09jRyjYsZo+0tMEFaToBeywA6pj50Mb3ny9w=="],
+ "@expo/plist": ["@expo/plist@0.2.2", "", { "dependencies": { "@xmldom/xmldom": "~0.7.7", "base64-js": "^1.2.3", "xmlbuilder": "^14.0.0" } }, "sha512-ZZGvTO6vEWq02UAPs3LIdja+HRO18+LRI5QuDl6Hs3Ps7KX7xU6Y6kjahWKY37Rx2YjNpX07dGpBFzzC+vKa2g=="],
- "@expo/package-manager": ["@expo/package-manager@1.9.9", "", { "dependencies": { "@expo/json-file": "^10.0.8", "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "resolve-workspace-root": "^2.0.0" } }, "sha512-Nv5THOwXzPprMJwbnXU01iXSrCp3vJqly9M4EJ2GkKko9Ifer2ucpg7x6OUsE09/lw+npaoUnHMXwkw7gcKxlg=="],
+ "@expo/prebuild-config": ["@expo/prebuild-config@8.0.28", "", { "dependencies": { "@expo/config": "~10.0.10", "@expo/config-plugins": "~9.0.15", "@expo/config-types": "^52.0.4", "@expo/image-utils": "^0.6.5", "@expo/json-file": "^9.0.2", "@react-native/normalize-colors": "0.76.7", "debug": "^4.3.1", "fs-extra": "^9.0.0", "resolve-from": "^5.0.0", "semver": "^7.6.0", "xml2js": "0.6.0" } }, "sha512-SDDgCKKS1wFNNm3de2vBP8Q5bnxcabuPDE9Mnk9p7Gb4qBavhwMbAtrLcAyZB+WRb4QM+yan3z3K95vvCfI/+A=="],
- "@expo/plist": ["@expo/plist@0.4.8", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.2.3", "xmlbuilder": "^15.1.1" } }, "sha512-pfNtErGGzzRwHP+5+RqswzPDKkZrx+Cli0mzjQaus1ZWFsog5ibL+nVT3NcporW51o8ggnt7x813vtRbPiyOrQ=="],
+ "@expo/react-native-action-sheet": ["@expo/react-native-action-sheet@4.1.0", "", { "dependencies": { "@types/hoist-non-react-statics": "^3.3.1", "hoist-non-react-statics": "^3.3.0" }, "peerDependencies": { "react": ">=18.0.0" } }, "sha512-RILoWhREgjMdr1NUSmZa/cHg8onV2YPDAMOy0iIP1c3H7nT9QQZf5dQNHK8ehcLM82sarVxriBJyYSSHAx7j6w=="],
- "@expo/prebuild-config": ["@expo/prebuild-config@54.0.8", "", { "dependencies": { "@expo/config": "~12.0.13", "@expo/config-plugins": "~54.0.4", "@expo/config-types": "^54.0.10", "@expo/image-utils": "^0.8.8", "@expo/json-file": "^10.0.8", "@react-native/normalize-colors": "0.81.5", "debug": "^4.3.1", "resolve-from": "^5.0.0", "semver": "^7.6.0", "xml2js": "0.6.0" }, "peerDependencies": { "expo": "*" } }, "sha512-EA7N4dloty2t5Rde+HP0IEE+nkAQiu4A/+QGZGT9mFnZ5KKjPPkqSyYcRvP5bhQE10D+tvz6X0ngZpulbMdbsg=="],
-
- "@expo/react-native-action-sheet": ["@expo/react-native-action-sheet@4.1.1", "", { "dependencies": { "@types/hoist-non-react-statics": "^3.3.1", "hoist-non-react-statics": "^3.3.0" }, "peerDependencies": { "react": ">=18.0.0" } }, "sha512-4KRaba2vhqDRR7ObBj6nrD5uJw8ePoNHdIOMETTpgGTX7StUbrF4j/sfrP1YUyaPEa1P8FXdwG6pB+2WtrJd1A=="],
-
- "@expo/schema-utils": ["@expo/schema-utils@0.1.8", "", {}, "sha512-9I6ZqvnAvKKDiO+ZF8BpQQFYWXOJvTAL5L/227RUbWG1OVZDInFifzCBiqAZ3b67NRfeAgpgvbA7rejsqhY62A=="],
+ "@expo/rudder-sdk-node": ["@expo/rudder-sdk-node@1.1.1", "", { "dependencies": { "@expo/bunyan": "^4.0.0", "@segment/loosely-validate-event": "^2.0.0", "fetch-retry": "^4.1.1", "md5": "^2.2.1", "node-fetch": "^2.6.1", "remove-trailing-slash": "^0.1.0", "uuid": "^8.3.2" } }, "sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ=="],
"@expo/sdk-runtime-versions": ["@expo/sdk-runtime-versions@1.0.0", "", {}, "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ=="],
+ "@expo/server": ["@expo/server@0.5.1", "", { "dependencies": { "@remix-run/node": "^2.12.0", "abort-controller": "^3.0.0", "debug": "^4.3.4", "source-map-support": "~0.5.21" } }, "sha512-lk8pKKw0eVP6rqkDR46vQB3vLA46z4KNGrqHpjD/SvMu1cGaRmQG2cQdX44mQtG8WyO9EYau+fBMHQQS2OTFKg=="],
+
"@expo/spawn-async": ["@expo/spawn-async@1.7.2", "", { "dependencies": { "cross-spawn": "^7.0.3" } }, "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew=="],
- "@expo/sudo-prompt": ["@expo/sudo-prompt@9.3.2", "", {}, "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw=="],
+ "@expo/vector-icons": ["@expo/vector-icons@14.0.4", "", { "dependencies": { "prop-types": "^15.8.1" } }, "sha512-+yKshcbpDfbV4zoXOgHxCwh7lkE9VVTT5T03OUlBsqfze1PLy6Hi4jp1vSb1GVbY6eskvMIivGVc9SKzIv0oEQ=="],
- "@expo/ui": ["@expo/ui@0.2.0-beta.9", "", { "dependencies": { "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-RaBcp0cMe5GykQogJwRZGy4o4JHDLtrr+HaurDPhwPKqVATsV0rR11ysmFe4QX8XWLP/L3od7NOkXUi5ailvaw=="],
-
- "@expo/vector-icons": ["@expo/vector-icons@15.0.3", "", { "peerDependencies": { "expo-font": ">=14.0.4", "react": "*", "react-native": "*" } }, "sha512-SBUyYKphmlfUBqxSfDdJ3jAdEVSALS2VUPOUyqn48oZmb2TL/O7t7/PQm5v4NQujYEPLPMTLn9KVw6H7twwbTA=="],
-
- "@expo/ws-tunnel": ["@expo/ws-tunnel@1.0.6", "", {}, "sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q=="],
+ "@expo/ws-tunnel": ["@expo/ws-tunnel@1.0.5", "", {}, "sha512-Ta9KzslHAIbw2ZoyZ7Ud7/QImucy+K4YvOqo9AhGfUfH76hQzaffQreOySzYusDfW8Y+EXh0ZNWE68dfCumFFw=="],
"@expo/xcpretty": ["@expo/xcpretty@4.3.2", "", { "dependencies": { "@babel/code-frame": "7.10.4", "chalk": "^4.1.0", "find-up": "^5.0.0", "js-yaml": "^4.1.0" }, "bin": { "excpretty": "build/cli.js" } }, "sha512-ReZxZ8pdnoI3tP/dNnJdnmAk7uLT4FjsKDGW7YeDdvdOMz2XCQSmSCM9IWlrXuWtMF9zeSB6WJtEhCQ41gQOfw=="],
- "@gorhom/bottom-sheet": ["@gorhom/bottom-sheet@5.2.8", "", { "dependencies": { "@gorhom/portal": "1.0.14", "invariant": "^2.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-native": "*", "react": "*", "react-native": "*", "react-native-gesture-handler": ">=2.16.1", "react-native-reanimated": ">=3.16.0 || >=4.0.0-" }, "optionalPeers": ["@types/react", "@types/react-native"] }, "sha512-+N27SMpbBxXZQ/IA2nlEV6RGxL/qSFHKfdFKcygvW+HqPG5jVNb1OqehLQsGfBP+Up42i0gW5ppI+DhpB7UCzA=="],
+ "@floating-ui/core": ["@floating-ui/core@1.6.9", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw=="],
+
+ "@floating-ui/dom": ["@floating-ui/dom@1.6.13", "", { "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.9" } }, "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w=="],
+
+ "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="],
+
+ "@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="],
+
+ "@futurejj/react-native-visibility-sensor": ["@futurejj/react-native-visibility-sensor@1.3.11", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-i2V5TaCJ9yu/QD5re5IH9P/98i8QxwLi7TMevv7cjnR+lhCrNuCKjXcUuvLyXHxIYJneXP8TdAYPFVazfyQUog=="],
+
+ "@gorhom/bottom-sheet": ["@gorhom/bottom-sheet@5.1.1", "", { "dependencies": { "@gorhom/portal": "1.0.14", "invariant": "^2.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-native": "*", "react": "*", "react-native": "*", "react-native-gesture-handler": ">=2.16.1", "react-native-reanimated": ">=3.16.0" }, "optionalPeers": ["@types/react", "@types/react-native"] }, "sha512-Y8FiuRmeZYaP+ZGQ0axDwWrrKqVp4ByYRs1D2fTJTxHMt081MHHRQsqmZ3SK7AFp3cSID+vTqnD8w/KAASpy+w=="],
"@gorhom/portal": ["@gorhom/portal@1.0.14", "", { "dependencies": { "nanoid": "^3.3.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A=="],
@@ -400,21 +454,15 @@
"@ide/backoff": ["@ide/backoff@1.0.0", "", {}, "sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g=="],
- "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="],
-
- "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
-
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
- "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
-
"@isaacs/ttlcache": ["@isaacs/ttlcache@1.4.1", "", {}, "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA=="],
"@istanbuljs/load-nyc-config": ["@istanbuljs/load-nyc-config@1.1.0", "", { "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" } }, "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ=="],
"@istanbuljs/schema": ["@istanbuljs/schema@0.1.3", "", {}, "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA=="],
- "@jellyfin/sdk": ["@jellyfin/sdk@0.13.0", "", { "peerDependencies": { "axios": "^1.12.0" } }, "sha512-oiBAOXH6s+dKdReSsYgNktBDzbxtg4JVWhEzIxZSxKcWMdSKmBtK41MhXRO7IWAC40DguKUm3nU/Z493qPAlWA=="],
+ "@jellyfin/sdk": ["@jellyfin/sdk@0.11.0", "", { "peerDependencies": { "axios": "^1.3.4" } }, "sha512-WmM4as9ptqH+CvC2YsUefNWQDmu2aWIamwAoj7h2BFR6l019pcRFG5FT22egwbdizR6DfdpmsoAWB4x9QCzcEQ=="],
"@jest/create-cache-key-function": ["@jest/create-cache-key-function@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3" } }, "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA=="],
@@ -430,37 +478,39 @@
"@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
- "@jimp/bmp": ["@jimp/bmp@0.22.12", "", { "dependencies": { "@jimp/utils": "^0.22.12", "bmp-js": "^0.1.0" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-aeI64HD0npropd+AR76MCcvvRaa+Qck6loCOS03CkkxGHN5/r336qTM5HPUdHKMDOGzqknuVPA8+kK1t03z12g=="],
+ "@jimp/bmp": ["@jimp/bmp@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "@jimp/utils": "^0.16.13", "bmp-js": "^0.1.0" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-9edAxu7N2FX7vzkdl5Jo1BbACfycUtBQX+XBMcHA2bk62P8R0otgkHg798frgAk/WxQIzwxqOH6wMiCwrlAzdQ=="],
- "@jimp/core": ["@jimp/core@0.22.12", "", { "dependencies": { "@jimp/utils": "^0.22.12", "any-base": "^1.1.0", "buffer": "^5.2.0", "exif-parser": "^0.1.12", "file-type": "^16.5.4", "isomorphic-fetch": "^3.0.0", "pixelmatch": "^4.0.2", "tinycolor2": "^1.6.0" } }, "sha512-l0RR0dOPyzMKfjUW1uebzueFEDtCOj9fN6pyTYWWOM/VS4BciXQ1VVrJs8pO3kycGYZxncRKhCoygbNr8eEZQA=="],
+ "@jimp/core": ["@jimp/core@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "@jimp/utils": "^0.16.13", "any-base": "^1.1.0", "buffer": "^5.2.0", "exif-parser": "^0.1.12", "file-type": "^16.5.4", "load-bmfont": "^1.3.1", "mkdirp": "^0.5.1", "phin": "^2.9.1", "pixelmatch": "^4.0.2", "tinycolor2": "^1.4.1" } }, "sha512-qXpA1tzTnlkTku9yqtuRtS/wVntvE6f3m3GNxdTdtmc+O+Wcg9Xo2ABPMh7Nc0AHbMKzwvwgB2JnjZmlmJEObg=="],
- "@jimp/custom": ["@jimp/custom@0.22.12", "", { "dependencies": { "@jimp/core": "^0.22.12" } }, "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q=="],
+ "@jimp/custom": ["@jimp/custom@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "@jimp/core": "^0.16.13" } }, "sha512-LTATglVUPGkPf15zX1wTMlZ0+AU7cGEGF6ekVF1crA8eHUWsGjrYTB+Ht4E3HTrCok8weQG+K01rJndCp/l4XA=="],
- "@jimp/gif": ["@jimp/gif@0.22.12", "", { "dependencies": { "@jimp/utils": "^0.22.12", "gifwrap": "^0.10.1", "omggif": "^1.0.9" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-y6BFTJgch9mbor2H234VSjd9iwAhaNf/t3US5qpYIs0TSbAvM02Fbc28IaDETj9+4YB4676sz4RcN/zwhfu1pg=="],
+ "@jimp/gif": ["@jimp/gif@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "@jimp/utils": "^0.16.13", "gifwrap": "^0.9.2", "omggif": "^1.0.9" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-yFAMZGv3o+YcjXilMWWwS/bv1iSqykFahFMSO169uVMtfQVfa90kt4/kDwrXNR6Q9i6VHpFiGZMlF2UnHClBvg=="],
- "@jimp/jpeg": ["@jimp/jpeg@0.22.12", "", { "dependencies": { "@jimp/utils": "^0.22.12", "jpeg-js": "^0.4.4" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-Rq26XC/uQWaQKyb/5lksCTCxXhtY01NJeBN+dQv5yNYedN0i7iYu+fXEoRsfaJ8xZzjoANH8sns7rVP4GE7d/Q=="],
+ "@jimp/jpeg": ["@jimp/jpeg@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "@jimp/utils": "^0.16.13", "jpeg-js": "^0.4.2" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-BJHlDxzTlCqP2ThqP8J0eDrbBfod7npWCbJAcfkKqdQuFk0zBPaZ6KKaQKyKxmWJ87Z6ohANZoMKEbtvrwz1AA=="],
- "@jimp/plugin-resize": ["@jimp/plugin-resize@0.22.12", "", { "dependencies": { "@jimp/utils": "^0.22.12" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg=="],
+ "@jimp/plugin-resize": ["@jimp/plugin-resize@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "@jimp/utils": "^0.16.13" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-qoqtN8LDknm3fJm9nuPygJv30O3vGhSBD2TxrsCnhtOsxKAqVPJtFVdGd/qVuZ8nqQANQmTlfqTiK9mVWQ7MiQ=="],
- "@jimp/png": ["@jimp/png@0.22.12", "", { "dependencies": { "@jimp/utils": "^0.22.12", "pngjs": "^6.0.0" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-Mrp6dr3UTn+aLK8ty/dSKELz+Otdz1v4aAXzV5q53UDD2rbB5joKVJ/ChY310B+eRzNxIovbUF1KVrUsYdE8Hg=="],
+ "@jimp/png": ["@jimp/png@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "@jimp/utils": "^0.16.13", "pngjs": "^3.3.3" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-8cGqINvbWJf1G0Her9zbq9I80roEX0A+U45xFby3tDWfzn+Zz8XKDF1Nv9VUwVx0N3zpcG1RPs9hfheG4Cq2kg=="],
- "@jimp/tiff": ["@jimp/tiff@0.22.12", "", { "dependencies": { "utif2": "^4.0.1" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-E1LtMh4RyJsoCAfAkBRVSYyZDTtLq9p9LUiiYP0vPtXyxX4BiYBUYihTLSBlCQg5nF2e4OpQg7SPrLdJ66u7jg=="],
+ "@jimp/tiff": ["@jimp/tiff@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "utif": "^2.0.1" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-oJY8d9u95SwW00VPHuCNxPap6Q1+E/xM5QThb9Hu+P6EGuu6lIeLaNBMmFZyblwFbwrH+WBOZlvIzDhi4Dm/6Q=="],
- "@jimp/types": ["@jimp/types@0.22.12", "", { "dependencies": { "@jimp/bmp": "^0.22.12", "@jimp/gif": "^0.22.12", "@jimp/jpeg": "^0.22.12", "@jimp/png": "^0.22.12", "@jimp/tiff": "^0.22.12", "timm": "^1.6.1" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-wwKYzRdElE1MBXFREvCto5s699izFHNVvALUv79GXNbsOVqlwlOxlWJ8DuyOGIXoLP4JW/m30YyuTtfUJgMRMA=="],
+ "@jimp/types": ["@jimp/types@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "@jimp/bmp": "^0.16.13", "@jimp/gif": "^0.16.13", "@jimp/jpeg": "^0.16.13", "@jimp/png": "^0.16.13", "@jimp/tiff": "^0.16.13", "timm": "^1.6.1" }, "peerDependencies": { "@jimp/custom": ">=0.3.5" } }, "sha512-mC0yVNUobFDjoYLg4hoUwzMKgNlxynzwt3cDXzumGvRJ7Kb8qQGOWJQjQFo5OxmGExqzPphkirdbBF88RVLBCg=="],
- "@jimp/utils": ["@jimp/utils@0.22.12", "", { "dependencies": { "regenerator-runtime": "^0.13.3" } }, "sha512-yJ5cWUknGnilBq97ZXOyOS0HhsHOyAyjHwYfHxGbSyMTohgQI6sVyE8KPgDwH8HHW/nMKXk8TrSwAE71zt716Q=="],
+ "@jimp/utils": ["@jimp/utils@0.16.13", "", { "dependencies": { "@babel/runtime": "^7.7.2", "regenerator-runtime": "^0.13.3" } }, "sha512-VyCpkZzFTHXtKgVO35iKN0sYR10psGpV6SkcSeV4oF7eSYlR8Bl6aQLCzVeFjvESF7mxTmIiI3/XrMobVrtxDA=="],
- "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
-
- "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
- "@jridgewell/source-map": ["@jridgewell/source-map@0.3.11", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA=="],
+ "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
- "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
+ "@jridgewell/source-map": ["@jridgewell/source-map@0.3.6", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ=="],
- "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
+
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
+
+ "@kesha-antonov/react-native-background-downloader": ["@kesha-antonov/react-native-background-downloader@3.2.6", "", { "peerDependencies": { "react-native": ">=0.57.0" } }, "sha512-J87PHzBh4knWTQNkCNM4LTMZ85RpMW/QSV+0LGdTxz4JmfLXoeg8R6ratbFU0DP/l8K1eL7r4S1Rc8bmqNJ3Ug=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@@ -468,115 +518,151 @@
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
+ "@npmcli/fs": ["@npmcli/fs@3.1.1", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg=="],
+
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
- "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
+ "@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
- "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="],
+ "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg=="],
- "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
+ "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw=="],
- "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
+ "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="],
- "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="],
+ "@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
- "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
+ "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-menu": "2.1.6", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aUP99QZ3VU84NPsHeaFt4cQUNgJqFsLLOt/RbbWXszZ6MP0DpDyjkFZORr4RpAEx3sUBk+Kc8h13yGtC5Qw8dg=="],
- "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="],
+ "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
- "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="],
+ "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
- "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
+ "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-menu": "2.1.6", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA=="],
- "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
+ "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="],
- "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
+ "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA=="],
- "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
+ "@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="],
- "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
+ "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-callback-ref": "1.1.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg=="],
- "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="],
+ "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.2", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-size": "1.1.0", "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA=="],
- "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
+ "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="],
- "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="],
+ "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
- "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
+ "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
- "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
+ "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.2", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw=="],
- "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
+ "@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="],
- "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
+ "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
- "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
+ "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="],
- "@react-native-community/cli": ["@react-native-community/cli@20.1.0", "", { "dependencies": { "@react-native-community/cli-clean": "20.1.0", "@react-native-community/cli-config": "20.1.0", "@react-native-community/cli-doctor": "20.1.0", "@react-native-community/cli-server-api": "20.1.0", "@react-native-community/cli-tools": "20.1.0", "@react-native-community/cli-types": "20.1.0", "commander": "^9.4.1", "deepmerge": "^4.3.0", "execa": "^5.0.0", "find-up": "^5.0.0", "fs-extra": "^8.1.0", "graceful-fs": "^4.1.3", "picocolors": "^1.1.1", "prompts": "^2.4.2", "semver": "^7.5.2" }, "bin": { "rnc-cli": "build/bin.js" } }, "sha512-441WsVtRe4nGJ9OzA+QMU1+22lA6Q2hRWqqIMKD0wjEMLqcSfOZyu2UL9a/yRpL/dRpyUsU4n7AxqKfTKO/Csg=="],
+ "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
- "@react-native-community/cli-clean": ["@react-native-community/cli-clean@20.1.0", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.0", "execa": "^5.0.0", "fast-glob": "^3.3.2", "picocolors": "^1.1.1" } }, "sha512-77L4DifWfxAT8ByHnkypge7GBMYpbJAjBGV+toowt5FQSGaTBDcBHCX+FFqFRukD5fH6i8sZ41Gtw+nbfCTTIA=="],
+ "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
- "@react-native-community/cli-config": ["@react-native-community/cli-config@20.1.0", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.0", "cosmiconfig": "^9.0.0", "deepmerge": "^4.3.0", "fast-glob": "^3.3.2", "joi": "^17.2.1", "picocolors": "^1.1.1" } }, "sha512-1x9rhLLR/dKKb92Lb5O0l0EmUG08FHf+ZVyVEf9M+tX+p5QIm52MRiy43R0UAZ2jJnFApxRk+N3sxoYK4Dtnag=="],
+ "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.0", "", { "dependencies": { "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ=="],
- "@react-native-community/cli-config-android": ["@react-native-community/cli-config-android@20.1.0", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.0", "fast-glob": "^3.3.2", "fast-xml-parser": "^4.4.1", "picocolors": "^1.1.1" } }, "sha512-3A01ZDyFeCALzzPcwP/fleHoP3sGNq1UX7FzxkTrOFX8RRL9ntXNXQd27E56VU4BBxGAjAJT4Utw8pcOjJceIA=="],
+ "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw=="],
- "@react-native-community/cli-config-apple": ["@react-native-community/cli-config-apple@20.1.0", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.0", "execa": "^5.0.0", "fast-glob": "^3.3.2", "picocolors": "^1.1.1" } }, "sha512-n6JVs8Q3yxRbtZQOy05ofeb1kGtspGN3SgwPmuaqvURF9fsuS7c4/9up2Kp9C+1D2J1remPJXiZLNGOcJvfpOA=="],
+ "@radix-ui/rect": ["@radix-ui/rect@1.1.0", "", {}, "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="],
- "@react-native-community/cli-doctor": ["@react-native-community/cli-doctor@20.1.0", "", { "dependencies": { "@react-native-community/cli-config": "20.1.0", "@react-native-community/cli-platform-android": "20.1.0", "@react-native-community/cli-platform-apple": "20.1.0", "@react-native-community/cli-platform-ios": "20.1.0", "@react-native-community/cli-tools": "20.1.0", "command-exists": "^1.2.8", "deepmerge": "^4.3.0", "envinfo": "^7.13.0", "execa": "^5.0.0", "node-stream-zip": "^1.9.1", "ora": "^5.4.1", "picocolors": "^1.1.1", "semver": "^7.5.2", "wcwidth": "^1.0.1", "yaml": "^2.2.1" } }, "sha512-QfJF1GVjA4PBrIT3SJ0vFFIu0km1vwOmLDlOYVqfojajZJ+Dnvl0f94GN1il/jT7fITAxom///XH3/URvi7YTQ=="],
+ "@react-native-community/cli": ["@react-native-community/cli@15.1.3", "", { "dependencies": { "@react-native-community/cli-clean": "15.1.3", "@react-native-community/cli-config": "15.1.3", "@react-native-community/cli-debugger-ui": "15.1.3", "@react-native-community/cli-doctor": "15.1.3", "@react-native-community/cli-server-api": "15.1.3", "@react-native-community/cli-tools": "15.1.3", "@react-native-community/cli-types": "15.1.3", "chalk": "^4.1.2", "commander": "^9.4.1", "deepmerge": "^4.3.0", "execa": "^5.0.0", "find-up": "^5.0.0", "fs-extra": "^8.1.0", "graceful-fs": "^4.1.3", "prompts": "^2.4.2", "semver": "^7.5.2" }, "bin": { "rnc-cli": "build/bin.js" } }, "sha512-+ih/WYUkJsEV2CMAnOHvVoSIz/Ahg5UJk+sqSIOmY79mWAglQzfLP71o7b0neJCnJWLmWiO6G6/S+kmULefD5g=="],
- "@react-native-community/cli-platform-android": ["@react-native-community/cli-platform-android@20.1.0", "", { "dependencies": { "@react-native-community/cli-config-android": "20.1.0", "@react-native-community/cli-tools": "20.1.0", "execa": "^5.0.0", "logkitty": "^0.7.1", "picocolors": "^1.1.1" } }, "sha512-TeHPDThOwDppQRpndm9kCdRCBI8AMy3HSIQ+iy7VYQXL5BtZ5LfmGdusoj7nVN/ZGn0Lc6Gwts5qowyupXdeKg=="],
+ "@react-native-community/cli-clean": ["@react-native-community/cli-clean@15.1.3", "", { "dependencies": { "@react-native-community/cli-tools": "15.1.3", "chalk": "^4.1.2", "execa": "^5.0.0", "fast-glob": "^3.3.2" } }, "sha512-3s9NGapIkONFoCUN2s77NYI987GPSCdr74rTf0TWyGIDf4vTYgKoWKKR+Ml3VTa1BCj51r4cYuHEKE1pjUSc0w=="],
- "@react-native-community/cli-platform-apple": ["@react-native-community/cli-platform-apple@20.1.0", "", { "dependencies": { "@react-native-community/cli-config-apple": "20.1.0", "@react-native-community/cli-tools": "20.1.0", "execa": "^5.0.0", "fast-xml-parser": "^4.4.1", "picocolors": "^1.1.1" } }, "sha512-0ih1hrYezSM2cuOlVnwBEFtMwtd8YgpTLmZauDJCv50rIumtkI1cQoOgLoS4tbPCj9U/Vn2a9BFH0DLFOOIacg=="],
+ "@react-native-community/cli-config": ["@react-native-community/cli-config@15.1.3", "", { "dependencies": { "@react-native-community/cli-tools": "15.1.3", "chalk": "^4.1.2", "cosmiconfig": "^9.0.0", "deepmerge": "^4.3.0", "fast-glob": "^3.3.2", "joi": "^17.2.1" } }, "sha512-fJ9MrWp+/SszEVg5Wja8A57Whl5EfjRCHWFNkvFBtfjVUfi2hWvSTW3VBxzJuCHnPIIwpQafwjEgOrIRUI8y6w=="],
- "@react-native-community/cli-platform-ios": ["@react-native-community/cli-platform-ios@20.1.0", "", { "dependencies": { "@react-native-community/cli-platform-apple": "20.1.0" } }, "sha512-XN7Da9z4WsJxtqVtEzY8q2bv22OsvzaFP5zy5+phMWNoJlU4lf7IvBSxqGYMpQ9XhYP7arDw5vmW4W34s06rnA=="],
+ "@react-native-community/cli-config-android": ["@react-native-community/cli-config-android@15.1.3", "", { "dependencies": { "@react-native-community/cli-tools": "15.1.3", "chalk": "^4.1.2", "fast-glob": "^3.3.2", "fast-xml-parser": "^4.4.1" } }, "sha512-v9okV/WQfMJEWRddI0S6no2v9Lvk54KgFkw1mvMYhJKVqloCNsIWzoqme0u7zIuYSzwsjXUQXVlGiDzbbwdkBw=="],
- "@react-native-community/cli-server-api": ["@react-native-community/cli-server-api@20.1.0", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.0", "body-parser": "^1.20.3", "compression": "^1.7.1", "connect": "^3.6.5", "errorhandler": "^1.5.1", "nocache": "^3.0.1", "open": "^6.2.0", "pretty-format": "^29.7.0", "serve-static": "^1.13.1", "ws": "^6.2.3" } }, "sha512-Tb415Oh8syXNT2zOzLzFkBXznzGaqKCiaichxKzGCDKg6JGHp3jSuCmcTcaPeYC7oc32n/S3Psw7798r4Q/7lA=="],
+ "@react-native-community/cli-config-apple": ["@react-native-community/cli-config-apple@15.1.3", "", { "dependencies": { "@react-native-community/cli-tools": "15.1.3", "chalk": "^4.1.2", "execa": "^5.0.0", "fast-glob": "^3.3.2" } }, "sha512-Qv6jaEaycv+7s8wR9l9bdpIeSNFCeVANfGCX1x76SgOmGfZNIa7J3l1HaeF/5ktERMYsw/hm4u3rUn4Ks0YV1g=="],
- "@react-native-community/cli-tools": ["@react-native-community/cli-tools@20.1.0", "", { "dependencies": { "@vscode/sudo-prompt": "^9.0.0", "appdirsjs": "^1.2.4", "execa": "^5.0.0", "find-up": "^5.0.0", "launch-editor": "^2.9.1", "mime": "^2.4.1", "ora": "^5.4.1", "picocolors": "^1.1.1", "prompts": "^2.4.2", "semver": "^7.5.2" } }, "sha512-/YmzHGOkY6Bgrv4OaA1L8rFqsBlQd1EB2/ipAoKPiieV0EcB5PUamUSuNeFU3sBZZTYQCUENwX4wgOHgFUlDnQ=="],
+ "@react-native-community/cli-debugger-ui": ["@react-native-community/cli-debugger-ui@15.1.3", "", { "dependencies": { "serve-static": "^1.13.1" } }, "sha512-m+fb9iAUNb9WiDdokCBLh0InJvollcgAM3gLjCT8DGTP6bH/jxtZ3DszzyIRqN9cMamItVrvDM0vkIg48xK7rQ=="],
- "@react-native-community/cli-types": ["@react-native-community/cli-types@20.1.0", "", { "dependencies": { "joi": "^17.2.1" } }, "sha512-D0kDspcwgbVXyNjwicT7Bb1JgXjijTw1JJd+qxyF/a9+sHv7TU4IchV+gN38QegeXqVyM4Ym7YZIvXMFBmyJqA=="],
+ "@react-native-community/cli-doctor": ["@react-native-community/cli-doctor@15.1.3", "", { "dependencies": { "@react-native-community/cli-config": "15.1.3", "@react-native-community/cli-platform-android": "15.1.3", "@react-native-community/cli-platform-apple": "15.1.3", "@react-native-community/cli-platform-ios": "15.1.3", "@react-native-community/cli-tools": "15.1.3", "chalk": "^4.1.2", "command-exists": "^1.2.8", "deepmerge": "^4.3.0", "envinfo": "^7.13.0", "execa": "^5.0.0", "node-stream-zip": "^1.9.1", "ora": "^5.4.1", "semver": "^7.5.2", "strip-ansi": "^5.2.0", "wcwidth": "^1.0.1", "yaml": "^2.2.1" } }, "sha512-WC9rawobuITAtJjyZ68E1M0geRt+b9A2CGB354L/tQp+XMKobGGVI4Y0DsattK83Wdg59GPyldE8C0Wevfgm/A=="],
+
+ "@react-native-community/cli-platform-android": ["@react-native-community/cli-platform-android@15.1.3", "", { "dependencies": { "@react-native-community/cli-config-android": "15.1.3", "@react-native-community/cli-tools": "15.1.3", "chalk": "^4.1.2", "execa": "^5.0.0", "logkitty": "^0.7.1" } }, "sha512-ZwrBK0UK4DRHoQm2v5m8+PlNHBK5gmibBU6rqNFLo4aZJ2Rufalbv3SF+DukgSyoI9/kI8UVrzSNj17e+HhN5A=="],
+
+ "@react-native-community/cli-platform-apple": ["@react-native-community/cli-platform-apple@15.1.3", "", { "dependencies": { "@react-native-community/cli-config-apple": "15.1.3", "@react-native-community/cli-tools": "15.1.3", "chalk": "^4.1.2", "execa": "^5.0.0", "fast-xml-parser": "^4.4.1" } }, "sha512-awotqCGVcTdeRmTlE3wlsZgNxZUDGojUhPYOVMKejgdCzNM2bvzF8fqhETH2sc64Hbw/tQJg8pYeD4MZR0bHxw=="],
+
+ "@react-native-community/cli-platform-ios": ["@react-native-community/cli-platform-ios@15.1.3", "", { "dependencies": { "@react-native-community/cli-platform-apple": "15.1.3" } }, "sha512-DxM8GYkqjrlGuuGiGjrcvUmKC2xv9zDzHbsc4+S160Nn0zbF2OH1DhMlnIuUeCmQnAO6QFMqU99O120iEzCAug=="],
+
+ "@react-native-community/cli-server-api": ["@react-native-community/cli-server-api@15.1.3", "", { "dependencies": { "@react-native-community/cli-debugger-ui": "15.1.3", "@react-native-community/cli-tools": "15.1.3", "compression": "^1.7.1", "connect": "^3.6.5", "errorhandler": "^1.5.1", "nocache": "^3.0.1", "pretty-format": "^26.6.2", "serve-static": "^1.13.1", "ws": "^6.2.3" } }, "sha512-kXZ0evedluLt6flWQiI3JqwnW8rSBspOoQ7JVTQYiG5lDHAeL3Om9PjAyiQBg1EZRMjiWZDV7nDxhR+m+6NX5Q=="],
+
+ "@react-native-community/cli-tools": ["@react-native-community/cli-tools@15.1.3", "", { "dependencies": { "appdirsjs": "^1.2.4", "chalk": "^4.1.2", "execa": "^5.0.0", "find-up": "^5.0.0", "mime": "^2.4.1", "open": "^6.2.0", "ora": "^5.4.1", "prompts": "^2.4.2", "semver": "^7.5.2", "shell-quote": "^1.7.3", "sudo-prompt": "^9.0.0" } }, "sha512-2RzoUKR+Y03ijBeeSoCSQihyN6dxy3fbHjraO4MheZZUzt/Yd1VMEDd0R5aa6rtiRDoknbHt45RL2GMa8MSaEA=="],
+
+ "@react-native-community/cli-types": ["@react-native-community/cli-types@15.1.3", "", { "dependencies": { "joi": "^17.2.1" } }, "sha512-0ZaM8LMsa7z7swBN+ObL2ysc6aA3AdS698Q6uEukym6RyX1uLbiofUdlvFSMpWSEL3D8f9OTymmL4RkCH8k6dw=="],
"@react-native-community/netinfo": ["@react-native-community/netinfo@11.4.1", "", { "peerDependencies": { "react-native": ">=0.59" } }, "sha512-B0BYAkghz3Q2V09BF88RA601XursIEA111tnc2JOaN7axJWmNefmfjZqw/KdSxKZp7CZUuPpjBmz/WCR9uaHYg=="],
- "@react-native-tvos/config-tv": ["@react-native-tvos/config-tv@0.1.4", "", { "dependencies": { "getenv": "^1.0.0" }, "peerDependencies": { "expo": ">=52.0.0" } }, "sha512-xfVDqSFjEUsb+xcMk0hE2Z/M6QZH0QzAJOSQZwo7W/ZRaLrd+xFQnx0LaXqt3kxlR3P7wskKHByDP/FSoUZnbA=="],
+ "@react-native-menu/menu": ["@react-native-menu/menu@1.2.2", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-Uk65PAhwNkCVBAqJu5t2H9biV+m0JLwJc7m3v2X2A/W8SFJmUqYabBsLH4fOWKI3a7kkR9QDT6HruliIKSfM8w=="],
- "@react-native/assets-registry": ["@react-native/assets-registry@0.81.5", "", {}, "sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w=="],
+ "@react-native-tvos/config-tv": ["@react-native-tvos/config-tv@0.1.1", "", { "dependencies": { "getenv": "^1.0.0" }, "peerDependencies": { "expo": "^52" } }, "sha512-Le/5wGElcNarDcoafCbvk/HMxcG3s0/468xXMWqAsOtBhGAdGtyXtjWEgp/uEr4GgZJlEIdM3ZqiuB8P7p8sjw=="],
- "@react-native/babel-plugin-codegen": ["@react-native/babel-plugin-codegen@0.81.5", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@react-native/codegen": "0.81.5" } }, "sha512-oF71cIH6je3fSLi6VPjjC3Sgyyn57JLHXs+mHWc9MoCiJJcM4nqsS5J38zv1XQ8d3zOW2JtHro+LF0tagj2bfQ=="],
+ "@react-native-tvos/virtualized-lists": ["@react-native-tvos/virtualized-lists@0.77.0-0", "", { "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@types/react": "^18.2.6", "react": "*", "react-native-tvos": "*" }, "optionalPeers": ["@types/react"] }, "sha512-em0PMjOD8XQvlygbFoNT4R76rSIRxekZ9TL6EbTIC/kJUDrSPB3W9RafA6n6p4OLoWgEF7MIJ9W+zfibdiVXbw=="],
- "@react-native/babel-preset": ["@react-native/babel-preset@0.81.5", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-transform-arrow-functions": "^7.24.7", "@babel/plugin-transform-async-generator-functions": "^7.25.4", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-computed-properties": "^7.24.7", "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-for-of": "^7.24.7", "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", "@babel/plugin-transform-numeric-separator": "^7.24.7", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-react-display-name": "^7.24.7", "@babel/plugin-transform-react-jsx": "^7.25.2", "@babel/plugin-transform-react-jsx-self": "^7.24.7", "@babel/plugin-transform-react-jsx-source": "^7.24.7", "@babel/plugin-transform-regenerator": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/plugin-transform-shorthand-properties": "^7.24.7", "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-typescript": "^7.25.2", "@babel/plugin-transform-unicode-regex": "^7.24.7", "@babel/template": "^7.25.0", "@react-native/babel-plugin-codegen": "0.81.5", "babel-plugin-syntax-hermes-parser": "0.29.1", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" } }, "sha512-UoI/x/5tCmi+pZ3c1+Ypr1DaRMDLI3y+Q70pVLLVgrnC3DHsHRIbHcCHIeG/IJvoeFqFM2sTdhSOLJrf8lOPrA=="],
+ "@react-native/assets-registry": ["@react-native/assets-registry@0.77.0", "", {}, "sha512-Ms4tYYAMScgINAXIhE4riCFJPPL/yltughHS950l0VP5sm5glbimn9n7RFn9Tc8cipX74/ddbk19+ydK2iDMmA=="],
- "@react-native/codegen": ["@react-native/codegen@0.81.5", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/parser": "^7.25.3", "glob": "^7.1.1", "hermes-parser": "0.29.1", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "yargs": "^17.6.2" } }, "sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g=="],
+ "@react-native/babel-plugin-codegen": ["@react-native/babel-plugin-codegen@0.76.7", "", { "dependencies": { "@react-native/codegen": "0.76.7" } }, "sha512-+8H4DXJREM4l/pwLF/wSVMRzVhzhGDix5jLezNrMD9J1U1AMfV2aSkWA1XuqR7pjPs/Vqf6TaPL7vJMZ4LU05Q=="],
- "@react-native/community-cli-plugin": ["@react-native/community-cli-plugin@0.81.5", "", { "dependencies": { "@react-native/dev-middleware": "0.81.5", "debug": "^4.4.0", "invariant": "^2.2.4", "metro": "^0.83.1", "metro-config": "^0.83.1", "metro-core": "^0.83.1", "semver": "^7.1.3" }, "peerDependencies": { "@react-native-community/cli": "*", "@react-native/metro-config": "*" }, "optionalPeers": ["@react-native-community/cli", "@react-native/metro-config"] }, "sha512-yWRlmEOtcyvSZ4+OvqPabt+NS36vg0K/WADTQLhrYrm9qdZSuXmq8PmdJWz/68wAqKQ+4KTILiq2kjRQwnyhQw=="],
+ "@react-native/babel-preset": ["@react-native/babel-preset@0.76.7", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-transform-arrow-functions": "^7.24.7", "@babel/plugin-transform-async-generator-functions": "^7.25.4", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-computed-properties": "^7.24.7", "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-for-of": "^7.24.7", "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", "@babel/plugin-transform-numeric-separator": "^7.24.7", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-react-display-name": "^7.24.7", "@babel/plugin-transform-react-jsx": "^7.25.2", "@babel/plugin-transform-react-jsx-self": "^7.24.7", "@babel/plugin-transform-react-jsx-source": "^7.24.7", "@babel/plugin-transform-regenerator": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/plugin-transform-shorthand-properties": "^7.24.7", "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-typescript": "^7.25.2", "@babel/plugin-transform-unicode-regex": "^7.24.7", "@babel/template": "^7.25.0", "@react-native/babel-plugin-codegen": "0.76.7", "babel-plugin-syntax-hermes-parser": "^0.25.1", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" } }, "sha512-/c5DYZ6y8tyg+g8tgXKndDT7mWnGmkZ9F+T3qNDfoE3Qh7ucrNeC2XWvU9h5pk8eRtj9l4SzF4aO1phzwoibyg=="],
- "@react-native/debugger-frontend": ["@react-native/debugger-frontend@0.81.5", "", {}, "sha512-bnd9FSdWKx2ncklOetCgrlwqSGhMHP2zOxObJbOWXoj7GHEmih4MKarBo5/a8gX8EfA1EwRATdfNBQ81DY+h+w=="],
+ "@react-native/codegen": ["@react-native/codegen@0.77.0", "", { "dependencies": { "@babel/parser": "^7.25.3", "glob": "^7.1.1", "hermes-parser": "0.25.1", "invariant": "^2.2.4", "jscodeshift": "^17.0.0", "nullthrows": "^1.1.1", "yargs": "^17.6.2" }, "peerDependencies": { "@babel/preset-env": "^7.1.6" } }, "sha512-rE9lXx41ZjvE8cG7e62y/yGqzUpxnSvJ6me6axiX+aDewmI4ZrddvRGYyxCnawxy5dIBHSnrpZse3P87/4Lm7w=="],
- "@react-native/dev-middleware": ["@react-native/dev-middleware@0.81.5", "", { "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.81.5", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.0", "connect": "^3.6.5", "debug": "^4.4.0", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "open": "^7.0.3", "serve-static": "^1.16.2", "ws": "^6.2.3" } }, "sha512-WfPfZzboYgo/TUtysuD5xyANzzfka8Ebni6RIb2wDxhb56ERi7qDrE4xGhtPsjCL4pQBXSVxyIlCy0d8I6EgGA=="],
+ "@react-native/community-cli-plugin": ["@react-native/community-cli-plugin@0.77.0", "", { "dependencies": { "@react-native/dev-middleware": "0.77.0", "@react-native/metro-babel-transformer": "0.77.0", "chalk": "^4.0.0", "debug": "^2.2.0", "invariant": "^2.2.4", "metro": "^0.81.0", "metro-config": "^0.81.0", "metro-core": "^0.81.0", "readline": "^1.3.0", "semver": "^7.1.3" }, "peerDependencies": { "@react-native-community/cli-server-api": "*" }, "optionalPeers": ["@react-native-community/cli-server-api"] }, "sha512-GRshwhCHhtupa3yyCbel14SlQligV8ffNYN5L1f8HCo2SeGPsBDNjhj2U+JTrMPnoqpwowPGvkCwyqwqYff4MQ=="],
- "@react-native/gradle-plugin": ["@react-native/gradle-plugin@0.81.5", "", {}, "sha512-hORRlNBj+ReNMLo9jme3yQ6JQf4GZpVEBLxmTXGGlIL78MAezDZr5/uq9dwElSbcGmLEgeiax6e174Fie6qPLg=="],
+ "@react-native/debugger-frontend": ["@react-native/debugger-frontend@0.76.7", "", {}, "sha512-89ZtZXt7ZxE94i7T94qzZMhp4Gfcpr/QVpGqEaejAxZD+gvDCH21cYSF+/Rz2ttBazm0rk5MZ0mFqb0Iqp1jmw=="],
- "@react-native/js-polyfills": ["@react-native/js-polyfills@0.81.5", "", {}, "sha512-fB7M1CMOCIUudTRuj7kzxIBTVw2KXnsgbQ6+4cbqSxo8NmRRhA0Ul4ZUzZj3rFd3VznTL4Brmocv1oiN0bWZ8w=="],
+ "@react-native/dev-middleware": ["@react-native/dev-middleware@0.76.7", "", { "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.76.7", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.0", "connect": "^3.6.5", "debug": "^2.2.0", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "open": "^7.0.3", "selfsigned": "^2.4.1", "serve-static": "^1.13.1", "ws": "^6.2.3" } }, "sha512-Jsw8g9DyLPnR9yHEGuT09yHZ7M88/GL9CtU9WmyChlBwdXSeE3AmRqLegsV3XcgULQ1fqdemokaOZ/MwLYkjdA=="],
- "@react-native/normalize-colors": ["@react-native/normalize-colors@0.81.5", "", {}, "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g=="],
+ "@react-native/gradle-plugin": ["@react-native/gradle-plugin@0.77.0", "", {}, "sha512-rmfh93jzbndSq7kihYHUQ/EGHTP8CCd3GDCmg5SbxSOHAaAYx2HZ28ZG7AVcGUsWeXp+e/90zGIyfOzDRx0Zaw=="],
- "@react-native/virtualized-lists": ["@react-native/virtualized-lists@0.81.5", "", { "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@types/react": "^19.1.0", "react": "*", "react-native": "*" }, "optionalPeers": ["@types/react"] }, "sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw=="],
+ "@react-native/js-polyfills": ["@react-native/js-polyfills@0.77.0", "", {}, "sha512-kHFcMJVkGb3ptj3yg1soUsMHATqal4dh0QTGAbYihngJ6zy+TnP65J3GJq4UlwqFE9K1RZkeCmTwlmyPFHOGvA=="],
- "@react-navigation/bottom-tabs": ["@react-navigation/bottom-tabs@7.8.4", "", { "dependencies": { "@react-navigation/elements": "^2.8.1", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { "@react-navigation/native": "^7.1.19", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-Ie+7EgUxfZmVXm4RCiJ96oaiwJVFgVE8NJoeUKLLcYEB/99wKbhuKPJNtbkpR99PHfhq64SE7476BpcP4xOFhw=="],
+ "@react-native/metro-babel-transformer": ["@react-native/metro-babel-transformer@0.77.0", "", { "dependencies": { "@babel/core": "^7.25.2", "@react-native/babel-preset": "0.77.0", "hermes-parser": "0.25.1", "nullthrows": "^1.1.1" } }, "sha512-19GfvhBRKCU3UDWwCnDR4QjIzz3B2ZuwhnxMRwfAgPxz7QY9uKour9RGmBAVUk1Wxi/SP7dLEvWnmnuBO39e2A=="],
- "@react-navigation/core": ["@react-navigation/core@7.13.0", "", { "dependencies": { "@react-navigation/routers": "^7.5.1", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "query-string": "^7.1.3", "react-is": "^19.1.0", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": ">= 18.2.0" } }, "sha512-Fc/SO23HnlGnkou/z8JQUzwEMvhxuUhr4rdPTIZp/c8q1atq3k632Nfh8fEiGtk+MP1wtIvXdN2a5hBIWpLq3g=="],
+ "@react-native/normalize-colors": ["@react-native/normalize-colors@0.76.7", "", {}, "sha512-ST1xxBuYVIXPdD81dR6+tzIgso7m3pa9+6rOBXTh5Xm7KEEFik7tnQX+GydXYMp3wr1gagJjragdXkPnxK6WNg=="],
- "@react-navigation/elements": ["@react-navigation/elements@2.9.2", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.1.25", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-J1GltOAGowNLznEphV/kr4zs0U7mUBO1wVA2CqpkN8ePBsoxrAmsd+T5sEYUCXN9KgTDFvc6IfcDqrGSQngd/g=="],
+ "@react-navigation/bottom-tabs": ["@react-navigation/bottom-tabs@7.2.0", "", { "dependencies": { "@react-navigation/elements": "^2.2.5", "color": "^4.2.3" }, "peerDependencies": { "@react-navigation/native": "^7.0.14", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-1LxjgnbPyFINyf9Qr5d1YE0pYhuJayg5TCIIFQmbcX4PRhX7FKUXV7cX8OzrKXEdZi/UE/VNXugtozPAR9zgvA=="],
- "@react-navigation/material-top-tabs": ["@react-navigation/material-top-tabs@7.4.9", "", { "dependencies": { "@react-navigation/elements": "^2.9.2", "color": "^4.2.3", "react-native-tab-view": "^4.2.0" }, "peerDependencies": { "@react-navigation/native": "^7.1.25", "react": ">= 18.2.0", "react-native": "*", "react-native-pager-view": ">= 6.0.0", "react-native-safe-area-context": ">= 4.0.0" } }, "sha512-oYpdTfa2D1Tn0HJER9dRCR260agKGgYe+ydSHt3RIsJ9sLg8hU7ntKYWo1FnEC/Nsv1/N1u/tRst7ZpQRjjl4A=="],
+ "@react-navigation/core": ["@react-navigation/core@7.3.1", "", { "dependencies": { "@react-navigation/routers": "^7.1.2", "escape-string-regexp": "^4.0.0", "nanoid": "3.3.8", "query-string": "^7.1.3", "react-is": "^18.2.0", "use-latest-callback": "^0.2.1", "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "react": ">= 18.2.0" } }, "sha512-S3KCGvNsoqVk8ErAtQI2EAhg9185lahF5OY01ofrrD4Ij/uk3QEHHjoGQhR5l5DXSCSKr1JbMQA7MEKMsBiWZA=="],
- "@react-navigation/native": ["@react-navigation/native@7.1.19", "", { "dependencies": { "@react-navigation/core": "^7.13.0", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*" } }, "sha512-fM7q8di4Q8sp2WUhiUWOe7bEDRyRhbzsKQOd5N2k+lHeCx3UncsRYuw4Q/KN0EovM3wWKqMMmhy/YWuEO04kgw=="],
+ "@react-navigation/elements": ["@react-navigation/elements@2.2.5", "", { "dependencies": { "color": "^4.2.3" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.0.14", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-sDhE+W14P7MNWLMxXg1MEVXwkLUpMZJGflE6nQNzLmolJQIHgcia0Mrm8uRa3bQovhxYu1UzEojLZ+caoZt7Fg=="],
- "@react-navigation/native-stack": ["@react-navigation/native-stack@7.6.2", "", { "dependencies": { "@react-navigation/elements": "^2.8.1", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0", "warn-once": "^0.1.1" }, "peerDependencies": { "@react-navigation/native": "^7.1.19", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-CB6chGNLwJYiyOeyCNUKx33yT7XJSwRZIeKHf4S1vs+Oqu3u9zMnvGUIsesNgbgX0xy16gBqYsrWgr0ZczBTtA=="],
+ "@react-navigation/material-top-tabs": ["@react-navigation/material-top-tabs@7.1.0", "", { "dependencies": { "@react-navigation/elements": "^2.2.5", "color": "^4.2.3", "react-native-tab-view": "^4.0.5" }, "peerDependencies": { "@react-navigation/native": "^7.0.14", "react": ">= 18.2.0", "react-native": "*", "react-native-pager-view": ">= 6.0.0" } }, "sha512-bTFgeHWZmkyE9CAVMTc+lw/b1n2ES3bk0JZoCNSTIrDP+tXfsS8CB4lpOhBybfX1q0C4NQ/i4qMlV7p1iO0eKA=="],
- "@react-navigation/routers": ["@react-navigation/routers@7.5.1", "", { "dependencies": { "nanoid": "^3.3.11" } }, "sha512-pxipMW/iEBSUrjxz2cDD7fNwkqR4xoi0E/PcfTQGCcdJwLoaxzab5kSadBLj1MTJyT0YRrOXL9umHpXtp+Dv4w=="],
+ "@react-navigation/native": ["@react-navigation/native@7.0.14", "", { "dependencies": { "@react-navigation/core": "^7.3.1", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "3.3.8", "use-latest-callback": "^0.2.1" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*" } }, "sha512-Gi6lLw4VOGSWAhmUdJOMauOKGK51/YA1CprjXm91sNfgERWvznqEMw8QmUQx9SEqYfi0LfZhbzpMst09SJ00lw=="],
- "@shopify/flash-list": ["@shopify/flash-list@2.0.2", "", { "dependencies": { "tslib": "2.8.1" }, "peerDependencies": { "@babel/runtime": "*", "react": "*", "react-native": "*" } }, "sha512-zhlrhA9eiuEzja4wxVvotgXHtqd3qsYbXkQ3rsBfOgbFA9BVeErpDE/yEwtlIviRGEqpuFj/oU5owD6ByaNX+w=="],
+ "@react-navigation/native-stack": ["@react-navigation/native-stack@7.2.0", "", { "dependencies": { "@react-navigation/elements": "^2.2.5", "warn-once": "^0.1.1" }, "peerDependencies": { "@react-navigation/native": "^7.0.14", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-mw7Nq9qQrGsmJmCTwIIWB7yY/3tWYXvQswx+HJScGAadIjemvytJXm1fcl3+YZ9T9Ym0aERcVe5kDs+ny3X4vA=="],
+
+ "@react-navigation/routers": ["@react-navigation/routers@7.1.2", "", { "dependencies": { "nanoid": "3.3.8" } }, "sha512-emdEjpVDK8zbiu2GChC8oYIAub9i/OpNuQJekVsbyFCBz4/TzaBzms38Q53YaNhdIFNmiYLfHv/Y1Ub7KYfm3w=="],
+
+ "@remix-run/node": ["@remix-run/node@2.15.3", "", { "dependencies": { "@remix-run/server-runtime": "2.15.3", "@remix-run/web-fetch": "^4.4.2", "@web3-storage/multipart-parser": "^1.0.0", "cookie-signature": "^1.1.0", "source-map-support": "^0.5.21", "stream-slice": "^0.1.2", "undici": "^6.11.1" }, "peerDependencies": { "typescript": "^5.1.0" }, "optionalPeers": ["typescript"] }, "sha512-TYfS6BPhbABBpSRZ6WBA4qIWSwWvJhRVQGXCHUtgOwkuW863rcFmjh9g2Xj/IHyTmbOYPdcjHsIgZ9el4CHOKQ=="],
+
+ "@remix-run/router": ["@remix-run/router@1.22.0", "", {}, "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw=="],
+
+ "@remix-run/server-runtime": ["@remix-run/server-runtime@2.15.3", "", { "dependencies": { "@remix-run/router": "1.22.0", "@types/cookie": "^0.6.0", "@web3-storage/multipart-parser": "^1.0.0", "cookie": "^0.6.0", "set-cookie-parser": "^2.4.8", "source-map": "^0.7.3", "turbo-stream": "2.4.0" }, "peerDependencies": { "typescript": "^5.1.0" }, "optionalPeers": ["typescript"] }, "sha512-taHBe1DEqxZNjjj6OfkSYbup+sZPjbTgUhykaI+nHqrC2NDQuTiisBXhLwtx60GctONR/x0lWhF7R9ZGC5WsHw=="],
+
+ "@remix-run/web-blob": ["@remix-run/web-blob@3.1.0", "", { "dependencies": { "@remix-run/web-stream": "^1.1.0", "web-encoding": "1.1.5" } }, "sha512-owGzFLbqPH9PlKb8KvpNJ0NO74HWE2euAn61eEiyCXX/oteoVzTVSN8mpLgDjaxBf2btj5/nUllSUgpyd6IH6g=="],
+
+ "@remix-run/web-fetch": ["@remix-run/web-fetch@4.4.2", "", { "dependencies": { "@remix-run/web-blob": "^3.1.0", "@remix-run/web-file": "^3.1.0", "@remix-run/web-form-data": "^3.1.0", "@remix-run/web-stream": "^1.1.0", "@web3-storage/multipart-parser": "^1.0.0", "abort-controller": "^3.0.0", "data-uri-to-buffer": "^3.0.1", "mrmime": "^1.0.0" } }, "sha512-jgKfzA713/4kAW/oZ4bC3MoLWyjModOVDjFPNseVqcJKSafgIscrYL9G50SurEYLswPuoU3HzSbO0jQCMYWHhA=="],
+
+ "@remix-run/web-file": ["@remix-run/web-file@3.1.0", "", { "dependencies": { "@remix-run/web-blob": "^3.1.0" } }, "sha512-dW2MNGwoiEYhlspOAXFBasmLeYshyAyhIdrlXBi06Duex5tDr3ut2LFKVj7tyHLmn8nnNwFf1BjNbkQpygC2aQ=="],
+
+ "@remix-run/web-form-data": ["@remix-run/web-form-data@3.1.0", "", { "dependencies": { "web-encoding": "1.1.5" } }, "sha512-NdeohLMdrb+pHxMQ/Geuzdp0eqPbea+Ieo8M8Jx2lGC6TBHsgHzYcBvr0LyPdPVycNRDEpWpiDdCOdCryo3f9A=="],
+
+ "@remix-run/web-stream": ["@remix-run/web-stream@1.1.0", "", { "dependencies": { "web-streams-polyfill": "^3.1.1" } }, "sha512-KRJtwrjRV5Bb+pM7zxcTJkhIqWWSy+MYsIxHK+0m5atcznsf15YwUBWHWulZerV2+vvHH1Lp1DD7pw6qKW8SgA=="],
+
+ "@segment/loosely-validate-event": ["@segment/loosely-validate-event@2.0.0", "", { "dependencies": { "component-type": "^1.2.1", "join-component": "^1.1.0" } }, "sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw=="],
+
+ "@shopify/flash-list": ["@shopify/flash-list@1.7.3", "", { "dependencies": { "recyclerlistview": "4.2.1", "tslib": "2.8.1" }, "peerDependencies": { "@babel/runtime": "*", "react": "*", "react-native": "*" } }, "sha512-RLhNptm02aqpqZvjj9pJPcU+EVYxOAJhPRCmDOaUbUP86+636w+plsbjpBPSYGvPZhPj56RtZ9FBlvolPeEmYA=="],
"@sideway/address": ["@sideway/address@4.1.5", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q=="],
@@ -590,41 +676,27 @@
"@sinonjs/fake-timers": ["@sinonjs/fake-timers@10.3.0", "", { "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA=="],
- "@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.4.0", "", {}, "sha512-RPfGuk2bDZgcu9bAJodvO2lnZeHuz4/71HjZ0bGb/SPg8+lyTA+RLSKQvo7fSmPSi8/vcH3aKQ8EM9ywf1olaw=="],
+ "@tanstack/query-core": ["@tanstack/query-core@5.66.4", "", {}, "sha512-skM/gzNX4shPkqmdTCSoHtJAPMTtmIJNS0hE+xwTTUVYwezArCT34NMermABmBVUg5Ls5aiUXEDXfqwR1oVkcA=="],
- "@tanstack/pacer": ["@tanstack/pacer@0.17.1", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.0", "@tanstack/store": "^0.8.0" } }, "sha512-52GytGu07L73lNCWB1N02NWBp/tzK2jZ20U8sFInXyiq2KHtHxbXaN1Qw/MR1REqFIKgEy5DOBNZRjuSy5zaRg=="],
-
- "@tanstack/query-core": ["@tanstack/query-core@5.90.16", "", {}, "sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww=="],
-
- "@tanstack/query-persist-client-core": ["@tanstack/query-persist-client-core@5.91.15", "", { "dependencies": { "@tanstack/query-core": "5.90.16" } }, "sha512-vnPSfQVo41EKJN8v20nkhWNZPyB1dMJIy5icOvCGzcCJzsmRefYY1owtr63ICOcjOiPPTuNEfPsdjdBhkzYnmA=="],
-
- "@tanstack/query-sync-storage-persister": ["@tanstack/query-sync-storage-persister@5.90.18", "", { "dependencies": { "@tanstack/query-core": "5.90.16", "@tanstack/query-persist-client-core": "5.91.15" } }, "sha512-tKngFopz/TuAe7LBDg7IOhWPh9blxdQ6QG/vVL2dFzRmlPNcSo4WdCSONqSDioJkcyTwh1YCSlcikmJ1WnSb3Q=="],
-
- "@tanstack/react-pacer": ["@tanstack/react-pacer@0.19.1", "", { "dependencies": { "@tanstack/pacer": "0.17.1", "@tanstack/react-store": "^0.8.0" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-wfGwKLo2gosKr5tsXico+jWJ8LsWsBC8MA1HVtUY/D6dhFduEVizKxRUcvP60I3dRvnoXDbN202g4feJHlivnA=="],
-
- "@tanstack/react-query": ["@tanstack/react-query@5.90.17", "", { "dependencies": { "@tanstack/query-core": "5.90.17" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-PGc2u9KLwohDUSchjW9MZqeDQJfJDON7y4W7REdNBgiFKxQy+Pf7eGjiFWEj5xPqKzAeHYdAb62IWI1a9UJyGQ=="],
-
- "@tanstack/react-query-persist-client": ["@tanstack/react-query-persist-client@5.90.18", "", { "dependencies": { "@tanstack/query-persist-client-core": "5.91.15" }, "peerDependencies": { "@tanstack/react-query": "^5.90.16", "react": "^18 || ^19" } }, "sha512-ToVRTVpjzTrd9S/p7JIvGdLs+Xtz9aDMM/7+TQGSV9notY8Jt64irfAAAkZ05syftLKS+3KPgyKAnHcVeKVbWQ=="],
-
- "@tanstack/react-store": ["@tanstack/react-store@0.8.0", "", { "dependencies": { "@tanstack/store": "0.8.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow=="],
-
- "@tanstack/store": ["@tanstack/store@0.8.0", "", {}, "sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ=="],
+ "@tanstack/react-query": ["@tanstack/react-query@5.66.9", "", { "dependencies": { "@tanstack/query-core": "5.66.4" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-NRI02PHJsP5y2gAuWKP+awamTIBFBSKMnO6UVzi03GTclmHHHInH5UzVgzi5tpu4+FmGfsdT7Umqegobtsp23A=="],
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
- "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
+ "@types/babel__generator": ["@types/babel__generator@7.6.8", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw=="],
"@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
- "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
+ "@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="],
+
+ "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
"@types/graceful-fs": ["@types/graceful-fs@4.1.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ=="],
"@types/hammerjs": ["@types/hammerjs@2.0.46", "", {}, "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw=="],
- "@types/hoist-non-react-statics": ["@types/hoist-non-react-statics@3.3.7", "", { "dependencies": { "hoist-non-react-statics": "^3.3.0" }, "peerDependencies": { "@types/react": "*" } }, "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g=="],
+ "@types/hoist-non-react-statics": ["@types/hoist-non-react-statics@3.3.6", "", { "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" } }, "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw=="],
"@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="],
@@ -634,73 +706,69 @@
"@types/jest": ["@types/jest@29.5.14", "", { "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ=="],
- "@types/lodash": ["@types/lodash@4.17.23", "", {}, "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA=="],
+ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
- "@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="],
+ "@types/lodash": ["@types/lodash@4.17.15", "", {}, "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw=="],
- "@types/react": ["@types/react@19.1.17", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA=="],
+ "@types/node": ["@types/node@10.17.60", "", {}, "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="],
- "@types/react-test-renderer": ["@types/react-test-renderer@19.1.0", "", { "dependencies": { "@types/react": "*" } }, "sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ=="],
+ "@types/node-forge": ["@types/node-forge@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ=="],
+
+ "@types/prop-types": ["@types/prop-types@15.7.14", "", {}, "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="],
+
+ "@types/react": ["@types/react@18.3.18", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ=="],
+
+ "@types/react-native": ["@types/react-native@0.70.19", "", { "dependencies": { "@types/react": "*" } }, "sha512-c6WbyCgWTBgKKMESj/8b4w+zWcZSsCforson7UdXtXMecG3MxCinYi6ihhrHVPyUrVzORsvEzK8zg32z4pK6Sg=="],
+
+ "@types/react-native-vector-icons": ["@types/react-native-vector-icons@6.4.18", "", { "dependencies": { "@types/react": "*", "@types/react-native": "^0.70" } }, "sha512-YGlNWb+k5laTBHd7+uZowB9DpIK3SXUneZqAiKQaj1jnJCZM0x71GDim5JCTMi4IFkhc9m8H/Gm28T5BjyivUw=="],
+
+ "@types/react-test-renderer": ["@types/react-test-renderer@19.0.0", "", { "dependencies": { "@types/react": "*" } }, "sha512-qDVnNybqFm2eZKJ4jD34EvRd6VHD67KjgnWaEMM0Id9L22EpWe3nOSVKHWL1XWRCxUWe3lhXwlEeCKD1BlJCQA=="],
"@types/stack-utils": ["@types/stack-utils@2.0.3", "", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="],
- "@types/yargs": ["@types/yargs@17.0.34", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A=="],
+ "@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="],
+
+ "@types/yargs": ["@types/yargs@17.0.33", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA=="],
"@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="],
- "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
+ "@urql/core": ["@urql/core@5.1.0", "", { "dependencies": { "@0no-co/graphql.web": "^1.0.5", "wonka": "^6.3.2" } }, "sha512-yC3sw8yqjbX45GbXxfiBY8GLYCiyW/hLBbQF9l3TJrv4ro00Y0ChkKaD9I2KntRxAVm9IYBqh0awX8fwWAe/Yw=="],
- "@urql/core": ["@urql/core@5.2.0", "", { "dependencies": { "@0no-co/graphql.web": "^1.0.13", "wonka": "^6.3.2" } }, "sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A=="],
+ "@urql/exchange-retry": ["@urql/exchange-retry@1.3.0", "", { "dependencies": { "@urql/core": "^5.0.0", "wonka": "^6.3.2" } }, "sha512-FLt+d81gP4oiHah4hWFDApimc+/xABWMU1AMYsZ1PVB0L0YPtrMCjbOp9WMM7hBzy4gbTDrG24sio0dCfSh/HQ=="],
- "@urql/exchange-retry": ["@urql/exchange-retry@1.3.2", "", { "dependencies": { "@urql/core": "^5.1.2", "wonka": "^6.3.2" } }, "sha512-TQMCz2pFJMfpNxmSfX1VSfTjwUIFx/mL+p1bnfM1xjjdla7Z+KnGMW/EhFbpckp3LyWAH4PgOsMwOMnIN+MBFg=="],
+ "@web3-storage/multipart-parser": ["@web3-storage/multipart-parser@1.0.0", "", {}, "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw=="],
- "@vibrant/color": ["@vibrant/color@4.0.0", "", {}, "sha512-S9ItdqS1135wTXoIIqAJu8df9dqlOo6Boc5Y4MGsBTu9UmUOvOwfj5b4Ga6S5yrLAKmKYIactkz7zYJdMddkig=="],
-
- "@vibrant/core": ["@vibrant/core@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/generator": "^4.0.0", "@vibrant/image": "^4.0.0", "@vibrant/quantizer": "^4.0.0", "@vibrant/worker": "^4.0.0" } }, "sha512-fqlVRUTDjEws9VNKvI3cDXM4wUT7fMFS+cVqEjJk3im+R5EvjJzPF6OAbNhfPzW04NvHNE555eY9FfhYuX3PRw=="],
-
- "@vibrant/generator": ["@vibrant/generator@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/types": "^4.0.0" } }, "sha512-CqKAjmgHVDXJVo3Q5+9pUJOvksR7cN3bzx/6MbURYh7lA4rhsIewkUK155M6q0vfcUN3ETi/eTneCi0tLuM2Sg=="],
-
- "@vibrant/generator-default": ["@vibrant/generator-default@4.0.3", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/generator": "^4.0.0" } }, "sha512-HZlfp19sDokODEkZF4p70QceARHgjP3a1Dmxg+dlblYMJM98jPq+azA0fzqKNR7R17JJNHxexpJEepEsNlG0gw=="],
-
- "@vibrant/image": ["@vibrant/image@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0" } }, "sha512-Asv/7R/L701norosgvbjOVkodFiwcFihkXixA/gbAd6C+5GCts1Wm1NPk14FNKnM7eKkfAN+0wwPkdOH+PY/YA=="],
-
- "@vibrant/image-browser": ["@vibrant/image-browser@4.0.0", "", { "dependencies": { "@vibrant/image": "^4.0.0" } }, "sha512-mXckzvJWiP575Y/wNtP87W/TPgyJoGlPBjW4E9YmNS6n4Jb6RqyHQA0ZVulqDslOxjSsihDzY7gpAORRclaoLg=="],
-
- "@vibrant/image-node": ["@vibrant/image-node@4.0.0", "", { "dependencies": { "@jimp/custom": "^0.22.12", "@jimp/plugin-resize": "^0.22.12", "@jimp/types": "^0.22.12", "@vibrant/image": "^4.0.0" } }, "sha512-m7yfnQtmo2y8z+tOjRFBx6q/qGnhl/ax2uCaj4TBkm4TtXfR4Dsn90wT6OWXmCFFzxIKHXKKEBShkxR+4RHseA=="],
-
- "@vibrant/quantizer": ["@vibrant/quantizer@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/image": "^4.0.0", "@vibrant/types": "^4.0.0" } }, "sha512-YDGxmCv/RvHFtZghDlVRwH5GMxdGGozWS1JpUOUt73/F5zAKGiiier8F31K1npIXARn6/Gspvg/Rbg7qqyEr2A=="],
-
- "@vibrant/quantizer-mmcq": ["@vibrant/quantizer-mmcq@4.0.0", "", { "dependencies": { "@vibrant/color": "^4.0.0", "@vibrant/image": "^4.0.0", "@vibrant/quantizer": "^4.0.0" } }, "sha512-TZqNiRoGGyCP8fH1XE6rvhFwLNv9D8MP1Xhz3K8tsuUweC6buWax3qLfrfEnkhtQnPJHaqvTfTOlIIXVMfRpow=="],
-
- "@vibrant/types": ["@vibrant/types@4.0.0", "", {}, "sha512-tA5TAbuROXcPkt+PWjmGfoaiEXyySVaNnCZovf6vXhCbMdrTTCQXvNCde2geiVl6YwtuU/Qrj9iZxS5jZ6yVIw=="],
-
- "@vibrant/worker": ["@vibrant/worker@4.0.0", "", { "dependencies": { "@vibrant/types": "^4.0.0" } }, "sha512-nSaZZwWQKOgN/nPYUAIRF0/uoa7KpK91A+gjLmZZDgfN1enqxaiihmn+75ayNadW0c6cxAEpEFEHTONR5u9tMw=="],
-
- "@vscode/sudo-prompt": ["@vscode/sudo-prompt@9.3.1", "", {}, "sha512-9ORTwwS74VaTn38tNbQhsA5U44zkJfcb0BdTSyyG6frP4e8KMtHuTXYmwefe5dpL8XB1aGSIVTaLjD3BbWb5iA=="],
-
- "@xmldom/xmldom": ["@xmldom/xmldom@0.8.11", "", {}, "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw=="],
+ "@xmldom/xmldom": ["@xmldom/xmldom@0.7.13", "", {}, "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g=="],
"@yarnpkg/lockfile": ["@yarnpkg/lockfile@1.1.0", "", {}, "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="],
+ "@zxing/text-encoding": ["@zxing/text-encoding@0.9.0", "", {}, "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA=="],
+
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
- "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
+ "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
- "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
+ "add": ["add@2.0.6", "", {}, "sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q=="],
+
+ "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
+ "ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="],
+
+ "ajv-keywords": ["ajv-keywords@5.1.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3" }, "peerDependencies": { "ajv": "^8.8.2" } }, "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw=="],
+
"anser": ["anser@1.4.10", "", {}, "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww=="],
- "ansi-escapes": ["ansi-escapes@7.2.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw=="],
+ "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
"ansi-fragments": ["ansi-fragments@0.2.1", "", { "dependencies": { "colorette": "^1.0.7", "slice-ansi": "^2.0.0", "strip-ansi": "^5.0.0" } }, "sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w=="],
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
- "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"any-base": ["any-base@1.1.0", "", {}, "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="],
@@ -710,25 +778,35 @@
"appdirsjs": ["appdirsjs@1.2.7", "", {}, "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw=="],
- "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
+ "application-config-path": ["application-config-path@0.1.1", "", {}, "sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw=="],
+
+ "arg": ["arg@4.1.0", "", {}, "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
- "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
+ "aria-hidden": ["aria-hidden@1.2.4", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A=="],
+
+ "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="],
"asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="],
"assert": ["assert@2.1.0", "", { "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", "object-is": "^1.1.5", "object.assign": "^4.1.4", "util": "^0.12.5" } }, "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw=="],
+ "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
+
"astral-regex": ["astral-regex@1.0.0", "", {}, "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg=="],
"async-limiter": ["async-limiter@1.0.1", "", {}, "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
+ "at-least-node": ["at-least-node@1.0.0", "", {}, "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="],
+
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
- "axios": ["axios@1.13.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA=="],
+ "axios": ["axios@1.7.9", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw=="],
+
+ "babel-core": ["babel-core@7.0.0-bridge.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg=="],
"babel-jest": ["babel-jest@29.7.0", "", { "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, "peerDependencies": { "@babel/core": "^7.8.0" } }, "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg=="],
@@ -736,23 +814,21 @@
"babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@29.6.3", "", { "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" } }, "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg=="],
- "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="],
+ "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.12", "", { "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og=="],
- "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="],
+ "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.11.1", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3", "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ=="],
- "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="],
+ "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.3", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q=="],
- "babel-plugin-react-compiler": ["babel-plugin-react-compiler@1.0.0", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw=="],
+ "babel-plugin-react-native-web": ["babel-plugin-react-native-web@0.19.13", "", {}, "sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ=="],
- "babel-plugin-react-native-web": ["babel-plugin-react-native-web@0.21.2", "", {}, "sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA=="],
-
- "babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.29.1", "", { "dependencies": { "hermes-parser": "0.29.1" } }, "sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA=="],
+ "babel-plugin-syntax-hermes-parser": ["babel-plugin-syntax-hermes-parser@0.25.1", "", { "dependencies": { "hermes-parser": "0.25.1" } }, "sha512-IVNpGzboFLfXZUAwkLFcI/bnqVbwky0jP3eBno4HKtqvQJAHBLdgxiG6lQ4to0+Q/YCN3PO0od5NZwIKyY4REQ=="],
"babel-plugin-transform-flow-enums": ["babel-plugin-transform-flow-enums@0.0.2", "", { "dependencies": { "@babel/plugin-syntax-flow": "^7.12.1" } }, "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ=="],
- "babel-preset-current-node-syntax": ["babel-preset-current-node-syntax@1.2.0", "", { "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg=="],
+ "babel-preset-current-node-syntax": ["babel-preset-current-node-syntax@1.1.0", "", { "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw=="],
- "babel-preset-expo": ["babel-preset-expo@54.0.9", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-transform-class-static-block": "^7.27.1", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.0", "@react-native/babel-preset": "0.81.5", "babel-plugin-react-compiler": "^1.0.0", "babel-plugin-react-native-web": "~0.21.0", "babel-plugin-syntax-hermes-parser": "^0.29.1", "babel-plugin-transform-flow-enums": "^0.0.2", "debug": "^4.3.4", "resolve-from": "^5.0.0" }, "peerDependencies": { "@babel/runtime": "^7.20.0", "expo": "*", "react-refresh": ">=0.14.0 <1.0.0" }, "optionalPeers": ["@babel/runtime", "expo"] }, "sha512-8J6hRdgEC2eJobjoft6mKJ294cLxmi3khCUy2JJQp4htOYYkllSLUq6vudWJkTJiIuGdVR4bR6xuz2EvJLWHNg=="],
+ "babel-preset-expo": ["babel-preset-expo@12.0.9", "", { "dependencies": { "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-transform-export-namespace-from": "^7.22.11", "@babel/plugin-transform-object-rest-spread": "^7.12.13", "@babel/plugin-transform-parameters": "^7.22.15", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.0", "@react-native/babel-preset": "0.76.7", "babel-plugin-react-native-web": "~0.19.13", "react-refresh": "^0.14.2" }, "peerDependencies": { "babel-plugin-react-compiler": "^19.0.0-beta-9ee70a1-20241017", "react-compiler-runtime": "^19.0.0-beta-8a03594-20241020" }, "optionalPeers": ["babel-plugin-react-compiler", "react-compiler-runtime"] }, "sha512-1c+ysrTavT49WgVAj0OX/TEzt1kU2mfPhDaDajstshNHXFKPenMPWSViA/DHrJKVIMwaqr+z3GbUOD9GtKgpdg=="],
"babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="],
@@ -762,8 +838,6 @@
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
- "baseline-browser-mapping": ["baseline-browser-mapping@2.8.25", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA=="],
-
"better-opn": ["better-opn@3.0.2", "", { "dependencies": { "open": "^8.0.4" } }, "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ=="],
"big-integer": ["big-integer@1.6.52", "", {}, "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="],
@@ -774,33 +848,45 @@
"bmp-js": ["bmp-js@0.1.0", "", {}, "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw=="],
- "body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g=="],
-
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
- "bplist-creator": ["bplist-creator@0.1.0", "", { "dependencies": { "stream-buffers": "2.2.x" } }, "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg=="],
+ "bplist-creator": ["bplist-creator@0.0.7", "", { "dependencies": { "stream-buffers": "~2.2.0" } }, "sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA=="],
"bplist-parser": ["bplist-parser@0.3.2", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ=="],
- "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
+ "brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
- "browserslist": ["browserslist@4.27.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", "electron-to-chromium": "^1.5.238", "node-releases": "^2.0.26", "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw=="],
+ "browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="],
"bser": ["bser@2.1.1", "", { "dependencies": { "node-int64": "^0.4.0" } }, "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ=="],
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
+ "buffer-alloc": ["buffer-alloc@1.2.0", "", { "dependencies": { "buffer-alloc-unsafe": "^1.1.0", "buffer-fill": "^1.0.0" } }, "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow=="],
+
+ "buffer-alloc-unsafe": ["buffer-alloc-unsafe@1.1.0", "", {}, "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="],
+
+ "buffer-equal": ["buffer-equal@0.0.1", "", {}, "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA=="],
+
+ "buffer-fill": ["buffer-fill@1.0.0", "", {}, "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ=="],
+
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
+ "cacache": ["cacache@18.0.4", "", { "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^10.0.1", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", "ssri": "^10.0.0", "tar": "^6.1.11", "unique-filename": "^3.0.0" } }, "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ=="],
+
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
- "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
+ "call-bound": ["call-bound@1.0.3", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "get-intrinsic": "^1.2.6" } }, "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA=="],
+
+ "caller-callsite": ["caller-callsite@2.0.0", "", { "dependencies": { "callsites": "^2.0.0" } }, "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ=="],
+
+ "caller-path": ["caller-path@2.0.0", "", { "dependencies": { "caller-callsite": "^2.0.0" } }, "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
@@ -810,13 +896,17 @@
"camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="],
- "caniuse-lite": ["caniuse-lite@1.0.30001754", "", {}, "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg=="],
+ "caniuse-lite": ["caniuse-lite@1.0.30001700", "", {}, "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ=="],
+
+ "centra": ["centra@2.7.0", "", { "dependencies": { "follow-redirects": "^1.15.6" } }, "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
+ "charenc": ["charenc@0.0.2", "", {}, "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA=="],
+
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
- "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
+ "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
"chrome-launcher": ["chrome-launcher@0.15.2", "", { "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", "is-wsl": "^2.2.0", "lighthouse-logger": "^1.0.0" }, "bin": { "print-chrome-path": "bin/print-chrome-path.js" } }, "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ=="],
@@ -824,27 +914,29 @@
"ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
+ "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="],
+
"cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="],
"cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
- "cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="],
-
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
"clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="],
- "color": ["color@5.0.2", "", { "dependencies": { "color-convert": "^3.0.1", "color-string": "^2.0.0" } }, "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA=="],
+ "clone-deep": ["clone-deep@4.0.1", "", { "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", "shallow-clone": "^3.0.0" } }, "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ=="],
- "color-convert": ["color-convert@3.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg=="],
+ "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
- "color-name": ["color-name@2.0.2", "", {}, "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A=="],
+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
- "color-string": ["color-string@2.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA=="],
+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
- "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
+ "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
+
+ "colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
@@ -852,28 +944,34 @@
"commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="],
+ "commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="],
+
+ "component-type": ["component-type@1.2.2", "", {}, "sha512-99VUHREHiN5cLeHm3YLq312p6v+HUEcwtLCAtelvUDI6+SH5g5Cr85oNR2S1o6ywzL0ykMbuwLzM2ANocjEOIA=="],
+
"compressible": ["compressible@2.0.18", "", { "dependencies": { "mime-db": ">= 1.43.0 < 2" } }, "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg=="],
- "compression": ["compression@1.8.1", "", { "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" } }, "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w=="],
+ "compression": ["compression@1.8.0", "", { "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", "on-headers": "~1.0.2", "safe-buffer": "5.2.1", "vary": "~1.1.2" } }, "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"connect": ["connect@3.7.0", "", { "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", "parseurl": "~1.3.3", "utils-merge": "1.0.1" } }, "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ=="],
- "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
-
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
- "core-js-compat": ["core-js-compat@3.46.0", "", { "dependencies": { "browserslist": "^4.26.3" } }, "sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law=="],
+ "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
+
+ "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
+
+ "core-js-compat": ["core-js-compat@3.40.0", "", { "dependencies": { "browserslist": "^4.24.3" } }, "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ=="],
"cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="],
- "cross-env": ["cross-env@10.1.0", "", { "dependencies": { "@epic-web/invariant": "^1.0.0", "cross-spawn": "^7.0.6" }, "bin": { "cross-env": "dist/bin/cross-env.js", "cross-env-shell": "dist/bin/cross-env-shell.js" } }, "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw=="],
-
"cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+ "crypt": ["crypt@0.0.2", "", {}, "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="],
+
"crypto-random-string": ["crypto-random-string@2.0.0", "", {}, "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="],
"css-color-keywords": ["css-color-keywords@1.0.0", "", {}, "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg=="],
@@ -882,21 +980,23 @@
"css-mediaquery": ["css-mediaquery@0.1.2", "", {}, "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q=="],
- "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
+ "css-select": ["css-select@5.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg=="],
"css-to-react-native": ["css-to-react-native@3.2.0", "", { "dependencies": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", "postcss-value-parser": "^4.0.2" } }, "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ=="],
"css-tree": ["css-tree@1.1.3", "", { "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" } }, "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q=="],
- "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
+ "css-what": ["css-what@6.1.0", "", {}, "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="],
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
- "dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="],
+ "data-uri-to-buffer": ["data-uri-to-buffer@3.0.1", "", {}, "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og=="],
- "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+ "dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="],
+
+ "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
@@ -906,6 +1006,8 @@
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
+ "default-gateway": ["default-gateway@4.2.0", "", { "dependencies": { "execa": "^1.0.0", "ip-regex": "^2.1.0" } }, "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA=="],
+
"defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="],
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
@@ -914,13 +1016,15 @@
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
+ "del": ["del@6.1.1", "", { "dependencies": { "globby": "^11.0.1", "graceful-fs": "^4.2.4", "is-glob": "^4.0.1", "is-path-cwd": "^2.2.0", "is-path-inside": "^3.0.2", "p-map": "^4.0.0", "rimraf": "^3.0.2", "slash": "^3.0.0" } }, "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg=="],
+
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
"destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="],
- "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
+ "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
@@ -928,10 +1032,14 @@
"diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="],
+ "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
+
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
+ "dom-walk": ["dom-walk@0.1.2", "", {}, "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="],
+
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
@@ -948,23 +1056,25 @@
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
- "electron-to-chromium": ["electron-to-chromium@1.5.249", "", {}, "sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg=="],
+ "electron-to-chromium": ["electron-to-chromium@1.5.103", "", {}, "sha512-P6+XzIkfndgsrjROJWfSvVEgNHtPgbhVyTkwLjUM2HU/h7pZRORgaTlHqfAikqxKmdJMLW8fftrdGWbd/Ds0FA=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
+ "end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="],
+
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"env-editor": ["env-editor@0.4.2", "", {}, "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA=="],
"env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
- "envinfo": ["envinfo@7.20.0", "", { "bin": { "envinfo": "dist/cli.js" } }, "sha512-+zUomDcLXsVkQ37vUqWBvQwLaLlj8eZPSi61llaEFAVBY5mhcXdaSw1pSJVl4yTYD5g/gEfpNl28YYk4IPvrrg=="],
+ "envinfo": ["envinfo@7.14.0", "", { "bin": { "envinfo": "dist/cli.js" } }, "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg=="],
- "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
+ "eol": ["eol@0.9.1", "", {}, "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg=="],
- "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="],
+ "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
"error-stack-parser": ["error-stack-parser@2.1.4", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ=="],
@@ -986,12 +1096,12 @@
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
+ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
+
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
- "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
-
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
"exec-async": ["exec-async@2.2.0", "", {}, "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw=="],
@@ -1002,109 +1112,117 @@
"expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="],
- "expo": ["expo@54.0.31", "", { "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "54.0.21", "@expo/config": "~12.0.13", "@expo/config-plugins": "~54.0.4", "@expo/devtools": "0.1.8", "@expo/fingerprint": "0.15.4", "@expo/metro": "~54.2.0", "@expo/metro-config": "54.0.13", "@expo/vector-icons": "^15.0.3", "@ungap/structured-clone": "^1.3.0", "babel-preset-expo": "~54.0.9", "expo-asset": "~12.0.12", "expo-constants": "~18.0.13", "expo-file-system": "~19.0.21", "expo-font": "~14.0.10", "expo-keep-awake": "~15.0.8", "expo-modules-autolinking": "3.0.24", "expo-modules-core": "3.0.29", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", "whatwg-url-without-unicode": "8.0.0-3" }, "peerDependencies": { "@expo/dom-webview": "*", "@expo/metro-runtime": "*", "react": "*", "react-native": "*", "react-native-webview": "*" }, "optionalPeers": ["@expo/dom-webview", "@expo/metro-runtime", "react-native-webview"], "bin": { "expo": "bin/cli", "fingerprint": "bin/fingerprint", "expo-modules-autolinking": "bin/autolinking" } }, "sha512-kQ3RDqA/a59I7y+oqQGyrPbbYlgPMUdKBOgvFLpoHbD2bCM+F75i4N0mUijy7dG5F/CUCu2qHmGGUCXBbMDkCg=="],
+ "expo": ["expo@52.0.37", "", { "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "0.22.18", "@expo/config": "~10.0.10", "@expo/config-plugins": "~9.0.15", "@expo/fingerprint": "0.11.11", "@expo/metro-config": "0.19.11", "@expo/vector-icons": "^14.0.0", "babel-preset-expo": "~12.0.9", "expo-asset": "~11.0.4", "expo-constants": "~17.0.7", "expo-file-system": "~18.0.11", "expo-font": "~13.0.4", "expo-keep-awake": "~14.0.3", "expo-modules-autolinking": "2.0.8", "expo-modules-core": "2.2.2", "fbemitter": "^3.0.0", "web-streams-polyfill": "^3.3.2", "whatwg-url-without-unicode": "8.0.0-3" }, "peerDependencies": { "@expo/dom-webview": "*", "@expo/metro-runtime": "*", "react": "*", "react-native": "*", "react-native-webview": "*" }, "optionalPeers": ["@expo/dom-webview", "@expo/metro-runtime", "react-native-webview"], "bin": { "expo": "bin/cli" } }, "sha512-fo37ClqjNLOVInerm7BU27H8lfPfeTC7Pmu72roPzq46DnJfs+KzTxTzE34GcJ0b6hMUx9FRSSGyTQqxzo2TVQ=="],
- "expo-application": ["expo-application@7.0.8", "", { "peerDependencies": { "expo": "*" } }, "sha512-qFGyxk7VJbrNOQWBbE09XUuGuvkOgFS9QfToaK2FdagM2aQ+x3CvGV2DuVgl/l4ZxPgIf3b/MNh9xHpwSwn74Q=="],
+ "expo-application": ["expo-application@6.0.2", "", { "peerDependencies": { "expo": "*" } }, "sha512-qcj6kGq3mc7x5yIb5KxESurFTJCoEKwNEL34RdPEvTB/xhl7SeVZlu05sZBqxB1V4Ryzq/LsCb7NHNfBbb3L7A=="],
- "expo-asset": ["expo-asset@12.0.12", "", { "dependencies": { "@expo/image-utils": "^0.8.8", "expo-constants": "~18.0.12" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-CsXFCQbx2fElSMn0lyTdRIyKlSXOal6ilLJd+yeZ6xaC7I9AICQgscY5nj0QcwgA+KYYCCEQEBndMsmj7drOWQ=="],
+ "expo-asset": ["expo-asset@11.0.4", "", { "dependencies": { "@expo/image-utils": "^0.6.5", "expo-constants": "~17.0.7", "invariant": "^2.2.4", "md5-file": "^3.2.3" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-CdIywU0HrR3wsW5c3n0cT3jW9hccZdnqGsRqY+EY/RWzJbDXtDfAQVEiFHO3mDK7oveUwrP2jK/6ZRNek41/sg=="],
- "expo-background-task": ["expo-background-task@1.0.10", "", { "dependencies": { "expo-task-manager": "~14.0.9" }, "peerDependencies": { "expo": "*" } }, "sha512-EbPnuf52Ps/RJiaSFwqKGT6TkvMChv7bI0wF42eADbH3J2EMm5y5Qvj0oFmF1CBOwc3mUhqj63o7Pl6OLkGPZQ=="],
+ "expo-background-fetch": ["expo-background-fetch@13.0.5", "", { "dependencies": { "expo-task-manager": "~12.0.5" }, "peerDependencies": { "expo": "*" } }, "sha512-rLRM+rYDRT0fA0Oaet5ibJK3nKVRkfdjXjISHxjUvIE4ktD9pE+UjAPPdjTXZ5CkNb3JyNNhQGJEGpdJC2HLKw=="],
- "expo-blur": ["expo-blur@15.0.8", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-rWyE1NBRZEu9WD+X+5l7gyPRszw7n12cW3IRNAb5i6KFzaBp8cxqT5oeaphJapqURvcqhkOZn2k5EtBSbsuU7w=="],
+ "expo-blur": ["expo-blur@14.0.3", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-BL3xnqBJbYm3Hg9t/HjNjdeY7N/q8eK5tsLYxswWG1yElISWZmMvrXYekl7XaVCPfyFyz8vQeaxd7q74ZY3Wrw=="],
- "expo-brightness": ["expo-brightness@14.0.8", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-WOg3UxzkHFTKBW3XvROlrVRmnJmZLhGBGd1RdzTfrtt2/MdSzvVmCevqWh4bohkeLABh0Yc9YRo1vFgfT73DWw=="],
+ "expo-brightness": ["expo-brightness@13.0.3", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-yR6+mM1hYEtt9ygbUNIOgsvsRxNReMr0cSZReu14rlTktq4K73bDuDs8oGuYl0c68wIVNK0AAfIJAF9RBePPMw=="],
- "expo-build-properties": ["expo-build-properties@1.0.10", "", { "dependencies": { "ajv": "^8.11.0", "semver": "^7.6.0" }, "peerDependencies": { "expo": "*" } }, "sha512-mFCZbrbrv0AP5RB151tAoRzwRJelqM7bCJzCkxpu+owOyH+p/rFC/q7H5q8B9EpVWj8etaIuszR+gKwohpmu1Q=="],
+ "expo-build-properties": ["expo-build-properties@0.13.2", "", { "dependencies": { "ajv": "^8.11.0", "semver": "^7.6.0" }, "peerDependencies": { "expo": "*" } }, "sha512-ML2GwBgn0Bo4yPgnSGb7h3XVxCigS/KFdid3xPC2HldEioTP3UewB/2Qa4WBsam9Fb7lAuRyVHAfRoA3swpDzg=="],
- "expo-constants": ["expo-constants@18.0.13", "", { "dependencies": { "@expo/config": "~12.0.13", "@expo/env": "~2.0.8" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ=="],
+ "expo-constants": ["expo-constants@17.0.7", "", { "dependencies": { "@expo/config": "~10.0.10", "@expo/env": "~0.4.2" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-sp5NUiV17I3JblVPIBDgoxgt7JIZS30vcyydCYHxsEoo+aKaeRYXxGYilCvb9lgI6BBwSL24sQ6ZjWsCWoF1VA=="],
- "expo-crypto": ["expo-crypto@15.0.8", "", { "dependencies": { "base64-js": "^1.3.0" }, "peerDependencies": { "expo": "*" } }, "sha512-aF7A914TB66WIlTJvl5J6/itejfY78O7dq3ibvFltL9vnTALJ/7LYHvLT4fwmx9yUNS6ekLBtDGWivFWnj2Fcw=="],
+ "expo-crypto": ["expo-crypto@14.0.2", "", { "dependencies": { "base64-js": "^1.3.0" }, "peerDependencies": { "expo": "*" } }, "sha512-WRc9PBpJraJN29VD5Ef7nCecxJmZNyRKcGkNiDQC1nhY5agppzwhqh7zEzNFarE/GqDgSiaDHS8yd5EgFhP9AQ=="],
- "expo-dev-client": ["expo-dev-client@6.0.20", "", { "dependencies": { "expo-dev-launcher": "6.0.20", "expo-dev-menu": "7.0.18", "expo-dev-menu-interface": "2.0.0", "expo-manifests": "~1.0.10", "expo-updates-interface": "~2.0.0" }, "peerDependencies": { "expo": "*" } }, "sha512-5XjoVlj1OxakNxy55j/AUaGPrDOlQlB6XdHLLWAw61w5ffSpUDHDnuZzKzs9xY1eIaogOqTOQaAzZ2ddBkdXLA=="],
+ "expo-dev-client": ["expo-dev-client@5.0.12", "", { "dependencies": { "expo-dev-launcher": "5.0.29", "expo-dev-menu": "6.0.19", "expo-dev-menu-interface": "1.9.3", "expo-manifests": "~0.15.5", "expo-updates-interface": "~1.0.0" }, "peerDependencies": { "expo": "*" } }, "sha512-F8Pz3ppxq0vhwVK2XgzmDUfxW1MEFpUdTLl+Pjwp9FDB+Br1wqyIz1yKshD7Hv1i/SR2BwjlJcriOPWt9NREuA=="],
- "expo-dev-launcher": ["expo-dev-launcher@6.0.20", "", { "dependencies": { "ajv": "^8.11.0", "expo-dev-menu": "7.0.18", "expo-manifests": "~1.0.10" }, "peerDependencies": { "expo": "*" } }, "sha512-a04zHEeT9sB0L5EB38fz7sNnUKJ2Ar1pXpcyl60Ki8bXPNCs9rjY7NuYrDkP/irM8+1DklMBqHpyHiLyJ/R+EA=="],
+ "expo-dev-launcher": ["expo-dev-launcher@5.0.29", "", { "dependencies": { "ajv": "8.11.0", "expo-dev-menu": "6.0.19", "expo-manifests": "~0.15.5", "resolve-from": "^5.0.0" }, "peerDependencies": { "expo": "*" } }, "sha512-wb48vIXUwuqD0Z2xvdkNt+o7OLT7FtMoSwYVkx28linG+9Sn2Zgjbs4SVnJYkpRfZZh9unOIRqB9EsJhM12Lzg=="],
- "expo-dev-menu": ["expo-dev-menu@7.0.18", "", { "dependencies": { "expo-dev-menu-interface": "2.0.0" }, "peerDependencies": { "expo": "*" } }, "sha512-4kTdlHrnZCAWCT6tZRQHSSjZ7vECFisL4T+nsG/GJDo/jcHNaOVGV5qPV9wzlTxyMk3YOPggRw4+g7Ownrg5eA=="],
+ "expo-dev-menu": ["expo-dev-menu@6.0.19", "", { "dependencies": { "expo-dev-menu-interface": "1.9.3" }, "peerDependencies": { "expo": "*" } }, "sha512-CzjsiUne/Zwr7/AqI5JwcocV2NKQ3lZ3GteVc7ksORU7UZr0a0uTgcmA4ogqbBnFebBRVntxPR6zKXYlsvUGow=="],
- "expo-dev-menu-interface": ["expo-dev-menu-interface@2.0.0", "", { "peerDependencies": { "expo": "*" } }, "sha512-BvAMPt6x+vyXpThsyjjOYyjwfjREV4OOpQkZ0tNl+nGpsPfcY9mc6DRACoWnH9KpLzyIt3BOgh3cuy/h/OxQjw=="],
+ "expo-dev-menu-interface": ["expo-dev-menu-interface@1.9.3", "", { "peerDependencies": { "expo": "*" } }, "sha512-KY/dWTBE1l47i9V366JN5rC6YIdOc9hz8yAmZzkl5DrPia5l3M2WIjtnpHC9zUkNjiSiG2urYoOAq4H/uLdmyg=="],
- "expo-device": ["expo-device@8.0.10", "", { "dependencies": { "ua-parser-js": "^0.7.33" }, "peerDependencies": { "expo": "*" } }, "sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA=="],
+ "expo-device": ["expo-device@7.0.2", "", { "dependencies": { "ua-parser-js": "^0.7.33" }, "peerDependencies": { "expo": "*" } }, "sha512-0PkTixE4Qi8VQBjixnj4aw2f6vE4tUZH7GK8zHROGKlBypZKcWmsA+W/Vp3RC5AyREjX71pO/hjKTSo/vF0E2w=="],
- "expo-doctor": ["expo-doctor@1.17.14", "", { "bin": { "expo-doctor": "build/index.js" } }, "sha512-+UsXFP5ZTVobDuGS5Du8aKU6O6s2sa49QOdGHdzP8UEjQKH8gPb59uw6hxEQmo6YtVboLwQd13QEdcSolBMvLw=="],
+ "expo-eas-client": ["expo-eas-client@0.13.2", "", {}, "sha512-2RAAGtkO9vseoJZuW4mhJkiNQ6+FfLrX66OTMq4Qj9mRKZV2Uq/ZquxUGIeJyYqBy4vNYeKbuPd2oJtsV9LBGQ=="],
- "expo-file-system": ["expo-file-system@19.0.21", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg=="],
+ "expo-file-system": ["expo-file-system@18.0.11", "", { "dependencies": { "web-streams-polyfill": "^3.3.2" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-yDwYfEzWgPXsBZHJW2RJ8Q66ceiFN9Wa5D20pp3fjXVkzPBDwxnYwiPWk4pVmCa5g4X5KYMoMne1pUrsL4OEpg=="],
- "expo-font": ["expo-font@14.0.10", "", { "dependencies": { "fontfaceobserver": "^2.1.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-UqyNaaLKRpj4pKAP4HZSLnuDQqueaO5tB1c/NWu5vh1/LF9ulItyyg2kF/IpeOp0DeOLk0GY0HrIXaKUMrwB+Q=="],
+ "expo-font": ["expo-font@13.0.4", "", { "dependencies": { "fontfaceobserver": "^2.1.0" }, "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-eAP5hyBgC8gafFtprsz0HMaB795qZfgJWqTmU0NfbSin1wUuVySFMEPMOrTkTgmazU73v4Cb4x7p86jY1XXYUw=="],
- "expo-haptics": ["expo-haptics@15.0.8", "", { "peerDependencies": { "expo": "*" } }, "sha512-lftutojy8Qs8zaDzzjwM3gKHFZ8bOOEZDCkmh2Ddpe95Ra6kt2izeOfOfKuP/QEh0MZ1j9TfqippyHdRd1ZM9g=="],
+ "expo-haptics": ["expo-haptics@14.0.1", "", { "peerDependencies": { "expo": "*" } }, "sha512-V81FZ7xRUfqM6uSI6FA1KnZ+QpEKnISqafob/xEfcx1ymwhm4V3snuLWWFjmAz+XaZQTqlYa8z3QbqEXz7G63w=="],
- "expo-image": ["expo-image@3.0.11", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA=="],
+ "expo-image": ["expo-image@2.0.6", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-NHpIZmGnrPbyDadil6eK+sUgyFMQfapEVb7YaGgxSFWBUQ1rSpjqdIQrCD24IZTO9uSH8V+hMh2ROxrAjAixzQ=="],
- "expo-json-utils": ["expo-json-utils@0.15.0", "", {}, "sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ=="],
+ "expo-json-utils": ["expo-json-utils@0.14.0", "", {}, "sha512-xjGfK9dL0B1wLnOqNkX0jM9p48Y0I5xEPzHude28LY67UmamUyAACkqhZGaPClyPNfdzczk7Ej6WaRMT3HfXvw=="],
- "expo-keep-awake": ["expo-keep-awake@15.0.8", "", { "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ=="],
+ "expo-keep-awake": ["expo-keep-awake@14.0.3", "", { "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-6Jh94G6NvTZfuLnm2vwIpKe3GdOiVBuISl7FI8GqN0/9UOg9E0WXXp5cDcfAG8bn80RfgLJS8P7EPUGTZyOvhg=="],
- "expo-linear-gradient": ["expo-linear-gradient@15.0.8", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-V2d8Wjn0VzhPHO+rrSBtcl+Fo+jUUccdlmQ6OoL9/XQB7Qk3d9lYrqKDJyccwDxmQT10JdST3Tmf2K52NLc3kw=="],
+ "expo-linear-gradient": ["expo-linear-gradient@14.0.2", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-nvac1sPUfFFJ4mY25UkvubpUV/olrBH+uQw5k+beqSvQaVQiUfFtYzfRr+6HhYBNb4AEsOtpsCRkpDww3M2iGQ=="],
- "expo-linking": ["expo-linking@8.0.11", "", { "dependencies": { "expo-constants": "~18.0.12", "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-+VSaNL5om3kOp/SSKO5qe6cFgfSIWnnQDSbA7XLs3ECkYzXRquk5unxNS3pg7eK5kNUmQ4kgLI7MhTggAEUBLA=="],
+ "expo-linking": ["expo-linking@7.0.5", "", { "dependencies": { "expo-constants": "~17.0.5", "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-3KptlJtcYDPWohk0MfJU75MJFh2ybavbtcSd84zEPfw9s1q3hjimw3sXnH03ZxP54kiEWldvKmmnGcVffBDB1g=="],
- "expo-localization": ["expo-localization@17.0.8", "", { "dependencies": { "rtl-detect": "^1.0.2" }, "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-UrdwklZBDJ+t+ZszMMiE0SXZ2eJxcquCuQcl6EvGHM9K+e6YqKVRQ+w8qE+iIB3H75v2RJy6MHAaLK+Mqeo04g=="],
+ "expo-localization": ["expo-localization@16.0.1", "", { "dependencies": { "rtl-detect": "^1.0.2" }, "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-kUrXiV/Pq9r7cG+TMt+Qa49IUQ9Y/czVwen4hmiboTclTopcWdIeCzYZv6JGtufoPpjEO9vVx1QJrXYl9V2u0Q=="],
- "expo-location": ["expo-location@19.0.8", "", { "peerDependencies": { "expo": "*" } }, "sha512-H/FI75VuJ1coodJbbMu82pf+Zjess8X8Xkiv9Bv58ZgPKS/2ztjC1YO1/XMcGz7+s9DrbLuMIw22dFuP4HqneA=="],
+ "expo-manifests": ["expo-manifests@0.15.6", "", { "dependencies": { "@expo/config": "~10.0.9", "expo-json-utils": "~0.14.0" }, "peerDependencies": { "expo": "*" } }, "sha512-z+TFICrijMaqBvcJkVx8WzgmOsV6ZJGvaPNQKZr4DA6uqugFMtvAQVikDjIq7SEc3n7IgPk0GR4ZN3/KnnkeVA=="],
- "expo-manifests": ["expo-manifests@1.0.10", "", { "dependencies": { "@expo/config": "~12.0.11", "expo-json-utils": "~0.15.0" }, "peerDependencies": { "expo": "*" } }, "sha512-oxDUnURPcL4ZsOBY6X1DGWGuoZgVAFzp6PISWV7lPP2J0r8u1/ucuChBgpK7u1eLGFp6sDIPwXyEUCkI386XSQ=="],
+ "expo-modules-autolinking": ["expo-modules-autolinking@2.0.8", "", { "dependencies": { "@expo/spawn-async": "^1.7.2", "chalk": "^4.1.0", "commander": "^7.2.0", "fast-glob": "^3.2.5", "find-up": "^5.0.0", "fs-extra": "^9.1.0", "require-from-string": "^2.0.2", "resolve-from": "^5.0.0" }, "bin": { "expo-modules-autolinking": "bin/expo-modules-autolinking.js" } }, "sha512-DezgnEYFQYic8hKGhkbztBA3QUmSftjaNDIKNAtS2iGJmzCcNIkatjN2slFDSWjSTNo8gOvPQyMKfyHWFvLpOQ=="],
- "expo-modules-autolinking": ["expo-modules-autolinking@3.0.24", "", { "dependencies": { "@expo/spawn-async": "^1.7.2", "chalk": "^4.1.0", "commander": "^7.2.0", "require-from-string": "^2.0.2", "resolve-from": "^5.0.0" }, "bin": { "expo-modules-autolinking": "bin/expo-modules-autolinking.js" } }, "sha512-TP+6HTwhL7orDvsz2VzauyQlXJcAWyU3ANsZ7JGL4DQu8XaZv/A41ZchbtAYLfozNA2Ya1Hzmhx65hXryBMjaQ=="],
+ "expo-modules-core": ["expo-modules-core@2.2.2", "", { "dependencies": { "invariant": "^2.2.4" } }, "sha512-SgjK86UD89gKAscRK3bdpn6Ojfs/KU4GujtuFx1wm4JaBjmXH4aakWkItkPlAV2pjIiHJHWQbENL9xjbw/Qr/g=="],
- "expo-modules-core": ["expo-modules-core@3.0.29", "", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-LzipcjGqk8gvkrOUf7O2mejNWugPkf3lmd9GkqL9WuNyeN2fRwU0Dn77e3ZUKI3k6sI+DNwjkq4Nu9fNN9WS7Q=="],
+ "expo-network": ["expo-network@7.0.5", "", { "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-5dlowKAimhIDN1/lBRnN6SSH6c07f12R3QrfLf3b3GEr6D+EijH2wE537mmwPh1p+254LAkm0Z5ZEXxbwII4sA=="],
- "expo-notifications": ["expo-notifications@0.32.16", "", { "dependencies": { "@expo/image-utils": "^0.8.8", "@ide/backoff": "^1.0.0", "abort-controller": "^3.0.0", "assert": "^2.0.0", "badgin": "^1.1.5", "expo-application": "~7.0.8", "expo-constants": "~18.0.13" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-QQD/UA6v7LgvwIJ+tS7tSvqJZkdp0nCSj9MxsDk/jU1GttYdK49/5L2LvE/4U0H7sNBz1NZAyhDZozg8xgBLXw=="],
+ "expo-notifications": ["expo-notifications@0.29.13", "", { "dependencies": { "@expo/image-utils": "^0.6.4", "@ide/backoff": "^1.0.0", "abort-controller": "^3.0.0", "assert": "^2.0.0", "badgin": "^1.1.5", "expo-application": "~6.0.2", "expo-constants": "~17.0.5" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-GHye6XeI1uEeVttJO/hGwUyA5cgQsxR3mi5q37yOE7cZN3cMj36pIfEEmjXEr0nWIWSzoJ0w8c2QxNj5xfP1pA=="],
- "expo-router": ["expo-router@6.0.21", "", { "dependencies": { "@expo/metro-runtime": "^6.1.2", "@expo/schema-utils": "^0.1.8", "@radix-ui/react-slot": "1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/native": "^7.1.8", "@react-navigation/native-stack": "^7.3.16", "client-only": "^0.0.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "expo-server": "^1.0.5", "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", "nanoid": "^3.3.8", "query-string": "^7.1.3", "react-fast-compare": "^3.2.2", "react-native-is-edge-to-edge": "^1.1.6", "semver": "~7.6.3", "server-only": "^0.0.1", "sf-symbols-typescript": "^2.1.0", "shallowequal": "^1.1.0", "use-latest-callback": "^0.2.1", "vaul": "^1.1.2" }, "peerDependencies": { "@react-navigation/drawer": "^7.5.0", "@testing-library/react-native": ">= 12.0.0", "expo": "*", "expo-constants": "^18.0.12", "expo-linking": "^8.0.11", "react": "*", "react-dom": "*", "react-native": "*", "react-native-gesture-handler": "*", "react-native-reanimated": "*", "react-native-safe-area-context": ">= 5.4.0", "react-native-screens": "*", "react-native-web": "*", "react-server-dom-webpack": "~19.0.3 || ~19.1.4 || ~19.2.3" }, "optionalPeers": ["@react-navigation/drawer", "@testing-library/react-native", "react-dom", "react-native-gesture-handler", "react-native-reanimated", "react-native-web", "react-server-dom-webpack"] }, "sha512-wjTUjrnWj6gRYjaYl1kYfcRnNE4ZAQ0kz0+sQf6/mzBd/OU6pnOdD7WrdAW3pTTpm52Q8sMoeX98tNQEddg2uA=="],
+ "expo-router": ["expo-router@4.0.17", "", { "dependencies": { "@expo/metro-runtime": "4.0.1", "@expo/server": "^0.5.1", "@radix-ui/react-slot": "1.0.1", "@react-navigation/bottom-tabs": "^7.2.0", "@react-navigation/native": "^7.0.14", "@react-navigation/native-stack": "^7.2.0", "client-only": "^0.0.1", "react-helmet-async": "^1.3.0", "react-native-helmet-async": "2.0.4", "react-native-is-edge-to-edge": "^1.1.6", "schema-utils": "^4.0.1", "semver": "~7.6.3", "server-only": "^0.0.1" }, "peerDependencies": { "@react-navigation/drawer": "^7.1.1", "expo": "*", "expo-constants": "*", "expo-linking": "*", "react-native-reanimated": "*", "react-native-safe-area-context": "*", "react-native-screens": "*" }, "optionalPeers": ["@react-navigation/drawer", "react-native-reanimated"] }, "sha512-8ybo6bVwdG1S9hafh9BTOjX1hpCgomdUvs6hKHMM01koo8mQ7zocH/+zxQeaMVDxGhboz2dO5GiDchWJ0OheRA=="],
- "expo-screen-orientation": ["expo-screen-orientation@9.0.8", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-qRoPi3E893o3vQHT4h1NKo51+7g2hjRSbDeg1fsSo/u2pOW5s6FCeoacLvD+xofOP33cH2MkE4ua54aWWO7Icw=="],
+ "expo-screen-orientation": ["expo-screen-orientation@8.0.4", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-kJrIZ/44+Gs5D5nyP6SXqTUbJEOsRgzk+nUcKeVZ0Vmx0szGrvSvhzdus7853xT/sqyAARsqBMlx626jAMu/Jg=="],
- "expo-secure-store": ["expo-secure-store@15.0.8", "", { "peerDependencies": { "expo": "*" } }, "sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw=="],
+ "expo-sensors": ["expo-sensors@14.0.2", "", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-nCb1Q3ctb0oVTZ9p6eFmQ2fINa6KoxXXIhagPpdN0qR82p00YosP27IuyxjVB3fnCJFeC4TffNxNjBxwAUk+nA=="],
- "expo-server": ["expo-server@1.0.5", "", {}, "sha512-IGR++flYH70rhLyeXF0Phle56/k4cee87WeQ4mamS+MkVAVP+dDlOHf2nN06Z9Y2KhU0Gp1k+y61KkghF7HdhA=="],
+ "expo-splash-screen": ["expo-splash-screen@0.29.22", "", { "dependencies": { "@expo/prebuild-config": "^8.0.27" }, "peerDependencies": { "expo": "*" } }, "sha512-f+bPpF06bqiuW1Fbrd3nxeaSsmTVTBEKEYe3epYt4IE6y4Ulli3qEUamMLlRQiDGuIXPU6zQlscpy2mdBUI5cA=="],
- "expo-sharing": ["expo-sharing@14.0.8", "", { "peerDependencies": { "expo": "*" } }, "sha512-A1pPr2iBrxypFDCWVAESk532HK+db7MFXbvO2sCV9ienaFXAk7lIBm6bkqgE6vzRd9O3RGdEGzYx80cYlc089Q=="],
+ "expo-status-bar": ["expo-status-bar@2.0.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-AkIPX7jWHRPp83UBZ1iXtVvyr0g+DgBVvIXTtlmPtmUsm8Vq9Bb5IGj86PW8osuFlgoTVAg7HI/+Ok7yEYwiRg=="],
- "expo-splash-screen": ["expo-splash-screen@31.0.13", "", { "dependencies": { "@expo/prebuild-config": "^54.0.8" }, "peerDependencies": { "expo": "*" } }, "sha512-1epJLC1cDlwwj089R2h8cxaU5uk4ONVAC+vzGiTZH4YARQhL4Stlz1MbR6yAS173GMosvkE6CAeihR7oIbCkDA=="],
+ "expo-structured-headers": ["expo-structured-headers@4.0.0", "", {}, "sha512-uPiwZjWq3AdFGgY52+I2nGPrNa6izxAglymPXHUZLekZW290GqIUOk7MBNDD4sg4JwUbSi3gdxEurpEvuq+FSg=="],
- "expo-status-bar": ["expo-status-bar@3.0.9", "", { "dependencies": { "react-native-is-edge-to-edge": "^1.2.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-xyYyVg6V1/SSOZWh4Ni3U129XHCnFHBTcUo0dhWtFDrZbNp/duw5AGsQfb2sVeU0gxWHXSY1+5F0jnKYC7WuOw=="],
+ "expo-system-ui": ["expo-system-ui@4.0.8", "", { "dependencies": { "@react-native/normalize-colors": "0.76.7", "debug": "^4.3.2" }, "peerDependencies": { "expo": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-0AmWXJ3ObwMYxi2YGagwRQikydoUZJXLeK4A0FY1PsZpnlorSQ4IAfEVS38JmA54tf5CpP4TjBp5ZVEjRyv1rw=="],
- "expo-system-ui": ["expo-system-ui@6.0.9", "", { "dependencies": { "@react-native/normalize-colors": "0.81.5", "debug": "^4.3.2" }, "peerDependencies": { "expo": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-eQTYGzw1V4RYiYHL9xDLYID3Wsec2aZS+ypEssmF64D38aDrqbDgz1a2MSlHLQp2jHXSs3FvojhZ9FVela1Zcg=="],
+ "expo-task-manager": ["expo-task-manager@12.0.5", "", { "dependencies": { "unimodules-app-loader": "~5.0.1" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-tDHOBYORA6wuO32NWwz/Egrvn+N6aANHAa0DFs+01VK/IJZfU9D05ZN6M5XYIlZv5ll4GSX1wJZyTCY0HZGapw=="],
- "expo-task-manager": ["expo-task-manager@14.0.9", "", { "dependencies": { "unimodules-app-loader": "~6.0.8" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-GKWtXrkedr4XChHfTm5IyTcSfMtCPxzx89y4CMVqKfyfROATibrE/8UI5j7UC/pUOfFoYlQvulQEvECMreYuUA=="],
+ "expo-updates": ["expo-updates@0.26.19", "", { "dependencies": { "@expo/code-signing-certificates": "0.0.5", "@expo/config": "~10.0.10", "@expo/config-plugins": "~9.0.15", "@expo/spawn-async": "^1.7.2", "arg": "4.1.0", "chalk": "^4.1.2", "expo-eas-client": "~0.13.2", "expo-manifests": "~0.15.6", "expo-structured-headers": "~4.0.0", "expo-updates-interface": "~1.0.0", "fast-glob": "^3.3.2", "fbemitter": "^3.0.0", "ignore": "^5.3.1", "resolve-from": "^5.0.0" }, "peerDependencies": { "expo": "*", "react": "*" }, "bin": { "expo-updates": "bin/cli.js" } }, "sha512-h40UrG0n1nCb2na1ffz+mNQtsnr7/BxxK+EtXJSqCaD9PIGaTGe20tasmo1oVskv3s37zfv0x93+6uTjanieQg=="],
- "expo-updates-interface": ["expo-updates-interface@2.0.0", "", { "peerDependencies": { "expo": "*" } }, "sha512-pTzAIufEZdVPKql6iMi5ylVSPqV1qbEopz9G6TSECQmnNde2nwq42PxdFBaUEd8IZJ/fdJLQnOT3m6+XJ5s7jg=="],
+ "expo-updates-interface": ["expo-updates-interface@1.0.0", "", { "peerDependencies": { "expo": "*" } }, "sha512-93oWtvULJOj+Pp+N/lpTcFfuREX1wNeHtp7Lwn8EbzYYmdn37MvZU3TPW2tYYCZuhzmKEXnUblYcruYoDu7IrQ=="],
- "expo-web-browser": ["expo-web-browser@15.0.10", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-fvDhW4bhmXAeWFNFiInmsGCK83PAqAcQaFyp/3pE/jbdKmFKoRCWr46uZGIfN4msLK/OODhaQ/+US7GSJNDHJg=="],
+ "expo-web-browser": ["expo-web-browser@14.0.2", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-Hncv2yojhTpHbP6SGWARBFdl7P6wBHc1O8IKaNsH0a/IEakq887o1eRhLxZ5IwztPQyRDhpqHdgJ+BjWolOnwA=="],
- "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
+ "exponential-backoff": ["exponential-backoff@3.1.2", "", {}, "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA=="],
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
+ "fast-base64-decode": ["fast-base64-decode@1.0.0", "", {}, "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q=="],
+
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
- "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
+ "fast-loops": ["fast-loops@1.1.4", "", {}, "sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg=="],
+
+ "fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="],
"fast-xml-parser": ["fast-xml-parser@4.5.3", "", { "dependencies": { "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig=="],
- "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
+ "fastq": ["fastq@1.19.0", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA=="],
"fb-watchman": ["fb-watchman@2.0.2", "", { "dependencies": { "bser": "2.1.1" } }, "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA=="],
+ "fbemitter": ["fbemitter@3.0.0", "", { "dependencies": { "fbjs": "^3.0.0" } }, "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw=="],
+
"fbjs": ["fbjs@3.0.5", "", { "dependencies": { "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", "loose-envify": "^1.0.0", "object-assign": "^4.1.0", "promise": "^7.1.1", "setimmediate": "^1.0.5", "ua-parser-js": "^1.0.35" } }, "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg=="],
"fbjs-css-vars": ["fbjs-css-vars@1.0.2", "", {}, "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="],
- "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
+ "fetch-retry": ["fetch-retry@4.1.1", "", {}, "sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA=="],
+
+ "ffmpeg-kit-react-native": ["ffmpeg-kit-react-native@6.0.2", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-r9uSmahq8TeyIb7fXf3ft+uUXyoeWRFa99+khjo0TAzWO9y0z9wU7eGnab9JLw1MmCB9v64o4yojNluJhVm9nQ=="],
"file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="],
@@ -1114,21 +1232,25 @@
"finalhandler": ["finalhandler@1.1.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "~2.3.0", "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" } }, "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA=="],
+ "find-cache-dir": ["find-cache-dir@2.1.0", "", { "dependencies": { "commondir": "^1.0.1", "make-dir": "^2.0.0", "pkg-dir": "^3.0.0" } }, "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ=="],
+
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
"find-yarn-workspace-root": ["find-yarn-workspace-root@2.0.0", "", { "dependencies": { "micromatch": "^4.0.2" } }, "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ=="],
"flow-enums-runtime": ["flow-enums-runtime@0.0.6", "", {}, "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw=="],
- "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
+ "flow-parser": ["flow-parser@0.261.2", "", {}, "sha512-RtunoakA3YjtpAxPSOBVW6lmP5NYmETwkpAfNkdr8Ovf86ENkbD3mtPWnswFTIUtRvjwv0i8ZSkHK+AzsUg1JA=="],
+
+ "follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
"fontfaceobserver": ["fontfaceobserver@2.3.0", "", {}, "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg=="],
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
- "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
+ "foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="],
- "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
+ "form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="],
"freeport-async": ["freeport-async@2.0.0", "", {}, "sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ=="],
@@ -1136,39 +1258,43 @@
"fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="],
+ "fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="],
+
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
- "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
-
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
- "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
-
- "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
+ "get-intrinsic": ["get-intrinsic@1.2.7", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA=="],
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
"get-package-type": ["get-package-type@0.1.0", "", {}, "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="],
+ "get-port": ["get-port@3.2.0", "", {}, "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg=="],
+
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
"getenv": ["getenv@1.0.0", "", {}, "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg=="],
- "gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
+ "gifwrap": ["gifwrap@0.9.4", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-MDMwbhASQuVeD4JKd1fKgNgCRL3fGqMM4WaqpNhWO0JiMOAjbQdumbs4BbBZEy9/M00EHEjKN3HieVhCUlwjeQ=="],
- "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
+ "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
- "global-dirs": ["global-dirs@0.1.1", "", { "dependencies": { "ini": "^1.3.4" } }, "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg=="],
+ "global": ["global@4.4.0", "", { "dependencies": { "min-document": "^2.19.0", "process": "^0.11.10" } }, "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w=="],
+
+ "globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
+
+ "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
@@ -1184,9 +1310,9 @@
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
- "hermes-estree": ["hermes-estree@0.29.1", "", {}, "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ=="],
+ "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="],
- "hermes-parser": ["hermes-parser@0.29.1", "", { "dependencies": { "hermes-estree": "0.29.1" } }, "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA=="],
+ "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="],
"hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
@@ -1196,17 +1322,11 @@
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
- "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
-
"human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
- "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="],
-
"hyphenate-style-name": ["hyphenate-style-name@1.1.0", "", {}, "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw=="],
- "i18next": ["i18next@25.6.1", "", { "dependencies": { "@babel/runtime": "^7.27.6" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-yUWvdXtalZztmKrKw3yz/AvSP3yKyqIkVPx/wyvoYy9lkLmwzItLxp0iHZLG5hfVQ539Jor4XLO+U+NHIXg7pw=="],
-
- "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
+ "i18next": ["i18next@24.2.2", "", { "dependencies": { "@babel/runtime": "^7.23.2" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-NE6i86lBCKRYZa5TaUDkU5S4HFgLIEJRLr3Whf2psgaxBleQ2LC1YW1Vc+SCgkAW7VEzndT6al6+CzegSUHcTQ=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
@@ -1214,39 +1334,53 @@
"image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="],
- "image-size": ["image-size@1.2.1", "", { "dependencies": { "queue": "6.0.2" }, "bin": { "image-size": "bin/image-size.js" } }, "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw=="],
+ "image-size": ["image-size@1.2.0", "", { "dependencies": { "queue": "6.0.2" }, "bin": { "image-size": "bin/image-size.js" } }, "sha512-4S8fwbO6w3GeCVN6OPtA9I5IGKkcDMPcKndtUlpJuCwu7JLjtj7JZpwqLuyY2nrmQT3AWsCJLSKPsc2mPBSl3w=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
+ "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
+
"inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
- "inline-style-prefixer": ["inline-style-prefixer@7.0.1", "", { "dependencies": { "css-in-js-utils": "^3.1.0" } }, "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw=="],
+ "inline-style-prefixer": ["inline-style-prefixer@6.0.4", "", { "dependencies": { "css-in-js-utils": "^3.1.0", "fast-loops": "^1.1.3" } }, "sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg=="],
+
+ "internal-ip": ["internal-ip@4.3.0", "", { "dependencies": { "default-gateway": "^4.2.0", "ipaddr.js": "^1.9.0" } }, "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg=="],
"invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="],
+ "ip-regex": ["ip-regex@2.1.0", "", {}, "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw=="],
+
+ "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
+
"is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="],
- "is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
+ "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
+ "is-buffer": ["is-buffer@1.1.6", "", {}, "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="],
+
"is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
+ "is-directory": ["is-directory@0.3.1", "", {}, "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw=="],
+
"is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
- "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="],
+ "is-function": ["is-function@1.0.2", "", {}, "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ=="],
+
+ "is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
@@ -1256,6 +1390,12 @@
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+ "is-path-cwd": ["is-path-cwd@2.2.0", "", {}, "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ=="],
+
+ "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="],
+
+ "is-plain-object": ["is-plain-object@2.0.4", "", { "dependencies": { "isobject": "^3.0.1" } }, "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og=="],
+
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
@@ -1270,7 +1410,7 @@
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
- "isomorphic-fetch": ["isomorphic-fetch@3.0.0", "", { "dependencies": { "node-fetch": "^2.6.1", "whatwg-fetch": "^3.4.1" } }, "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA=="],
+ "isobject": ["isobject@3.0.1", "", {}, "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="],
"istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="],
@@ -1306,7 +1446,9 @@
"joi": ["joi@17.13.3", "", { "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA=="],
- "jotai": ["jotai@2.16.2", "", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-DH0lBiTXvewsxtqqwjDW6Hg9JPTDnq9LcOsXSFWCAUEt+qj5ohl9iRVX9zQXPPHKLXCdH+5mGvM28fsXMl17/g=="],
+ "join-component": ["join-component@1.1.0", "", {}, "sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ=="],
+
+ "jotai": ["jotai@2.12.1", "", { "peerDependencies": { "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@types/react", "react"] }, "sha512-VUW0nMPYIru5g89tdxwr9ftiVdc/nGV9jvHISN8Ucx+m1vI9dBeHemfqYzEuw5XSkmYjD/MEyApN9k6yrATsZQ=="],
"jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="],
@@ -1314,15 +1456,21 @@
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
+ "jsc-android": ["jsc-android@250231.0.0", "", {}, "sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw=="],
+
"jsc-safe-url": ["jsc-safe-url@0.2.4", "", {}, "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q=="],
+ "jscodeshift": ["jscodeshift@17.1.2", "", { "dependencies": { "@babel/core": "^7.24.7", "@babel/parser": "^7.24.7", "@babel/plugin-transform-class-properties": "^7.24.7", "@babel/plugin-transform-modules-commonjs": "^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/preset-flow": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@babel/register": "^7.24.6", "flow-parser": "0.*", "graceful-fs": "^4.2.4", "micromatch": "^4.0.7", "neo-async": "^2.5.0", "picocolors": "^1.0.1", "recast": "^0.23.9", "tmp": "^0.2.3", "write-file-atomic": "^5.0.1" }, "peerDependencies": { "@babel/preset-env": "^7.1.6" }, "optionalPeers": ["@babel/preset-env"], "bin": { "jscodeshift": "bin/jscodeshift.js" } }, "sha512-uime4vFOiZ1o3ICT4Sm/AbItHEVw2oCxQ3a0egYVy3JMMOctxe07H3SKL1v175YqjMt27jn1N+3+Bj9SKDNgdQ=="],
+
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+ "json-parse-better-errors": ["json-parse-better-errors@1.0.2", "", {}, "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="],
+
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
- "json-stable-stringify": ["json-stable-stringify@1.3.0", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg=="],
+ "json-stable-stringify": ["json-stable-stringify@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA=="],
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
@@ -1330,49 +1478,43 @@
"jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="],
+ "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
+
"klaw-sync": ["klaw-sync@6.0.0", "", { "dependencies": { "graceful-fs": "^4.1.11" } }, "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ=="],
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
- "lan-network": ["lan-network@0.1.7", "", { "bin": { "lan-network": "dist/lan-network-cli.js" } }, "sha512-mnIlAEMu4OyEvUNdzco9xpuB9YVcPkQec+QsgycBCtPZvEqWPCDPfbAE4OJMdBBWpZWtpCn1xw9jJYlwjWI5zQ=="],
-
- "launch-editor": ["launch-editor@2.12.0", "", { "dependencies": { "picocolors": "^1.1.1", "shell-quote": "^1.8.3" } }, "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg=="],
-
"leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
"lighthouse-logger": ["lighthouse-logger@1.4.2", "", { "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" } }, "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g=="],
- "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
+ "lightningcss": ["lightningcss@1.27.0", "", { "dependencies": { "detect-libc": "^1.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.27.0", "lightningcss-darwin-x64": "1.27.0", "lightningcss-freebsd-x64": "1.27.0", "lightningcss-linux-arm-gnueabihf": "1.27.0", "lightningcss-linux-arm64-gnu": "1.27.0", "lightningcss-linux-arm64-musl": "1.27.0", "lightningcss-linux-x64-gnu": "1.27.0", "lightningcss-linux-x64-musl": "1.27.0", "lightningcss-win32-arm64-msvc": "1.27.0", "lightningcss-win32-x64-msvc": "1.27.0" } }, "sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ=="],
- "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.27.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ=="],
- "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.27.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg=="],
- "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.27.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA=="],
- "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="],
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.27.0", "", { "os": "linux", "cpu": "arm" }, "sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA=="],
- "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A=="],
- "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="],
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg=="],
- "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A=="],
- "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="],
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA=="],
- "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.27.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ=="],
- "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="],
-
- "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="],
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw=="],
"lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
- "lint-staged": ["lint-staged@16.2.7", "", { "dependencies": { "commander": "^14.0.2", "listr2": "^9.0.5", "micromatch": "^4.0.8", "nano-spawn": "^2.0.0", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow=="],
-
- "listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="],
+ "load-bmfont": ["load-bmfont@1.4.2", "", { "dependencies": { "buffer-equal": "0.0.1", "mime": "^1.3.4", "parse-bmfont-ascii": "^1.0.3", "parse-bmfont-binary": "^1.0.5", "parse-bmfont-xml": "^1.1.4", "phin": "^3.7.1", "xhr": "^2.0.1", "xtend": "^4.0.0" } }, "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
@@ -1384,23 +1526,25 @@
"log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
- "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="],
-
"logkitty": ["logkitty@0.7.1", "", { "dependencies": { "ansi-fragments": "^0.2.1", "dayjs": "^1.8.15", "yargs": "^15.1.0" }, "bin": { "logkitty": "bin/logkitty.js" } }, "sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ=="],
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
+ "make-dir": ["make-dir@2.1.0", "", { "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" } }, "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA=="],
+
"makeerror": ["makeerror@1.0.12", "", { "dependencies": { "tmpl": "1.0.5" } }, "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg=="],
- "marky": ["marky@1.3.0", "", {}, "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ=="],
+ "marky": ["marky@1.2.5", "", {}, "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
- "mdn-data": ["mdn-data@2.0.14", "", {}, "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="],
+ "md5": ["md5@2.3.0", "", { "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", "is-buffer": "~1.1.6" } }, "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g=="],
- "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
+ "md5-file": ["md5-file@3.2.3", "", { "dependencies": { "buffer-alloc": "^1.1.0" }, "bin": { "md5-file": "cli.js" } }, "sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw=="],
+
+ "mdn-data": ["mdn-data@2.0.14", "", {}, "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="],
"memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="],
@@ -1408,33 +1552,33 @@
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
- "metro": ["metro@0.83.3", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.32.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.3", "metro-cache": "0.83.3", "metro-cache-key": "0.83.3", "metro-config": "0.83.3", "metro-core": "0.83.3", "metro-file-map": "0.83.3", "metro-resolver": "0.83.3", "metro-runtime": "0.83.3", "metro-source-map": "0.83.3", "metro-symbolicate": "0.83.3", "metro-transform-plugins": "0.83.3", "metro-transform-worker": "0.83.3", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q=="],
+ "metro": ["metro@0.81.1", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^2.2.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.25.1", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.6.3", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.81.1", "metro-cache": "0.81.1", "metro-cache-key": "0.81.1", "metro-config": "0.81.1", "metro-core": "0.81.1", "metro-file-map": "0.81.1", "metro-resolver": "0.81.1", "metro-runtime": "0.81.1", "metro-source-map": "0.81.1", "metro-symbolicate": "0.81.1", "metro-transform-plugins": "0.81.1", "metro-transform-worker": "0.81.1", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-fqRu4fg8ONW7VfqWFMGgKAcOuMzyoQah2azv9Y3VyFXAmG+AoTU6YIFWqAADESCGVWuWEIvxTJhMf3jxU6jwjA=="],
- "metro-babel-transformer": ["metro-babel-transformer@0.83.3", "", { "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.32.0", "nullthrows": "^1.1.1" } }, "sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g=="],
+ "metro-babel-transformer": ["metro-babel-transformer@0.81.1", "", { "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.25.1", "nullthrows": "^1.1.1" } }, "sha512-JECKDrQaUnDmj0x/Q/c8c5YwsatVx38Lu+BfCwX9fR8bWipAzkvJocBpq5rOAJRDXRgDcPv2VO4Q4nFYrpYNQg=="],
- "metro-cache": ["metro-cache@0.83.3", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.83.3" } }, "sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q=="],
+ "metro-cache": ["metro-cache@0.81.1", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "metro-core": "0.81.1" } }, "sha512-Uqcmn6sZ+Y0VJHM88VrG5xCvSeU7RnuvmjPmSOpEcyJJBe02QkfHL05MX2ZyGDTyZdbKCzaX0IijrTe4hN3F0Q=="],
- "metro-cache-key": ["metro-cache-key@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw=="],
+ "metro-cache-key": ["metro-cache-key@0.81.1", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-5fDaHR1yTvpaQuwMAeEoZGsVyvjrkw9IFAS7WixSPvaNY5YfleqoJICPc6hbXFJjvwCCpwmIYFkjqzR/qJ6yqA=="],
- "metro-config": ["metro-config@0.83.3", "", { "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", "metro": "0.83.3", "metro-cache": "0.83.3", "metro-core": "0.83.3", "metro-runtime": "0.83.3", "yaml": "^2.6.1" } }, "sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA=="],
+ "metro-config": ["metro-config@0.81.1", "", { "dependencies": { "connect": "^3.6.5", "cosmiconfig": "^5.0.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.6.3", "metro": "0.81.1", "metro-cache": "0.81.1", "metro-core": "0.81.1", "metro-runtime": "0.81.1" } }, "sha512-VAAJmxsKIZ+Fz5/z1LVgxa32gE6+2TvrDSSx45g85WoX4EtLmdBGP3DSlpQW3DqFUfNHJCGwMLGXpJnxifd08g=="],
- "metro-core": ["metro-core@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", "metro-resolver": "0.83.3" } }, "sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw=="],
+ "metro-core": ["metro-core@0.81.1", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", "metro-resolver": "0.81.1" } }, "sha512-4d2/+02IYqOwJs4dmM0dC8hIZqTzgnx2nzN4GTCaXb3Dhtmi/SJ3v6744zZRnithhN4lxf8TTJSHnQV75M7SSA=="],
- "metro-file-map": ["metro-file-map@0.83.3", "", { "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "nullthrows": "^1.1.1", "walker": "^1.0.7" } }, "sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA=="],
+ "metro-file-map": ["metro-file-map@0.81.1", "", { "dependencies": { "debug": "^2.2.0", "fb-watchman": "^2.0.0", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "invariant": "^2.2.4", "jest-worker": "^29.6.3", "micromatch": "^4.0.4", "nullthrows": "^1.1.1", "walker": "^1.0.7" } }, "sha512-aY72H2ujmRfFxcsbyh83JgqFF+uQ4HFN1VhV2FmcfQG4s1bGKf2Vbkk+vtZ1+EswcBwDZFbkpvAjN49oqwGzAA=="],
- "metro-minify-terser": ["metro-minify-terser@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" } }, "sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ=="],
+ "metro-minify-terser": ["metro-minify-terser@0.81.1", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" } }, "sha512-p/Qz3NNh1nebSqMlxlUALAnESo6heQrnvgHtAuxufRPtKvghnVDq9hGGex8H7z7YYLsqe42PWdt4JxTA3mgkvg=="],
- "metro-resolver": ["metro-resolver@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ=="],
+ "metro-resolver": ["metro-resolver@0.81.1", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-E61t6fxRoYRkl6Zo3iUfCKW4DYfum/bLjcejXBMt1y3I7LFkK84TCR/Rs9OAwsMCY/7GOPB4+CREYZOtCC7CNA=="],
- "metro-runtime": ["metro-runtime@0.83.3", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw=="],
+ "metro-runtime": ["metro-runtime@0.81.1", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-pqu5j5d01rjF85V/K8SDDJ0NR3dRp6bE3z5bKVVb5O2Rx0nbR9KreUxYALQCRCcQHaYySqCg5fYbGKBHC295YQ=="],
- "metro-source-map": ["metro-source-map@0.83.3", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.3", "nullthrows": "^1.1.1", "ob1": "0.83.3", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg=="],
+ "metro-source-map": ["metro-source-map@0.81.1", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.81.1", "nullthrows": "^1.1.1", "ob1": "0.81.1", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-1i8ROpNNiga43F0ZixAXoFE/SS3RqcRDCCslpynb+ytym0VI7pkTH1woAN2HI9pczYtPrp3Nq0AjRpsuY35ieA=="],
- "metro-symbolicate": ["metro-symbolicate@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.83.3", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw=="],
+ "metro-symbolicate": ["metro-symbolicate@0.81.1", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.81.1", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-Lgk0qjEigtFtsM7C0miXITbcV47E1ZYIfB+m/hCraihiwRWkNUQEPCWvqZmwXKSwVE5mXA0EzQtghAvQSjZDxw=="],
- "metro-transform-plugins": ["metro-transform-plugins@0.83.3", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" } }, "sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A=="],
+ "metro-transform-plugins": ["metro-transform-plugins@0.81.1", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" } }, "sha512-7L1lI44/CyjIoBaORhY9fVkoNe8hrzgxjSCQ/lQlcfrV31cZb7u0RGOQrKmUX7Bw4FpejrB70ArQ7Mse9mk7+Q=="],
- "metro-transform-worker": ["metro-transform-worker@0.83.3", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "metro": "0.83.3", "metro-babel-transformer": "0.83.3", "metro-cache": "0.83.3", "metro-cache-key": "0.83.3", "metro-minify-terser": "0.83.3", "metro-source-map": "0.83.3", "metro-transform-plugins": "0.83.3", "nullthrows": "^1.1.1" } }, "sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA=="],
+ "metro-transform-worker": ["metro-transform-worker@0.81.1", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "metro": "0.81.1", "metro-babel-transformer": "0.81.1", "metro-cache": "0.81.1", "metro-cache-key": "0.81.1", "metro-minify-terser": "0.81.1", "metro-source-map": "0.81.1", "metro-transform-plugins": "0.81.1", "nullthrows": "^1.1.1" } }, "sha512-M+2hVT3rEy5K7PBmGDgQNq3Zx53TjScOcO/CieyLnCRFtBGWZiSJ2+bLAXXOKyKa/y3bI3i0owxtyxuPGDwbZg=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
@@ -1446,7 +1590,7 @@
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
- "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
+ "min-document": ["min-document@2.19.0", "", { "dependencies": { "dom-walk": "^0.1.0" } }, "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ=="],
"minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
@@ -1454,37 +1598,49 @@
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
- "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
+ "minipass-collect": ["minipass-collect@2.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="],
- "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
+ "minipass-flush": ["minipass-flush@1.0.5", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw=="],
+
+ "minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="],
+
+ "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
+
+ "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
+
+ "mrmime": ["mrmime@1.0.1", "", {}, "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
- "nano-spawn": ["nano-spawn@2.0.0", "", {}, "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw=="],
-
- "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+ "nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
"nativewind": ["nativewind@2.0.11", "", { "dependencies": { "@babel/generator": "^7.18.7", "@babel/helper-module-imports": "7.18.6", "@babel/types": "7.19.0", "css-mediaquery": "^0.1.2", "css-to-react-native": "^3.0.0", "micromatch": "^4.0.5", "postcss": "^8.4.12", "postcss-calc": "^8.2.4", "postcss-color-functional-notation": "^4.2.2", "postcss-css-variables": "^0.18.0", "postcss-nested": "^5.0.6", "react-is": "^18.1.0", "use-sync-external-store": "^1.1.0" }, "peerDependencies": { "tailwindcss": "~3" } }, "sha512-qCEXUwKW21RYJ33KRAJl3zXq2bCq82WoI564fI21D/TiqhfmstZOqPN53RF8qK1NDK6PGl56b2xaTxgObEePEg=="],
"negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
+ "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
+
"nested-error-stacks": ["nested-error-stacks@2.0.1", "", {}, "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A=="],
+ "nice-try": ["nice-try@1.0.5", "", {}, "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="],
+
"nocache": ["nocache@3.0.4", "", {}, "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw=="],
+ "node-dir": ["node-dir@0.1.17", "", { "dependencies": { "minimatch": "^3.0.2" } }, "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg=="],
+
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
- "node-forge": ["node-forge@1.3.3", "", {}, "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg=="],
+ "node-forge": ["node-forge@1.3.1", "", {}, "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="],
"node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="],
- "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
+ "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
"node-stream-zip": ["node-stream-zip@1.15.0", "", {}, "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw=="],
- "node-vibrant": ["node-vibrant@4.0.3", "", { "dependencies": { "@types/node": "^18.15.3", "@vibrant/core": "^4.0.0", "@vibrant/generator-default": "^4.0.3", "@vibrant/image-browser": "^4.0.0", "@vibrant/image-node": "^4.0.0", "@vibrant/quantizer-mmcq": "^4.0.0" } }, "sha512-kzoIuJK90BH/k65Avt077JCX4Nhqz1LNc8cIOm2rnYEvFdJIYd8b3SQwU1MTpzcHtr8z8jxkl1qdaCfbP3olFg=="],
+ "node-vibrant": ["node-vibrant@3.1.6", "", { "dependencies": { "@jimp/custom": "^0.16.1", "@jimp/plugin-resize": "^0.16.1", "@jimp/types": "^0.16.1", "@types/lodash": "^4.14.53", "@types/node": "^10.11.7", "lodash": "^4.17.20", "url": "^0.11.0" } }, "sha512-Wlc/hQmBMOu6xon12ZJHS2N3M+I6J8DhrD3Yo6m5175v8sFkVIN+UjhKVRcO+fqvre89ASTpmiFEP3nPO13SwA=="],
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
@@ -1496,7 +1652,7 @@
"nullthrows": ["nullthrows@1.1.1", "", {}, "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="],
- "ob1": ["ob1@0.83.3", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA=="],
+ "ob1": ["ob1@0.81.1", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-1PEbvI+AFvOcgdNcO79FtDI1TUO8S3lhiKOyAiyWQF3sFDDKS+aw2/BZvGlArFnSmqckwOOB9chQuIX0/OahoQ=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
@@ -1514,7 +1670,7 @@
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
- "on-headers": ["on-headers@1.1.0", "", {}, "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="],
+ "on-headers": ["on-headers@1.0.2", "", {}, "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
@@ -1524,10 +1680,16 @@
"ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="],
+ "os-tmpdir": ["os-tmpdir@1.0.2", "", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="],
+
+ "p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="],
+
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
+ "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="],
+
"p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
@@ -1536,13 +1698,23 @@
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
+ "parse-bmfont-ascii": ["parse-bmfont-ascii@1.0.6", "", {}, "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="],
+
+ "parse-bmfont-binary": ["parse-bmfont-binary@1.0.6", "", {}, "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA=="],
+
+ "parse-bmfont-xml": ["parse-bmfont-xml@1.1.6", "", { "dependencies": { "xml-parse-from-string": "^1.0.0", "xml2js": "^0.5.0" } }, "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA=="],
+
+ "parse-headers": ["parse-headers@2.0.5", "", {}, "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA=="],
+
"parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
"parse-png": ["parse-png@2.1.0", "", { "dependencies": { "pngjs": "^3.3.0" } }, "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ=="],
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
- "patch-package": ["patch-package@8.0.1", "", { "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" } }, "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw=="],
+ "password-prompt": ["password-prompt@1.1.3", "", { "dependencies": { "ansi-escapes": "^4.3.2", "cross-spawn": "^7.0.3" } }, "sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw=="],
+
+ "patch-package": ["patch-package@8.0.0", "", { "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^9.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "rimraf": "^2.6.3", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.0.33", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" } }, "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
@@ -1552,29 +1724,33 @@
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
- "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="],
+ "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
+
+ "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
"peek-readable": ["peek-readable@4.1.0", "", {}, "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="],
+ "phin": ["phin@2.9.3", "", {}, "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA=="],
+
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
- "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
-
- "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="],
+ "picomatch": ["picomatch@3.0.1", "", {}, "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag=="],
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
- "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
+ "pirates": ["pirates@4.0.6", "", {}, "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="],
"pixelmatch": ["pixelmatch@4.0.2", "", { "dependencies": { "pngjs": "^3.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA=="],
+ "pkg-dir": ["pkg-dir@3.0.0", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw=="],
+
"plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="],
"pngjs": ["pngjs@3.4.0", "", {}, "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="],
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
- "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
+ "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
"postcss-calc": ["postcss-calc@8.2.4", "", { "dependencies": { "postcss-selector-parser": "^6.0.9", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.2" } }, "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q=="],
@@ -1584,7 +1760,7 @@
"postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
- "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="],
+ "postcss-js": ["postcss-js@4.0.1", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw=="],
"postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="],
@@ -1594,6 +1770,8 @@
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
+ "postinstall-postinstall": ["postinstall-postinstall@2.1.0", "", {}, "sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ=="],
+
"pretty-bytes": ["pretty-bytes@5.6.0", "", {}, "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="],
"pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="],
@@ -1612,11 +1790,13 @@
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
+ "pump": ["pump@3.0.2", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw=="],
+
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"qrcode-terminal": ["qrcode-terminal@0.11.0", "", { "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ=="],
- "qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
+ "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
"query-string": ["query-string@7.1.3", "", { "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", "split-on-first": "^1.0.0", "strict-uri-encode": "^2.0.0" } }, "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg=="],
@@ -1626,99 +1806,101 @@
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
- "raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="],
-
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
- "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
+ "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
- "react-devtools-core": ["react-devtools-core@6.1.5", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA=="],
+ "react-devtools-core": ["react-devtools-core@6.1.1", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-TFo1MEnkqE6hzAbaztnyR5uLTMoz6wnEWwWBsCUzNt+sVXJycuRJdDqvL078M4/h65BI/YO5XWTaxZDWVsW0fw=="],
- "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
+ "react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="],
"react-fast-compare": ["react-fast-compare@3.2.2", "", {}, "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="],
"react-freeze": ["react-freeze@1.0.4", "", { "peerDependencies": { "react": ">=17.0.0" } }, "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA=="],
- "react-i18next": ["react-i18next@16.5.3", "", { "dependencies": { "@babel/runtime": "^7.28.4", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 25.6.2", "react": ">= 16.8.0", "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-fo+/NNch37zqxOzlBYrWMx0uy/yInPkRfjSuy4lqKdaecR17nvCHnEUt3QyzA8XjQ2B/0iW/5BhaHR3ZmukpGw=="],
+ "react-helmet-async": ["react-helmet-async@1.3.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "invariant": "^2.2.4", "prop-types": "^15.7.2", "react-fast-compare": "^3.2.0", "shallowequal": "^1.1.0" }, "peerDependencies": { "react": "^16.6.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg=="],
- "react-is": ["react-is@19.2.3", "", {}, "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA=="],
+ "react-i18next": ["react-i18next@15.4.1", "", { "dependencies": { "@babel/runtime": "^7.25.0", "html-parse-stringify": "^3.0.1" }, "peerDependencies": { "i18next": ">= 23.2.3", "react": ">= 16.8.0" } }, "sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw=="],
- "react-native": ["react-native@0.81.5", "", { "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.81.5", "@react-native/codegen": "0.81.5", "@react-native/community-cli-plugin": "0.81.5", "@react-native/gradle-plugin": "0.81.5", "@react-native/js-polyfills": "0.81.5", "@react-native/normalize-colors": "0.81.5", "@react-native/virtualized-lists": "0.81.5", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.29.1", "base64-js": "^1.5.1", "commander": "^12.0.0", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "invariant": "^2.2.4", "jest-environment-node": "^29.7.0", "memoize-one": "^5.0.0", "metro-runtime": "^0.83.1", "metro-source-map": "^0.83.1", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.1.5", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.26.0", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^19.1.0", "react": "^19.1.0" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js" } }, "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw=="],
+ "react-is": ["react-is@19.0.0", "", {}, "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g=="],
+
+ "react-native": ["react-native-tvos@0.77.0-0", "", { "dependencies": { "@jest/create-cache-key-function": "^29.6.3", "@react-native-tvos/virtualized-lists": "0.77.0-0", "@react-native/assets-registry": "0.77.0", "@react-native/codegen": "0.77.0", "@react-native/community-cli-plugin": "0.77.0", "@react-native/gradle-plugin": "0.77.0", "@react-native/js-polyfills": "0.77.0", "@react-native/normalize-colors": "0.77.0", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.25.1", "base64-js": "^1.5.1", "chalk": "^4.0.0", "commander": "^12.0.0", "event-target-shim": "^5.0.1", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "invariant": "^2.2.4", "jest-environment-node": "^29.6.3", "jsc-android": "^250231.0.0", "memoize-one": "^5.0.0", "metro-runtime": "^0.81.0", "metro-source-map": "^0.81.0", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.0.1", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.24.0-canary-efb381bbf-20230505", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^18.2.6", "react": "^18.2.0" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js" } }, "sha512-edIOqGrPadpXHmt5R/LuhekHHLx/0DyrfY5A9odS2AlS+03S0ada7H5oDvusOUVcyq1vc3isrwZpUSQzudoR1g=="],
"react-native-awesome-slider": ["react-native-awesome-slider@2.9.0", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-gesture-handler": ">=2.0.0", "react-native-reanimated": ">=3.0.0" } }, "sha512-sc5qgX4YtM6IxjtosjgQLdsal120MvU+YWs0F2MdgQWijps22AXLDCUoBnZZ8vxVhVyJ2WnnIPrmtVBvVJjSuQ=="],
- "react-native-bottom-tabs": ["react-native-bottom-tabs@1.1.0", "", { "dependencies": { "react-freeze": "^1.0.0", "sf-symbols-typescript": "^2.0.0", "use-latest-callback": "^0.2.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-Uu1gvM3i1Hb4DjVvR/38J1QVQEs0RkPc7K6yon99HgvRWWOyLs7kjPDhUswtb8ije4pKW712skIXWJ0lgKzbyQ=="],
+ "react-native-bottom-tabs": ["react-native-bottom-tabs@0.8.6", "", { "dependencies": { "sf-symbols-typescript": "^2.0.0", "use-latest-callback": "^0.2.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-N5b3MoSfsEqlmvFyIyL0X0bd+QAtB+cXH1rl/+R2Kr0BefBTC7ZldGcPhgK3FhBbt0vJDpd3kLb/dvmqZd+Eag=="],
"react-native-circular-progress": ["react-native-circular-progress@1.4.1", "", { "dependencies": { "prop-types": "^15.8.1" }, "peerDependencies": { "react": ">=16.0.0", "react-native": ">=0.50.0", "react-native-svg": ">=7.0.0" } }, "sha512-HEzvI0WPuWvsCgWE3Ff2HBTMgAEQB2GvTFw0KHyD/t1STAlDDRiolu0mEGhVvihKR3jJu3v3V4qzvSklY/7XzQ=="],
- "react-native-collapsible": ["react-native-collapsible@1.6.2", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-MCOBVJWqHNjnDaGkvxX997VONmJeebh6wyJxnHEgg0L1PrlcXU1e/bo6eK+CDVFuMrCafw8Qh4DOv/C4V/+Iew=="],
+ "react-native-compressor": ["react-native-compressor@1.10.4", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-58gbmJ+8IvsKP8JKK1E8XW5trfQY3dNuH7S0hYw0tSRQc6l0GZ3k8TYtoUbySOc1xcQSrUo51o0Chwe8x7mUTg=="],
"react-native-country-flag": ["react-native-country-flag@2.0.2", "", {}, "sha512-5LMWxS79ZQ0Q9ntYgDYzWp794+HcQGXQmzzZNBR1AT7z5HcJHtX7rlk8RHi7RVzfp5gW6plWSZ4dKjRpu/OafQ=="],
- "react-native-device-info": ["react-native-device-info@15.0.1", "", { "peerDependencies": { "react-native": "*" } }, "sha512-U5waZRXtT3l1SgZpZMlIvMKPTkFZPH8W7Ks6GrJhdH723aUIPxjVer7cRSij1mvQdOAAYFJV/9BDzlC8apG89A=="],
+ "react-native-device-info": ["react-native-device-info@14.0.4", "", { "peerDependencies": { "react-native": "*" } }, "sha512-NX0wMAknSDBeFnEnSFQ8kkAcQrFHrG4Cl0mVjoD+0++iaKrOupiGpBXqs8xR0SeJyPC5zpdPl4h/SaBGly6UxA=="],
- "react-native-draggable-flatlist": ["react-native-draggable-flatlist@4.0.3", "", { "dependencies": { "@babel/preset-typescript": "^7.17.12" }, "peerDependencies": { "react-native": ">=0.64.0", "react-native-gesture-handler": ">=2.0.0", "react-native-reanimated": ">=2.8.0" } }, "sha512-2F4x5BFieWdGq9SetD2nSAR7s7oQCSgNllYgERRXXtNfSOuAGAVbDb/3H3lP0y5f7rEyNwabKorZAD/SyyNbDw=="],
+ "react-native-edge-to-edge": ["react-native-edge-to-edge@1.4.3", "", { "peerDependencies": { "react": ">=18.2.0", "react-native": ">=0.74.0" } }, "sha512-fYchwiQ2D/8NzcvJK1sD9Cm25GFQfsLgYmGpohoSpRxwBwR5UCL0wUf4scoQgYncRh9Hmc2t8ml/sikTwMM3ng=="],
- "react-native-edge-to-edge": ["react-native-edge-to-edge@1.7.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-ERegbsq28yoMndn/Uq49i4h6aAhMvTEjOfkFh50yX9H/dMjjCr/Tix/es/9JcPRvC+q7VzCMWfxWDUb6Jrq1OQ=="],
+ "react-native-gesture-handler": ["react-native-gesture-handler@2.22.0", "", { "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-m5Ps1cOSxSiMP4re+XsbeWcC9DNJuIEjMSmtUxBdyfYEJtdu5iAAiX7KlHHrf2mnK4I/56Ncy4PvPKWBwSpWpQ=="],
- "react-native-gesture-handler": ["react-native-gesture-handler@2.28.0", "", { "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A=="],
+ "react-native-get-random-values": ["react-native-get-random-values@1.11.0", "", { "dependencies": { "fast-base64-decode": "^1.0.0" }, "peerDependencies": { "react-native": ">=0.56" } }, "sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ=="],
- "react-native-glass-effect-view": ["react-native-glass-effect-view@1.0.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-ABYG0oIiqbXsxe2R/cMhNgDn3YgwDLz/2TIN2XOxQopXC+MiGsG9C32VYQvO2sYehcu5JmI3h3EzwLwl6lJhhA=="],
+ "react-native-google-cast": ["react-native-google-cast@4.8.3", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-2s/dBr+YYSXYTo7Btx9Az9yOPjnr2GQ8GWU+qDXMsHijrYDafe9uIj7RWFFmx7cL/4RSICx1RwC3vUkHSqaWEA=="],
- "react-native-google-cast": ["react-native-google-cast@4.9.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-/HvIKAaWHtG6aTNCxrNrqA2ftWGkfH0M/2iN+28pdGUXpKmueb33mgL1m8D4zzwEODQMcmpfoCsym1IwDvugBQ=="],
+ "react-native-helmet-async": ["react-native-helmet-async@2.0.4", "", { "dependencies": { "invariant": "^2.2.4", "react-fast-compare": "^3.2.2", "shallowequal": "^1.1.0" }, "peerDependencies": { "react": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, "sha512-m3CkXWss6B1dd6mCMleLpzDCJJGGaHOLQsUzZv8kAASJmMfmVT4d2fx375iXKTRWT25ThBfae3dECuX5cq/8hg=="],
- "react-native-image-colors": ["react-native-image-colors@2.5.0", "", { "dependencies": { "node-vibrant": "^4.0.3" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-3zSDgNj5HaZ0PDWaXkc4BpWpZRM5N4gBsoPC7DBfM/+op69Yvwbc0S1T7CnxBWbvShtOvRE+b2BUBadVn+6z/g=="],
+ "react-native-image-colors": ["react-native-image-colors@2.4.0", "", { "dependencies": { "node-vibrant": "3.1.6" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-qlC31+UNVthByNLVuYSEQeZghOXn3uy1GLF6lHKlvT1HM1GGFH/LXNhU8iXAoQvUyzNa1bEAOTo09Hwinvp/rA=="],
- "react-native-ios-context-menu": ["react-native-ios-context-menu@3.2.1", "", { "dependencies": { "@dominicstop/ts-event-emitter": "^1.1.0" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-ios-utilities": "*" } }, "sha512-OBQbb3I/VUx2wQgz4cqN614kt3nJ+qx5wxEdtGN1Aj4nYYL1orp7VLFkV6axof6DgOyv0YD6af2RUTok6a2xDQ=="],
+ "react-native-ios-context-menu": ["react-native-ios-context-menu@3.1.0", "", { "dependencies": { "@dominicstop/ts-event-emitter": "^1.1.0" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-ios-utilities": "*" } }, "sha512-qdPSXMKUp5lDgmZeUPdv5sgBFhkFrIqma+zsnqJQYOvekb6Qs17yJy1Rqhrj0bJrwuduHzZX0aYbaA8whxqpDw=="],
- "react-native-ios-utilities": ["react-native-ios-utilities@5.2.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-RTw1Gk8rQhBL43+U80I+Nu8T7mLTNkj5RaG8vTs3ETEDqphS3L0Mrzk79RX0Jmm64HMad70GXHctXFlW1n0V8w=="],
+ "react-native-ios-utilities": ["react-native-ios-utilities@5.1.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-fOm7IR2KCn3MzghITbrnZfpJ3Z7wai4S46GwXwTql1fzX25eO8MXVgaUeMd5EvPwg1zAqF5I6c3T6Dby8DoF3A=="],
- "react-native-is-edge-to-edge": ["react-native-is-edge-to-edge@1.2.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q=="],
+ "react-native-is-edge-to-edge": ["react-native-is-edge-to-edge@1.1.6", "", { "peerDependencies": { "react": ">=18.2.0", "react-native": ">=0.73.0" } }, "sha512-1pHnFTlBahins6UAajXUqeCOHew9l9C2C8tErnpGC3IyLJzvxD+TpYAixnCbrVS52f7+NvMttbiSI290XfwN0w=="],
- "react-native-mmkv": ["react-native-mmkv@4.1.1", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }, "sha512-nYFjM27l7zVhIiyAqWEFRagGASecb13JMIlzAuOeakRRz9GMJ49hCQntUBE2aeuZRE4u4ehSqTOomB0mTF56Ew=="],
+ "react-native-mmkv": ["react-native-mmkv@2.12.2", "", { "peerDependencies": { "react": "*", "react-native": ">=0.71.0" } }, "sha512-6058Aq0p57chPrUutLGe9fYoiDVDNMU2PKV+lLFUJ3GhoHvUrLdsS1PDSCLr00yqzL4WJQ7TTzH+V8cpyrNcfg=="],
- "react-native-nitro-modules": ["react-native-nitro-modules@0.33.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-Kdo8qiqlkGAEs7fq29i0yiZs0Gf7ucmMiFsH8PH4uzsnSGEt2CQRBJGnQKKMl9vJYL8e7rzA0TZKRwO/L8G/Sg=="],
+ "react-native-pager-view": ["react-native-pager-view@6.5.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-YdX7bP+rPYvATMU7HzlMq9JaG3ui/+cVRbFZFGW+QshDULANFg9ECR1BA7H7JTIcO/ZgWCwF+1aVmYG5yBA9Og=="],
- "react-native-pager-view": ["react-native-pager-view@6.9.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-uUT0MMMbNtoSbxe9pRvdJJKEi9snjuJ3fXlZhG8F2vVMOBJVt/AFtqMPUHu9yMflmqOr08PewKzj9EPl/Yj+Gw=="],
+ "react-native-progress": ["react-native-progress@5.0.1", "", { "dependencies": { "prop-types": "^15.7.2" }, "peerDependencies": { "react-native-svg": "*" } }, "sha512-TYfJ4auAe5vubDma2yfFvt7ktSI+UCfysqJnkdHEcLXqAitRFOozgF/cLgN5VNi/iLdaf3ga1ETi2RF4jVZ/+g=="],
- "react-native-reanimated": ["react-native-reanimated@4.1.3", "", { "dependencies": { "react-native-is-edge-to-edge": "^1.2.1", "semver": "7.7.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*", "react-native-worklets": ">=0.5.0" } }, "sha512-GP8wsi1u3nqvC1fMab/m8gfFwFyldawElCcUSBJQgfrXeLmsPPUOpDw44lbLeCpcwUuLa05WTVePdTEwCLTUZg=="],
+ "react-native-reanimated": ["react-native-reanimated@3.16.7", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "invariant": "^2.2.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-qoUUQOwE1pHlmQ9cXTJ2MX9FQ9eHllopCLiWOkDkp6CER95ZWeXhJCP4cSm6AD4jigL5jHcZf/SkWrg8ttZUsw=="],
- "react-native-reanimated-carousel": ["react-native-reanimated-carousel@4.0.3", "", { "peerDependencies": { "react": ">=18.0.0", "react-native": ">=0.70.3", "react-native-gesture-handler": ">=2.9.0", "react-native-reanimated": ">=3.0.0" } }, "sha512-YZXlvZNghR5shFcI9hTA7h7bEhh97pfUSLZvLBAshpbkuYwJDKmQXejO/199T6hqGq0wCRwR0CWf2P4Vs6A4Fw=="],
+ "react-native-reanimated-carousel": ["react-native-reanimated-carousel@3.5.1", "", { "peerDependencies": { "react": ">=16.8.0", "react-native": ">=0.6.0", "react-native-gesture-handler": ">=2.0.0", "react-native-reanimated": ">=3.0.0" } }, "sha512-9BBQV6JAYSQm2lV7MFtT4mzapXmW4IZO6s38gfiJL84Jg23ivGB1UykcNQauKgtHyhtW2NuZJzItb1s42lM+hA=="],
- "react-native-safe-area-context": ["react-native-safe-area-context@5.6.2", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg=="],
+ "react-native-safe-area-context": ["react-native-safe-area-context@5.1.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-Y4vyJX+0HPJUQNVeIJTj2/UOjbSJcB09OEwirAWDrOZ67Lz5p43AmjxSy8nnZft1rMzoh3rcPuonB6jJyHTfCw=="],
- "react-native-screens": ["react-native-screens@4.18.0", "", { "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-mRTLWL7Uc1p/RFNveEIIrhP22oxHduC2ZnLr/2iHwBeYpGXR0rJZ7Bgc0ktxQSHRjWTPT70qc/7yd4r9960PBQ=="],
+ "react-native-screens": ["react-native-screens@4.5.0", "", { "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-yBWeN5EHNeew9f0ia9VE7JSlUQzCZEwkb87r7A7/Sg41OJHuRKHNRhmdCOiMBUqwwQi3F+b4NZGywjeM/gWMyg=="],
- "react-native-svg": ["react-native-svg@15.12.1", "", { "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", "warn-once": "0.1.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g=="],
+ "react-native-svg": ["react-native-svg@15.11.1", "", { "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", "warn-once": "0.1.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-Qmwx/yJKt+AHUr4zjxx/Q69qwKtRfr1+uIfFMQoq3WFRhqU76aL9db1DyvPiY632DAsVGba1pHf92OZPkpjrdQ=="],
- "react-native-tab-view": ["react-native-tab-view@4.2.0", "", { "dependencies": { "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*", "react-native-pager-view": ">= 6.0.0" } }, "sha512-TUbh7Yr0tE/99t1pJQLbQ+4/Px67xkT7/r3AhfV+93Q3WoUira0Lx7yuKUP2C118doqxub8NCLERwcqsHr29nQ=="],
+ "react-native-tab-view": ["react-native-tab-view@4.0.5", "", { "dependencies": { "use-latest-callback": "^0.2.1" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*", "react-native-pager-view": ">= 6.0.0" } }, "sha512-Xn3TpYo4yvKRC/f4+cOcvsXlitdnSaYkacshckrEI3JiDmFKNFIRVNxtZFggm4MwbJafq2RzuzR6xrgKoxgkTw=="],
- "react-native-text-ticker": ["react-native-text-ticker@1.15.0", "", {}, "sha512-d/uK+PIOhsYMy1r8h825iq/nADiHsabz3WMbRJSnkpQYn+K9aykUAXRRhu8ZbTAzk4CgnUWajJEFxS5ZDygsdg=="],
-
- "react-native-track-player": ["react-native-track-player@github:lovegaoshi/react-native-track-player#003afd0", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-windows": "*", "shaka-player": "^4.7.9" }, "optionalPeers": ["react-native-windows", "shaka-player"] }, "lovegaoshi-react-native-track-player-003afd0"],
+ "react-native-tvos": ["react-native-tvos@0.77.0-0", "", { "dependencies": { "@jest/create-cache-key-function": "^29.6.3", "@react-native-tvos/virtualized-lists": "0.77.0-0", "@react-native/assets-registry": "0.77.0", "@react-native/codegen": "0.77.0", "@react-native/community-cli-plugin": "0.77.0", "@react-native/gradle-plugin": "0.77.0", "@react-native/js-polyfills": "0.77.0", "@react-native/normalize-colors": "0.77.0", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.25.1", "base64-js": "^1.5.1", "chalk": "^4.0.0", "commander": "^12.0.0", "event-target-shim": "^5.0.1", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "invariant": "^2.2.4", "jest-environment-node": "^29.6.3", "jsc-android": "^250231.0.0", "memoize-one": "^5.0.0", "metro-runtime": "^0.81.0", "metro-source-map": "^0.81.0", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.0.1", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.24.0-canary-efb381bbf-20230505", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^18.2.6", "react": "^18.2.0" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js" } }, "sha512-edIOqGrPadpXHmt5R/LuhekHHLx/0DyrfY5A9odS2AlS+03S0ada7H5oDvusOUVcyq1vc3isrwZpUSQzudoR1g=="],
"react-native-udp": ["react-native-udp@4.1.7", "", { "dependencies": { "buffer": "^5.6.0", "events": "^3.1.0" } }, "sha512-NUE3zewu61NCdSsLlj+l0ad6qojcVEZPT4hVG/x6DU9U4iCzwtfZSASh9vm7teAcVzLkdD+cO3411LHshAi/wA=="],
+ "react-native-uitextview": ["react-native-uitextview@1.4.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-itm/frzkn/ma3+lwmKn2CkBOXPNo4bL8iVwQwjlzix5gVO59T2+axdfoj/Wi+Ra6F76KzNKxSah+7Y8dYmCHbQ=="],
+
"react-native-url-polyfill": ["react-native-url-polyfill@2.0.0", "", { "dependencies": { "whatwg-url-without-unicode": "8.0.0-3" }, "peerDependencies": { "react-native": "*" } }, "sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA=="],
"react-native-uuid": ["react-native-uuid@2.0.3", "", {}, "sha512-f/YfIS2f5UB+gut7t/9BKGSCYbRA9/74A5R1MDp+FLYsuS+OSWoiM/D8Jko6OJB6Jcu3v6ONuddvZKHdIGpeiw=="],
+ "react-native-video": ["react-native-video@6.10.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-NyDnpSBRJkj7TxMku2dEys54qKynW/l9kSPCVvayyqXWrc24cxHhLvAaxORdJDb6tS4FhUbR8tFIoOY65/XKZg=="],
+
"react-native-volume-manager": ["react-native-volume-manager@2.0.8", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-aZM47/mYkdQ4CbXpKYO6Ajiczv7fxbQXZ9c0H8gRuQUaS3OCz/MZABer6o9aDWq0KMNsQ7q7GVFLRPnSSeeMmw=="],
- "react-native-web": ["react-native-web@0.21.2", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", "fbjs": "^3.0.4", "inline-style-prefixer": "^7.0.1", "memoize-one": "^6.0.0", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "styleq": "^0.1.3" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg=="],
+ "react-native-web": ["react-native-web@0.19.13", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", "fbjs": "^3.0.4", "inline-style-prefixer": "^6.0.1", "memoize-one": "^6.0.0", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "styleq": "^0.1.3" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }, "sha512-etv3bN8rJglrRCp/uL4p7l8QvUNUC++QwDbdZ8CB7BvZiMvsxfFIRM1j04vxNldG3uo2puRd6OSWR3ibtmc29A=="],
- "react-native-worklets": ["react-native-worklets@0.5.1", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "semver": "7.7.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w=="],
+ "react-native-webview": ["react-native-webview@13.13.2", "", { "dependencies": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-zACPDTF0WnaEnKZ9mA/r/UpcOpV2gQM06AAIrOOexnO8UJvXL8Pjso0b/wTqKFxUZZnmjKuwd8gHVUosVOdVrw=="],
"react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
- "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
+ "react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="],
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
"react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
- "react-test-renderer": ["react-test-renderer@19.2.3", "", { "dependencies": { "react-is": "^19.2.3", "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-TMR1LnSFiWZMJkCgNf5ATSvAheTT2NvKIwiVwdBPHxjBI7n/JbWd4gaZ16DVd9foAXdvDz+sB5yxZTwMjPRxpw=="],
+ "react-test-renderer": ["react-test-renderer@19.0.0", "", { "dependencies": { "react-is": "^19.0.0", "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-oX5u9rOQlHzqrE/64CNr0HB0uWxkCQmZNSfozlYvwE71TLVgeZxVf0IjouGEr1v7r1kcDifdAJBeOhdhxsG/DA=="],
"read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
@@ -1728,17 +1910,27 @@
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
+ "readline": ["readline@1.3.0", "", {}, "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg=="],
+
+ "recast": ["recast@0.23.9", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q=="],
+
+ "recyclerlistview": ["recyclerlistview@4.2.1", "", { "dependencies": { "lodash.debounce": "4.0.8", "prop-types": "15.8.1", "ts-object-utils": "0.0.5" }, "peerDependencies": { "react": ">= 15.2.1", "react-native": ">= 0.30.0" } }, "sha512-NtVYjofwgUCt1rEsTp6jHQg/47TWjnO92TU2kTVgJ9wsc/ely4HnizHHa+f/dI7qaw4+zcSogElrLjhMltN2/g=="],
+
"regenerate": ["regenerate@1.4.2", "", {}, "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="],
- "regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
+ "regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.0", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA=="],
"regenerator-runtime": ["regenerator-runtime@0.13.11", "", {}, "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="],
- "regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="],
+ "regenerator-transform": ["regenerator-transform@0.15.2", "", { "dependencies": { "@babel/runtime": "^7.8.4" } }, "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg=="],
+
+ "regexpu-core": ["regexpu-core@6.2.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" } }, "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA=="],
"regjsgen": ["regjsgen@0.8.0", "", {}, "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q=="],
- "regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
+ "regjsparser": ["regjsparser@0.12.0", "", { "dependencies": { "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ=="],
+
+ "remove-trailing-slash": ["remove-trailing-slash@0.1.1", "", {}, "sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA=="],
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
@@ -1748,23 +1940,19 @@
"requireg": ["requireg@0.2.2", "", { "dependencies": { "nested-error-stacks": "~2.0.1", "rc": "~1.2.7", "resolve": "~1.7.1" } }, "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg=="],
- "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
+ "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
"resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
- "resolve-global": ["resolve-global@1.0.0", "", { "dependencies": { "global-dirs": "^0.1.1" } }, "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw=="],
-
"resolve-workspace-root": ["resolve-workspace-root@2.0.0", "", {}, "sha512-IsaBUZETJD5WsI11Wt8PKHwaIe45or6pwNc8yflvLJ4DWtImK9kuLoH5kUva/2Mmx/RdIyr4aONNSa2v9LTJsw=="],
"resolve.exports": ["resolve.exports@2.0.3", "", {}, "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A=="],
"restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="],
- "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
+ "reusify": ["reusify@1.0.4", "", {}, "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="],
- "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
-
- "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
+ "rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="],
"rtl-detect": ["rtl-detect@1.1.2", "", {}, "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ=="],
@@ -1774,15 +1962,17 @@
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
- "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
+ "sax": ["sax@1.4.1", "", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="],
- "sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="],
+ "scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="],
- "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
+ "schema-utils": ["schema-utils@4.3.0", "", { "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", "ajv-formats": "^2.1.1", "ajv-keywords": "^5.1.0" } }, "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g=="],
+
+ "selfsigned": ["selfsigned@2.4.1", "", { "dependencies": { "@types/node-forge": "^1.3.0", "node-forge": "^1" } }, "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q=="],
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
- "send": ["send@0.19.1", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg=="],
+ "send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
"serialize-error": ["serialize-error@2.1.0", "", {}, "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw=="],
@@ -1792,6 +1982,8 @@
"set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
+ "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="],
+
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
"setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="],
@@ -1800,13 +1992,15 @@
"sf-symbols-typescript": ["sf-symbols-typescript@2.1.0", "", {}, "sha512-ezT7gu/SHTPIOEEoG6TF+O0m5eewl0ZDAO4AtdBi5HjsrUI6JdCG17+Q8+aKp0heM06wZKApRCn5olNbs0Wb/A=="],
+ "shallow-clone": ["shallow-clone@3.0.1", "", { "dependencies": { "kind-of": "^6.0.2" } }, "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA=="],
+
"shallowequal": ["shallowequal@1.1.0", "", {}, "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
- "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
+ "shell-quote": ["shell-quote@1.8.2", "", {}, "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA=="],
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
@@ -1820,17 +2014,17 @@
"simple-plist": ["simple-plist@1.3.1", "", { "dependencies": { "bplist-creator": "0.1.0", "bplist-parser": "0.3.1", "plist": "^3.0.5" } }, "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw=="],
- "simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
+ "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
"slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="],
- "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="],
+ "slice-ansi": ["slice-ansi@2.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" } }, "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ=="],
"slugify": ["slugify@1.6.6", "", {}, "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="],
- "sonner-native": ["sonner-native@0.21.2", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-gesture-handler": ">=2.16.1", "react-native-reanimated": ">=3.10.1", "react-native-safe-area-context": ">=4.10.5", "react-native-screens": ">=3.31.1", "react-native-svg": ">=15.6.0" } }, "sha512-LnGPmfgzrNIwcc+FvcLJqx8aH1dEHePRzvNR8aIR4kl9spySRkXK160GmQIazjfm6mSMlPqZwRa5eycvrzg/eQ=="],
+ "sonner-native": ["sonner-native@0.17.0", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-gesture-handler": ">=2.16.1", "react-native-reanimated": ">=3.10.1", "react-native-safe-area-context": ">=4.10.5", "react-native-screens": ">=3.31.1", "react-native-svg": ">=15.6.0" } }, "sha512-RRkgnuiuccgEDJdeYnFjPzTgunN75RVB9yFh6vc9D4QEKKITrhkBGaNz0wLY85/A5MBCMmaKtfKv/uxQDxqg+g=="],
"source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
@@ -1838,10 +2032,14 @@
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
+ "split": ["split@1.0.1", "", { "dependencies": { "through": "2" } }, "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg=="],
+
"split-on-first": ["split-on-first@1.1.0", "", {}, "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="],
"sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
+ "ssri": ["ssri@10.0.6", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ=="],
+
"stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
"stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="],
@@ -1852,9 +2050,9 @@
"stream-buffers": ["stream-buffers@2.2.0", "", {}, "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg=="],
- "strict-uri-encode": ["strict-uri-encode@2.0.0", "", {}, "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="],
+ "stream-slice": ["stream-slice@0.1.2", "", {}, "sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA=="],
- "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="],
+ "strict-uri-encode": ["strict-uri-encode@2.0.0", "", {}, "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="],
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -1862,15 +2060,17 @@
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
- "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+ "strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+ "strip-eof": ["strip-eof@1.0.0", "", {}, "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q=="],
+
"strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
"strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
- "strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="],
+ "strnum": ["strnum@1.1.1", "", {}, "sha512-O7aCHfYCamLCctjAiaucmE+fHf2DYHkus2OKCn4Wv03sykfFtgeECn505X6K4mPl8CRNd/qurC9guq+ynoN4pw=="],
"strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="],
@@ -1880,6 +2080,8 @@
"sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
+ "sudo-prompt": ["sudo-prompt@9.2.1", "", {}, "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw=="],
+
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"supports-hyperlinks": ["supports-hyperlinks@2.3.0", "", { "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA=="],
@@ -1888,13 +2090,17 @@
"tailwindcss": ["tailwindcss@3.3.2", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.12", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.18.2", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", "postcss": "^8.4.23", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w=="],
- "tar": ["tar@7.5.2", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg=="],
+ "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
+
+ "temp": ["temp@0.8.4", "", { "dependencies": { "rimraf": "~2.6.2" } }, "sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg=="],
"temp-dir": ["temp-dir@2.0.0", "", {}, "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg=="],
+ "tempy": ["tempy@0.7.1", "", { "dependencies": { "del": "^6.0.0", "is-stream": "^2.0.0", "temp-dir": "^2.0.0", "type-fest": "^0.16.0", "unique-string": "^2.0.0" } }, "sha512-vXPxwOyaNVi9nyczO16mxmHGpl6ASC5/TVhRRHpqeYHvKQm58EaWNvZXxAhR0lYYnBOQFjXjhzeLsaXdjxLjRg=="],
+
"terminal-link": ["terminal-link@2.1.1", "", { "dependencies": { "ansi-escapes": "^4.2.1", "supports-hyperlinks": "^2.0.0" } }, "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ=="],
- "terser": ["terser@5.44.1", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw=="],
+ "terser": ["terser@5.39.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw=="],
"test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="],
@@ -1904,13 +2110,15 @@
"throat": ["throat@5.0.0", "", {}, "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA=="],
+ "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="],
+
"timm": ["timm@1.7.1", "", {}, "sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw=="],
+ "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
+
"tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
- "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
-
- "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],
+ "tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
"tmpl": ["tmpl@1.0.5", "", {}, "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="],
@@ -1926,31 +2134,35 @@
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
+ "ts-object-utils": ["ts-object-utils@0.0.5", "", {}, "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA=="],
+
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+ "turbo-stream": ["turbo-stream@2.4.0", "", {}, "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g=="],
+
"type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="],
"type-fest": ["type-fest@0.7.1", "", {}, "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="],
- "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
+ "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
- "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+ "ua-parser-js": ["ua-parser-js@0.7.40", "", { "bin": { "ua-parser-js": "script/cli.js" } }, "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ=="],
- "ua-parser-js": ["ua-parser-js@0.7.41", "", { "bin": { "ua-parser-js": "script/cli.js" } }, "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg=="],
-
- "undici": ["undici@6.22.0", "", {}, "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw=="],
-
- "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
+ "undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="],
"unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="],
"unicode-match-property-ecmascript": ["unicode-match-property-ecmascript@2.0.0", "", { "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" } }, "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q=="],
- "unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="],
+ "unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.0", "", {}, "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg=="],
- "unicode-property-aliases-ecmascript": ["unicode-property-aliases-ecmascript@2.2.0", "", {}, "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ=="],
+ "unicode-property-aliases-ecmascript": ["unicode-property-aliases-ecmascript@2.1.0", "", {}, "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w=="],
- "unimodules-app-loader": ["unimodules-app-loader@6.0.8", "", {}, "sha512-fqS8QwT/MC/HAmw1NKCHdzsPA6WaLm0dNmoC5Pz6lL+cDGYeYCNdHMO9fy08aL2ZD7cVkNM0pSR/AoNRe+rslA=="],
+ "unimodules-app-loader": ["unimodules-app-loader@5.0.1", "", {}, "sha512-JI4dUMOovvLrZ1U/mrQrR73cxGH26H7NpfBxwE0hk59CBOyHO4YYpliI3hPSGgZzt+YEy2VZR6nrspSUXY8jyw=="],
+
+ "unique-filename": ["unique-filename@3.0.0", "", { "dependencies": { "unique-slug": "^4.0.0" } }, "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g=="],
+
+ "unique-slug": ["unique-slug@4.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ=="],
"unique-string": ["unique-string@2.0.0", "", { "dependencies": { "crypto-random-string": "^2.0.0" } }, "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg=="],
@@ -1958,19 +2170,23 @@
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
- "update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+ "update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="],
+
+ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
+
+ "url": ["url@0.11.4", "", { "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" } }, "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg=="],
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
- "use-debounce": ["use-debounce@10.0.6", "", { "peerDependencies": { "react": "*" } }, "sha512-C5OtPyhAZgVoteO9heXMTdW7v/IbFI+8bSVKYCJrSmiWWCLsbUxiBSp4t9v0hNBTGY97bT72ydDIDyGSFWfwXg=="],
+ "use-debounce": ["use-debounce@10.0.4", "", { "peerDependencies": { "react": "*" } }, "sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw=="],
- "use-latest-callback": ["use-latest-callback@0.2.6", "", { "peerDependencies": { "react": ">=16.8" } }, "sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg=="],
+ "use-latest-callback": ["use-latest-callback@0.2.3", "", { "peerDependencies": { "react": ">=16.8" } }, "sha512-7vI3fBuyRcP91pazVboc4qu+6ZqM8izPWX9k7cRnT8hbD5svslcknsh3S9BUhaK11OmgTV4oWZZVSeQAiV53SQ=="],
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
- "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
+ "use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="],
- "utif2": ["utif2@4.1.0", "", { "dependencies": { "pako": "^1.0.11" } }, "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w=="],
+ "utif": ["utif@2.0.1", "", { "dependencies": { "pako": "^1.0.5" } }, "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg=="],
"util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="],
@@ -1978,14 +2194,12 @@
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
- "uuid": ["uuid@7.0.3", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="],
+ "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
"validate-npm-package-name": ["validate-npm-package-name@5.0.1", "", {}, "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ=="],
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
- "vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="],
-
"vlq": ["vlq@1.0.1", "", {}, "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w=="],
"void-elements": ["void-elements@3.1.0", "", {}, "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="],
@@ -1996,6 +2210,10 @@
"wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="],
+ "web-encoding": ["web-encoding@1.1.5", "", { "dependencies": { "util": "^0.12.3" }, "optionalDependencies": { "@zxing/text-encoding": "0.9.0" } }, "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA=="],
+
+ "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
+
"webidl-conversions": ["webidl-conversions@5.0.0", "", {}, "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA=="],
"whatwg-fetch": ["whatwg-fetch@3.6.20", "", {}, "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="],
@@ -2008,31 +2226,37 @@
"which-module": ["which-module@2.0.1", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="],
- "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
+ "which-typed-array": ["which-typed-array@1.1.18", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.3", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA=="],
- "wonka": ["wonka@6.3.5", "", {}, "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw=="],
+ "wonka": ["wonka@6.3.4", "", {}, "sha512-CjpbqNtBGNAeyNS/9W6q3kSkKE52+FjIj7AkFlLr11s/VWGUu6a2CdYSdGxocIhIVjaW/zchesBQUKPVU69Cqg=="],
- "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
+ "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
- "write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="],
+ "write-file-atomic": ["write-file-atomic@2.4.3", "", { "dependencies": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", "signal-exit": "^3.0.2" } }, "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ=="],
"ws": ["ws@6.2.3", "", { "dependencies": { "async-limiter": "~1.0.0" } }, "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA=="],
"xcode": ["xcode@3.0.1", "", { "dependencies": { "simple-plist": "^1.1.0", "uuid": "^7.0.3" } }, "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA=="],
+ "xhr": ["xhr@2.6.0", "", { "dependencies": { "global": "~4.4.0", "is-function": "^1.0.1", "parse-headers": "^2.0.0", "xtend": "^4.0.0" } }, "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA=="],
+
+ "xml-parse-from-string": ["xml-parse-from-string@1.0.1", "", {}, "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g=="],
+
"xml2js": ["xml2js@0.6.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w=="],
- "xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
+ "xmlbuilder": ["xmlbuilder@14.0.0", "", {}, "sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg=="],
+
+ "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
- "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
+ "yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="],
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
@@ -2040,147 +2264,87 @@
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
- "zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="],
+ "zeego": ["zeego@2.0.4", "", { "dependencies": { "@radix-ui/react-context-menu": "^2.0.1", "@radix-ui/react-dropdown-menu": "^2.0.1", "sf-symbols-typescript": "^2.0.0" }, "peerDependencies": { "@react-native-menu/menu": "*", "react": "*", "react-native": "*", "react-native-ios-context-menu": "~2.5.1" } }, "sha512-mkKfUJmgcSGCTqWXW7ccqap8d8z6guQoLF5/mWlH17Jckd0BaFLVZ74y/uWvfBGaC9OawYVt/1MchPnQ8ieSxg=="],
- "@babel/helper-annotate-as-pure/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+ "zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
- "@babel/helper-create-class-features-plugin/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/helper-define-polyfill-provider/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
-
- "@babel/helper-member-expression-to-functions/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/helper-member-expression-to-functions/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/helper-module-imports/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
-
- "@babel/helper-optimise-call-expression/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/helper-remap-async-to-generator/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/helper-replace-supers/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/helper-skip-transparent-expression-wrappers/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/helper-wrap-function/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/helper-wrap-function/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/helper-wrap-function/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+ "@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="],
"@babel/highlight/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
- "@babel/plugin-transform-async-generator-functions/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+ "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="],
- "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "@babel/plugin-transform-react-jsx/@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="],
- "@babel/plugin-transform-classes/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
+ "@babel/plugin-transform-runtime/@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="],
- "@babel/plugin-transform-classes/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+ "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.10.6", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2", "core-js-compat": "^3.38.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA=="],
- "@babel/plugin-transform-computed-properties/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
+ "@babel/runtime/regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
- "@babel/plugin-transform-destructuring/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+ "@config-plugins/ffmpeg-kit-react-native/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "@babel/plugin-transform-function-name/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
+ "@expo/bunyan/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
- "@babel/plugin-transform-function-name/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+ "@expo/cli/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
- "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
+ "@expo/cli/form-data": ["form-data@3.0.3", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.35" } }, "sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w=="],
- "@babel/plugin-transform-object-rest-spread/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
-
- "@babel/plugin-transform-object-rest-spread/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/plugin-transform-react-jsx/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
-
- "@babel/plugin-transform-react-jsx/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-runtime/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
-
- "@babel/traverse--for-generate-function-map/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/traverse--for-generate-function-map/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/traverse--for-generate-function-map/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/traverse--for-generate-function-map/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/traverse--for-generate-function-map/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@expo/cli/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
-
- "@expo/cli/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
+ "@expo/cli/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"@expo/cli/ora": ["ora@3.4.0", "", { "dependencies": { "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-spinners": "^2.0.0", "log-symbols": "^2.2.0", "strip-ansi": "^5.2.0", "wcwidth": "^1.0.1" } }, "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg=="],
- "@expo/cli/picomatch": ["picomatch@3.0.1", "", {}, "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag=="],
+ "@expo/cli/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "@expo/cli/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
-
- "@expo/cli/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
-
- "@expo/cli/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
+ "@expo/cli/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
"@expo/config/@babel/code-frame": ["@babel/code-frame@7.10.4", "", { "dependencies": { "@babel/highlight": "^7.10.4" } }, "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg=="],
- "@expo/config/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
+ "@expo/config/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "@expo/config/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
-
- "@expo/config/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
-
- "@expo/config/sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
-
- "@expo/config-plugins/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
-
- "@expo/config-plugins/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
-
- "@expo/config-plugins/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "@expo/config-plugins/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
"@expo/config-plugins/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"@expo/devcert/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
- "@expo/env/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
+ "@expo/devcert/sudo-prompt": ["sudo-prompt@8.2.5", "", {}, "sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw=="],
- "@expo/fingerprint/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
+ "@expo/fingerprint/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
- "@expo/fingerprint/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
+ "@expo/fingerprint/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
- "@expo/fingerprint/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "@expo/fingerprint/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "@expo/image-utils/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
+ "@expo/image-utils/fs-extra": ["fs-extra@9.0.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^1.0.0" } }, "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g=="],
- "@expo/image-utils/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "@expo/image-utils/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
"@expo/json-file/@babel/code-frame": ["@babel/code-frame@7.10.4", "", { "dependencies": { "@babel/highlight": "^7.10.4" } }, "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg=="],
- "@expo/metro-config/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
+ "@expo/metro-config/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
- "@expo/metro-config/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
-
- "@expo/metro-config/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@expo/metro-config/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
-
- "@expo/metro-config/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
+ "@expo/metro-config/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"@expo/metro-config/postcss": ["postcss@8.4.49", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA=="],
+ "@expo/package-manager/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
+
"@expo/package-manager/ora": ["ora@3.4.0", "", { "dependencies": { "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-spinners": "^2.0.0", "log-symbols": "^2.2.0", "strip-ansi": "^5.2.0", "wcwidth": "^1.0.1" } }, "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg=="],
- "@expo/prebuild-config/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "@expo/package-manager/sudo-prompt": ["sudo-prompt@9.1.1", "", {}, "sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA=="],
+
+ "@expo/prebuild-config/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
+
+ "@expo/prebuild-config/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
+
+ "@expo/rudder-sdk-node/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
"@expo/xcpretty/@babel/code-frame": ["@babel/code-frame@7.10.4", "", { "dependencies": { "@babel/highlight": "^7.10.4" } }, "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg=="],
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
- "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
+ "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
@@ -2190,103 +2354,85 @@
"@istanbuljs/load-nyc-config/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
- "@jest/transform/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
-
"@jest/transform/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
- "@jimp/png/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
+ "@jest/transform/write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="],
- "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
+ "@npmcli/fs/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
+ "@radix-ui/react-collection/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
- "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
+ "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
- "@react-native-community/cli/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
- "@react-native-community/cli-doctor/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "@radix-ui/react-dropdown-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
- "@react-native-community/cli-server-api/open": ["open@6.4.0", "", { "dependencies": { "is-wsl": "^1.1.0" } }, "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg=="],
+ "@radix-ui/react-focus-scope/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
- "@react-native-community/cli-tools/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "@radix-ui/react-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
- "@react-native/babel-plugin-codegen/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+ "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
- "@react-native/babel-preset/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
+ "@radix-ui/react-popper/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
- "@react-native/babel-preset/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
+ "@radix-ui/react-presence/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
- "@react-native/codegen/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
+ "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
- "@react-native/codegen/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
+ "@radix-ui/react-roving-focus/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
- "@react-native/community-cli-plugin/metro": ["metro@0.83.2", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.32.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.2", "metro-cache": "0.83.2", "metro-cache-key": "0.83.2", "metro-config": "0.83.2", "metro-core": "0.83.2", "metro-file-map": "0.83.2", "metro-resolver": "0.83.2", "metro-runtime": "0.83.2", "metro-source-map": "0.83.2", "metro-symbolicate": "0.83.2", "metro-transform-plugins": "0.83.2", "metro-transform-worker": "0.83.2", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-HQgs9H1FyVbRptNSMy/ImchTTE5vS2MSqLoOo7hbDoBq6hPPZokwJvBMwrYSxdjQZmLXz2JFZtdvS+ZfgTc9yw=="],
+ "@react-native-community/cli/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "@react-native/community-cli-plugin/metro-config": ["metro-config@0.83.2", "", { "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", "metro": "0.83.2", "metro-cache": "0.83.2", "metro-core": "0.83.2", "metro-runtime": "0.83.2", "yaml": "^2.6.1" } }, "sha512-1FjCcdBe3e3D08gSSiU9u3Vtxd7alGH3x/DNFqWDFf5NouX4kLgbVloDDClr1UrLz62c0fHh2Vfr9ecmrOZp+g=="],
+ "@react-native-community/cli-doctor/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "@react-native/community-cli-plugin/metro-core": ["metro-core@0.83.2", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", "metro-resolver": "0.83.2" } }, "sha512-8DRb0O82Br0IW77cNgKMLYWUkx48lWxUkvNUxVISyMkcNwE/9ywf1MYQUE88HaKwSrqne6kFgCSA/UWZoUT0Iw=="],
+ "@react-native-community/cli-server-api/pretty-format": ["pretty-format@26.6.2", "", { "dependencies": { "@jest/types": "^26.6.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^17.0.1" } }, "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg=="],
- "@react-native/community-cli-plugin/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "@react-native-community/cli-tools/open": ["open@6.4.0", "", { "dependencies": { "is-wsl": "^1.1.0" } }, "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg=="],
- "@react-navigation/bottom-tabs/@react-navigation/elements": ["@react-navigation/elements@2.8.1", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.1.19", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-MLmuS5kPAeAFFOylw89WGjgEFBqGj/KBK6ZrFrAOqLnTqEzk52/SO1olb5GB00k6ZUCDZKJOp1BrLXslxE6TgQ=="],
+ "@react-native-community/cli-tools/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "@react-navigation/bottom-tabs/color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen": ["@react-native/codegen@0.76.7", "", { "dependencies": { "@babel/parser": "^7.25.3", "glob": "^7.1.1", "hermes-parser": "0.23.1", "invariant": "^2.2.4", "jscodeshift": "^0.14.0", "mkdirp": "^0.5.1", "nullthrows": "^1.1.1", "yargs": "^17.6.2" }, "peerDependencies": { "@babel/preset-env": "^7.1.6" } }, "sha512-FAn585Ll65YvkSrKDyAcsdjHhhAGiMlSTUpHh0x7J5ntudUns+voYms0xMP+pEPt0XuLdjhD7zLIIlAWP407+g=="],
- "@react-navigation/core/react-is": ["react-is@19.2.0", "", {}, "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA=="],
+ "@react-native/codegen/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
- "@react-navigation/elements/color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
+ "@react-native/community-cli-plugin/@react-native/dev-middleware": ["@react-native/dev-middleware@0.77.0", "", { "dependencies": { "@isaacs/ttlcache": "^1.4.1", "@react-native/debugger-frontend": "0.77.0", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.0", "connect": "^3.6.5", "debug": "^2.2.0", "nullthrows": "^1.1.1", "open": "^7.0.3", "selfsigned": "^2.4.1", "serve-static": "^1.16.2", "ws": "^6.2.3" } }, "sha512-DAlEYujm43O+Dq98KP2XfLSX5c/TEGtt+JBDEIOQewk374uYY52HzRb1+Gj6tNaEj/b33no4GibtdxbO5zmPhg=="],
- "@react-navigation/material-top-tabs/color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
+ "@react-native/community-cli-plugin/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
- "@react-navigation/native-stack/@react-navigation/elements": ["@react-navigation/elements@2.8.1", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.1.19", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-MLmuS5kPAeAFFOylw89WGjgEFBqGj/KBK6ZrFrAOqLnTqEzk52/SO1olb5GB00k6ZUCDZKJOp1BrLXslxE6TgQ=="],
+ "@react-native/community-cli-plugin/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "@react-navigation/native-stack/color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
+ "@react-native/dev-middleware/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
- "@tanstack/react-query/@tanstack/query-core": ["@tanstack/query-core@5.90.17", "", {}, "sha512-hDww+RyyYhjhUfoYQ4es6pbgxY7LNiPWxt4l1nJqhByjndxJ7HIjDxTBtfvMr5HwjYavMrd+ids5g4Rfev3lVQ=="],
+ "@react-native/metro-babel-transformer/@react-native/babel-preset": ["@react-native/babel-preset@0.77.0", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-transform-arrow-functions": "^7.24.7", "@babel/plugin-transform-async-generator-functions": "^7.25.4", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-computed-properties": "^7.24.7", "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-for-of": "^7.24.7", "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", "@babel/plugin-transform-numeric-separator": "^7.24.7", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-react-display-name": "^7.24.7", "@babel/plugin-transform-react-jsx": "^7.25.2", "@babel/plugin-transform-react-jsx-self": "^7.24.7", "@babel/plugin-transform-react-jsx-source": "^7.24.7", "@babel/plugin-transform-regenerator": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/plugin-transform-shorthand-properties": "^7.24.7", "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-typescript": "^7.25.2", "@babel/plugin-transform-unicode-regex": "^7.24.7", "@babel/template": "^7.25.0", "@react-native/babel-plugin-codegen": "0.77.0", "babel-plugin-syntax-hermes-parser": "0.25.1", "babel-plugin-transform-flow-enums": "^0.0.2", "react-refresh": "^0.14.0" } }, "sha512-Z4yxE66OvPyQ/iAlaETI1ptRLcDm7Tk6ZLqtCPuUX3AMg+JNgIA86979T4RSk486/JrBUBH5WZe2xjj7eEHXsA=="],
- "@types/babel__core/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
+ "@react-navigation/core/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
- "@types/babel__core/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@types/babel__generator/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@types/babel__template/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@types/babel__template/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@types/babel__traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+ "@remix-run/server-runtime/source-map": ["source-map@0.7.4", "", {}, "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="],
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
- "ansi-fragments/colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="],
+ "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
- "ansi-fragments/slice-ansi": ["slice-ansi@2.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" } }, "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ=="],
-
- "ansi-fragments/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
+ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"babel-jest/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
- "babel-plugin-jest-hoist/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "babel-plugin-jest-hoist/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "babel-plugin-polyfill-corejs2/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
-
- "babel-plugin-react-compiler/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "babel-preset-expo/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
-
"better-opn/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
- "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
+ "cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
- "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+ "caller-callsite/callsites": ["callsites@2.0.0", "", {}, "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ=="],
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
- "cli-truncate/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="],
+ "chromium-edge-launcher/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
- "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
+ "chromium-edge-launcher/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
+
+ "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "compressible/mime-db": ["mime-db@1.53.0", "", {}, "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg=="],
"compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
@@ -2294,21 +2440,29 @@
"css-tree/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
+ "default-gateway/execa": ["execa@1.0.0", "", { "dependencies": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" } }, "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA=="],
+
+ "del/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
+
+ "del/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
+
"error-ex/is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
- "expo-build-properties/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "expo-build-properties/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "expo-manifests/@expo/config": ["@expo/config@12.0.11", "", { "dependencies": { "@babel/code-frame": "~7.10.4", "@expo/config-plugins": "~54.0.3", "@expo/config-types": "^54.0.9", "@expo/json-file": "^10.0.7", "deepmerge": "^4.3.1", "getenv": "^2.0.0", "glob": "^13.0.0", "require-from-string": "^2.0.2", "resolve-from": "^5.0.0", "resolve-workspace-root": "^2.0.0", "semver": "^7.6.0", "slugify": "^1.3.4", "sucrase": "~3.35.1" } }, "sha512-bGKNCbHirwgFlcOJHXpsAStQvM0nU3cmiobK0o07UkTfcUxl9q9lOQQh2eoMGqpm6Vs1IcwBpYye6thC3Nri/w=="],
+ "expo-dev-launcher/ajv": ["ajv@8.11.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg=="],
"expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
+ "expo-modules-autolinking/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
+
"expo-router/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"fbjs/promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="],
- "fbjs/ua-parser-js": ["ua-parser-js@1.0.41", "", { "bin": { "ua-parser-js": "script/cli.js" } }, "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug=="],
+ "fbjs/ua-parser-js": ["ua-parser-js@1.0.40", "", { "bin": { "ua-parser-js": "script/cli.js" } }, "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew=="],
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
@@ -2320,7 +2474,7 @@
"foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
- "glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+ "globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
@@ -2330,303 +2484,189 @@
"import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
- "istanbul-lib-instrument/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
-
- "istanbul-lib-instrument/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "jest-message-util/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
"jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
+ "jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
"jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
+ "jscodeshift/tmp": ["tmp@0.2.3", "", {}, "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w=="],
+
+ "jscodeshift/write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="],
+
"lighthouse-logger/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
- "lint-staged/commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="],
+ "load-bmfont/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
- "log-update/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
-
- "log-update/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
+ "load-bmfont/phin": ["phin@3.7.1", "", { "dependencies": { "centra": "^2.7.0" } }, "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ=="],
"logkitty/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
- "metro/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
+ "make-dir/pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="],
- "metro/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
-
- "metro/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "metro/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "metro/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "metro/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "metro/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+ "make-dir/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
"metro/ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="],
- "metro/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="],
+ "metro/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"metro/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
- "metro-babel-transformer/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
+ "metro-config/cosmiconfig": ["cosmiconfig@5.2.1", "", { "dependencies": { "import-fresh": "^2.0.0", "is-directory": "^0.3.1", "js-yaml": "^3.13.1", "parse-json": "^4.0.0" } }, "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA=="],
- "metro-babel-transformer/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="],
+ "metro-file-map/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
- "metro-source-map/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+ "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
- "metro-source-map/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+ "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
- "metro-transform-plugins/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
+ "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
- "metro-transform-plugins/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
+ "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
- "metro-transform-plugins/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "metro-transform-plugins/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "metro-transform-worker/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
-
- "metro-transform-worker/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "metro-transform-worker/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "metro-transform-worker/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "nativewind/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
+ "minizlib/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"nativewind/@babel/types": ["@babel/types@7.19.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.18.10", "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" } }, "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA=="],
"nativewind/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
- "node-vibrant/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
+ "node-dir/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
- "npm-package-arg/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
+ "npm-package-arg/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
- "parse-json/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
+ "ora/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
- "patch-package/fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
+ "parse-bmfont-xml/xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
- "patch-package/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
+ "patch-package/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
- "path-scurry/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="],
+ "patch-package/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
+
+ "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+
+ "pkg-dir/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="],
+
+ "plist/@xmldom/xmldom": ["@xmldom/xmldom@0.8.10", "", {}, "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw=="],
+
+ "plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
"postcss-css-variables/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
"postcss-load-config/lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
+ "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
+
"pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
"react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
- "react-dom/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
+ "react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
+
+ "react-native/@react-native/normalize-colors": ["@react-native/normalize-colors@0.77.0", "", {}, "sha512-qjmxW3xRZe4T0ZBEaXZNHtuUbRgyfybWijf1yUuQwjBt24tSapmIslwhCjpKidA0p93ssPcepquhY0ykH25mew=="],
"react-native/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
- "react-native/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
+ "react-native/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
- "react-native/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "react-native/scheduler": ["scheduler@0.24.0-canary-efb381bbf-20230505", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA=="],
- "react-native-reanimated/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "react-native/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
+
+ "react-native-tvos/@react-native/normalize-colors": ["@react-native/normalize-colors@0.77.0", "", {}, "sha512-qjmxW3xRZe4T0ZBEaXZNHtuUbRgyfybWijf1yUuQwjBt24tSapmIslwhCjpKidA0p93ssPcepquhY0ykH25mew=="],
+
+ "react-native-tvos/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
+
+ "react-native-tvos/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
+
+ "react-native-tvos/scheduler": ["scheduler@0.24.0-canary-efb381bbf-20230505", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA=="],
+
+ "react-native-tvos/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
"react-native-web/@react-native/normalize-colors": ["@react-native/normalize-colors@0.74.89", "", {}, "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg=="],
"react-native-web/memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
- "react-native-worklets/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
-
"readable-web-to-node-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
+ "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
+
+ "regjsparser/jsesc": ["jsesc@3.0.2", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="],
+
"requireg/resolve": ["resolve@1.7.1", "", { "dependencies": { "path-parse": "^1.0.5" } }, "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw=="],
+ "rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
+
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
+ "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
+
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
- "serve-static/send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
+ "simple-plist/bplist-creator": ["bplist-creator@0.1.0", "", { "dependencies": { "stream-buffers": "2.2.x" } }, "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg=="],
"simple-plist/bplist-parser": ["bplist-parser@0.3.1", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA=="],
- "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+ "slice-ansi/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
- "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
+ "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="],
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
+ "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+
+ "strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="],
+
"sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
- "sucrase/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
+ "tailwindcss/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
"tailwindcss/postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="],
- "tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
+ "tar/fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
- "terminal-link/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
+ "tar/minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
+
+ "tar/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
+
+ "tar/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
+
+ "temp/rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="],
+
+ "tempy/type-fest": ["type-fest@0.16.0", "", {}, "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg=="],
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
+ "test-exclude/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
+
"test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
- "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
+ "url/punycode": ["punycode@1.4.1", "", {}, "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="],
"whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
- "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+ "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
- "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
+ "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
- "wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
-
- "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+ "xcode/uuid": ["uuid@7.0.3", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="],
"xml2js/xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
- "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/helper-define-polyfill-provider/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
-
- "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/helper-replace-supers/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/helper-replace-supers/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/helper-replace-supers/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/helper-replace-supers/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/helper-replace-supers/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/helper-wrap-function/@babel/template/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/helper-wrap-function/@babel/template/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/helper-wrap-function/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/helper-wrap-function/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/helper-wrap-function/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
"@babel/highlight/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"@babel/highlight/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
"@babel/highlight/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
- "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-classes/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
-
- "@babel/plugin-transform-classes/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-classes/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/plugin-transform-classes/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-classes/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/plugin-transform-classes/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-computed-properties/@babel/template/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-computed-properties/@babel/template/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-computed-properties/@babel/template/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-destructuring/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-destructuring/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/plugin-transform-destructuring/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-destructuring/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/plugin-transform-destructuring/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-function-name/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
-
- "@babel/plugin-transform-function-name/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-function-name/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/plugin-transform-function-name/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-function-name/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/plugin-transform-function-name/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
-
- "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/plugin-transform-object-rest-spread/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
-
- "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-react-jsx/@babel/helper-module-imports/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/plugin-transform-runtime/@babel/helper-module-imports/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@babel/plugin-transform-runtime/@babel/helper-module-imports/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@expo/cli/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
+ "@expo/cli/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
"@expo/cli/ora/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
@@ -2634,37 +2674,19 @@
"@expo/cli/ora/log-symbols": ["log-symbols@2.2.0", "", { "dependencies": { "chalk": "^2.0.1" } }, "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg=="],
- "@expo/cli/ora/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
+ "@expo/fingerprint/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
- "@expo/cli/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+ "@expo/image-utils/fs-extra/jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
- "@expo/config-plugins/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
+ "@expo/image-utils/fs-extra/universalify": ["universalify@1.0.0", "", {}, "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug=="],
- "@expo/config/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
+ "@expo/metro-config/fs-extra/jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
- "@expo/config/sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
+ "@expo/metro-config/fs-extra/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
- "@expo/fingerprint/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
+ "@expo/metro-config/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
- "@expo/metro-config/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
-
- "@expo/metro-config/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
-
- "@expo/metro-config/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
-
- "@expo/metro-config/@babel/core/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@expo/metro-config/@babel/core/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@expo/metro-config/@babel/core/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@expo/metro-config/@babel/core/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@expo/metro-config/@babel/generator/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@expo/metro-config/@babel/generator/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@expo/metro-config/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
+ "@expo/package-manager/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
"@expo/package-manager/ora/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
@@ -2672,218 +2694,70 @@
"@expo/package-manager/ora/log-symbols": ["log-symbols@2.2.0", "", { "dependencies": { "chalk": "^2.0.1" } }, "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg=="],
- "@expo/package-manager/ora/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
+ "@expo/prebuild-config/fs-extra/jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
+
+ "@expo/prebuild-config/fs-extra/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
- "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+ "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
- "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+ "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
"@istanbuljs/load-nyc-config/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
"@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
- "@jest/transform/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
+ "@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
- "@jest/transform/@babel/core/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
+ "@react-native-community/cli-server-api/pretty-format/@jest/types": ["@jest/types@26.6.2", "", { "dependencies": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^15.0.0", "chalk": "^4.0.0" } }, "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ=="],
- "@jest/transform/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
+ "@react-native-community/cli-server-api/pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
- "@jest/transform/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
+ "@react-native-community/cli-tools/open/is-wsl": ["is-wsl@1.1.0", "", {}, "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw=="],
- "@jest/transform/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
- "@jest/transform/@babel/core/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser": ["hermes-parser@0.23.1", "", { "dependencies": { "hermes-estree": "0.23.1" } }, "sha512-oxl5h2DkFW83hT4DAUJorpah8ou4yvmweUzLJmmr6YV2cezduCdlil1AvU/a/xSsAFo4WUcNA4GoV5Bvq6JffA=="],
- "@jest/transform/@babel/core/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen/jscodeshift": ["jscodeshift@0.14.0", "", { "dependencies": { "@babel/core": "^7.13.16", "@babel/parser": "^7.13.16", "@babel/plugin-proposal-class-properties": "^7.13.0", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", "@babel/plugin-proposal-optional-chaining": "^7.13.12", "@babel/plugin-transform-modules-commonjs": "^7.13.8", "@babel/preset-flow": "^7.13.13", "@babel/preset-typescript": "^7.13.0", "@babel/register": "^7.13.16", "babel-core": "^7.0.0-bridge.0", "chalk": "^4.1.2", "flow-parser": "0.*", "graceful-fs": "^4.2.4", "micromatch": "^4.0.4", "neo-async": "^2.5.0", "node-dir": "^0.1.17", "recast": "^0.21.0", "temp": "^0.8.4", "write-file-atomic": "^2.3.0" }, "peerDependencies": { "@babel/preset-env": "^7.1.6" }, "bin": { "jscodeshift": "bin/jscodeshift.js" } }, "sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA=="],
- "@jest/transform/@babel/core/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+ "@react-native/codegen/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
- "@jest/transform/@babel/core/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+ "@react-native/community-cli-plugin/@react-native/dev-middleware/@react-native/debugger-frontend": ["@react-native/debugger-frontend@0.77.0", "", {}, "sha512-glOvSEjCbVXw+KtfiOAmrq21FuLE1VsmBsyT7qud4KWbXP43aUEhzn70mWyFuiIdxnzVPKe2u8iWTQTdJksR1w=="],
- "@react-native-community/cli-server-api/open/is-wsl": ["is-wsl@1.1.0", "", {}, "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw=="],
+ "@react-native/community-cli-plugin/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "@react-native/babel-plugin-codegen/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
+ "@react-native/dev-middleware/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "@react-native/babel-plugin-codegen/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
+ "@react-native/metro-babel-transformer/@react-native/babel-preset/@react-native/babel-plugin-codegen": ["@react-native/babel-plugin-codegen@0.77.0", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@react-native/codegen": "0.77.0" } }, "sha512-5TYPn1k+jdDOZJU4EVb1kZ0p9TCVICXK3uplRev5Gul57oWesAaiWGZOzfRS3lonWeuR4ij8v8PFfIHOaq0vmA=="],
- "@react-native/babel-plugin-codegen/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@react-native/babel-plugin-codegen/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@react-native/babel-plugin-codegen/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@react-native/babel-preset/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@react-native/babel-preset/@babel/core/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@react-native/babel-preset/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
-
- "@react-native/babel-preset/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
-
- "@react-native/babel-preset/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
-
- "@react-native/babel-preset/@babel/core/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@react-native/babel-preset/@babel/core/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@react-native/babel-preset/@babel/core/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@react-native/babel-preset/@babel/template/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@react-native/babel-preset/@babel/template/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@react-native/babel-preset/@babel/template/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@react-native/codegen/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@react-native/codegen/@babel/core/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@react-native/codegen/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
-
- "@react-native/codegen/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
-
- "@react-native/codegen/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
-
- "@react-native/codegen/@babel/core/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@react-native/codegen/@babel/core/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@react-native/codegen/@babel/core/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@react-native/codegen/@babel/parser/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@react-native/community-cli-plugin/metro/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@react-native/community-cli-plugin/metro/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
-
- "@react-native/community-cli-plugin/metro/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@react-native/community-cli-plugin/metro/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@react-native/community-cli-plugin/metro/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@react-native/community-cli-plugin/metro/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "@react-native/community-cli-plugin/metro/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@react-native/community-cli-plugin/metro/ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="],
-
- "@react-native/community-cli-plugin/metro/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="],
-
- "@react-native/community-cli-plugin/metro/metro-babel-transformer": ["metro-babel-transformer@0.83.2", "", { "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.32.0", "nullthrows": "^1.1.1" } }, "sha512-rirY1QMFlA1uxH3ZiNauBninwTioOgwChnRdDcbB4tgRZ+bGX9DiXoh9QdpppiaVKXdJsII932OwWXGGV4+Nlw=="],
-
- "@react-native/community-cli-plugin/metro/metro-cache": ["metro-cache@0.83.2", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.83.2" } }, "sha512-Z43IodutUZeIS7OTH+yQFjc59QlFJ6s5OvM8p2AP9alr0+F8UKr8ADzFzoGKoHefZSKGa4bJx7MZJLF6GwPDHQ=="],
-
- "@react-native/community-cli-plugin/metro/metro-cache-key": ["metro-cache-key@0.83.2", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-3EMG/GkGKYoTaf5RqguGLSWRqGTwO7NQ0qXKmNBjr0y6qD9s3VBXYlwB+MszGtmOKsqE9q3FPrE5Nd9Ipv7rZw=="],
-
- "@react-native/community-cli-plugin/metro/metro-file-map": ["metro-file-map@0.83.2", "", { "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "nullthrows": "^1.1.1", "walker": "^1.0.7" } }, "sha512-cMSWnEqZrp/dzZIEd7DEDdk72PXz6w5NOKriJoDN9p1TDQ5nAYrY2lHi8d6mwbcGLoSlWmpPyny9HZYFfPWcGQ=="],
-
- "@react-native/community-cli-plugin/metro/metro-resolver": ["metro-resolver@0.83.2", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-Yf5mjyuiRE/Y+KvqfsZxrbHDA15NZxyfg8pIk0qg47LfAJhpMVEX+36e6ZRBq7KVBqy6VDX5Sq55iHGM4xSm7Q=="],
-
- "@react-native/community-cli-plugin/metro/metro-runtime": ["metro-runtime@0.83.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-nnsPtgRvFbNKwemqs0FuyFDzXLl+ezuFsUXDbX8o0SXOfsOPijqiQrf3kuafO1Zx1aUWf4NOrKJMAQP5EEHg9A=="],
-
- "@react-native/community-cli-plugin/metro/metro-source-map": ["metro-source-map@0.83.2", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.2", "nullthrows": "^1.1.1", "ob1": "0.83.2", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-5FL/6BSQvshIKjXOennt9upFngq2lFvDakZn5LfauIVq8+L4sxXewIlSTcxAtzbtjAIaXeOSVMtCJ5DdfCt9AA=="],
-
- "@react-native/community-cli-plugin/metro/metro-symbolicate": ["metro-symbolicate@0.83.2", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.83.2", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-KoU9BLwxxED6n33KYuQQuc5bXkIxF3fSwlc3ouxrrdLWwhu64muYZNQrukkWzhVKRNFIXW7X2iM8JXpi2heIPw=="],
-
- "@react-native/community-cli-plugin/metro/metro-transform-plugins": ["metro-transform-plugins@0.83.2", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" } }, "sha512-5WlW25WKPkiJk2yA9d8bMuZrgW7vfA4f4MBb9ZeHbTB3eIAoNN8vS8NENgG/X/90vpTB06X66OBvxhT3nHwP6A=="],
-
- "@react-native/community-cli-plugin/metro/metro-transform-worker": ["metro-transform-worker@0.83.2", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/types": "^7.25.2", "flow-enums-runtime": "^0.0.6", "metro": "0.83.2", "metro-babel-transformer": "0.83.2", "metro-cache": "0.83.2", "metro-cache-key": "0.83.2", "metro-minify-terser": "0.83.2", "metro-source-map": "0.83.2", "metro-transform-plugins": "0.83.2", "nullthrows": "^1.1.1" } }, "sha512-G5DsIg+cMZ2KNfrdLnWMvtppb3+Rp1GMyj7Bvd9GgYc/8gRmvq1XVEF9XuO87Shhb03kFhGqMTgZerz3hZ1v4Q=="],
-
- "@react-native/community-cli-plugin/metro/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
-
- "@react-native/community-cli-plugin/metro-config/metro-cache": ["metro-cache@0.83.2", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.83.2" } }, "sha512-Z43IodutUZeIS7OTH+yQFjc59QlFJ6s5OvM8p2AP9alr0+F8UKr8ADzFzoGKoHefZSKGa4bJx7MZJLF6GwPDHQ=="],
-
- "@react-native/community-cli-plugin/metro-config/metro-runtime": ["metro-runtime@0.83.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-nnsPtgRvFbNKwemqs0FuyFDzXLl+ezuFsUXDbX8o0SXOfsOPijqiQrf3kuafO1Zx1aUWf4NOrKJMAQP5EEHg9A=="],
-
- "@react-native/community-cli-plugin/metro-core/metro-resolver": ["metro-resolver@0.83.2", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-Yf5mjyuiRE/Y+KvqfsZxrbHDA15NZxyfg8pIk0qg47LfAJhpMVEX+36e6ZRBq7KVBqy6VDX5Sq55iHGM4xSm7Q=="],
-
- "@react-navigation/bottom-tabs/color/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "@react-navigation/bottom-tabs/color/color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
-
- "@react-navigation/elements/color/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "@react-navigation/elements/color/color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
-
- "@react-navigation/material-top-tabs/color/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "@react-navigation/material-top-tabs/color/color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
-
- "@react-navigation/native-stack/color/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "@react-navigation/native-stack/color/color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
-
- "ansi-fragments/slice-ansi/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
-
- "ansi-fragments/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="],
-
- "ansi-fragments/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="],
-
- "babel-plugin-jest-hoist/@babel/template/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "babel-plugin-jest-hoist/@babel/template/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "babel-preset-expo/@babel/helper-module-imports/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "babel-preset-expo/@babel/helper-module-imports/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
-
- "chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "cli-truncate/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
-
- "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+ "chromium-edge-launcher/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"connect/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "expo-manifests/@expo/config/@babel/code-frame": ["@babel/code-frame@7.10.4", "", { "dependencies": { "@babel/highlight": "^7.10.4" } }, "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg=="],
+ "default-gateway/execa/cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="],
- "expo-manifests/@expo/config/@expo/config-plugins": ["@expo/config-plugins@54.0.3", "", { "dependencies": { "@expo/config-types": "^54.0.9", "@expo/json-file": "~10.0.7", "@expo/plist": "^0.4.7", "@expo/sdk-runtime-versions": "^1.0.0", "chalk": "^4.1.2", "debug": "^4.3.5", "getenv": "^2.0.0", "glob": "^13.0.0", "resolve-from": "^5.0.0", "semver": "^7.5.4", "slash": "^3.0.0", "slugify": "^1.6.6", "xcode": "^3.0.1", "xml2js": "0.6.0" } }, "sha512-tBIUZIxLQfCu5jmqTO+UOeeDUGIB0BbK6xTMkPRObAXRQeTLPPfokZRCo818d2owd+Bcmq1wBaDz0VY3g+glfw=="],
+ "default-gateway/execa/get-stream": ["get-stream@4.1.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w=="],
- "expo-manifests/@expo/config/@expo/config-types": ["@expo/config-types@54.0.9", "", {}, "sha512-Llf4jwcrAnrxgE5WCdAOxtMf8FGwS4Sk0SSgI0NnIaSyCnmOCAm80GPFvsK778Oj19Ub4tSyzdqufPyeQPksWw=="],
+ "default-gateway/execa/is-stream": ["is-stream@1.1.0", "", {}, "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="],
- "expo-manifests/@expo/config/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
+ "default-gateway/execa/npm-run-path": ["npm-run-path@2.0.2", "", { "dependencies": { "path-key": "^2.0.0" } }, "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw=="],
- "expo-manifests/@expo/config/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
+ "del/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
- "expo-manifests/@expo/config/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+ "expo-modules-autolinking/fs-extra/jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
- "expo-manifests/@expo/config/sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
+ "expo-modules-autolinking/fs-extra/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "istanbul-lib-instrument/@babel/parser/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+ "jscodeshift/write-file-atomic/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "log-update/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
-
- "log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
-
"logkitty/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
"logkitty/yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
@@ -2892,154 +2766,52 @@
"logkitty/yargs/yargs-parser": ["yargs-parser@18.1.3", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="],
- "metro-babel-transformer/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
+ "metro-config/cosmiconfig/import-fresh": ["import-fresh@2.0.0", "", { "dependencies": { "caller-path": "^2.0.0", "resolve-from": "^3.0.0" } }, "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg=="],
- "metro-babel-transformer/@babel/core/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
+ "metro-config/cosmiconfig/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
- "metro-babel-transformer/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
+ "metro-config/cosmiconfig/parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="],
- "metro-babel-transformer/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
+ "metro-file-map/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "metro-babel-transformer/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
+ "metro/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "metro-babel-transformer/@babel/core/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
+ "minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
- "metro-babel-transformer/@babel/core/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
+ "minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
- "metro-babel-transformer/@babel/core/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+ "node-dir/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
- "metro-babel-transformer/@babel/core/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+ "parse-bmfont-xml/xml2js/xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
- "metro-babel-transformer/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="],
-
- "metro-source-map/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "metro-source-map/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "metro-source-map/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "metro-source-map/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "metro-transform-plugins/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "metro-transform-plugins/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
-
- "metro-transform-plugins/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
-
- "metro-transform-plugins/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
-
- "metro-transform-plugins/@babel/core/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "metro-transform-plugins/@babel/core/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "metro-transform-plugins/@babel/generator/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "metro-transform-plugins/@babel/generator/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "metro-transform-plugins/@babel/template/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "metro-transform-plugins/@babel/template/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "metro-transform-plugins/@babel/template/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "metro-transform-plugins/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "metro-transform-plugins/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "metro-transform-plugins/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "metro-transform-worker/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "metro-transform-worker/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
-
- "metro-transform-worker/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
-
- "metro-transform-worker/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
-
- "metro-transform-worker/@babel/core/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "metro-transform-worker/@babel/core/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
-
- "metro/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
-
- "metro/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
-
- "metro/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
-
- "metro/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="],
-
- "nativewind/@babel/generator/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "nativewind/@babel/generator/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "node-vibrant/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
-
- "patch-package/fs-extra/jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
+ "patch-package/fs-extra/jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
"patch-package/fs-extra/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
+ "pkg-dir/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
+
+ "react-native-tvos/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
+ "react-native/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
"readable-web-to-node-stream/readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
+ "rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
- "serve-static/send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
+ "slice-ansi/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
- "serve-static/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
+ "tar/fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
- "serve-static/send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
+ "temp/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
- "sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
-
- "terminal-link/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
-
- "test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
-
- "wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
-
- "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+ "test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
"@babel/highlight/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"@babel/highlight/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
- "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/helper-module-imports/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
-
- "@babel/plugin-transform-react-jsx/@babel/helper-module-imports/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-react-jsx/@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/plugin-transform-react-jsx/@babel/helper-module-imports/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-react-jsx/@babel/helper-module-imports/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "@babel/plugin-transform-runtime/@babel/helper-module-imports/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "@babel/plugin-transform-runtime/@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "@babel/plugin-transform-runtime/@babel/helper-module-imports/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "@babel/plugin-transform-runtime/@babel/helper-module-imports/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
"@expo/cli/ora/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"@expo/cli/ora/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
@@ -3048,13 +2820,7 @@
"@expo/cli/ora/cli-cursor/restore-cursor": ["restore-cursor@2.0.0", "", { "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" } }, "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q=="],
- "@expo/cli/ora/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="],
-
- "@expo/cli/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "@expo/metro-config/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
-
- "@expo/metro-config/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "@expo/image-utils/fs-extra/jsonfile/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
"@expo/package-manager/ora/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
@@ -3064,81 +2830,33 @@
"@expo/package-manager/ora/cli-cursor/restore-cursor": ["restore-cursor@2.0.0", "", { "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" } }, "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q=="],
- "@expo/package-manager/ora/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="],
-
"@istanbuljs/load-nyc-config/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
- "@jest/transform/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+ "@react-native-community/cli-server-api/pretty-format/@jest/types/@types/yargs": ["@types/yargs@15.0.19", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA=="],
- "@jest/transform/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
- "@react-native/babel-preset/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen/hermes-parser/hermes-estree": ["hermes-estree@0.23.1", "", {}, "sha512-eT5MU3f5aVhTqsfIReZ6n41X5sYn4IdQL0nvz6yO+MMlPxw49aSARHLg/MSehQftyjnrE8X6bYregzSumqc6cg=="],
- "@react-native/babel-preset/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen/jscodeshift/recast": ["recast@0.21.5", "", { "dependencies": { "ast-types": "0.15.2", "esprima": "~4.0.0", "source-map": "~0.6.1", "tslib": "^2.0.1" } }, "sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg=="],
- "@react-native/codegen/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+ "@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
- "@react-native/codegen/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "chromium-edge-launcher/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
- "@react-native/community-cli-plugin/metro/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
+ "default-gateway/execa/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="],
- "@react-native/community-cli-plugin/metro/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
+ "default-gateway/execa/cross-spawn/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
- "@react-native/community-cli-plugin/metro/@babel/core/@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
+ "default-gateway/execa/cross-spawn/shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="],
- "@react-native/community-cli-plugin/metro/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+ "default-gateway/execa/cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="],
- "@react-native/community-cli-plugin/metro/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="],
+ "default-gateway/execa/npm-run-path/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="],
- "@react-native/community-cli-plugin/metro/metro-source-map/ob1": ["ob1@0.83.2", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-XlK3w4M+dwd1g1gvHzVbxiXEbUllRONEgcF2uEO0zm4nxa0eKlh41c6N65q1xbiDOeKKda1tvNOAD33fNjyvCg=="],
+ "del/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
- "@react-native/community-cli-plugin/metro/metro-transform-worker/metro-minify-terser": ["metro-minify-terser@0.83.2", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" } }, "sha512-zvIxnh7U0JQ7vT4quasKsijId3dOAWgq+ip2jF/8TMrPUqQabGrs04L2dd0haQJ+PA+d4VvK/bPOY8X/vL2PWw=="],
-
- "@react-navigation/bottom-tabs/color/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "@react-navigation/bottom-tabs/color/color-string/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "@react-navigation/elements/color/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "@react-navigation/elements/color/color-string/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "@react-navigation/material-top-tabs/color/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "@react-navigation/material-top-tabs/color/color-string/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "@react-navigation/native-stack/color/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "@react-navigation/native-stack/color/color-string/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "ansi-fragments/slice-ansi/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
-
- "babel-preset-expo/@babel/helper-module-imports/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
-
- "babel-preset-expo/@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
-
- "babel-preset-expo/@babel/helper-module-imports/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
-
- "babel-preset-expo/@babel/helper-module-imports/@babel/traverse/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
-
- "chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
-
- "cliui/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "expo-manifests/@expo/config/@expo/config-plugins/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
-
- "expo-manifests/@expo/config/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
-
- "expo-manifests/@expo/config/sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
-
- "istanbul-lib-instrument/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
-
- "log-update/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
-
- "log-update/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+ "logkitty/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"logkitty/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
@@ -3146,27 +2864,23 @@
"logkitty/yargs/yargs-parser/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
- "metro-babel-transformer/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+ "metro-config/cosmiconfig/import-fresh/resolve-from": ["resolve-from@3.0.0", "", {}, "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="],
- "metro-babel-transformer/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "metro-config/cosmiconfig/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
- "metro-transform-plugins/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+ "pkg-dir/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
- "metro-transform-plugins/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "pkg-dir/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
- "metro-transform-worker/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+ "react-native-tvos/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
- "metro-transform-worker/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "react-native/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
- "metro/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+ "rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
- "metro/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "slice-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
- "serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
-
- "sucrase/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
-
- "wrap-ansi-cjs/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+ "temp/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"@babel/highlight/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
@@ -3176,8 +2890,6 @@
"@expo/cli/ora/cli-cursor/restore-cursor/onetime": ["onetime@2.0.1", "", { "dependencies": { "mimic-fn": "^1.0.0" } }, "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ=="],
- "@expo/cli/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
"@expo/package-manager/ora/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"@expo/package-manager/ora/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
@@ -3186,18 +2898,24 @@
"@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
- "@react-native/community-cli-plugin/metro/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
- "@react-native/community-cli-plugin/metro/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen/jscodeshift/recast/ast-types": ["ast-types@0.15.2", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg=="],
- "ansi-fragments/slice-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
+ "@react-native/babel-plugin-codegen/@react-native/codegen/jscodeshift/recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
- "cliui/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+ "chromium-edge-launcher/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
- "logkitty/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+ "default-gateway/execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="],
+
+ "del/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
"logkitty/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
+ "pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
+
+ "temp/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
+
"@expo/cli/ora/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
"@expo/cli/ora/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@1.2.0", "", {}, "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="],
@@ -3206,10 +2924,6 @@
"@expo/package-manager/ora/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@1.2.0", "", {}, "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="],
- "logkitty/yargs/cliui/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
"logkitty/yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
-
- "logkitty/yargs/cliui/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
}
}
diff --git a/components/AddToFavorites.tsx b/components/AddToFavorites.tsx
index 35221f1a..945e9988 100644
--- a/components/AddToFavorites.tsx
+++ b/components/AddToFavorites.tsx
@@ -1,22 +1,22 @@
-import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
-import type { FC } from "react";
-import { View, type ViewProps } from "react-native";
-import { RoundButton } from "@/components/RoundButton";
+
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useFavorite } from "@/hooks/useFavorite";
+import { View } from "react-native";
+import { RoundButton } from "@/components/RoundButton";
interface Props extends ViewProps {
item: BaseItemDto;
}
-export const AddToFavorites: FC = ({ item, ...props }) => {
- const { isFavorite, toggleFavorite } = useFavorite(item);
-
+export const AddToFavorites = ({ item, ...props }) => {
+ const { isFavorite, toggleFavorite, _} = useFavorite(item);
+
return (
diff --git a/components/Chromecast.tsx b/components/Chromecast.tsx
index a4c823bb..d3e60c60 100644
--- a/components/Chromecast.tsx
+++ b/components/Chromecast.tsx
@@ -15,6 +15,7 @@ import GoogleCast, {
} from "react-native-google-cast";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { RoundButton } from "./RoundButton";
+import { useRouter } from "expo-router";
export function Chromecast({
width = 48,
@@ -33,6 +34,8 @@ export function Chromecast({
const lastReportedProgressRef = useRef(0);
+ const router = useRouter();
+
useEffect(() => {
(async () => {
if (!discoveryManager) {
@@ -121,7 +124,7 @@ export function Chromecast({
className='mr-2'
background={false}
onPress={() => {
- if (mediaStatus?.currentItemId) CastContext.showExpandedControls();
+ if (mediaStatus?.currentItemId) router.push('/player/google-cast-player');
else CastContext.showCastDialog();
}}
{...props}
@@ -135,7 +138,7 @@ export function Chromecast({
{
- if (mediaStatus?.currentItemId) CastContext.showExpandedControls();
+ if (mediaStatus?.currentItemId) router.push('/player/google-cast-player');
else CastContext.showCastDialog();
}}
{...props}
diff --git a/components/ChromecastControls.tsx b/components/ChromecastControls.tsx
new file mode 100644
index 00000000..f9a3bfbd
--- /dev/null
+++ b/components/ChromecastControls.tsx
@@ -0,0 +1,897 @@
+import React, { useMemo, useRef, useState } from "react";
+import { Alert, TouchableOpacity, View } from "react-native";
+import { Text } from "@/components/common/Text";
+import { Loader } from "@/components/Loader";
+import { Feather, Ionicons } from "@expo/vector-icons";
+import { RoundButton } from "@/components/RoundButton";
+
+import {
+ CastButton,
+ CastContext,
+ MediaStatus,
+ RemoteMediaClient,
+ useStreamPosition,
+} from "react-native-google-cast";
+import { useCallback, useEffect } from "react";
+import { Platform } from "react-native";
+import { Image } from "expo-image";
+import { Slider } from "react-native-awesome-slider";
+import {
+ runOnJS,
+ SharedValue,
+ useAnimatedReaction,
+ useSharedValue,
+} from "react-native-reanimated";
+import { debounce } from "lodash";
+import { useSettings } from "@/utils/atoms/settings";
+import { useHaptic } from "@/hooks/useHaptic";
+import { writeToLog } from "@/utils/log";
+import { formatTimeString } from "@/utils/time";
+import SkipButton from "@/components/video-player/controls/SkipButton";
+import NextEpisodeCountDownButton from "@/components/video-player/controls/NextEpisodeCountDownButton";
+import { useIntroSkipper } from "@/hooks/useIntroSkipper";
+import { useCreditSkipper } from "@/hooks/useCreditSkipper";
+import { useAdjacentItems } from "@/hooks/useAdjacentEpisodes";
+import { useTrickplay } from "@/hooks/useTrickplay";
+import { secondsToTicks } from "@/utils/secondsToTicks";
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
+import { chromecastLoadMedia } from "@/utils/chromecastLoadMedia";
+import { useAtomValue } from "jotai";
+import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
+import { getParentBackdropImageUrl } from "@/utils/jellyfin/image/getParentBackdropImageUrl";
+import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
+import { chromecast as chromecastProfile } from "@/utils/profiles/chromecast";
+import { SelectedOptions } from "./ItemContent";
+import {
+ getDefaultPlaySettings,
+ previousIndexes,
+} from "@/utils/jellyfin/getDefaultPlaySettings";
+import { useQuery } from "@tanstack/react-query";
+import {
+ getPlaystateApi,
+ getUserLibraryApi,
+} from "@jellyfin/sdk/lib/utils/api";
+import { useTranslation } from "react-i18next";
+import { Colors } from "@/constants/Colors";
+import { useRouter } from "expo-router";
+import { ParallaxScrollView } from "@/components/ParallaxPage";
+import { ItemImage } from "@/components/common/ItemImage";
+import { BitrateSelector } from "@/components/BitrateSelector";
+import { ItemHeader } from "@/components/ItemHeader";
+import { MediaSourceSelector } from "@/components/MediaSourceSelector";
+import { AudioTrackSelector } from "@/components/AudioTrackSelector";
+import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
+import { ItemTechnicalDetails } from "@/components/ItemTechnicalDetails";
+import { OverviewText } from "@/components/OverviewText";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+import { PlayedStatus } from "./PlayedStatus";
+import { AddToFavorites } from "./AddToFavorites";
+
+export default function ChromecastControls({
+ mediaStatus,
+ client,
+ setWasMediaPlaying,
+ reportPlaybackStopedRef,
+}: {
+ mediaStatus: MediaStatus;
+ client: RemoteMediaClient;
+ setWasMediaPlaying: (wasPlaying: boolean) => void;
+ reportPlaybackStopedRef: React.MutableRefObject<() => void>;
+}) {
+ const lightHapticFeedback = useHaptic("light");
+
+ const api = useAtomValue(apiAtom);
+ const user = useAtomValue(userAtom);
+
+ const [settings] = useSettings();
+
+ const [currentTime, setCurrentTime] = useState(0);
+ const [remainingTime, setRemainingTime] = useState(Infinity);
+ const max = useSharedValue(mediaStatus.mediaInfo?.streamDuration || 0);
+
+ const streamPosition = useStreamPosition();
+ const progress = useSharedValue(streamPosition || 0);
+
+ const wasPlayingRef = useRef(false);
+
+ const isSeeking = useSharedValue(false);
+ const isPlaying = useMemo(
+ () => mediaStatus.playerState === "playing",
+ [mediaStatus.playerState]
+ );
+ const isBufferingOrLoading = useMemo(
+ () =>
+ mediaStatus.playerState === null ||
+ mediaStatus.playerState === "buffering" ||
+ mediaStatus.playerState === "loading",
+ [mediaStatus.playerState]
+ );
+
+ // request update of media status every player state change
+ useEffect(() => {
+ client.requestStatus();
+ }, [mediaStatus.playerState]);
+
+ // update max progress
+ useEffect(() => {
+ if (mediaStatus.mediaInfo?.streamDuration)
+ max.value = mediaStatus.mediaInfo?.streamDuration;
+ }, [mediaStatus.mediaInfo?.streamDuration]);
+
+ const updateTimes = useCallback(
+ (currentProgress: number, maxValue: number) => {
+ setCurrentTime(currentProgress);
+ setRemainingTime(maxValue - currentProgress);
+ },
+ []
+ );
+
+ useAnimatedReaction(
+ () => ({
+ progress: progress.value,
+ max: max.value,
+ isSeeking: isSeeking.value,
+ }),
+ (result) => {
+ if (result.isSeeking === false) {
+ runOnJS(updateTimes)(result.progress, result.max);
+ }
+ },
+ [updateTimes]
+ );
+
+ const { mediaMetadata, itemId, streamURL } = useMemo(
+ () => ({
+ mediaMetadata: mediaStatus.mediaInfo?.metadata,
+ itemId: mediaStatus.mediaInfo?.contentId,
+ streamURL: mediaStatus.mediaInfo?.contentUrl,
+ }),
+ [mediaStatus]
+ );
+
+ const type = useMemo(
+ () => mediaMetadata?.type || "generic",
+ [mediaMetadata?.type]
+ );
+ const images = useMemo(
+ () => mediaMetadata?.images || [],
+ [mediaMetadata?.images]
+ );
+
+ const { playbackOptions, sessionId, mediaSourceId } = useMemo(() => {
+ const mediaCustomData = mediaStatus.mediaInfo?.customData as
+ | {
+ playbackOptions: SelectedOptions;
+ sessionId?: string;
+ mediaSourceId?: string;
+ }
+ | undefined;
+
+ return (
+ mediaCustomData || {
+ playbackOptions: undefined,
+ sessionId: undefined,
+ mediaSourceId: undefined,
+ }
+ );
+ }, [mediaStatus.mediaInfo?.customData]);
+
+ const {
+ data: item,
+ // currently nothing is indicating that item is loading, because most of the time it loads very fast
+ isLoading: isLoadingItem,
+ isError: isErrorItem,
+ error,
+ refetch,
+ } = useQuery({
+ queryKey: ["item", itemId],
+ queryFn: async () => {
+ if (!itemId) return;
+ const res = await getUserLibraryApi(api!).getItem({
+ itemId,
+ userId: user?.Id,
+ });
+
+ return res.data;
+ },
+ enabled: !!itemId,
+ staleTime: 0,
+ });
+
+ const onProgress = useCallback(
+ async (progressInTicks: number, isPlaying: boolean) => {
+ if (!item?.Id || !streamURL) return;
+
+ await getPlaystateApi(api!).onPlaybackProgress({
+ itemId: item.Id,
+ audioStreamIndex: playbackOptions?.audioIndex,
+ subtitleStreamIndex: playbackOptions?.subtitleIndex,
+ mediaSourceId,
+ positionTicks: Math.floor(progressInTicks),
+ isPaused: !isPlaying,
+ playMethod: streamURL.includes("m3u8") ? "Transcode" : "DirectStream",
+ playSessionId: sessionId,
+ });
+ },
+ [api, item, playbackOptions, mediaSourceId, streamURL, sessionId]
+ );
+
+ // update progess on stream position change
+ useEffect(() => {
+ if (streamPosition) {
+ progress.value = streamPosition;
+ onProgress(secondsToTicks(streamPosition), isPlaying);
+ }
+ }, [streamPosition, isPlaying]);
+
+ const reportPlaybackStart = useCallback(async () => {
+ if (!streamURL) return;
+
+ await getPlaystateApi(api!).onPlaybackStart({
+ itemId: item?.Id!,
+ audioStreamIndex: playbackOptions?.audioIndex,
+ subtitleStreamIndex: playbackOptions?.subtitleIndex,
+ mediaSourceId,
+ playMethod: streamURL.includes("m3u8") ? "Transcode" : "DirectStream",
+ playSessionId: sessionId,
+ });
+ }, [api, item, playbackOptions, mediaSourceId, streamURL, sessionId]);
+
+ // report playback started
+ useEffect(() => {
+ setWasMediaPlaying(true);
+ reportPlaybackStart();
+ }, [reportPlaybackStart]);
+
+ // update the reportPlaybackStoppedRef
+ useEffect(() => {
+ reportPlaybackStopedRef.current = async () => {
+ if (!streamURL) return;
+
+ await getPlaystateApi(api!).onPlaybackStopped({
+ itemId: item?.Id!,
+ mediaSourceId,
+ positionTicks: secondsToTicks(progress.value),
+ playSessionId: sessionId,
+ });
+ };
+ }, [
+ api,
+ item,
+ playbackOptions,
+ progress,
+ mediaSourceId,
+ streamURL,
+ sessionId,
+ ]);
+
+ const { previousItem, nextItem } = useAdjacentItems({
+ item: {
+ Id: itemId,
+ SeriesId: item?.SeriesId,
+ Type: item?.Type,
+ },
+ });
+
+ const goToItem = useCallback(
+ async (item: BaseItemDto) => {
+ if (!api) {
+ console.warn("Failed to go to item: No api!");
+ return;
+ }
+
+ const previousIndexes: previousIndexes = {
+ subtitleIndex: playbackOptions?.subtitleIndex || undefined,
+ audioIndex: playbackOptions?.audioIndex || undefined,
+ };
+
+ const {
+ mediaSource,
+ audioIndex: defaultAudioIndex,
+ subtitleIndex: defaultSubtitleIndex,
+ } = getDefaultPlaySettings(item, settings, previousIndexes, undefined);
+
+ // Get a new URL with the Chromecast device profile:
+ const data = await getStreamUrl({
+ api,
+ item,
+ deviceProfile: chromecastProfile,
+ startTimeTicks: item?.UserData?.PlaybackPositionTicks!,
+ userId: user?.Id,
+ audioStreamIndex: defaultAudioIndex,
+ // maxStreamingBitrate: playbackOptions.bitrate?.value, // TODO handle bitrate limit
+ subtitleStreamIndex: defaultSubtitleIndex,
+ mediaSourceId: mediaSource?.Id,
+ });
+
+ if (!data?.url) {
+ console.warn("No URL returned from getStreamUrl", data);
+ Alert.alert("Client error", "Could not create stream for Chromecast");
+ return;
+ }
+
+ await chromecastLoadMedia({
+ client,
+ item,
+ contentUrl: data.url,
+ sessionId: data.sessionId || undefined,
+ mediaSourceId: data.mediaSource?.Id || undefined,
+ playbackOptions,
+ images: [
+ {
+ url: getParentBackdropImageUrl({
+ api,
+ item,
+ quality: 90,
+ width: 2000,
+ })!,
+ },
+ ],
+ });
+
+ await client.requestStatus();
+ },
+ [client, api]
+ );
+
+ const goToNextItem = useCallback(() => {
+ if (!nextItem) {
+ console.warn("Failed to skip to next item: No next item!");
+ return;
+ }
+ lightHapticFeedback();
+ goToItem(nextItem);
+ }, [nextItem, lightHapticFeedback]);
+
+ const goToPreviousItem = useCallback(() => {
+ if (!previousItem) {
+ console.warn("Failed to skip to next item: No next item!");
+ return;
+ }
+ lightHapticFeedback();
+ goToItem(previousItem);
+ }, [previousItem, lightHapticFeedback]);
+
+ const pause = useCallback(() => {
+ client.pause();
+ }, [client]);
+
+ const play = useCallback(() => {
+ client.play();
+ }, [client]);
+
+ const seek = useCallback(
+ (time: number) => {
+ // skip to next episode if seeking to end (for credit skipping)
+ // with 1 second room to react
+ if (nextItem && time >= max.value - 1) {
+ goToNextItem();
+ return;
+ }
+ client.seek({
+ position: time,
+ });
+ },
+ [client, goToNextItem, nextItem, max]
+ );
+
+ const togglePlay = useCallback(() => {
+ if (isPlaying) pause();
+ else play();
+ }, [isPlaying, play, pause]);
+
+ const handleSkipBackward = useCallback(async () => {
+ if (!settings?.rewindSkipTime) return;
+ wasPlayingRef.current = isPlaying;
+ lightHapticFeedback();
+ try {
+ const curr = progress.value;
+ if (curr !== undefined) {
+ const newTime = Math.max(0, curr - settings.rewindSkipTime);
+ seek(newTime);
+ if (wasPlayingRef.current === true) play();
+ }
+ } catch (error) {
+ writeToLog("ERROR", "Error seeking video backwards", error);
+ }
+ }, [settings, isPlaying]);
+
+ const handleSkipForward = useCallback(async () => {
+ if (!settings?.forwardSkipTime) return;
+ wasPlayingRef.current = isPlaying;
+ lightHapticFeedback();
+ try {
+ const curr = progress.value;
+ if (curr !== undefined) {
+ const newTime = curr + settings.forwardSkipTime;
+ seek(Math.max(0, newTime));
+ if (wasPlayingRef.current === true) play();
+ }
+ } catch (error) {
+ writeToLog("ERROR", "Error seeking video forwards", error);
+ }
+ }, [settings, isPlaying]);
+
+ const { showSkipButton, skipIntro } = useIntroSkipper(
+ itemId,
+ currentTime,
+ seek,
+ play,
+ false
+ );
+
+ const { showSkipCreditButton, skipCredit } = useCreditSkipper(
+ itemId,
+ currentTime,
+ seek,
+ play,
+ false
+ );
+
+ // Android requires the cast button to be present for startDiscovery to work
+ const AndroidCastButton = useCallback(
+ () =>
+ Platform.OS === "android" ? (
+
+ ) : (
+ <>>
+ ),
+ [Platform.OS]
+ );
+
+ const TrickplaySliderMemoized = useMemo(
+ () => (
+
+ ),
+ [
+ item,
+ progress,
+ wasPlayingRef,
+ isPlaying,
+ isSeeking,
+ max,
+ play,
+ pause,
+ seek,
+ ]
+ );
+
+ const NextEpisodeButtonMemoized = useMemo(
+ () => (
+ 0 && remainingTime < 10}
+ onFinish={goToNextItem}
+ onPress={goToNextItem}
+ />
+ ),
+ [nextItem, max, remainingTime, goToNextItem]
+ );
+
+ const { t } = useTranslation();
+ const router = useRouter();
+
+ const insets = useSafeAreaInsets();
+
+ const [loadingLogo, setLoadingLogo] = useState(true);
+ const [headerHeight, setHeaderHeight] = useState(350);
+
+ const logoUrl = useMemo(() => images[0]?.url, [images]);
+
+ if (isErrorItem) {
+ return (
+
+
+
+ {t("chromecast.error_loading_item")}
+
+ {error && (
+ {error.message}
+ )}
+
+
+ refetch()}
+ >
+
+
+ {t("chromecast.retry_load_item")}
+
+
+ {
+ router.push("/(auth)/(home)/");
+ }}
+ >
+
+
+ {t("chromecast.go_home")}
+
+
+
+
+ );
+ }
+
+ if (!item) {
+ return Do something when item is undefined ;
+ }
+
+ if (!playbackOptions) {
+ return Do something when playbackOptions is undefined ;
+ }
+
+ return (
+
+ {/* TODO do navigation header properly */}
+
+
+ {item.Type !== "Program" && (
+
+ {
+ CastContext.showCastDialog();
+ }}
+ >
+
+
+
+
+
+
+ )}
+
+
+
+
+ }
+ logo={
+ <>
+ {logoUrl ? (
+ setLoadingLogo(false)}
+ onError={() => setLoadingLogo(false)}
+ />
+ ) : null}
+ >
+ }
+ >
+
+
+
+ {item.Type !== "Program" && !Platform.isTV && (
+
+
+ // setSelectedOptions(
+ // (prev) => prev && { ...prev, bitrate: val }
+ // )
+ console.log("new selected options", val)
+ }
+ selected={playbackOptions.bitrate}
+ />
+
+ // setSelectedOptions((prev) =>
+ // prev && {
+ // ...prev,
+ // mediaSource: val,
+ // }
+ // )
+ console.log("new selected options", val)
+ }
+ selected={playbackOptions.mediaSource}
+ />
+ {
+ // setSelectedOptions((prev) =>
+ // prev && {
+ // ...prev,
+ // audioIndex: val,
+ // }
+ // );
+ console.log("new selected options", val);
+ }}
+ selected={playbackOptions.audioIndex}
+ />
+
+ // setSelectedOptions(
+ // (prev) =>
+ // prev && {
+ // ...prev,
+ // subtitleIndex: val,
+ // }
+ // )
+ console.log("new selected options", val)
+ }
+ selected={playbackOptions.subtitleIndex}
+ />
+
+ )}
+
+
+
+
+
+
+ {TrickplaySliderMemoized}
+
+
+ {formatTimeString(currentTime, "s")}
+
+
+ -{formatTimeString(remainingTime, "s")}
+
+
+
+
+
+
+
+
+
+ togglePlay()}
+ className="flex w-14 h-14 items-center justify-center"
+ >
+ {!isBufferingOrLoading ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+ {/* TODO find proper placement for these buttons */}
+ {/*
+
+
+ {NextEpisodeButtonMemoized}
+ */}
+
+ );
+}
+
+type TrickplaySliderProps = {
+ item?: BaseItemDto;
+ progress: SharedValue;
+ wasPlayingRef: React.MutableRefObject;
+ isPlaying: boolean;
+ isSeeking: SharedValue;
+ range: { min?: SharedValue; max: SharedValue };
+ play: () => void;
+ pause: () => void;
+ seek: (time: number) => void;
+};
+
+function TrickplaySlider({
+ item,
+ progress,
+ wasPlayingRef,
+ isPlaying,
+ isSeeking,
+ range,
+ play,
+ pause,
+ seek,
+}: TrickplaySliderProps) {
+ const [isSliding, setIsSliding] = useState(false);
+ const lastProgressRef = useRef(0);
+
+ const min = useSharedValue(range.min?.value || 0);
+
+ const {
+ trickPlayUrl,
+ calculateTrickplayUrl,
+ trickplayInfo,
+ prefetchAllTrickplayImages,
+ } = useTrickplay(
+ {
+ Id: item?.Id,
+ RunTimeTicks: secondsToTicks(progress.value),
+ Trickplay: item?.Trickplay,
+ },
+ true
+ );
+
+ useEffect(() => {
+ prefetchAllTrickplayImages();
+ }, []);
+
+ const handleSliderStart = useCallback(() => {
+ setIsSliding(true);
+ wasPlayingRef.current = isPlaying;
+ lastProgressRef.current = progress.value;
+
+ pause();
+ isSeeking.value = true;
+ }, [isPlaying]);
+
+ const handleSliderComplete = useCallback(async (value: number) => {
+ isSeeking.value = false;
+ progress.value = value;
+ setIsSliding(false);
+
+ seek(Math.max(0, Math.floor(value)));
+ if (wasPlayingRef.current === true) play();
+ }, []);
+
+ const [time, setTime] = useState({ hours: 0, minutes: 0, seconds: 0 });
+ const handleSliderChange = useCallback(
+ debounce((value: number) => {
+ calculateTrickplayUrl(secondsToTicks(value));
+ const progressInSeconds = Math.floor(value);
+ const hours = Math.floor(progressInSeconds / 3600);
+ const minutes = Math.floor((progressInSeconds % 3600) / 60);
+ const seconds = progressInSeconds % 60;
+ setTime({ hours, minutes, seconds });
+ }, 3),
+ []
+ );
+
+ const memoizedRenderBubble = useCallback(() => {
+ if (!trickPlayUrl || !trickplayInfo) {
+ return null;
+ }
+ const { x, y, url } = trickPlayUrl;
+ const tileWidth = 150;
+ const tileHeight = 150 / trickplayInfo.aspectRatio!;
+
+ return (
+
+
+
+
+
+ {`${time.hours > 0 ? `${time.hours}:` : ""}${
+ time.minutes < 10 ? `0${time.minutes}` : time.minutes
+ }:${time.seconds < 10 ? `0${time.seconds}` : time.seconds}`}
+
+
+ );
+ }, [trickPlayUrl, trickplayInfo, time]);
+
+ return (
+ null}
+ onSlidingStart={handleSliderStart}
+ onSlidingComplete={handleSliderComplete}
+ onValueChange={handleSliderChange}
+ containerStyle={{
+ borderRadius: 100,
+ }}
+ renderBubble={() => isSliding && memoizedRenderBubble()}
+ sliderHeight={10}
+ thumbWidth={0}
+ progress={progress}
+ minimumValue={min}
+ maximumValue={range.max}
+ />
+ );
+}
diff --git a/components/DownloadItem.tsx b/components/DownloadItem.tsx
index e50b4efc..e7286023 100644
--- a/components/DownloadItem.tsx
+++ b/components/DownloadItem.tsx
@@ -1,31 +1,30 @@
+import { useRemuxHlsToMp4 } from "@/hooks/useRemuxHlsToMp4";
+import { useDownload } from "@/providers/DownloadProvider";
+import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
+import { queueActions, queueAtom } from "@/utils/atoms/queue";
+import { DownloadMethod, useSettings } from "@/utils/atoms/settings";
+import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
+import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
+import { saveDownloadItemInfoToDiskTmp } from "@/utils/optimize-server";
+import download from "@/utils/profiles/download";
import Ionicons from "@expo/vector-icons/Ionicons";
import {
BottomSheetBackdrop,
- type BottomSheetBackdropProps,
+ BottomSheetBackdropProps,
BottomSheetModal,
BottomSheetView,
} from "@gorhom/bottom-sheet";
-import type {
+import {
BaseItemDto,
MediaSourceInfo,
} from "@jellyfin/sdk/lib/generated-client/models";
-import { type Href } from "expo-router";
-import { t } from "i18next";
+import { Href, router, useFocusEffect } from "expo-router";
import { useAtom } from "jotai";
-import type React from "react";
-import { useCallback, useEffect, useMemo, useRef, useState } from "react";
-import { Alert, Platform, Switch, View, type ViewProps } from "react-native";
+import React, { useCallback, useMemo, useRef, useState } from "react";
+import { Alert, Platform, View, ViewProps } from "react-native";
import { toast } from "sonner-native";
-import useRouter from "@/hooks/useAppRouter";
-import useDefaultPlaySettings from "@/hooks/useDefaultPlaySettings";
-import { useDownload } from "@/providers/DownloadProvider";
-import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
-import { queueAtom } from "@/utils/atoms/queue";
-import { useSettings } from "@/utils/atoms/settings";
-import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
-import { getDownloadUrl } from "@/utils/jellyfin/media/getDownloadUrl";
import { AudioTrackSelector } from "./AudioTrackSelector";
-import { type Bitrate, BitrateSelector } from "./BitrateSelector";
+import { Bitrate, BitrateSelector } from "./BitrateSelector";
import { Button } from "./Button";
import { Text } from "./common/Text";
import { Loader } from "./Loader";
@@ -33,13 +32,7 @@ import { MediaSourceSelector } from "./MediaSourceSelector";
import ProgressCircle from "./ProgressCircle";
import { RoundButton } from "./RoundButton";
import { SubtitleTrackSelector } from "./SubtitleTrackSelector";
-
-export type SelectedOptions = {
- bitrate: Bitrate;
- mediaSource: MediaSourceInfo | undefined;
- audioIndex: number | undefined;
- subtitleIndex: number;
-};
+import { t } from "i18next";
interface DownloadProps extends ViewProps {
items: BaseItemDto[];
@@ -61,28 +54,32 @@ export const DownloadItems: React.FC = ({
}) => {
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
- const [queue, _setQueue] = useAtom(queueAtom);
- const { settings } = useSettings();
- const router = useRouter();
- const [downloadUnwatchedOnly, setDownloadUnwatchedOnly] = useState(false);
+ const [queue, setQueue] = useAtom(queueAtom);
+ const [settings] = useSettings();
- const { processes, startBackgroundDownload, downloadedItems } = useDownload();
- const downloadedFiles = downloadedItems;
+ const { processes, startBackgroundDownload, downloadedFiles } = useDownload();
+ const { startRemuxing } = useRemuxHlsToMp4();
- const [selectedOptions, setSelectedOptions] = useState<
- SelectedOptions | undefined
+ const [selectedMediaSource, setSelectedMediaSource] = useState<
+ MediaSourceInfo | undefined | null
>(undefined);
-
- const {
- defaultAudioIndex,
- defaultBitrate,
- defaultMediaSource,
- defaultSubtitleIndex,
- } = useDefaultPlaySettings(items[0], settings);
+ const [selectedAudioStream, setSelectedAudioStream] = useState(-1);
+ const [selectedSubtitleStream, setSelectedSubtitleStream] =
+ useState(0);
+ const [maxBitrate, setMaxBitrate] = useState(
+ settings?.defaultBitrate ?? {
+ key: "Max",
+ value: undefined,
+ }
+ );
const userCanDownload = useMemo(
() => user?.Policy?.EnableContentDownloading,
- [user],
+ [user]
+ );
+ const usingOptimizedServer = useMemo(
+ () => settings?.downloadMethod === DownloadMethod.Optimized,
+ [settings]
);
const bottomSheetModalRef = useRef(null);
@@ -91,9 +88,7 @@ export const DownloadItems: React.FC = ({
bottomSheetModalRef.current?.present();
}, []);
- const handleSheetChanges = useCallback((_index: number) => {
- // Modal state tracking handled by BottomSheetModal
- }, []);
+ const handleSheetChanges = useCallback((index: number) => {}, []);
const closeModal = useCallback(() => {
bottomSheetModalRef.current?.dismiss();
@@ -104,45 +99,21 @@ export const DownloadItems: React.FC = ({
const itemsNotDownloaded = useMemo(
() =>
items.filter((i) => !downloadedFiles?.some((f) => f.item.Id === i.Id)),
- [items, downloadedFiles],
+ [items, downloadedFiles]
);
- // Initialize selectedOptions with default values
- useEffect(() => {
- setSelectedOptions(() => ({
- bitrate: defaultBitrate,
- mediaSource: defaultMediaSource ?? undefined,
- subtitleIndex: defaultSubtitleIndex ?? -1,
- audioIndex: defaultAudioIndex,
- }));
- }, [
- defaultAudioIndex,
- defaultBitrate,
- defaultSubtitleIndex,
- defaultMediaSource,
- ]);
-
- const itemsToDownload = useMemo(() => {
- if (downloadUnwatchedOnly) {
- return itemsNotDownloaded.filter((item) => !item.UserData?.Played);
- }
- return itemsNotDownloaded;
- }, [itemsNotDownloaded, downloadUnwatchedOnly]);
-
const allItemsDownloaded = useMemo(() => {
if (items.length === 0) return false;
return itemsNotDownloaded.length === 0;
}, [items, itemsNotDownloaded]);
const itemsProcesses = useMemo(
- () =>
- processes?.filter((p) => p?.item?.Id && itemIds.includes(p.item.Id)) ||
- [],
- [processes, itemIds],
+ () => processes?.filter((p) => itemIds.includes(p.item.Id)),
+ [processes, itemIds]
);
const progress = useMemo(() => {
- if (itemIds.length === 1)
- return itemsProcesses.reduce((acc, p) => acc + (p.progress || 0), 0);
+ if (itemIds.length == 1)
+ return itemsProcesses.reduce((acc, p) => acc + p.progress, 0);
return (
((itemIds.length -
queue.filter((q) => itemIds.includes(q.item.Id)).length) /
@@ -154,16 +125,9 @@ export const DownloadItems: React.FC = ({
const itemsQueued = useMemo(() => {
return (
itemsNotDownloaded.length > 0 &&
- itemsNotDownloaded.every((p) => queue.some((q) => p.Id === q.item.Id))
+ itemsNotDownloaded.every((p) => queue.some((q) => p.Id == q.item.Id))
);
}, [queue, itemsNotDownloaded]);
-
- const itemsInProgressOrQueued = useMemo(() => {
- const inProgress = itemsProcesses.length;
- const inQueue = queue.filter((q) => itemIds.includes(q.item.Id)).length;
- return inProgress + inQueue;
- }, [itemsProcesses, queue, itemIds]);
-
const navigateToDownloads = () => router.push("/downloads");
const onDownloadedPress = () => {
@@ -172,125 +136,123 @@ export const DownloadItems: React.FC = ({
firstItem.Type !== "Episode"
? "/downloads"
: ({
- pathname: "/series/[id]",
+ pathname: `/downloads/${firstItem.SeriesId}`,
params: {
- id: firstItem.SeriesId!,
- seasonIndex: firstItem.ParentIndexNumber?.toString(),
- offline: "true",
+ episodeSeasonIndex: firstItem.ParentIndexNumber,
},
- } as Href),
+ } as Href)
);
};
+ const acceptDownloadOptions = useCallback(() => {
+ if (userCanDownload === true) {
+ if (itemsNotDownloaded.some((i) => !i.Id)) {
+ throw new Error("No item id");
+ }
+ closeModal();
+
+ if (usingOptimizedServer) initiateDownload(...itemsNotDownloaded);
+ else {
+ queueActions.enqueue(
+ queue,
+ setQueue,
+ ...itemsNotDownloaded.map((item) => ({
+ id: item.Id!,
+ execute: async () => await initiateDownload(item),
+ item,
+ }))
+ );
+ }
+ } else {
+ toast.error(
+ t("home.downloads.toasts.you_are_not_allowed_to_download_files")
+ );
+ }
+ }, [
+ queue,
+ setQueue,
+ itemsNotDownloaded,
+ usingOptimizedServer,
+ userCanDownload,
+ maxBitrate,
+ selectedMediaSource,
+ selectedAudioStream,
+ selectedSubtitleStream,
+ ]);
+
const initiateDownload = useCallback(
async (...items: BaseItemDto[]) => {
if (
!api ||
!user?.Id ||
items.some((p) => !p.Id) ||
- (itemsNotDownloaded.length === 1 && !selectedOptions?.mediaSource?.Id)
+ (itemsNotDownloaded.length === 1 && !selectedMediaSource?.Id)
) {
throw new Error(
- "DownloadItem ~ initiateDownload: No api or user or item",
+ "DownloadItem ~ initiateDownload: No api or user or item"
);
}
- const downloadDetailsPromises = items.map(async (item) => {
- const { mediaSource, audioIndex, subtitleIndex } =
- itemsNotDownloaded.length > 1
- ? getDefaultPlaySettings(item, settings!)
- : {
- mediaSource: selectedOptions?.mediaSource,
- audioIndex: selectedOptions?.audioIndex,
- subtitleIndex: selectedOptions?.subtitleIndex,
- };
+ let mediaSource = selectedMediaSource;
+ let audioIndex: number | undefined = selectedAudioStream;
+ let subtitleIndex: number | undefined = selectedSubtitleStream;
- const downloadDetails = await getDownloadUrl({
+ for (const item of items) {
+ if (itemsNotDownloaded.length > 1) {
+ const defaults = getDefaultPlaySettings(item, settings!);
+ mediaSource = defaults.mediaSource;
+ audioIndex = defaults.audioIndex;
+ subtitleIndex = defaults.subtitleIndex;
+ // Keep using the selected bitrate for consistency across all downloads
+ }
+
+ const res = await getStreamUrl({
api,
item,
- userId: user.Id!,
- mediaSource: mediaSource!,
- audioStreamIndex: audioIndex ?? -1,
- subtitleStreamIndex: subtitleIndex ?? -1,
- maxBitrate: selectedOptions?.bitrate || defaultBitrate,
- deviceId: api.deviceInfo.id,
- audioMode: settings?.audioTranscodeMode,
+ startTimeTicks: 0,
+ userId: user?.Id,
+ audioStreamIndex: audioIndex,
+ maxStreamingBitrate: maxBitrate.value,
+ mediaSourceId: mediaSource?.Id,
+ subtitleStreamIndex: subtitleIndex,
+ deviceProfile: download,
});
- return {
- url: downloadDetails?.url,
- item,
- mediaSource: downloadDetails?.mediaSource,
- };
- });
-
- const downloadDetails = await Promise.all(downloadDetailsPromises);
- for (const { url, item, mediaSource } of downloadDetails) {
- if (!url) {
+ if (!res) {
Alert.alert(
t("home.downloads.something_went_wrong"),
- t("home.downloads.could_not_get_stream_url_from_jellyfin"),
+ t("home.downloads.could_not_get_stream_url_from_jellyfin")
);
continue;
}
- if (!mediaSource) {
- console.error(`Could not get download URL for ${item.Name}`);
- toast.error(
- t("home.downloads.toasts.could_not_get_download_url_for_item", {
- itemName: item.Name,
- }),
- );
- continue;
- }
- // Get the audio/subtitle indices that were used for this download
- const downloadAudioIndex =
- itemsNotDownloaded.length > 1
- ? getDefaultPlaySettings(item, settings!).audioIndex
- : selectedOptions?.audioIndex;
- const downloadSubtitleIndex =
- itemsNotDownloaded.length > 1
- ? getDefaultPlaySettings(item, settings!).subtitleIndex
- : selectedOptions?.subtitleIndex;
- await startBackgroundDownload(
- url,
- item,
- mediaSource,
- selectedOptions?.bitrate || defaultBitrate,
- downloadAudioIndex,
- downloadSubtitleIndex,
- );
+ const { mediaSource: source, url } = res;
+
+ if (!url || !source) throw new Error("No url");
+
+ saveDownloadItemInfoToDiskTmp(item, source, url);
+
+ if (usingOptimizedServer) {
+ await startBackgroundDownload(url, item, source);
+ } else {
+ await startRemuxing(item, url, source);
+ }
}
},
[
api,
user?.Id,
itemsNotDownloaded,
- selectedOptions,
+ selectedMediaSource,
+ selectedAudioStream,
+ selectedSubtitleStream,
settings,
- defaultBitrate,
+ maxBitrate,
+ usingOptimizedServer,
startBackgroundDownload,
- ],
+ startRemuxing,
+ ]
);
- const acceptDownloadOptions = useCallback(async () => {
- if (userCanDownload === true) {
- if (itemsToDownload.some((i) => !i.Id)) {
- throw new Error("No item id");
- }
-
- closeModal();
-
- // Wait for modal dismiss animation to complete
- setTimeout(() => {
- initiateDownload(...itemsToDownload);
- }, 300);
- } else {
- toast.error(
- t("home.downloads.toasts.you_are_not_allowed_to_download_files"),
- );
- }
- }, [closeModal, initiateDownload, itemsToDownload, userCanDownload]);
-
const renderBackdrop = useCallback(
(props: BottomSheetBackdropProps) => (
= ({
appearsOnIndex={0}
/>
),
- [],
+ []
+ );
+ useFocusEffect(
+ useCallback(() => {
+ if (!settings) return;
+ if (itemsNotDownloaded.length !== 1) return;
+ const { bitrate, mediaSource, audioIndex, subtitleIndex } =
+ getDefaultPlaySettings(items[0], settings);
+
+ setSelectedMediaSource(mediaSource ?? undefined);
+ setSelectedAudioStream(audioIndex ?? 0);
+ setSelectedSubtitleStream(subtitleIndex ?? -1);
+ setMaxBitrate(bitrate);
+ }, [items, itemsNotDownloaded, settings])
);
const renderButtonContent = () => {
- // For single item downloads, show progress if item is being processed
- // For multi-item downloads (season/series), show progress only if 2+ items are in progress or queued
- const shouldShowProgress =
- itemIds.length === 1
- ? itemsProcesses.length > 0
- : itemsInProgressOrQueued > 1;
-
- if (processes.length > 0 && shouldShowProgress) {
+ if (processes && itemsProcesses.length > 0) {
return progress === 0 ? (
) : (
-
+
);
- }
-
- if (itemsQueued) {
- return ;
- }
-
- if (allItemsDownloaded) {
+ } else if (itemsQueued) {
+ return ;
+ } else if (allItemsDownloaded) {
return ;
+ } else {
+ return ;
}
-
- return ;
};
const onButtonPress = () => {
@@ -365,100 +329,64 @@ export const DownloadItems: React.FC = ({
}}
onChange={handleSheetChanges}
backdropComponent={renderBackdrop}
- enablePanDownToClose
- enableDismissOnClose
- android_keyboardInputMode='adjustResize'
- keyboardBehavior='interactive'
- keyboardBlurBehavior='restore'
>
-
+
-
+
{title}
-
+
{subtitle ||
t("item_card.download.download_x_item", {
- item_count: itemsToDownload.length,
+ item_count: itemsNotDownloaded.length,
})}
-
-
-
- setSelectedOptions(
- (prev) => prev && { ...prev, bitrate: val },
- )
- }
- selected={selectedOptions?.bitrate}
- />
-
- {itemsNotDownloaded.length > 1 && (
-
- {t("item_card.download.download_unwatched_only")}
-
-
- )}
+
+
{itemsNotDownloaded.length === 1 && (
-
-
-
- setSelectedOptions(
- (prev) =>
- prev && {
- ...prev,
- mediaSource: val,
- },
- )
- }
- selected={selectedOptions?.mediaSource}
- />
-
- {selectedOptions?.mediaSource && (
-
+ <>
+
+ {selectedMediaSource && (
+
{
- setSelectedOptions(
- (prev) =>
- prev && {
- ...prev,
- audioIndex: val,
- },
- );
- }}
- selected={selectedOptions.audioIndex}
+ source={selectedMediaSource}
+ onChange={setSelectedAudioStream}
+ selected={selectedAudioStream}
/>
{
- setSelectedOptions(
- (prev) =>
- prev && {
- ...prev,
- subtitleIndex: val,
- },
- );
- }}
- selected={selectedOptions.subtitleIndex}
+ source={selectedMediaSource}
+ onChange={setSelectedSubtitleStream}
+ selected={selectedSubtitleStream}
/>
)}
-
+ >
)}
-
-
+
{t("item_card.download.download_button")}
+
+
+ {usingOptimizedServer
+ ? t("item_card.download.using_optimized_server")
+ : t("item_card.download.using_default_method")}
+
+
@@ -476,17 +404,17 @@ export const DownloadSingleItem: React.FC<{
(
-
+
)}
DownloadedIconComponent={() => (
-
+
)}
/>
);
diff --git a/components/ItemContent.tsx b/components/ItemContent.tsx
index 1b2cbbac..1f9077f8 100644
--- a/components/ItemContent.tsx
+++ b/components/ItemContent.tsx
@@ -1,4 +1,25 @@
-import type {
+import { AudioTrackSelector } from "@/components/AudioTrackSelector";
+import { Bitrate, BitrateSelector } from "@/components/BitrateSelector";
+import { DownloadSingleItem } from "@/components/DownloadItem";
+import { OverviewText } from "@/components/OverviewText";
+import { ParallaxScrollView } from "@/components/ParallaxPage";
+// const PlayButton = !Platform.isTV ? require("@/components/PlayButton") : null;
+import { PlayButton } from "@/components/PlayButton";
+import { PlayedStatus } from "@/components/PlayedStatus";
+import { SimilarItems } from "@/components/SimilarItems";
+import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
+import { ItemImage } from "@/components/common/ItemImage";
+import { CastAndCrew } from "@/components/series/CastAndCrew";
+import { CurrentSeries } from "@/components/series/CurrentSeries";
+import { SeasonEpisodesCarousel } from "@/components/series/SeasonEpisodesCarousel";
+import useDefaultPlaySettings from "@/hooks/useDefaultPlaySettings";
+import { useImageColors } from "@/hooks/useImageColors";
+import { useOrientation } from "@/hooks/useOrientation";
+import * as ScreenOrientation from "@/packages/expo-screen-orientation";
+import { apiAtom } from "@/providers/JellyfinProvider";
+import { useSettings } from "@/utils/atoms/settings";
+import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
+import {
BaseItemDto,
MediaSourceInfo,
} from "@jellyfin/sdk/lib/generated-client/models";
@@ -8,33 +29,11 @@ import { useAtom } from "jotai";
import React, { useEffect, useMemo, useState } from "react";
import { Platform, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
-import { type Bitrate } from "@/components/BitrateSelector";
-import { ItemImage } from "@/components/common/ItemImage";
-import { DownloadSingleItem } from "@/components/DownloadItem";
-import { ItemPeopleSections } from "@/components/item/ItemPeopleSections";
-import { MediaSourceButton } from "@/components/MediaSourceButton";
-import { OverviewText } from "@/components/OverviewText";
-import { ParallaxScrollView } from "@/components/ParallaxPage";
-// const PlayButton = !Platform.isTV ? require("@/components/PlayButton") : null;
-import { PlayButton } from "@/components/PlayButton";
-import { PlayedStatus } from "@/components/PlayedStatus";
-import { SimilarItems } from "@/components/SimilarItems";
-import { CurrentSeries } from "@/components/series/CurrentSeries";
-import { SeasonEpisodesCarousel } from "@/components/series/SeasonEpisodesCarousel";
-import useDefaultPlaySettings from "@/hooks/useDefaultPlaySettings";
-import { useImageColorsReturn } from "@/hooks/useImageColorsReturn";
-import { useOrientation } from "@/hooks/useOrientation";
-import * as ScreenOrientation from "@/packages/expo-screen-orientation";
-import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
-import { useOfflineMode } from "@/providers/OfflineModeProvider";
-import { useSettings } from "@/utils/atoms/settings";
-import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
import { AddToFavorites } from "./AddToFavorites";
-import { AddToWatchlist } from "./AddToWatchlist";
import { ItemHeader } from "./ItemHeader";
import { ItemTechnicalDetails } from "./ItemTechnicalDetails";
-import { PlayInRemoteSessionButton } from "./PlayInRemoteSession";
-
+import { MediaSourceSelector } from "./MediaSourceSelector";
+import { MoreMoviesWithActor } from "./MoreMoviesWithActor";
const Chromecast = !Platform.isTV ? require("./Chromecast") : null;
export type SelectedOptions = {
@@ -44,22 +43,14 @@ export type SelectedOptions = {
subtitleIndex: number;
};
-interface ItemContentProps {
- item: BaseItemDto;
- itemWithSources?: BaseItemDto | null;
-}
-
-export const ItemContent: React.FC = React.memo(
- ({ item, itemWithSources }) => {
+export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo(
+ ({ item }) => {
const [api] = useAtom(apiAtom);
- const isOffline = useOfflineMode();
- const { settings } = useSettings();
+ const [settings] = useSettings();
const { orientation } = useOrientation();
const navigation = useNavigation();
const insets = useSafeAreaInsets();
- const [user] = useAtom(userAtom);
-
- const itemColors = useImageColorsReturn({ item });
+ useImageColors({ item });
const [loadingLogo, setLoadingLogo] = useState(true);
const [headerHeight, setHeaderHeight] = useState(350);
@@ -68,32 +59,18 @@ export const ItemContent: React.FC = React.memo(
SelectedOptions | undefined
>(undefined);
- // Use itemWithSources for play settings since it has MediaSources data
const {
defaultAudioIndex,
defaultBitrate,
defaultMediaSource,
defaultSubtitleIndex,
- } = useDefaultPlaySettings(itemWithSources ?? item, settings);
-
- const logoUrl = useMemo(
- () => (item ? getLogoImageUrlById({ api, item }) : null),
- [api, item],
- );
-
- const onLogoLoad = React.useCallback(() => {
- setLoadingLogo(false);
- }, []);
-
- const loading = useMemo(() => {
- return Boolean(logoUrl && loadingLogo);
- }, [loadingLogo, logoUrl]);
+ } = useDefaultPlaySettings(item, settings);
// Needs to automatically change the selected to the default values for default indexes.
useEffect(() => {
setSelectedOptions(() => ({
bitrate: defaultBitrate,
- mediaSource: defaultMediaSource ?? undefined,
+ mediaSource: defaultMediaSource,
subtitleIndex: defaultSubtitleIndex ?? -1,
audioIndex: defaultAudioIndex,
}));
@@ -104,89 +81,56 @@ export const ItemContent: React.FC = React.memo(
defaultMediaSource,
]);
- useEffect(() => {
- if (!Platform.isTV && itemWithSources) {
+ if (!Platform.isTV) {
+ useEffect(() => {
navigation.setOptions({
headerRight: () =>
- item &&
- (Platform.OS === "ios" ? (
-
-
+ item && (
+
+
{item.Type !== "Program" && (
-
+
{!Platform.isTV && (
-
+
)}
- {user?.Policy?.IsAdministrator &&
- !settings.hideRemoteSessionButton && (
-
- )}
-
-
+
- {settings.streamyStatsServerUrl &&
- !settings.hideWatchlistsTab && (
-
- )}
)}
- ) : (
-
-
- {item.Type !== "Program" && (
-
- {!Platform.isTV && (
-
- )}
- {user?.Policy?.IsAdministrator &&
- !settings.hideRemoteSessionButton && (
-
- )}
-
-
-
- {settings.streamyStatsServerUrl &&
- !settings.hideWatchlistsTab && (
-
- )}
-
- )}
-
- )),
+ ),
});
- }
- }, [
- item,
- navigation,
- user,
- itemWithSources,
- settings.hideRemoteSessionButton,
- settings.streamyStatsServerUrl,
- settings.hideWatchlistsTab,
- ]);
+ }, [item]);
+ }
useEffect(() => {
- if (item) {
- if (orientation !== ScreenOrientation.OrientationLock.PORTRAIT_UP)
- setHeaderHeight(230);
- else if (item.Type === "Movie") setHeaderHeight(500);
- else setHeaderHeight(350);
- }
- }, [item, orientation]);
+ if (orientation !== ScreenOrientation.OrientationLock.PORTRAIT_UP)
+ setHeaderHeight(230);
+ else if (item.Type === "Movie") setHeaderHeight(500);
+ else setHeaderHeight(350);
+ }, [item.Type, orientation]);
- if (!item || !selectedOptions) return null;
+ const logoUrl = useMemo(() => getLogoImageUrlById({ api, item }), [item]);
+
+ const loading = useMemo(() => {
+ return Boolean(logoUrl && loadingLogo);
+ }, [loadingLogo, logoUrl]);
+ if (!selectedOptions) return null;
return (
@@ -203,71 +147,123 @@ export const ItemContent: React.FC = React.memo(
}
logo={
- logoUrl ? (
-
- ) : (
-
- )
+ <>
+ {logoUrl ? (
+ setLoadingLogo(false)}
+ onError={() => setLoadingLogo(false)}
+ />
+ ) : null}
+ >
}
>
-
-
-
-
-
-
-
- {!isOffline && (
-
+
+
+ {item.Type !== "Program" && !Platform.isTV && (
+
+
+ setSelectedOptions(
+ (prev) => prev && { ...prev, bitrate: val }
+ )
+ }
+ selected={selectedOptions.bitrate}
/>
- )}
-
+
+ setSelectedOptions(
+ (prev) =>
+ prev && {
+ ...prev,
+ mediaSource: val,
+ }
+ )
+ }
+ selected={selectedOptions.mediaSource}
+ />
+ {
+ setSelectedOptions(
+ (prev) =>
+ prev && {
+ ...prev,
+ audioIndex: val,
+ }
+ );
+ }}
+ selected={selectedOptions.audioIndex}
+ />
+
+ setSelectedOptions(
+ (prev) =>
+ prev && {
+ ...prev,
+ subtitleIndex: val,
+ }
+ )
+ }
+ selected={selectedOptions.subtitleIndex}
+ />
+
+ )}
+
+
+
{item.Type === "Episode" && (
)}
- {!isOffline &&
- selectedOptions.mediaSource?.MediaStreams &&
- selectedOptions.mediaSource.MediaStreams.length > 0 && (
-
- )}
-
-
+
+
{item.Type !== "Program" && (
<>
- {item.Type === "Episode" && !isOffline && (
-
+ {item.Type === "Episode" && (
+
)}
-
+
- {!isOffline && }
+ {item.People && item.People.length > 0 && (
+
+ {item.People.slice(0, 3).map((person, idx) => (
+
+ ))}
+
+ )}
+
+
>
)}
);
- },
+ }
);
diff --git a/components/ItemTechnicalDetails.tsx b/components/ItemTechnicalDetails.tsx
index 49614a2c..e2570dc8 100644
--- a/components/ItemTechnicalDetails.tsx
+++ b/components/ItemTechnicalDetails.tsx
@@ -1,38 +1,39 @@
import { Ionicons } from "@expo/vector-icons";
import {
- BottomSheetBackdrop,
- type BottomSheetBackdropProps,
- BottomSheetModal,
- BottomSheetScrollView,
-} from "@gorhom/bottom-sheet";
-import type {
MediaSourceInfo,
- MediaStream,
+ type MediaStream,
} from "@jellyfin/sdk/lib/generated-client";
-import type React from "react";
-import { useMemo, useRef } from "react";
-import { useTranslation } from "react-i18next";
+import React, { useMemo, useRef } from "react";
import { TouchableOpacity, View } from "react-native";
-import { formatBitrate } from "@/utils/bitrate";
import { Badge } from "./Badge";
import { Text } from "./common/Text";
+import {
+ BottomSheetModal,
+ BottomSheetBackdropProps,
+ BottomSheetBackdrop,
+ BottomSheetView,
+ BottomSheetScrollView,
+} from "@gorhom/bottom-sheet";
+import { Button } from "./Button";
+import { useTranslation } from "react-i18next";
+import { formatBitrate } from "@/utils/bitrate";
interface Props {
source?: MediaSourceInfo;
}
-export const ItemTechnicalDetails: React.FC = ({ source }) => {
+export const ItemTechnicalDetails: React.FC = ({ source, ...props }) => {
const bottomSheetModalRef = useRef(null);
const { t } = useTranslation();
return (
-
- {t("item_card.video")}
+
+ {t("item_card.video")}
bottomSheetModalRef.current?.present()}>
-
+
- {t("item_card.more_details")}
+ {t("item_card.more_details")}
= ({ source }) => {
)}
>
-
-
-
+
+
+
{t("item_card.video")}
-
+
-
-
+
+
{t("item_card.audio")}
stream.Type === "Audio",
+ (stream) => stream.Type === "Audio"
) || []
}
/>
-
-
+
+
{t("item_card.subtitles")}
stream.Type === "Subtitle",
+ (stream) => stream.Type === "Subtitle"
) || []
}
/>
@@ -100,25 +101,25 @@ const SubtitleStreamInfo = ({
subtitleStreams: MediaStream[];
}) => {
return (
-
- {subtitleStreams.map((stream, _index) => (
-
-
+
+ {subtitleStreams.map((stream, index) => (
+
+
{stream.DisplayTitle}
-
+
+
}
text={stream.Language}
/>
+
}
/>
@@ -130,40 +131,40 @@ const SubtitleStreamInfo = ({
const AudioStreamInfo = ({ audioStreams }: { audioStreams: MediaStream[] }) => {
return (
-
+
{audioStreams.map((audioStreams, index) => (
-
-
+
+
{audioStreams.DisplayTitle}
-
+
+
}
text={audioStreams.Language}
/>
}
text={audioStreams.Codec}
/>
}
+ variant="gray"
+ iconLeft={ }
text={audioStreams.ChannelLayout}
/>
+
}
text={formatBitrate(audioStreams.BitRate)}
/>
@@ -175,70 +176,53 @@ const AudioStreamInfo = ({ audioStreams }: { audioStreams: MediaStream[] }) => {
};
const VideoStreamInfo = ({ source }: { source?: MediaSourceInfo }) => {
+ if (!source) return null;
+
const videoStream = useMemo(() => {
- return source?.MediaStreams?.find((stream) => stream.Type === "Video") as
- | MediaStream
- | undefined;
- }, [source?.MediaStreams]);
+ return source.MediaStreams?.find(
+ (stream) => stream.Type === "Video"
+ ) as MediaStream;
+ }, [source.MediaStreams]);
- if (!source || !videoStream) return null;
-
- // Dolby Vision video check
- const isDolbyVision =
- videoStream.VideoRangeType === "DOVI" ||
- videoStream.DvVersionMajor != null ||
- videoStream.DvVersionMinor != null;
+ if (!videoStream) return null;
return (
-
+
}
+ variant="gray"
+ iconLeft={ }
text={formatFileSize(source.Size)}
/>
}
+ variant="gray"
+ iconLeft={ }
text={`${videoStream.Width}x${videoStream.Height}`}
/>
- {isDolbyVision && (
-
- }
- text={"DV"}
- />
- )}
+
}
text={videoStream.VideoRange}
/>
+
}
text={videoStream.Codec}
/>
+
}
text={formatBitrate(videoStream.BitRate)}
/>
}
- text={
- videoStream.AverageFrameRate != null
- ? `${videoStream.AverageFrameRate.toFixed(0)} fps`
- : ""
- }
+ variant="gray"
+ iconLeft={ }
+ text={`${videoStream.AverageFrameRate?.toFixed(0)} fps`}
/>
);
@@ -249,9 +233,6 @@ const formatFileSize = (bytes?: number | null) => {
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
if (bytes === 0) return "0 Byte";
- const i = Number.parseInt(
- Math.floor(Math.log(bytes) / Math.log(1024)).toString(),
- 10,
- );
- return `${Math.round((bytes / 1024 ** i) * 100) / 100} ${sizes[i]}`;
+ const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString());
+ return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + " " + sizes[i];
};
diff --git a/components/PlayButton.tsx b/components/PlayButton.tsx
index 1c3fd46f..ce4733f3 100644
--- a/components/PlayButton.tsx
+++ b/components/PlayButton.tsx
@@ -1,14 +1,21 @@
+import { Platform, Pressable } from "react-native";
+import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
+import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
+import { useSettings } from "@/utils/atoms/settings";
+import { getParentBackdropImageUrl } from "@/utils/jellyfin/image/getParentBackdropImageUrl";
+import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
+import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
+import ios from "@/utils/profiles/ios";
+import { runtimeTicksToMinutes } from "@/utils/time";
import { useActionSheet } from "@expo/react-native-action-sheet";
-import { Feather, Ionicons } from "@expo/vector-icons";
-import { BottomSheetView } from "@gorhom/bottom-sheet";
-import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
+import { Feather, Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
+import { useRouter } from "expo-router";
import { useAtom, useAtomValue } from "jotai";
import { useCallback, useEffect } from "react";
-import { useTranslation } from "react-i18next";
-import { Alert, Platform, TouchableOpacity, View } from "react-native";
+import { Alert, TouchableOpacity, View } from "react-native";
import CastContext, {
CastButton,
- MediaStreamType,
PlayServicesState,
useMediaStatus,
useRemoteMediaClient,
@@ -23,29 +30,17 @@ import Animated, {
useSharedValue,
withTiming,
} from "react-native-reanimated";
-import useRouter from "@/hooks/useAppRouter";
-import { useHaptic } from "@/hooks/useHaptic";
-import type { ThemeColors } from "@/hooks/useImageColorsReturn";
-import { getDownloadedItemById } from "@/providers/Downloads/database";
-import { useGlobalModal } from "@/providers/GlobalModalProvider";
-import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
-import { useOfflineMode } from "@/providers/OfflineModeProvider";
-import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
-import { useSettings } from "@/utils/atoms/settings";
-import { getParentBackdropImageUrl } from "@/utils/jellyfin/image/getParentBackdropImageUrl";
-import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
-import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
+import { Button } from "./Button";
+import { SelectedOptions } from "./ItemContent";
import { chromecast } from "@/utils/profiles/chromecast";
import { chromecasth265 } from "@/utils/profiles/chromecasth265";
-import { runtimeTicksToMinutes } from "@/utils/time";
-import { Button } from "./Button";
-import { Text } from "./common/Text";
-import type { SelectedOptions } from "./ItemContent";
+import { useTranslation } from "react-i18next";
+import { useHaptic } from "@/hooks/useHaptic";
+import { chromecastLoadMedia } from "@/utils/chromecastLoadMedia";
-interface Props extends React.ComponentProps {
+interface Props extends React.ComponentProps {
item: BaseItemDto;
selectedOptions: SelectedOptions;
- colors?: ThemeColors;
}
const ANIMATION_DURATION = 500;
@@ -54,60 +49,53 @@ const MIN_PLAYBACK_WIDTH = 15;
export const PlayButton: React.FC = ({
item,
selectedOptions,
- colors,
+ ...props
}: Props) => {
- const isOffline = useOfflineMode();
const { showActionSheetWithOptions } = useActionSheet();
const client = useRemoteMediaClient();
const mediaStatus = useMediaStatus();
const { t } = useTranslation();
- const { showModal, hideModal } = useGlobalModal();
- const [globalColorAtom] = useAtom(itemThemeColorAtom);
+ const [colorAtom] = useAtom(itemThemeColorAtom);
const api = useAtomValue(apiAtom);
const user = useAtomValue(userAtom);
- // Use colors prop if provided, otherwise fallback to global atom
- const effectiveColors = colors || globalColorAtom;
-
const router = useRouter();
const startWidth = useSharedValue(0);
const targetWidth = useSharedValue(0);
- const endColor = useSharedValue(effectiveColors);
- const startColor = useSharedValue(effectiveColors);
+ const endColor = useSharedValue(colorAtom);
+ const startColor = useSharedValue(colorAtom);
const widthProgress = useSharedValue(0);
const colorChangeProgress = useSharedValue(0);
- const { settings, updateSettings } = useSettings();
+ const [settings] = useSettings();
const lightHapticFeedback = useHaptic("light");
const goToPlayer = useCallback(
- (q: string) => {
- if (settings.maxAutoPlayEpisodeCount.value !== -1) {
- updateSettings({ autoPlayEpisodeCount: 0 });
- }
+ (q: string, bitrateValue: number | undefined) => {
router.push(`/player/direct-player?${q}`);
},
- [router, isOffline],
+ [router]
);
- const handleNormalPlayFlow = useCallback(async () => {
+ const onPress = useCallback(async () => {
+ console.log("onPress");
if (!item) return;
+ lightHapticFeedback();
+
const queryParams = new URLSearchParams({
itemId: item.Id!,
audioIndex: selectedOptions.audioIndex?.toString() ?? "",
subtitleIndex: selectedOptions.subtitleIndex?.toString() ?? "",
mediaSourceId: selectedOptions.mediaSource?.Id ?? "",
bitrateValue: selectedOptions.bitrate?.value?.toString() ?? "",
- playbackPosition: item.UserData?.PlaybackPositionTicks?.toString() ?? "0",
- offline: isOffline ? "true" : "false",
});
const queryString = queryParams.toString();
if (!client) {
- goToPlayer(queryString);
+ goToPlayer(queryString, selectedOptions.bitrate?.value);
return;
}
@@ -133,42 +121,14 @@ export const PlayButton: React.FC = ({
// Check if user wants H265 for Chromecast
const enableH265 = settings.enableH265ForChromecast;
- // Validate required parameters before calling getStreamUrl
- if (!api) {
- console.warn("API not available for Chromecast streaming");
- Alert.alert(
- t("player.client_error"),
- t("player.missing_parameters"),
- );
- return;
- }
- if (!user?.Id) {
- console.warn(
- "User not authenticated for Chromecast streaming",
- );
- Alert.alert(
- t("player.client_error"),
- t("player.missing_parameters"),
- );
- return;
- }
- if (!item?.Id) {
- console.warn("Item not available for Chromecast streaming");
- Alert.alert(
- t("player.client_error"),
- t("player.missing_parameters"),
- );
- return;
- }
-
// Get a new URL with the Chromecast device profile
try {
const data = await getStreamUrl({
api,
item,
deviceProfile: enableH265 ? chromecasth265 : chromecast,
- startTimeTicks: item?.UserData?.PlaybackPositionTicks ?? 0,
- userId: user.Id,
+ startTimeTicks: item?.UserData?.PlaybackPositionTicks!,
+ userId: user?.Id,
audioStreamIndex: selectedOptions.audioIndex,
maxStreamingBitrate: selectedOptions.bitrate?.value,
mediaSourceId: selectedOptions.mediaSource?.Id,
@@ -181,88 +141,35 @@ export const PlayButton: React.FC = ({
console.warn("No URL returned from getStreamUrl", data);
Alert.alert(
t("player.client_error"),
- t("player.could_not_create_stream_for_chromecast"),
+ t("player.could_not_create_stream_for_chromecast")
);
return;
}
- // Calculate start time in seconds from playback position
- const startTimeSeconds =
- (item?.UserData?.PlaybackPositionTicks ?? 0) / 10000000;
-
- // Calculate stream duration in seconds from runtime
- const streamDurationSeconds = item.RunTimeTicks
- ? item.RunTimeTicks / 10000000
- : undefined;
-
- client
- .loadMedia({
- mediaInfo: {
- contentId: item.Id,
- contentUrl: data?.url,
- contentType: "video/mp4",
- streamType: MediaStreamType.BUFFERED,
- streamDuration: streamDurationSeconds,
- metadata:
- item.Type === "Episode"
- ? {
- type: "tvShow",
- title: item.Name || "",
- episodeNumber: item.IndexNumber || 0,
- seasonNumber: item.ParentIndexNumber || 0,
- seriesTitle: item.SeriesName || "",
- images: [
- {
- url: getParentBackdropImageUrl({
- api,
- item,
- quality: 90,
- width: 2000,
- })!,
- },
- ],
- }
- : item.Type === "Movie"
- ? {
- type: "movie",
- title: item.Name || "",
- subtitle: item.Overview || "",
- images: [
- {
- url: getPrimaryImageUrl({
- api,
- item,
- quality: 90,
- width: 2000,
- })!,
- },
- ],
- }
- : {
- type: "generic",
- title: item.Name || "",
- subtitle: item.Overview || "",
- images: [
- {
- url: getPrimaryImageUrl({
- api,
- item,
- quality: 90,
- width: 2000,
- })!,
- },
- ],
- },
+ chromecastLoadMedia({
+ client,
+ item,
+ contentUrl: data.url,
+ sessionId: data.sessionId || undefined,
+ mediaSourceId: data.mediaSource?.Id || undefined,
+ playbackOptions: selectedOptions,
+ images: [
+ {
+ url: getParentBackdropImageUrl({
+ api,
+ item,
+ quality: 90,
+ width: 2000,
+ })!,
},
- startTime: startTimeSeconds,
- })
- .then(() => {
- // state is already set when reopening current media, so skip it here.
- if (isOpeningCurrentlyPlayingMedia) {
- return;
- }
- CastContext.showExpandedControls();
- });
+ ],
+ }).then(() => {
+ // state is already set when reopening current media, so skip it here.
+ if (isOpeningCurrentlyPlayingMedia) {
+ return;
+ }
+ router.push("/player/google-cast-player");
+ });
} catch (e) {
console.log(e);
}
@@ -270,12 +177,12 @@ export const PlayButton: React.FC = ({
});
break;
case 1:
- goToPlayer(queryString);
+ goToPlayer(queryString, selectedOptions.bitrate?.value);
break;
case cancelButtonIndex:
break;
}
- },
+ }
);
}, [
item,
@@ -287,140 +194,16 @@ export const PlayButton: React.FC = ({
showActionSheetWithOptions,
mediaStatus,
selectedOptions,
- goToPlayer,
- isOffline,
- t,
- ]);
-
- const onPress = useCallback(async () => {
- if (!item) return;
-
- lightHapticFeedback();
-
- // Check if item is downloaded
- const downloadedItem = item.Id ? getDownloadedItemById(item.Id) : undefined;
-
- // If already in offline mode, play downloaded file directly
- if (isOffline && downloadedItem) {
- const queryParams = new URLSearchParams({
- itemId: item.Id!,
- offline: "true",
- playbackPosition:
- item.UserData?.PlaybackPositionTicks?.toString() ?? "0",
- });
- goToPlayer(queryParams.toString());
- return;
- }
-
- // If online but file is downloaded, ask user which version to play
- if (downloadedItem) {
- if (Platform.OS === "android") {
- // Show bottom sheet for Android
- showModal(
-
-
-
-
- {t("player.downloaded_file_title")}
-
-
- {t("player.downloaded_file_message")}
-
-
-
- {
- hideModal();
- const queryParams = new URLSearchParams({
- itemId: item.Id!,
- offline: "true",
- playbackPosition:
- item.UserData?.PlaybackPositionTicks?.toString() ?? "0",
- });
- goToPlayer(queryParams.toString());
- }}
- color='purple'
- >
- {Platform.OS === "android"
- ? "Play downloaded file"
- : t("player.downloaded_file_yes")}
-
- {
- hideModal();
- handleNormalPlayFlow();
- }}
- color='white'
- variant='border'
- >
- {Platform.OS === "android"
- ? "Stream file"
- : t("player.downloaded_file_no")}
-
-
-
- ,
- {
- snapPoints: ["35%"],
- enablePanDownToClose: true,
- },
- );
- } else {
- // Show alert for iOS
- Alert.alert(
- t("player.downloaded_file_title"),
- t("player.downloaded_file_message"),
- [
- {
- text: t("player.downloaded_file_yes"),
- onPress: () => {
- const queryParams = new URLSearchParams({
- itemId: item.Id!,
- offline: "true",
- playbackPosition:
- item.UserData?.PlaybackPositionTicks?.toString() ?? "0",
- });
- goToPlayer(queryParams.toString());
- },
- isPreferred: true,
- },
- {
- text: t("player.downloaded_file_no"),
- onPress: () => {
- handleNormalPlayFlow();
- },
- },
- {
- text: t("player.downloaded_file_cancel"),
- style: "cancel",
- },
- ],
- );
- }
- return;
- }
-
- // If not downloaded, proceed with normal flow
- handleNormalPlayFlow();
- }, [
- item,
- lightHapticFeedback,
- handleNormalPlayFlow,
- goToPlayer,
- t,
- showModal,
- hideModal,
- effectiveColors,
]);
const derivedTargetWidth = useDerivedValue(() => {
if (!item || !item.RunTimeTicks) return 0;
const userData = item.UserData;
- if (userData?.PlaybackPositionTicks) {
+ if (userData && userData.PlaybackPositionTicks) {
return userData.PlaybackPositionTicks > 0
? Math.max(
(userData.PlaybackPositionTicks / item.RunTimeTicks) * 100,
- MIN_PLAYBACK_WIDTH,
+ MIN_PLAYBACK_WIDTH
)
: 0;
}
@@ -437,11 +220,11 @@ export const PlayButton: React.FC = ({
easing: Easing.bezier(0.7, 0, 0.3, 1.0),
});
},
- [item],
+ [item]
);
useAnimatedReaction(
- () => effectiveColors,
+ () => colorAtom,
(newColor) => {
endColor.value = newColor;
colorChangeProgress.value = 0;
@@ -450,19 +233,19 @@ export const PlayButton: React.FC = ({
easing: Easing.bezier(0.9, 0, 0.31, 0.99),
});
},
- [effectiveColors],
+ [colorAtom]
);
useEffect(() => {
const timeout_2 = setTimeout(() => {
- startColor.value = effectiveColors;
+ startColor.value = colorAtom;
startWidth.value = targetWidth.value;
}, ANIMATION_DURATION);
return () => {
clearTimeout(timeout_2);
};
- }, [effectiveColors, item]);
+ }, [colorAtom, item]);
/**
* ANIMATED STYLES
@@ -471,7 +254,7 @@ export const PlayButton: React.FC = ({
backgroundColor: interpolateColor(
colorChangeProgress.value,
[0, 1],
- [startColor.value.primary, endColor.value.primary],
+ [startColor.value.primary, endColor.value.primary]
),
}));
@@ -479,7 +262,7 @@ export const PlayButton: React.FC = ({
backgroundColor: interpolateColor(
colorChangeProgress.value,
[0, 1],
- [startColor.value.primary, endColor.value.primary],
+ [startColor.value.primary, endColor.value.primary]
),
}));
@@ -487,7 +270,7 @@ export const PlayButton: React.FC = ({
width: `${interpolate(
widthProgress.value,
[0, 1],
- [startWidth.value, targetWidth.value],
+ [startWidth.value, targetWidth.value]
)}%`,
}));
@@ -495,19 +278,23 @@ export const PlayButton: React.FC = ({
color: interpolateColor(
colorChangeProgress.value,
[0, 1],
- [startColor.value.text, endColor.value.text],
+ [startColor.value.text, endColor.value.text]
),
}));
+ /**
+ * *********************
+ */
return (
-
+
= ({
-
+
- {runtimeTicksToMinutes(
- (item?.RunTimeTicks || 0) -
- (item?.UserData?.PlaybackPositionTicks || 0),
- )}
- {(item?.UserData?.PlaybackPositionTicks || 0) > 0 && " left"}
+ {runtimeTicksToMinutes(item?.RunTimeTicks)}
-
+
{client && (
-
-
+
+
+
+ )}
+ {!client && settings?.openInVLC && (
+
+
)}
diff --git a/components/PlayButton.tv.tsx b/components/PlayButton.tv.tsx
index f79a3174..1fb0563c 100644
--- a/components/PlayButton.tv.tsx
+++ b/components/PlayButton.tv.tsx
@@ -1,8 +1,15 @@
-import { Ionicons } from "@expo/vector-icons";
-import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
-import { useAtom } from "jotai";
+import { Platform } from "react-native";
+import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
+import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
+import { useSettings } from "@/utils/atoms/settings";
+import { runtimeTicksToMinutes } from "@/utils/time";
+import { useActionSheet } from "@expo/react-native-action-sheet";
+import { Feather, Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
+import { useRouter } from "expo-router";
+import { useAtom, useAtomValue } from "jotai";
import { useCallback, useEffect } from "react";
-import { TouchableOpacity, View } from "react-native";
+import { Alert, TouchableOpacity, View } from "react-native";
import Animated, {
Easing,
interpolate,
@@ -13,18 +20,14 @@ import Animated, {
useSharedValue,
withTiming,
} from "react-native-reanimated";
-import useRouter from "@/hooks/useAppRouter";
+import { Button } from "./Button";
+import { SelectedOptions } from "./ItemContent";
+import { useTranslation } from "react-i18next";
import { useHaptic } from "@/hooks/useHaptic";
-import type { ThemeColors } from "@/hooks/useImageColorsReturn";
-import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
-import { runtimeTicksToMinutes } from "@/utils/time";
-import type { Button } from "./Button";
-import type { SelectedOptions } from "./ItemContent";
interface Props extends React.ComponentProps {
item: BaseItemDto;
selectedOptions: SelectedOptions;
- colors?: ThemeColors;
}
const ANIMATION_DURATION = 500;
@@ -33,32 +36,35 @@ const MIN_PLAYBACK_WIDTH = 15;
export const PlayButton: React.FC = ({
item,
selectedOptions,
- colors,
...props
}: Props) => {
- const [globalColorAtom] = useAtom(itemThemeColorAtom);
+ const { showActionSheetWithOptions } = useActionSheet();
+ const { t } = useTranslation();
- // Use colors prop if provided, otherwise fallback to global atom
- const effectiveColors = colors || globalColorAtom;
+ const [colorAtom] = useAtom(itemThemeColorAtom);
+ const api = useAtomValue(apiAtom);
+ const user = useAtomValue(userAtom);
const router = useRouter();
const startWidth = useSharedValue(0);
const targetWidth = useSharedValue(0);
- const endColor = useSharedValue(effectiveColors);
- const startColor = useSharedValue(effectiveColors);
+ const endColor = useSharedValue(colorAtom);
+ const startColor = useSharedValue(colorAtom);
const widthProgress = useSharedValue(0);
const colorChangeProgress = useSharedValue(0);
+ const [settings] = useSettings();
const lightHapticFeedback = useHaptic("light");
const goToPlayer = useCallback(
- (q: string) => {
+ (q: string, bitrateValue: number | undefined) => {
router.push(`/player/direct-player?${q}`);
},
- [router],
+ [router]
);
const onPress = () => {
+ console.log("onpress");
if (!item) return;
lightHapticFeedback();
@@ -72,18 +78,18 @@ export const PlayButton: React.FC = ({
});
const queryString = queryParams.toString();
- goToPlayer(queryString);
+ goToPlayer(queryString, selectedOptions.bitrate?.value);
return;
};
const derivedTargetWidth = useDerivedValue(() => {
if (!item || !item.RunTimeTicks) return 0;
const userData = item.UserData;
- if (userData?.PlaybackPositionTicks) {
+ if (userData && userData.PlaybackPositionTicks) {
return userData.PlaybackPositionTicks > 0
? Math.max(
(userData.PlaybackPositionTicks / item.RunTimeTicks) * 100,
- MIN_PLAYBACK_WIDTH,
+ MIN_PLAYBACK_WIDTH
)
: 0;
}
@@ -100,11 +106,11 @@ export const PlayButton: React.FC = ({
easing: Easing.bezier(0.7, 0, 0.3, 1.0),
});
},
- [item],
+ [item]
);
useAnimatedReaction(
- () => effectiveColors,
+ () => colorAtom,
(newColor) => {
endColor.value = newColor;
colorChangeProgress.value = 0;
@@ -113,19 +119,19 @@ export const PlayButton: React.FC = ({
easing: Easing.bezier(0.9, 0, 0.31, 0.99),
});
},
- [effectiveColors],
+ [colorAtom]
);
useEffect(() => {
const timeout_2 = setTimeout(() => {
- startColor.value = effectiveColors;
+ startColor.value = colorAtom;
startWidth.value = targetWidth.value;
}, ANIMATION_DURATION);
return () => {
clearTimeout(timeout_2);
};
- }, [effectiveColors, item]);
+ }, [colorAtom, item]);
/**
* ANIMATED STYLES
@@ -134,7 +140,7 @@ export const PlayButton: React.FC = ({
backgroundColor: interpolateColor(
colorChangeProgress.value,
[0, 1],
- [startColor.value.primary, endColor.value.primary],
+ [startColor.value.primary, endColor.value.primary]
),
}));
@@ -142,7 +148,7 @@ export const PlayButton: React.FC = ({
backgroundColor: interpolateColor(
colorChangeProgress.value,
[0, 1],
- [startColor.value.primary, endColor.value.primary],
+ [startColor.value.primary, endColor.value.primary]
),
}));
@@ -150,7 +156,7 @@ export const PlayButton: React.FC = ({
width: `${interpolate(
widthProgress.value,
[0, 1],
- [startWidth.value, targetWidth.value],
+ [startWidth.value, targetWidth.value]
)}%`,
}));
@@ -158,7 +164,7 @@ export const PlayButton: React.FC = ({
color: interpolateColor(
colorChangeProgress.value,
[0, 1],
- [startColor.value.text, endColor.value.text],
+ [startColor.value.text, endColor.value.text]
),
}));
/**
@@ -167,13 +173,13 @@ export const PlayButton: React.FC = ({
return (
-
+
= ({
-
+
{runtimeTicksToMinutes(item?.RunTimeTicks)}
-
+
+ {settings?.openInVLC && (
+
+
+
+ )}
diff --git a/components/SubtitleTrackSelector.tsx b/components/SubtitleTrackSelector.tsx
index 6fca1955..ac5051e1 100644
--- a/components/SubtitleTrackSelector.tsx
+++ b/components/SubtitleTrackSelector.tsx
@@ -1,10 +1,10 @@
-import type { MediaSourceInfo } from "@jellyfin/sdk/lib/generated-client/models";
-import { useMemo, useState } from "react";
-import { useTranslation } from "react-i18next";
-import { Platform, TouchableOpacity, View } from "react-native";
import { tc } from "@/utils/textTools";
+import { MediaSourceInfo } from "@jellyfin/sdk/lib/generated-client/models";
+import { useMemo } from "react";
+import { Platform, TouchableOpacity, View } from "react-native";
+const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
import { Text } from "./common/Text";
-import { type OptionGroup, PlatformDropdown } from "./PlatformDropdown";
+import { useTranslation } from "react-i18next";
interface Props extends React.ComponentProps {
source?: MediaSourceInfo;
@@ -18,95 +18,76 @@ export const SubtitleTrackSelector: React.FC = ({
selected,
...props
}) => {
- const { t } = useTranslation();
- const [open, setOpen] = useState(false);
-
+ if (Platform.isTV) return null;
const subtitleStreams = useMemo(() => {
return source?.MediaStreams?.filter((x) => x.Type === "Subtitle");
}, [source]);
const selectedSubtitleSteam = useMemo(
() => subtitleStreams?.find((x) => x.Index === selected),
- [subtitleStreams, selected],
+ [subtitleStreams, selected]
);
- const optionGroups: OptionGroup[] = useMemo(() => {
- const options = [
- {
- type: "radio" as const,
- label: t("item_card.none"),
- value: -1,
- selected: selected === -1,
- onPress: () => onChange(-1),
- },
- ...(subtitleStreams?.map((subtitle, idx) => ({
- type: "radio" as const,
- label: subtitle.DisplayTitle || `Subtitle Stream ${idx + 1}`,
- value: subtitle.Index,
- selected: subtitle.Index === selected,
- onPress: () => onChange(subtitle.Index ?? -1),
- })) || []),
- ];
+ if (subtitleStreams?.length === 0) return null;
- return [
- {
- options,
- },
- ];
- }, [subtitleStreams, selected, t, onChange]);
-
- const handleOptionSelect = (optionId: string) => {
- if (optionId === "none") {
- onChange(-1);
- } else {
- const selectedStream = subtitleStreams?.find(
- (subtitle, idx) => `${subtitle.Index || idx}` === optionId,
- );
- if (
- selectedStream &&
- selectedStream.Index !== undefined &&
- selectedStream.Index !== null
- ) {
- onChange(selectedStream.Index);
- }
- }
- setOpen(false);
- };
-
- const trigger = (
-
-
- {t("item_card.subtitles")}
-
- setOpen(true)}
- >
-
- {selectedSubtitleSteam
- ? tc(selectedSubtitleSteam?.DisplayTitle, 7)
- : t("item_card.none")}
-
-
-
- );
-
- if (Platform.isTV || subtitleStreams?.length === 0) return null;
+ const { t } = useTranslation();
return (
-
+ >
+
+
+
+
+ {t("item_card.subtitles")}
+
+
+
+ {selectedSubtitleSteam
+ ? tc(selectedSubtitleSteam?.DisplayTitle, 7)
+ : t("item_card.none")}
+
+
+
+
+
+ Subtitle tracks
+ {
+ onChange(-1);
+ }}
+ >
+ None
+
+ {subtitleStreams?.map((subtitle, idx: number) => (
+ {
+ if (subtitle.Index !== undefined && subtitle.Index !== null)
+ onChange(subtitle.Index);
+ }}
+ >
+
+ {subtitle.DisplayTitle}
+
+
+ ))}
+
+
+
);
};
diff --git a/components/common/Text.tsx b/components/common/Text.tsx
index 739177d7..624b9da6 100644
--- a/components/common/Text.tsx
+++ b/components/common/Text.tsx
@@ -1,5 +1,12 @@
-import { Platform, Text as RNText, type TextProps } from "react-native";
-export function Text(props: TextProps) {
+import React from "react";
+import { Platform, TextProps } from "react-native";
+import { UITextView } from "react-native-uitextview";
+import { Text as RNText } from "react-native";
+export function Text(
+ props: TextProps & {
+ uiTextView?: boolean;
+ }
+) {
const { style, ...otherProps } = props;
if (Platform.isTV)
return (
@@ -9,12 +16,12 @@ export function Text(props: TextProps) {
{...otherProps}
/>
);
-
- return (
-
- );
+ else
+ return (
+
+ );
}
diff --git a/components/common/TouchableItemRouter.tsx b/components/common/TouchableItemRouter.tsx
index 2c85e094..db89e52d 100644
--- a/components/common/TouchableItemRouter.tsx
+++ b/components/common/TouchableItemRouter.tsx
@@ -1,33 +1,32 @@
-import { useActionSheet } from "@expo/react-native-action-sheet";
-import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
-import { useSegments } from "expo-router";
-import { type PropsWithChildren, useCallback } from "react";
-import { TouchableOpacity, type TouchableOpacityProps } from "react-native";
-import useRouter from "@/hooks/useAppRouter";
-import { useFavorite } from "@/hooks/useFavorite";
import { useMarkAsPlayed } from "@/hooks/useMarkAsPlayed";
-import { useDownload } from "@/providers/DownloadProvider";
-import { useOfflineMode } from "@/providers/OfflineModeProvider";
+import { useFavorite } from "@/hooks/useFavorite";
+import {
+ BaseItemDto,
+ BaseItemPerson,
+} from "@jellyfin/sdk/lib/generated-client/models";
+import { useRouter, useSegments } from "expo-router";
+import { PropsWithChildren, useCallback } from "react";
+import { TouchableOpacity, TouchableOpacityProps } from "react-native";
+import { useActionSheet } from "@expo/react-native-action-sheet";
interface Props extends TouchableOpacityProps {
item: BaseItemDto;
}
-export const itemRouter = (item: BaseItemDto, from: string) => {
+export const itemRouter = (
+ item: BaseItemDto | BaseItemPerson,
+ from: string
+) => {
if ("CollectionType" in item && item.CollectionType === "livetv") {
return `/(auth)/(tabs)/${from}/livetv`;
}
- if ("CollectionType" in item && item.CollectionType === "music") {
- return `/(auth)/(tabs)/(libraries)/music/${item.Id}`;
- }
-
if (item.Type === "Series") {
return `/(auth)/(tabs)/${from}/series/${item.Id}`;
}
- if (item.Type === "Person") {
- return `/(auth)/(tabs)/${from}/persons/${item.Id}`;
+ if (item.Type === "Person" || item.Type === "Actor") {
+ return `/(auth)/(tabs)/${from}/actors/${item.Id}`;
}
if (item.Type === "BoxSet") {
@@ -49,145 +48,28 @@ export const itemRouter = (item: BaseItemDto, from: string) => {
return `/(auth)/(tabs)/${from}/items/page?id=${item.Id}`;
};
-export const getItemNavigation = (item: BaseItemDto, _from: string) => {
- if ("CollectionType" in item && item.CollectionType === "livetv") {
- return {
- pathname: "/livetv" as const,
- };
- }
-
- if ("CollectionType" in item && item.CollectionType === "music") {
- return {
- pathname: "/music/[libraryId]" as const,
- params: { libraryId: item.Id! },
- };
- }
-
- if (item.Type === "Series") {
- return {
- pathname: "/series/[id]" as const,
- params: { id: item.Id! },
- };
- }
-
- if (item.Type === "Person") {
- return {
- pathname: "/persons/[personId]" as const,
- params: { personId: item.Id! },
- };
- }
-
- if (item.Type === "BoxSet" || item.Type === "UserView") {
- return {
- pathname: "/collections/[collectionId]" as const,
- params: { collectionId: item.Id! },
- };
- }
-
- if (item.Type === "CollectionFolder") {
- return {
- pathname: "/[libraryId]" as const,
- params: { libraryId: item.Id! },
- };
- }
-
- // Music types - use shared routes for proper back navigation
- if (item.Type === "MusicArtist") {
- return {
- pathname: "/music/artist/[artistId]" as const,
- params: { artistId: item.Id! },
- };
- }
-
- if (item.Type === "MusicAlbum") {
- return {
- pathname: "/music/album/[albumId]" as const,
- params: { albumId: item.Id! },
- };
- }
-
- if (item.Type === "Audio") {
- // Navigate to the album if available, otherwise to the item page
- if (item.AlbumId) {
- return {
- pathname: "/music/album/[albumId]" as const,
- params: { albumId: item.AlbumId },
- };
- }
- return {
- pathname: "/items/page" as const,
- params: { id: item.Id! },
- };
- }
-
- if (item.Type === "Playlist") {
- return {
- pathname: "/music/playlist/[playlistId]" as const,
- params: { playlistId: item.Id! },
- };
- }
-
- // Default case - items page
- return {
- pathname: "/items/page" as const,
- params: { id: item.Id! },
- };
-};
-
export const TouchableItemRouter: React.FC> = ({
item,
children,
...props
}) => {
+ const router = useRouter();
const segments = useSegments();
const { showActionSheetWithOptions } = useActionSheet();
const markAsPlayedStatus = useMarkAsPlayed([item]);
const { isFavorite, toggleFavorite } = useFavorite(item);
- const router = useRouter();
- const isOffline = useOfflineMode();
- const { deleteFile } = useDownload();
-
- const from = (segments as string[])[2] || "(home)";
-
- const handlePress = useCallback(() => {
- // Force music libraries to navigate via the explicit string route.
- // This avoids losing the dynamic [libraryId] param when going through a nested navigator.
- if ("CollectionType" in item && item.CollectionType === "music") {
- router.push(itemRouter(item, from) as any);
- return;
- }
-
- const navigation = getItemNavigation(item, from);
- router.push(navigation as any);
- }, [from, item, router]);
+
+ const from = segments[2];
const showActionSheet = useCallback(() => {
- if (
- !(
- item.Type === "Movie" ||
- item.Type === "Episode" ||
- item.Type === "Series"
- )
- )
- return;
-
- const options: string[] = [
- "Mark as Played",
- "Mark as Not Played",
- isFavorite ? "Unmark as Favorite" : "Mark as Favorite",
- ...(isOffline ? ["Delete Download"] : []),
- "Cancel",
- ];
- const cancelButtonIndex = options.length - 1;
- const destructiveButtonIndex = isOffline
- ? cancelButtonIndex - 1
- : undefined;
+ if (!(item.Type === "Movie" || item.Type === "Episode" || item.Type === "Series")) return;
+ const options = ["Mark as Played", "Mark as Not Played", isFavorite ? "Unmark as Favorite" : "Mark as Favorite", "Cancel"];
+ const cancelButtonIndex = 3;
showActionSheetWithOptions(
{
options,
cancelButtonIndex,
- destructiveButtonIndex,
},
async (selectedIndex) => {
if (selectedIndex === 0) {
@@ -195,38 +77,29 @@ export const TouchableItemRouter: React.FC> = ({
} else if (selectedIndex === 1) {
await markAsPlayedStatus(false);
} else if (selectedIndex === 2) {
- toggleFavorite();
- } else if (isOffline && selectedIndex === 3 && item.Id) {
- deleteFile(item.Id);
+ toggleFavorite()
}
- },
+ }
);
- }, [
- showActionSheetWithOptions,
- isFavorite,
- markAsPlayedStatus,
- toggleFavorite,
- isOffline,
- deleteFile,
- item.Id,
- ]);
+ }, [showActionSheetWithOptions, isFavorite, markAsPlayedStatus]);
if (
from === "(home)" ||
from === "(search)" ||
from === "(libraries)" ||
- from === "(favorites)" ||
- from === "(watchlists)"
+ from === "(favorites)"
)
return (
{
+ const url = itemRouter(item, from);
+ // @ts-expect-error
+ router.push(url);
+ }}
{...props}
>
{children}
);
-
- return null;
};
diff --git a/components/downloads/ActiveDownloads.tsx b/components/downloads/ActiveDownloads.tsx
index 7835cdf1..773efab4 100644
--- a/components/downloads/ActiveDownloads.tsx
+++ b/components/downloads/ActiveDownloads.tsx
@@ -1,40 +1,205 @@
-import { t } from "i18next";
-import { View, type ViewProps } from "react-native";
import { Text } from "@/components/common/Text";
import { useDownload } from "@/providers/DownloadProvider";
-import { JobStatus } from "@/providers/Downloads/types";
-import { DownloadCard } from "./DownloadCard";
+import { DownloadMethod, useSettings } from "@/utils/atoms/settings";
+import { storage } from "@/utils/mmkv";
+import { JobStatus } from "@/utils/optimize-server";
+import { formatTimeString } from "@/utils/time";
+import { Ionicons } from "@expo/vector-icons";
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { Image } from "expo-image";
+import { useRouter } from "expo-router";
+import { t } from "i18next";
+import { useMemo } from "react";
+import {
+ ActivityIndicator,
+ Platform,
+ TouchableOpacity,
+ TouchableOpacityProps,
+ View,
+ ViewProps,
+} from "react-native";
+import { toast } from "sonner-native";
+import { Button } from "../Button";
+const BackGroundDownloader = !Platform.isTV
+ ? require("@kesha-antonov/react-native-background-downloader")
+ : null;
+const FFmpegKitProvider = !Platform.isTV
+ ? require("ffmpeg-kit-react-native")
+ : null;
-interface ActiveDownloadsProps extends ViewProps {}
+interface Props extends ViewProps {}
-export default function ActiveDownloads({ ...props }: ActiveDownloadsProps) {
+export const ActiveDownloads: React.FC = ({ ...props }) => {
const { processes } = useDownload();
-
- // Filter out any invalid processes before rendering
- const validProcesses = processes?.filter((p) => p?.item?.Id) || [];
-
- if (validProcesses.length === 0)
+ if (processes?.length === 0)
return (
-
-
+
+
{t("home.downloads.active_download")}
-
+
{t("home.downloads.no_active_downloads")}
);
return (
-
-
+
+
{t("home.downloads.active_downloads")}
-
- {validProcesses.map((p: JobStatus) => (
-
+
+ {processes?.map((p: JobStatus) => (
+
))}
);
+};
+
+interface DownloadCardProps extends TouchableOpacityProps {
+ process: JobStatus;
}
+
+const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
+ const { processes, startDownload } = useDownload();
+ const router = useRouter();
+ const { removeProcess, setProcesses } = useDownload();
+ const [settings] = useSettings();
+ const queryClient = useQueryClient();
+
+ const cancelJobMutation = useMutation({
+ mutationFn: async (id: string) => {
+ if (!process) throw new Error("No active download");
+
+ if (settings?.downloadMethod === DownloadMethod.Optimized) {
+ try {
+ const tasks = await BackGroundDownloader.checkForExistingDownloads();
+ for (const task of tasks) {
+ if (task.id === id) {
+ task.stop();
+ }
+ }
+ } catch (e) {
+ throw e;
+ } finally {
+ await removeProcess(id);
+ await queryClient.refetchQueries({ queryKey: ["jobs"] });
+ }
+ } else {
+ FFmpegKitProvider.FFmpegKit.cancel(Number(id));
+ setProcesses((prev: any[]) =>
+ prev.filter((p: { id: string }) => p.id !== id)
+ );
+ }
+ },
+ onSuccess: () => {
+ toast.success(t("home.downloads.toasts.download_cancelled"));
+ },
+ onError: (e) => {
+ console.error(e);
+ toast.error(t("home.downloads.toasts.could_not_cancel_download"));
+ },
+ });
+
+ const eta = (p: JobStatus) => {
+ if (!p.speed || !p.progress) return null;
+
+ const length = p?.item?.RunTimeTicks || 0;
+ const timeLeft = (length - length * (p.progress / 100)) / p.speed;
+ return formatTimeString(timeLeft, "tick");
+ };
+
+ const base64Image = useMemo(() => {
+ return storage.getString(process.item.Id!);
+ }, []);
+
+ return (
+ router.push(`/(auth)/items/page?id=${process.item.Id}`)}
+ className="relative bg-neutral-900 border border-neutral-800 rounded-2xl overflow-hidden"
+ {...props}
+ >
+ {(process.status === "optimizing" ||
+ process.status === "downloading") && (
+
+ )}
+
+
+ {base64Image && (
+
+
+
+ )}
+
+ {process.item.Type}
+ {process.item.Name}
+
+ {process.item.ProductionYear}
+
+
+ {process.progress === 0 ? (
+
+ ) : (
+ {process.progress.toFixed(0)}%
+ )}
+ {process.speed && (
+ {process.speed?.toFixed(2)}x
+ )}
+ {eta(process) && (
+
+ {t("home.downloads.eta", { eta: eta(process) })}
+
+ )}
+
+
+
+ {process.status}
+
+
+ cancelJobMutation.mutate(process.id)}
+ className="ml-auto"
+ >
+ {cancelJobMutation.isPending ? (
+
+ ) : (
+
+ )}
+
+
+ {process.status === "completed" && (
+
+ {
+ startDownload(process);
+ }}
+ className="w-full"
+ >
+ Download now
+
+
+ )}
+
+
+ );
+};
diff --git a/components/jellyseerr/DetailFacts.tsx b/components/jellyseerr/DetailFacts.tsx
index 0250920f..e6ef013a 100644
--- a/components/jellyseerr/DetailFacts.tsx
+++ b/components/jellyseerr/DetailFacts.tsx
@@ -1,15 +1,15 @@
-import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
-import { uniqBy } from "lodash";
-import { useMemo } from "react";
-import { useTranslation } from "react-i18next";
-import { View, type ViewProps } from "react-native";
-import CountryFlag from "react-native-country-flag";
+import { View, ViewProps } from "react-native";
+import { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
+import { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
import { Text } from "@/components/common/Text";
+import { useMemo } from "react";
import { useJellyseerr } from "@/hooks/useJellyseerr";
+import { uniqBy } from "lodash";
+import { TmdbRelease } from "@/utils/jellyseerr/server/api/themoviedb/interfaces";
+import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
+import CountryFlag from "react-native-country-flag";
import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants";
-import type { TmdbRelease } from "@/utils/jellyseerr/server/api/themoviedb/interfaces";
-import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
-import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
+import { useTranslation } from "react-i18next";
interface Release {
certification: string;
@@ -30,12 +30,12 @@ const Facts: React.FC<
> = ({ title, facts, ...props }) =>
facts &&
facts?.length > 0 && (
-
- {title}
+
+ {title}
-
+
{facts.map((f, idx) =>
- typeof f === "string" ? {f} : f,
+ typeof f === "string" ? {f} : f
)}
@@ -50,16 +50,15 @@ const Fact: React.FC<{ title: string; fact?: string | null } & ViewProps> = ({
const DetailFacts: React.FC<
{ details?: MovieDetails | TvDetails } & ViewProps
> = ({ details, className, ...props }) => {
- const { jellyseerrRegion: region, jellyseerrLocale: locale } =
- useJellyseerr();
+ const { jellyseerrUser, jellyseerrRegion: region, jellyseerrLocale: locale } = useJellyseerr();
const { t } = useTranslation();
const releases = useMemo(
() =>
(details as MovieDetails)?.releases?.results.find(
- (r: TmdbRelease) => r.iso_3166_1 === region,
+ (r: TmdbRelease) => r.iso_3166_1 === region
)?.release_dates as TmdbRelease["release_dates"],
- [details],
+ [details]
);
// Release date types:
@@ -73,9 +72,9 @@ const DetailFacts: React.FC<
() =>
uniqBy(
releases?.filter((r: Release) => r.type > 2 && r.type < 6),
- "type",
+ "type"
),
- [releases],
+ [releases]
);
const firstAirDate = useMemo(() => {
@@ -83,7 +82,7 @@ const DetailFacts: React.FC<
if (firstAirDate) {
return new Date(firstAirDate).toLocaleDateString(
`${locale}-${region}`,
- dateOpts,
+ dateOpts
);
}
}, [details]);
@@ -94,7 +93,7 @@ const DetailFacts: React.FC<
if (nextAirDate && firstAirDate !== nextAirDate) {
return new Date(nextAirDate).toLocaleDateString(
`${locale}-${region}`,
- dateOpts,
+ dateOpts
);
}
}, [details]);
@@ -103,26 +102,26 @@ const DetailFacts: React.FC<
() =>
(details as MovieDetails)?.revenue?.toLocaleString?.(
`${locale}-${region}`,
- { style: "currency", currency: "USD" },
+ { style: "currency", currency: "USD" }
),
- [details],
+ [details]
);
const budget = useMemo(
() =>
(details as MovieDetails)?.budget?.toLocaleString?.(
`${locale}-${region}`,
- { style: "currency", currency: "USD" },
+ { style: "currency", currency: "USD" }
),
- [details],
+ [details]
);
const streamingProviders = useMemo(
() =>
details?.watchProviders?.find(
- (provider) => provider.iso_3166_1 === region,
+ (provider) => provider.iso_3166_1 === region
)?.flatrate,
- [details],
+ [details]
);
const networks = useMemo(() => (details as TvDetails)?.networks, [details]);
@@ -130,15 +129,15 @@ const DetailFacts: React.FC<
const spokenLanguage = useMemo(
() =>
details?.spokenLanguages.find(
- (lng) => lng.iso_639_1 === details.originalLanguage,
+ (lng) => lng.iso_639_1 === details.originalLanguage
)?.name,
- [details],
+ [details]
);
return (
details && (
-
- {t("jellyseerr.details")}
+
+ {t("jellyseerr.details")}
{details.keywords.some(
- (keyword) => keyword.id === ANIME_KEYWORD_ID,
- ) && }
+ (keyword) => keyword.id === ANIME_KEYWORD_ID
+ ) && }
(
-
+
{r.type === 3 ? (
// Theatrical
-
+
) : r.type === 4 ? (
// Digital
-
+
) : (
// Physical
)}
{new Date(r.release_date).toLocaleDateString(
`${locale}-${region}`,
- dateOpts,
+ dateOpts
)}
@@ -182,14 +181,11 @@ const DetailFacts: React.FC<
-
+
(
-
+
{n.name}
@@ -198,13 +194,10 @@ const DetailFacts: React.FC<
n.name,
+ (n) => n.name
)}
/>
- n.name)}
- />
+ n.name)} />
s.name)}
diff --git a/components/jellyseerr/discover/GenreSlide.tsx b/components/jellyseerr/discover/GenreSlide.tsx
index 31248540..3ec7f733 100644
--- a/components/jellyseerr/discover/GenreSlide.tsx
+++ b/components/jellyseerr/discover/GenreSlide.tsx
@@ -1,38 +1,35 @@
-import { useQuery } from "@tanstack/react-query";
-import { useSegments } from "expo-router";
-import type React from "react";
-import { useCallback } from "react";
-import { TouchableOpacity, type ViewProps } from "react-native";
import GenericSlideCard from "@/components/jellyseerr/discover/GenericSlideCard";
-import Slide, { type SlideProps } from "@/components/jellyseerr/discover/Slide";
-import useRouter from "@/hooks/useAppRouter";
+import Slide, { SlideProps } from "@/components/jellyseerr/discover/Slide";
import { Endpoints, useJellyseerr } from "@/hooks/useJellyseerr";
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
-import type { GenreSliderItem } from "@/utils/jellyseerr/server/interfaces/api/discoverInterfaces";
+import { GenreSliderItem } from "@/utils/jellyseerr/server/interfaces/api/discoverInterfaces";
import { genreColorMap } from "@/utils/jellyseerr/src/components/Discover/constants";
+import { useQuery } from "@tanstack/react-query";
+import { router, useSegments } from "expo-router";
+import React, { useCallback } from "react";
+import { TouchableOpacity, ViewProps } from "react-native";
const GenreSlide: React.FC = ({ slide, ...props }) => {
const segments = useSegments();
const { jellyseerrApi } = useJellyseerr();
- const router = useRouter();
- const from = (segments as string[])[2] || "(home)";
+ const from = segments[2];
const navigate = useCallback(
(genre: GenreSliderItem) =>
router.push({
- pathname: `/(auth)/(tabs)/${from}/jellyseerr/genre/${genre.id}` as any,
+ pathname: `/(auth)/(tabs)/${from}/jellyseerr/genre/${genre.id}`,
params: { type: slide.type, name: genre.name },
}),
- [slide],
+ [slide]
);
- const { data } = useQuery({
+ const { data, isFetching, isLoading } = useQuery({
queryKey: ["jellyseerr", "discover", slide.type, slide.id],
queryFn: async () => {
return jellyseerrApi?.getGenreSliders(
- slide.type === DiscoverSliderType.MOVIE_GENRES
+ slide.type == DiscoverSliderType.MOVIE_GENRES
? Endpoints.MOVIE
- : Endpoints.TV,
+ : Endpoints.TV
);
},
enabled: !!jellyseerrApi,
@@ -45,19 +42,19 @@ const GenreSlide: React.FC = ({ slide, ...props }) => {
slide={slide}
data={data}
keyExtractor={(item) => item.id.toString()}
- renderItem={(item, _index) => (
- navigate(item)}>
+ renderItem={(item, index) => (
+ navigate(item)}>
diff --git a/components/list/ListGroup.tsx b/components/list/ListGroup.tsx
index b9752bac..03f218d1 100644
--- a/components/list/ListGroup.tsx
+++ b/components/list/ListGroup.tsx
@@ -1,11 +1,12 @@
import {
+ PropsWithChildren,
Children,
- cloneElement,
isValidElement,
- type PropsWithChildren,
- type ReactElement,
+ cloneElement,
+ ReactElement,
} from "react";
-import { StyleSheet, View, type ViewProps, type ViewStyle } from "react-native";
+import { StyleSheet, View, ViewProps, ViewStyle } from "react-native";
+import { ListItem } from "./ListItem";
import { Text } from "../common/Text";
interface Props extends ViewProps {
@@ -23,12 +24,12 @@ export const ListGroup: React.FC> = ({
return (
-
+
{title}
{Children.map(childrenArray, (child, index) => {
if (isValidElement<{ style?: ViewStyle }>(child)) {
@@ -37,14 +38,14 @@ export const ListGroup: React.FC> = ({
child.props.style,
index < childrenArray.length - 1
? styles.borderBottom
- : undefined,
+ : undefined
),
});
}
return child;
})}
- {description && {description} }
+ {description && {description} }
);
};
diff --git a/components/list/ListItem.tsx b/components/list/ListItem.tsx
index fed62315..ea7774a4 100644
--- a/components/list/ListItem.tsx
+++ b/components/list/ListItem.tsx
@@ -1,12 +1,15 @@
import { Ionicons } from "@expo/vector-icons";
-import type { PropsWithChildren, ReactNode } from "react";
-import { TouchableOpacity, View, type ViewProps } from "react-native";
+import { PropsWithChildren, ReactNode } from "react";
+import {
+ TouchableOpacity,
+ TouchableOpacityProps,
+ View,
+ ViewProps,
+} from "react-native";
import { Text } from "../common/Text";
-interface Props extends ViewProps {
+interface Props extends TouchableOpacityProps, ViewProps {
title?: string | null | undefined;
- subtitle?: string | null | undefined;
- subtitleColor?: "default" | "red";
value?: string | null | undefined;
children?: ReactNode;
iconAfter?: ReactNode;
@@ -14,13 +17,10 @@ interface Props extends ViewProps {
showArrow?: boolean;
textColor?: "default" | "blue" | "red";
onPress?: () => void;
- disabled?: boolean;
- disabledByAdmin?: boolean;
}
export const ListItem: React.FC> = ({
title,
- subtitle,
value,
iconAfter,
children,
@@ -29,23 +29,20 @@ export const ListItem: React.FC> = ({
textColor = "default",
onPress,
disabled = false,
- disabledByAdmin = false,
- ...viewProps
+ ...props
}) => {
- const effectiveSubtitle = disabledByAdmin ? "Disabled by admin" : subtitle;
- const isDisabled = disabled || disabledByAdmin;
if (onPress)
return (
> = ({
);
return (
> = ({
const ListItemContent = ({
title,
- subtitle,
- subtitleColor,
textColor,
icon,
value,
showArrow,
iconAfter,
children,
+ ...props
}: Props) => {
return (
<>
-
+
{icon && (
-
-
+
+
)}
-
-
- {title}
-
- {subtitle && (
-
- {subtitle}
-
- )}
-
+
+ {title}
+
{value && (
-
-
+
+
{value}
)}
- {children && {children} }
+ {children && {children} }
{showArrow && (
-
+
)}
diff --git a/components/series/JellyseerrSeasons.tsx b/components/series/JellyseerrSeasons.tsx
index 3b130683..a1a1fb7c 100644
--- a/components/series/JellyseerrSeasons.tsx
+++ b/components/series/JellyseerrSeasons.tsx
@@ -1,34 +1,30 @@
-import { Ionicons } from "@expo/vector-icons";
-import { FlashList } from "@shopify/flash-list";
-import {
- type QueryObserverResult,
- type RefetchOptions,
- useQuery,
-} from "@tanstack/react-query";
-import { Image } from "expo-image";
-import { t } from "i18next";
-import { orderBy } from "lodash";
-import type React from "react";
-import { useCallback, useMemo, useState } from "react";
-import { Alert, TouchableOpacity, View } from "react-native";
-import { HorizontalScroll } from "@/components/common/HorizontalScroll";
import { Text } from "@/components/common/Text";
+import React, { useCallback, useMemo, useState } from "react";
+import { Alert, TouchableOpacity, View } from "react-native";
+import { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
+import { FlashList } from "@shopify/flash-list";
+import { orderBy } from "lodash";
import { Tags } from "@/components/GenreTags";
-import { dateOpts } from "@/components/jellyseerr/DetailFacts";
-import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
-import { RoundButton } from "@/components/RoundButton";
-import { useJellyseerr } from "@/hooks/useJellyseerr";
+import Season from "@/utils/jellyseerr/server/entity/Season";
import {
MediaStatus,
MediaType,
} from "@/utils/jellyseerr/server/constants/media";
-import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
-import type Season from "@/utils/jellyseerr/server/entity/Season";
-import type { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
-import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
-import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
+import { Ionicons } from "@expo/vector-icons";
+import { RoundButton } from "@/components/RoundButton";
+import { useJellyseerr } from "@/hooks/useJellyseerr";
+import { TvResult } from "@/utils/jellyseerr/server/models/Search";
+import {QueryObserverResult, RefetchOptions, useQuery} from "@tanstack/react-query";
+import { HorizontalScroll } from "@/components/common/HorrizontalScroll";
+import { Image } from "expo-image";
+import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
import { Loader } from "../Loader";
+import { t } from "i18next";
+import {MovieDetails} from "@/utils/jellyseerr/server/models/Movie";
+import {MediaRequestBody} from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
+import {textShadowStyle} from "@/components/jellyseerr/discover/GenericSlideCard";
+import {dateOpts} from "@/components/jellyseerr/DetailFacts";
const JellyseerrSeasonEpisodes: React.FC<{
details: TvDetails;
@@ -47,8 +43,9 @@ const JellyseerrSeasonEpisodes: React.FC<{
horizontal
loading={isLoading}
showsHorizontalScrollIndicator={false}
+ estimatedItemSize={50}
data={seasonWithEpisodes?.episodes}
- keyExtractor={(item) => item.id.toString()}
+ keyExtractor={(item) => item.id}
renderItem={(item, index) => (
)}
@@ -56,27 +53,27 @@ const JellyseerrSeasonEpisodes: React.FC<{
);
};
-const RenderItem = ({ item }: any) => {
- const {
- jellyseerrApi,
- jellyseerrRegion: region,
- jellyseerrLocale: locale,
- } = useJellyseerr();
+const RenderItem = ({ item, index }: any) => {
+ const { jellyseerrApi, jellyseerrRegion: region, jellyseerrLocale: locale } = useJellyseerr();
const [imageError, setImageError] = useState(false);
const upcomingAirDate = useMemo(() => {
const airDate = item.airDate;
if (airDate) {
- const airDateObj = new Date(airDate);
+ let airDateObj = new Date(airDate);
+
if (new Date() < airDateObj) {
- return airDateObj.toLocaleDateString(`${locale}-${region}`, dateOpts);
+ return airDateObj.toLocaleDateString(
+ `${locale}-${region}`,
+ dateOpts
+ );
}
}
- }, [item, locale, region]);
+ }, [item]);
return (
-
-
+
+
{!imageError ? (
<>
{
uri: jellyseerrApi?.imageProxy(item.stillPath),
}}
cachePolicy={"memory-disk"}
- contentFit='cover'
- className='w-full h-full'
- onError={(_e) => {
+ contentFit="cover"
+ className="w-full h-full"
+ onError={(e) => {
setImageError(true);
}}
/>
{upcomingAirDate && (
-
-
-
+
+
+
{upcomingAirDate}
@@ -106,25 +100,26 @@ const RenderItem = ({ item }: any) => {
)}
>
) : (
-
+
)}
-
-
+
+
{item.name}
-
+
{`S${item.seasonNumber}:E${item.episodeNumber}`}
-
+
+
{item.overview}
@@ -133,51 +128,54 @@ const RenderItem = ({ item }: any) => {
const JellyseerrSeasons: React.FC<{
isLoading: boolean;
+ result?: TvResult;
details?: TvDetails;
- hasAdvancedRequest?: boolean;
+ hasAdvancedRequest?: boolean,
onAdvancedRequest?: (data: MediaRequestBody) => void;
- refetch: (
- options?: RefetchOptions | undefined,
- ) => Promise<
- QueryObserverResult
- >;
+ refetch: (options?: (RefetchOptions | undefined)) => Promise>;
}> = ({
isLoading,
+ result,
details,
refetch,
hasAdvancedRequest,
onAdvancedRequest,
}) => {
+ if (!details) return null;
+
const { jellyseerrApi, requestMedia } = useJellyseerr();
- const [seasonStates, setSeasonStates] = useState<{ [key: number]: boolean }>(
- {},
- );
+ const [seasonStates, setSeasonStates] = useState<{
+ [key: number]: boolean;
+ }>();
const seasons = useMemo(() => {
- if (!details) return [];
- const mediaInfoSeasons = details.mediaInfo?.seasons?.filter(
- (s: Season) => s.seasonNumber !== 0,
+ const mediaInfoSeasons = details?.mediaInfo?.seasons?.filter(
+ (s: Season) => s.seasonNumber !== 0
);
- const requestedSeasons =
- details.mediaInfo?.requests?.flatMap((r: MediaRequest) => r.seasons) ??
- [];
- return (
- details.seasons?.map((season) => ({
+ const requestedSeasons = details?.mediaInfo?.requests?.flatMap(
+ (r: MediaRequest) => r.seasons
+ );
+ return details.seasons?.map((season) => {
+ return {
...season,
status:
+ // What our library status is
mediaInfoSeasons?.find(
(mediaSeason: Season) =>
- mediaSeason.seasonNumber === season.seasonNumber,
+ mediaSeason.seasonNumber === season.seasonNumber
)?.status ??
+ // What our request status is
requestedSeasons?.find(
- (s: Season) => s.seasonNumber === season.seasonNumber,
+ (s: Season) => s.seasonNumber === season.seasonNumber
)?.status ??
+ // Otherwise set it as unknown
MediaStatus.UNKNOWN,
- })) ?? []
- );
+ };
+ });
}, [details]);
+
const allSeasonsAvailable = useMemo(
- () => seasons.every((season) => season.status === MediaStatus.AVAILABLE),
- [seasons],
+ () => seasons?.every((season) => season.status === MediaStatus.AVAILABLE),
+ [seasons]
);
const requestAll = useCallback(() => {
@@ -188,74 +186,59 @@ const JellyseerrSeasons: React.FC<{
tvdbId: details.externalIds?.tvdbId,
seasons: seasons
.filter(
- (s) => s.status === MediaStatus.UNKNOWN && s.seasonNumber !== 0,
+ (s) => s.status === MediaStatus.UNKNOWN && s.seasonNumber !== 0
)
.map((s) => s.seasonNumber),
- };
- if (hasAdvancedRequest) {
- return onAdvancedRequest?.(body);
}
- requestMedia(details.name, body, refetch);
+
+ if (hasAdvancedRequest) {
+ return onAdvancedRequest?.(body)
+ }
+
+ requestMedia(result?.name!!, body, refetch);
}
- }, [
- jellyseerrApi,
- seasons,
- details,
- hasAdvancedRequest,
- onAdvancedRequest,
- requestMedia,
- refetch,
- ]);
+ }, [jellyseerrApi, seasons, details, hasAdvancedRequest, onAdvancedRequest]);
const promptRequestAll = useCallback(
() =>
- Alert.alert(
- t("jellyseerr.confirm"),
- t("jellyseerr.are_you_sure_you_want_to_request_all_seasons"),
- [
- {
- text: t("jellyseerr.cancel"),
- style: "cancel",
- },
- {
- text: t("jellyseerr.yes"),
- onPress: requestAll,
- },
- ],
- ),
- [requestAll],
+ Alert.alert(t("jellyseerr.confirm"), t("jellyseerr.are_you_sure_you_want_to_request_all_seasons"), [
+ {
+ text: t("jellyseerr.cancel"),
+ style: "cancel",
+ },
+ {
+ text: t("jellyseerr.yes"),
+ onPress: requestAll,
+ },
+ ]),
+ [requestAll]
);
- const requestSeason = useCallback(
- async (canRequest: boolean, seasonNumber: number) => {
- if (canRequest && details) {
- const body: MediaRequestBody = {
- mediaId: details.id,
- mediaType: MediaType.TV,
- tvdbId: details.externalIds?.tvdbId,
- seasons: [seasonNumber],
- };
- if (hasAdvancedRequest) {
- return onAdvancedRequest?.(body);
- }
- requestMedia(`${details.name}, Season ${seasonNumber}`, body, refetch);
+ const requestSeason = useCallback(async (canRequest: Boolean, seasonNumber: number) => {
+ if (canRequest) {
+ const body: MediaRequestBody = {
+ mediaId: details.id,
+ mediaType: MediaType.TV,
+ tvdbId: details.externalIds?.tvdbId,
+ seasons: [seasonNumber],
}
- },
- [requestMedia, hasAdvancedRequest, onAdvancedRequest, refetch, details],
- );
- if (!details) return null;
+ if (hasAdvancedRequest) {
+ return onAdvancedRequest?.(body)
+ }
+
+ requestMedia(`${result?.name!!}, Season ${seasonNumber}`, body, refetch);
+ }
+ }, [requestMedia, hasAdvancedRequest, onAdvancedRequest]);
if (isLoading)
return (
-
-
- {t("item_card.seasons")}
-
+
+ {t("item_card.seasons")}
{!allSeasonsAvailable && (
-
-
+
+
)}
@@ -266,23 +249,22 @@ const JellyseerrSeasons: React.FC<{
return (
s.seasonNumber !== 0),
+ details.seasons.filter((s) => s.seasonNumber !== 0),
"seasonNumber",
- "desc",
+ "desc"
)}
ListHeaderComponent={() => (
-
-
- {t("item_card.seasons")}
-
+
+ {t("item_card.seasons")}
{!allSeasonsAvailable && (
-
-
+
+
)}
)}
- ItemSeparatorComponent={() => }
+ ItemSeparatorComponent={() => }
+ estimatedItemSize={250}
renderItem={({ item: season }) => (
<>
{[0].map(() => {
- const canRequest = season.status === MediaStatus.UNKNOWN;
+ const canRequest =
+ seasons?.find((s) => s.seasonNumber === season.seasonNumber)
+ ?.status === MediaStatus.UNKNOWN;
return (
- requestSeason(canRequest, season.seasonNumber)
- }
+ onPress={() => requestSeason(canRequest, season.seasonNumber)}
className={canRequest ? "bg-gray-700/40" : undefined}
- mediaStatus={season.status}
+ mediaStatus={
+ seasons?.find(
+ (s) => s.seasonNumber === season.seasonNumber
+ )?.status
+ }
showRequestIcon={canRequest}
/>
);
diff --git a/components/settings/ChromecastSettings.tsx b/components/settings/ChromecastSettings.tsx
index 4da06332..63cddac9 100644
--- a/components/settings/ChromecastSettings.tsx
+++ b/components/settings/ChromecastSettings.tsx
@@ -1,10 +1,10 @@
import { Switch, View } from "react-native";
-import { useSettings } from "@/utils/atoms/settings";
import { ListGroup } from "../list/ListGroup";
+import { useSettings } from "@/utils/atoms/settings";
import { ListItem } from "../list/ListItem";
export const ChromecastSettings: React.FC = ({ ...props }) => {
- const { settings, updateSettings } = useSettings();
+ const [settings, updateSettings] = useSettings();
return (
diff --git a/components/settings/Dashboard.tsx b/components/settings/Dashboard.tsx
index d41de008..1ffe57a1 100644
--- a/components/settings/Dashboard.tsx
+++ b/components/settings/Dashboard.tsx
@@ -1,14 +1,15 @@
-import { useTranslation } from "react-i18next";
-import { View } from "react-native";
-import useRouter from "@/hooks/useAppRouter";
-import { useSessions, type useSessionsProps } from "@/hooks/useSessions";
import { useSettings } from "@/utils/atoms/settings";
+import { useRouter } from "expo-router";
+import React from "react";
+import { View } from "react-native";
import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem";
+import { useTranslation } from "react-i18next";
+import { useSessions, useSessionsProps } from "@/hooks/useSessions";
export const Dashboard = () => {
- const { settings } = useSettings();
- const { sessions = [] } = useSessions({} as useSessionsProps);
+ const [settings, updateSettings] = useSettings();
+ const { sessions = [], isLoading } = useSessions({} as useSessionsProps);
const router = useRouter();
const { t } = useTranslation();
@@ -16,9 +17,9 @@ export const Dashboard = () => {
if (!settings) return null;
return (
-
+
router.push("/settings/dashboard/sessions")}
title={t("home.settings.dashboard.sessions_title")}
showArrow
diff --git a/components/settings/DownloadSettings.tsx b/components/settings/DownloadSettings.tsx
index 3a0017ac..0d1df837 100644
--- a/components/settings/DownloadSettings.tsx
+++ b/components/settings/DownloadSettings.tsx
@@ -1,3 +1,143 @@
-export default function DownloadSettings() {
- return null;
+import { Stepper } from "@/components/inputs/Stepper";
+import { useDownload } from "@/providers/DownloadProvider";
+import { DownloadMethod, Settings, useSettings } from "@/utils/atoms/settings";
+import { Ionicons } from "@expo/vector-icons";
+import { useQueryClient } from "@tanstack/react-query";
+import { useRouter } from "expo-router";
+import React, { useMemo } from "react";
+import { Platform, Switch, TouchableOpacity } from "react-native";
+const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
+import { Text } from "../common/Text";
+import { ListGroup } from "../list/ListGroup";
+import { ListItem } from "../list/ListItem";
+import { useTranslation } from "react-i18next";
+import DisabledSetting from "@/components/settings/DisabledSetting";
+
+export default function DownloadSettings({ ...props }) {
+ const [settings, updateSettings, pluginSettings] = useSettings();
+ const { setProcesses } = useDownload();
+ const router = useRouter();
+ const queryClient = useQueryClient();
+ const { t } = useTranslation();
+
+ const allDisabled = useMemo(
+ () =>
+ pluginSettings?.downloadMethod?.locked === true &&
+ pluginSettings?.remuxConcurrentLimit?.locked === true &&
+ pluginSettings?.autoDownload.locked === true,
+ [pluginSettings]
+ );
+
+ if (!settings) return null;
+
+ return (
+
+
+
+
+
+
+
+ {settings.downloadMethod === DownloadMethod.Remux
+ ? t("home.settings.downloads.default")
+ : t("home.settings.downloads.optimized")}
+
+
+
+
+
+
+ {t("home.settings.downloads.download_method")}
+
+ {
+ updateSettings({ downloadMethod: DownloadMethod.Remux });
+ setProcesses([]);
+ }}
+ >
+
+ {t("home.settings.downloads.default")}
+
+
+ {
+ updateSettings({ downloadMethod: DownloadMethod.Optimized });
+ setProcesses([]);
+ queryClient.invalidateQueries({ queryKey: ["search"] });
+ }}
+ >
+
+ {t("home.settings.downloads.optimized")}
+
+
+
+
+
+
+
+
+ updateSettings({
+ remuxConcurrentLimit: value as Settings["remuxConcurrentLimit"],
+ })
+ }
+ />
+
+
+
+ updateSettings({ autoDownload: value })}
+ />
+
+
+ router.push("/settings/optimized-server/page")}
+ showArrow
+ title={t("home.settings.downloads.optimized_versions_server")}
+ >
+
+
+ );
}
diff --git a/components/settings/DownloadSettings.tv.tsx b/components/settings/DownloadSettings.tv.tsx
index 3a0017ac..8cd6fa73 100644
--- a/components/settings/DownloadSettings.tv.tsx
+++ b/components/settings/DownloadSettings.tv.tsx
@@ -1,3 +1,5 @@
-export default function DownloadSettings() {
- return null;
+import React from "react";
+
+export default function DownloadSettings({ ...props }) {
+ return <>>;
}
diff --git a/components/settings/HomeIndex.tsx b/components/settings/HomeIndex.tsx
new file mode 100644
index 00000000..9c0919f5
--- /dev/null
+++ b/components/settings/HomeIndex.tsx
@@ -0,0 +1,507 @@
+import { Button } from "@/components/Button";
+import { Text } from "@/components/common/Text";
+import { LargeMovieCarousel } from "@/components/home/LargeMovieCarousel";
+import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList";
+import { Loader } from "@/components/Loader";
+import { MediaListSection } from "@/components/medialists/MediaListSection";
+import { Colors } from "@/constants/Colors";
+import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
+import { useDownload } from "@/providers/DownloadProvider";
+import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
+import { useSettings } from "@/utils/atoms/settings";
+import { eventBus } from "@/utils/eventBus";
+import { Feather, Ionicons } from "@expo/vector-icons";
+import { Api } from "@jellyfin/sdk";
+import {
+ BaseItemDto,
+ BaseItemKind,
+} from "@jellyfin/sdk/lib/generated-client/models";
+import {
+ getItemsApi,
+ getSuggestionsApi,
+ getTvShowsApi,
+ getUserLibraryApi,
+ getUserViewsApi,
+} from "@jellyfin/sdk/lib/utils/api";
+import NetInfo from "@react-native-community/netinfo";
+import { QueryFunction, useQuery } from "@tanstack/react-query";
+import {
+ useNavigation,
+ usePathname,
+ useRouter,
+ useSegments,
+} from "expo-router";
+import { useAtomValue } from "jotai";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ ActivityIndicator,
+ RefreshControl,
+ ScrollView,
+ TouchableOpacity,
+ View,
+} from "react-native";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+
+type ScrollingCollectionListSection = {
+ type: "ScrollingCollectionList";
+ title?: string;
+ queryKey: (string | undefined | null)[];
+ queryFn: QueryFunction;
+ orientation?: "horizontal" | "vertical";
+};
+
+type MediaListSection = {
+ type: "MediaListSection";
+ queryKey: (string | undefined)[];
+ queryFn: QueryFunction;
+};
+
+type Section = ScrollingCollectionListSection | MediaListSection;
+
+export const HomeIndex = () => {
+ const router = useRouter();
+
+ const { t } = useTranslation();
+
+ const api = useAtomValue(apiAtom);
+ const user = useAtomValue(userAtom);
+
+ const [loading, setLoading] = useState(false);
+ const [
+ settings,
+ updateSettings,
+ pluginSettings,
+ setPluginSettings,
+ refreshStreamyfinPluginSettings,
+ ] = useSettings();
+
+ const [isConnected, setIsConnected] = useState(null);
+ const [loadingRetry, setLoadingRetry] = useState(false);
+
+ const navigation = useNavigation();
+
+ const insets = useSafeAreaInsets();
+
+ const scrollViewRef = useRef(null);
+
+ const { downloadedFiles, cleanCacheDirectory } = useDownload();
+ useEffect(() => {
+ const hasDownloads = downloadedFiles && downloadedFiles.length > 0;
+ navigation.setOptions({
+ headerLeft: () => (
+ {
+ router.push("/(auth)/downloads");
+ }}
+ className="p-2"
+ >
+
+
+ ),
+ });
+ }, [downloadedFiles, navigation, router]);
+
+ useEffect(() => {
+ cleanCacheDirectory().catch((e) =>
+ console.error("Something went wrong cleaning cache directory")
+ );
+ }, []);
+
+ const segments = useSegments();
+ useEffect(() => {
+ const unsubscribe = eventBus.on("scrollToTop", () => {
+ if (segments[2] === "(home)")
+ scrollViewRef.current?.scrollTo({ y: -152, animated: true });
+ });
+
+ return () => {
+ unsubscribe();
+ };
+ }, [segments]);
+
+ const checkConnection = useCallback(async () => {
+ setLoadingRetry(true);
+ const state = await NetInfo.fetch();
+ setIsConnected(state.isConnected);
+ setLoadingRetry(false);
+ }, []);
+
+ useEffect(() => {
+ const unsubscribe = NetInfo.addEventListener((state) => {
+ if (state.isConnected == false || state.isInternetReachable === false)
+ setIsConnected(false);
+ else setIsConnected(true);
+ });
+
+ NetInfo.fetch().then((state) => {
+ setIsConnected(state.isConnected);
+ });
+
+ // cleanCacheDirectory().catch((e) =>
+ // console.error("Something went wrong cleaning cache directory")
+ // );
+
+ return () => {
+ unsubscribe();
+ };
+ }, []);
+
+ const {
+ data,
+ isError: e1,
+ isLoading: l1,
+ } = useQuery({
+ queryKey: ["home", "userViews", user?.Id],
+ queryFn: async () => {
+ if (!api || !user?.Id) {
+ return null;
+ }
+
+ const response = await getUserViewsApi(api).getUserViews({
+ userId: user.Id,
+ });
+
+ return response.data.Items || null;
+ },
+ enabled: !!api && !!user?.Id,
+ staleTime: 60 * 1000,
+ });
+
+ const userViews = useMemo(
+ () => data?.filter((l) => !settings?.hiddenLibraries?.includes(l.Id!)),
+ [data, settings?.hiddenLibraries]
+ );
+
+ const collections = useMemo(() => {
+ const allow = ["movies", "tvshows"];
+ return (
+ userViews?.filter(
+ (c) => c.CollectionType && allow.includes(c.CollectionType)
+ ) || []
+ );
+ }, [userViews]);
+
+ const invalidateCache = useInvalidatePlaybackProgressCache();
+
+ const refetch = useCallback(async () => {
+ setLoading(true);
+ await refreshStreamyfinPluginSettings();
+ await invalidateCache();
+ setLoading(false);
+ }, []);
+
+ const createCollectionConfig = useCallback(
+ (
+ title: string,
+ queryKey: string[],
+ includeItemTypes: BaseItemKind[],
+ parentId: string | undefined
+ ): ScrollingCollectionListSection => ({
+ title,
+ queryKey,
+ queryFn: async () => {
+ if (!api) return [];
+ return (
+ (
+ await getUserLibraryApi(api).getLatestMedia({
+ userId: user?.Id,
+ limit: 20,
+ fields: ["PrimaryImageAspectRatio", "Path"],
+ imageTypeLimit: 1,
+ enableImageTypes: ["Primary", "Backdrop", "Thumb"],
+ includeItemTypes,
+ parentId,
+ })
+ ).data || []
+ );
+ },
+ type: "ScrollingCollectionList",
+ }),
+ [api, user?.Id]
+ );
+
+ let sections: Section[] = [];
+ if (!settings?.home || !settings?.home?.sections) {
+ sections = useMemo(() => {
+ if (!api || !user?.Id) return [];
+
+ const latestMediaViews = collections.map((c) => {
+ const includeItemTypes: BaseItemKind[] =
+ c.CollectionType === "tvshows" ? ["Series"] : ["Movie"];
+ const title = t("home.recently_added_in", { libraryName: c.Name });
+ const queryKey = [
+ "home",
+ "recentlyAddedIn" + c.CollectionType,
+ user?.Id!,
+ c.Id!,
+ ];
+ return createCollectionConfig(
+ title || "",
+ queryKey,
+ includeItemTypes,
+ c.Id
+ );
+ });
+
+ const ss: Section[] = [
+ {
+ title: t("home.continue_watching"),
+ queryKey: ["home", "resumeItems"],
+ queryFn: async () =>
+ (
+ await getItemsApi(api).getResumeItems({
+ userId: user.Id,
+ enableImageTypes: ["Primary", "Backdrop", "Thumb"],
+ includeItemTypes: ["Movie", "Series", "Episode"],
+ })
+ ).data.Items || [],
+ type: "ScrollingCollectionList",
+ orientation: "horizontal",
+ },
+ {
+ title: t("home.next_up"),
+ queryKey: ["home", "nextUp-all"],
+ queryFn: async () =>
+ (
+ await getTvShowsApi(api).getNextUp({
+ userId: user?.Id,
+ fields: ["MediaSourceCount"],
+ limit: 20,
+ enableImageTypes: ["Primary", "Backdrop", "Thumb"],
+ enableResumable: false,
+ })
+ ).data.Items || [],
+ type: "ScrollingCollectionList",
+ orientation: "horizontal",
+ },
+ ...latestMediaViews,
+ // ...(mediaListCollections?.map(
+ // (ml) =>
+ // ({
+ // title: ml.Name,
+ // queryKey: ["home", "mediaList", ml.Id!],
+ // queryFn: async () => ml,
+ // type: "MediaListSection",
+ // orientation: "vertical",
+ // } as Section)
+ // ) || []),
+ {
+ title: t("home.suggested_movies"),
+ queryKey: ["home", "suggestedMovies", user?.Id],
+ queryFn: async () =>
+ (
+ await getSuggestionsApi(api).getSuggestions({
+ userId: user?.Id,
+ limit: 10,
+ mediaType: ["Video"],
+ type: ["Movie"],
+ })
+ ).data.Items || [],
+ type: "ScrollingCollectionList",
+ orientation: "vertical",
+ },
+ {
+ title: t("home.suggested_episodes"),
+ queryKey: ["home", "suggestedEpisodes", user?.Id],
+ queryFn: async () => {
+ try {
+ const suggestions = await getSuggestions(api, user.Id);
+ const nextUpPromises = suggestions.map((series) =>
+ getNextUp(api, user.Id, series.Id)
+ );
+ const nextUpResults = await Promise.all(nextUpPromises);
+
+ return nextUpResults.filter((item) => item !== null) || [];
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ return [];
+ }
+ },
+ type: "ScrollingCollectionList",
+ orientation: "horizontal",
+ },
+ ];
+ return ss;
+ }, [api, user?.Id, collections]);
+ } else {
+ sections = useMemo(() => {
+ if (!api || !user?.Id) return [];
+ const ss: Section[] = [];
+
+ for (const key in settings.home?.sections) {
+ // @ts-expect-error
+ const section = settings.home?.sections[key];
+ const id = section.title || key;
+ ss.push({
+ title: id,
+ queryKey: ["home", id],
+ queryFn: async () => {
+ if (section.items) {
+ const response = await getItemsApi(api).getItems({
+ userId: user?.Id,
+ limit: section.items?.limit || 25,
+ recursive: true,
+ includeItemTypes: section.items?.includeItemTypes,
+ sortBy: section.items?.sortBy,
+ sortOrder: section.items?.sortOrder,
+ filters: section.items?.filters,
+ parentId: section.items?.parentId,
+ });
+ return response.data.Items || [];
+ } else if (section.nextUp) {
+ const response = await getTvShowsApi(api).getNextUp({
+ userId: user?.Id,
+ fields: ["MediaSourceCount"],
+ limit: section.items?.limit || 25,
+ enableImageTypes: ["Primary", "Backdrop", "Thumb"],
+ enableResumable: section.items?.enableResumable || false,
+ enableRewatching: section.items?.enableRewatching || false,
+ });
+ return response.data.Items || [];
+ }
+ return [];
+ },
+ type: "ScrollingCollectionList",
+ orientation: section?.orientation || "vertical",
+ });
+ }
+ return ss;
+ }, [api, user?.Id, settings.home?.sections]);
+ }
+
+ if (isConnected === false) {
+ return (
+
+ {t("home.no_internet")}
+
+ {t("home.no_internet_message")}
+
+
+ router.push("/(auth)/downloads")}
+ justify="center"
+ iconRight={
+
+ }
+ >
+ {t("home.go_to_downloads")}
+
+ {
+ checkConnection();
+ }}
+ justify="center"
+ className="mt-2"
+ iconRight={
+ loadingRetry ? null : (
+
+ )
+ }
+ >
+ {loadingRetry ? (
+
+ ) : (
+ "Retry"
+ )}
+
+
+
+ );
+ }
+
+ if (e1)
+ return (
+
+ {t("home.oops")}
+
+ {t("home.error_message")}
+
+
+ );
+
+ if (l1)
+ return (
+
+
+
+ );
+
+ return (
+
+ }
+ contentContainerStyle={{
+ paddingLeft: insets.left,
+ paddingRight: insets.right,
+ paddingBottom: 16,
+ }}
+ >
+
+
+
+ {sections.map((section, index) => {
+ if (section.type === "ScrollingCollectionList") {
+ return (
+
+ );
+ } else if (section.type === "MediaListSection") {
+ return (
+
+ );
+ }
+ return null;
+ })}
+
+
+ );
+};
+
+// Function to get suggestions
+async function getSuggestions(api: Api, userId: string | undefined) {
+ if (!userId) return [];
+ const response = await getSuggestionsApi(api).getSuggestions({
+ userId,
+ limit: 10,
+ mediaType: ["Unknown"],
+ type: ["Series"],
+ });
+ return response.data.Items ?? [];
+}
+
+// Function to get the next up TV show for a series
+async function getNextUp(
+ api: Api,
+ userId: string | undefined,
+ seriesId: string | undefined
+) {
+ if (!userId || !seriesId) return null;
+ const response = await getTvShowsApi(api).getNextUp({
+ userId,
+ seriesId,
+ limit: 1,
+ });
+ return response.data.Items?.[0] ?? null;
+}
diff --git a/components/settings/HomeIndex.tv.tsx b/components/settings/HomeIndex.tv.tsx
new file mode 100644
index 00000000..b7a8633c
--- /dev/null
+++ b/components/settings/HomeIndex.tv.tsx
@@ -0,0 +1,453 @@
+import { Button } from "@/components/Button";
+import { Text } from "@/components/common/Text";
+import { LargeMovieCarousel } from "@/components/home/LargeMovieCarousel";
+import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList";
+import { Loader } from "@/components/Loader";
+import { MediaListSection } from "@/components/medialists/MediaListSection";
+import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
+import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
+import { useSettings } from "@/utils/atoms/settings";
+import { Ionicons } from "@expo/vector-icons";
+import { Api } from "@jellyfin/sdk";
+import {
+ BaseItemDto,
+ BaseItemKind,
+} from "@jellyfin/sdk/lib/generated-client/models";
+import {
+ getItemsApi,
+ getSuggestionsApi,
+ getTvShowsApi,
+ getUserLibraryApi,
+ getUserViewsApi,
+} from "@jellyfin/sdk/lib/utils/api";
+import NetInfo from "@react-native-community/netinfo";
+import { QueryFunction, useQuery } from "@tanstack/react-query";
+import { useRouter } from "expo-router";
+import { useAtomValue } from "jotai";
+import { useCallback, useEffect, useMemo, useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ ActivityIndicator,
+ RefreshControl,
+ ScrollView,
+ View,
+} from "react-native";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+
+type ScrollingCollectionListSection = {
+ type: "ScrollingCollectionList";
+ title?: string;
+ queryKey: (string | undefined | null)[];
+ queryFn: QueryFunction;
+ orientation?: "horizontal" | "vertical";
+};
+
+type MediaListSection = {
+ type: "MediaListSection";
+ queryKey: (string | undefined)[];
+ queryFn: QueryFunction;
+};
+
+type Section = ScrollingCollectionListSection | MediaListSection;
+
+export const HomeIndex = () => {
+ const router = useRouter();
+
+ const { t } = useTranslation();
+
+ const api = useAtomValue(apiAtom);
+ const user = useAtomValue(userAtom);
+
+ const [loading, setLoading] = useState(false);
+ const [
+ settings,
+ updateSettings,
+ pluginSettings,
+ setPluginSettings,
+ refreshStreamyfinPluginSettings,
+ ] = useSettings();
+
+ const [isConnected, setIsConnected] = useState(null);
+ const [loadingRetry, setLoadingRetry] = useState(false);
+
+ const insets = useSafeAreaInsets();
+
+ const checkConnection = useCallback(async () => {
+ setLoadingRetry(true);
+ const state = await NetInfo.fetch();
+ setIsConnected(state.isConnected);
+ setLoadingRetry(false);
+ }, []);
+
+ useEffect(() => {
+ const unsubscribe = NetInfo.addEventListener((state) => {
+ if (state.isConnected == false || state.isInternetReachable === false)
+ setIsConnected(false);
+ else setIsConnected(true);
+ });
+
+ NetInfo.fetch().then((state) => {
+ setIsConnected(state.isConnected);
+ });
+
+ // cleanCacheDirectory().catch((e) =>
+ // console.error("Something went wrong cleaning cache directory")
+ // );
+
+ return () => {
+ unsubscribe();
+ };
+ }, []);
+
+ const {
+ data,
+ isError: e1,
+ isLoading: l1,
+ } = useQuery({
+ queryKey: ["home", "userViews", user?.Id],
+ queryFn: async () => {
+ if (!api || !user?.Id) {
+ return null;
+ }
+
+ const response = await getUserViewsApi(api).getUserViews({
+ userId: user.Id,
+ });
+
+ return response.data.Items || null;
+ },
+ enabled: !!api && !!user?.Id,
+ staleTime: 60 * 1000,
+ });
+
+ const userViews = useMemo(
+ () => data?.filter((l) => !settings?.hiddenLibraries?.includes(l.Id!)),
+ [data, settings?.hiddenLibraries]
+ );
+
+ const collections = useMemo(() => {
+ const allow = ["movies", "tvshows"];
+ return (
+ userViews?.filter(
+ (c) => c.CollectionType && allow.includes(c.CollectionType)
+ ) || []
+ );
+ }, [userViews]);
+
+ const invalidateCache = useInvalidatePlaybackProgressCache();
+
+ const refetch = useCallback(async () => {
+ setLoading(true);
+ await refreshStreamyfinPluginSettings();
+ await invalidateCache();
+ setLoading(false);
+ }, []);
+
+ const createCollectionConfig = useCallback(
+ (
+ title: string,
+ queryKey: string[],
+ includeItemTypes: BaseItemKind[],
+ parentId: string | undefined
+ ): ScrollingCollectionListSection => ({
+ title,
+ queryKey,
+ queryFn: async () => {
+ if (!api) return [];
+ return (
+ (
+ await getUserLibraryApi(api).getLatestMedia({
+ userId: user?.Id,
+ limit: 20,
+ fields: ["PrimaryImageAspectRatio", "Path"],
+ imageTypeLimit: 1,
+ enableImageTypes: ["Primary", "Backdrop", "Thumb"],
+ includeItemTypes,
+ parentId,
+ })
+ ).data || []
+ );
+ },
+ type: "ScrollingCollectionList",
+ }),
+ [api, user?.Id]
+ );
+
+ let sections: Section[] = [];
+ if (!settings?.home || !settings?.home?.sections) {
+ sections = useMemo(() => {
+ if (!api || !user?.Id) return [];
+
+ const latestMediaViews = collections.map((c) => {
+ const includeItemTypes: BaseItemKind[] =
+ c.CollectionType === "tvshows" ? ["Series"] : ["Movie"];
+ const title = t("home.recently_added_in", { libraryName: c.Name });
+ const queryKey = [
+ "home",
+ "recentlyAddedIn" + c.CollectionType,
+ user?.Id!,
+ c.Id!,
+ ];
+ return createCollectionConfig(
+ title || "",
+ queryKey,
+ includeItemTypes,
+ c.Id
+ );
+ });
+
+ const ss: Section[] = [
+ {
+ title: t("home.continue_watching"),
+ queryKey: ["home", "resumeItems"],
+ queryFn: async () =>
+ (
+ await getItemsApi(api).getResumeItems({
+ userId: user.Id,
+ enableImageTypes: ["Primary", "Backdrop", "Thumb"],
+ includeItemTypes: ["Movie", "Series", "Episode"],
+ })
+ ).data.Items || [],
+ type: "ScrollingCollectionList",
+ orientation: "horizontal",
+ },
+ {
+ title: t("home.next_up"),
+ queryKey: ["home", "nextUp-all"],
+ queryFn: async () =>
+ (
+ await getTvShowsApi(api).getNextUp({
+ userId: user?.Id,
+ fields: ["MediaSourceCount"],
+ limit: 20,
+ enableImageTypes: ["Primary", "Backdrop", "Thumb"],
+ enableResumable: false,
+ })
+ ).data.Items || [],
+ type: "ScrollingCollectionList",
+ orientation: "horizontal",
+ },
+ ...latestMediaViews,
+ // ...(mediaListCollections?.map(
+ // (ml) =>
+ // ({
+ // title: ml.Name,
+ // queryKey: ["home", "mediaList", ml.Id!],
+ // queryFn: async () => ml,
+ // type: "MediaListSection",
+ // orientation: "vertical",
+ // } as Section)
+ // ) || []),
+ {
+ title: t("home.suggested_movies"),
+ queryKey: ["home", "suggestedMovies", user?.Id],
+ queryFn: async () =>
+ (
+ await getSuggestionsApi(api).getSuggestions({
+ userId: user?.Id,
+ limit: 10,
+ mediaType: ["Video"],
+ type: ["Movie"],
+ })
+ ).data.Items || [],
+ type: "ScrollingCollectionList",
+ orientation: "vertical",
+ },
+ {
+ title: t("home.suggested_episodes"),
+ queryKey: ["home", "suggestedEpisodes", user?.Id],
+ queryFn: async () => {
+ try {
+ const suggestions = await getSuggestions(api, user.Id);
+ const nextUpPromises = suggestions.map((series) =>
+ getNextUp(api, user.Id, series.Id)
+ );
+ const nextUpResults = await Promise.all(nextUpPromises);
+
+ return nextUpResults.filter((item) => item !== null) || [];
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ return [];
+ }
+ },
+ type: "ScrollingCollectionList",
+ orientation: "horizontal",
+ },
+ ];
+ return ss;
+ }, [api, user?.Id, collections]);
+ } else {
+ sections = useMemo(() => {
+ if (!api || !user?.Id) return [];
+ const ss: Section[] = [];
+
+ for (const key in settings.home?.sections) {
+ // @ts-expect-error
+ const section = settings.home?.sections[key];
+ const id = section.title || key;
+ ss.push({
+ title: id,
+ queryKey: ["home", id],
+ queryFn: async () => {
+ if (section.items) {
+ const response = await getItemsApi(api).getItems({
+ userId: user?.Id,
+ limit: section.items?.limit || 25,
+ recursive: true,
+ includeItemTypes: section.items?.includeItemTypes,
+ sortBy: section.items?.sortBy,
+ sortOrder: section.items?.sortOrder,
+ filters: section.items?.filters,
+ parentId: section.items?.parentId,
+ });
+ return response.data.Items || [];
+ } else if (section.nextUp) {
+ const response = await getTvShowsApi(api).getNextUp({
+ userId: user?.Id,
+ fields: ["MediaSourceCount"],
+ limit: section.items?.limit || 25,
+ enableImageTypes: ["Primary", "Backdrop", "Thumb"],
+ enableResumable: section.items?.enableResumable || false,
+ enableRewatching: section.items?.enableRewatching || false,
+ });
+ return response.data.Items || [];
+ }
+ return [];
+ },
+ type: "ScrollingCollectionList",
+ orientation: section?.orientation || "vertical",
+ });
+ }
+ return ss;
+ }, [api, user?.Id, settings.home?.sections]);
+ }
+
+ if (isConnected === false) {
+ return (
+
+ {t("home.no_internet")}
+
+ {t("home.no_internet_message")}
+
+
+ router.push("/(auth)/downloads")}
+ justify="center"
+ iconRight={
+
+ }
+ >
+ {t("home.go_to_downloads")}
+
+ {
+ checkConnection();
+ }}
+ justify="center"
+ className="mt-2"
+ iconRight={
+ loadingRetry ? null : (
+
+ )
+ }
+ >
+ {loadingRetry ? (
+
+ ) : (
+ "Retry"
+ )}
+
+
+
+ );
+ }
+
+ if (e1)
+ return (
+
+ {t("home.oops")}
+
+ {t("home.error_message")}
+
+
+ );
+
+ if (l1)
+ return (
+
+
+
+ );
+
+ return (
+
+ }
+ contentContainerStyle={{
+ paddingLeft: insets.left,
+ paddingRight: insets.right,
+ paddingBottom: 16,
+ }}
+ >
+
+
+
+ {sections.map((section, index) => {
+ if (section.type === "ScrollingCollectionList") {
+ return (
+
+ );
+ } else if (section.type === "MediaListSection") {
+ return (
+
+ );
+ }
+ return null;
+ })}
+
+
+ );
+};
+
+// Function to get suggestions
+async function getSuggestions(api: Api, userId: string | undefined) {
+ if (!userId) return [];
+ const response = await getSuggestionsApi(api).getSuggestions({
+ userId,
+ limit: 10,
+ mediaType: ["Unknown"],
+ type: ["Series"],
+ });
+ return response.data.Items ?? [];
+}
+
+// Function to get the next up TV show for a series
+async function getNextUp(
+ api: Api,
+ userId: string | undefined,
+ seriesId: string | undefined
+) {
+ if (!userId || !seriesId) return null;
+ const response = await getTvShowsApi(api).getNextUp({
+ userId,
+ seriesId,
+ limit: 1,
+ });
+ return response.data.Items?.[0] ?? null;
+}
diff --git a/components/settings/Jellyseerr.tsx b/components/settings/Jellyseerr.tsx
index 470d40a2..bd381c11 100644
--- a/components/settings/Jellyseerr.tsx
+++ b/components/settings/Jellyseerr.tsx
@@ -1,12 +1,12 @@
-import { useMutation } from "@tanstack/react-query";
-import { useAtom } from "jotai";
-import { useState } from "react";
-import { useTranslation } from "react-i18next";
-import { View } from "react-native";
-import { toast } from "sonner-native";
import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
import { userAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings";
+import { useMutation } from "@tanstack/react-query";
+import { useTranslation } from "react-i18next";
+import { useAtom } from "jotai";
+import { useState } from "react";
+import { View } from "react-native";
+import { toast } from "sonner-native";
import { Button } from "../Button";
import { Input } from "../common/Input";
import { Text } from "../common/Text";
@@ -14,13 +14,17 @@ import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem";
export const JellyseerrSettings = () => {
- const { jellyseerrUser, setJellyseerrUser, clearAllJellyseerData } =
- useJellyseerr();
+ const {
+ jellyseerrApi,
+ jellyseerrUser,
+ setJellyseerrUser,
+ clearAllJellyseerData,
+ } = useJellyseerr();
const { t } = useTranslation();
const [user] = useAtom(userAtom);
- const { settings, updateSettings } = useSettings();
+ const [settings, updateSettings, pluginSettings] = useSettings();
const [jellyseerrPassword, setJellyseerrPassword] = useState<
string | undefined
@@ -37,7 +41,7 @@ export const JellyseerrSettings = () => {
if (!user?.Name)
throw new Error("Missing required information for login");
const jellyseerrTempApi = new JellyseerrApi(
- jellyseerrServerUrl || settings.jellyseerrServerUrl || "",
+ jellyseerrServerUrl || settings.jellyseerrServerUrl || ""
);
const testResult = await jellyseerrTempApi.test();
if (!testResult.isValid) throw new Error("Invalid server url");
@@ -64,14 +68,14 @@ export const JellyseerrSettings = () => {
};
return (
-
+
{jellyseerrUser ? (
<>
@@ -105,69 +109,69 @@ export const JellyseerrSettings = () => {
/>
-
-
+
+
{t(
- "home.settings.plugins.jellyseerr.reset_jellyseerr_config_button",
+ "home.settings.plugins.jellyseerr.reset_jellyseerr_config_button"
)}
>
) : (
-
-
+
+
{t("home.settings.plugins.jellyseerr.jellyseerr_warning")}
-
+
{t("home.settings.plugins.jellyseerr.server_url")}
-
-
+
+
{t("home.settings.plugins.jellyseerr.server_url_hint")}
-
+
{t("home.settings.plugins.jellyseerr.password")}
loginToJellyseerrMutation.mutate()}
>
{t("home.settings.plugins.jellyseerr.login_button")}
diff --git a/components/settings/OtherSettings.tsx b/components/settings/OtherSettings.tsx
index fcca2498..e14c00cd 100644
--- a/components/settings/OtherSettings.tsx
+++ b/components/settings/OtherSettings.tsx
@@ -1,33 +1,75 @@
-import { Ionicons } from "@expo/vector-icons";
-import { TFunction } from "i18next";
-import type React from "react";
-import { useMemo } from "react";
-import { useTranslation } from "react-i18next";
-import { Linking, Switch, View } from "react-native";
-import { BITRATES } from "@/components/BitrateSelector";
-import { PlatformDropdown } from "@/components/PlatformDropdown";
-import DisabledSetting from "@/components/settings/DisabledSetting";
-import useRouter from "@/hooks/useAppRouter";
-import * as ScreenOrientation from "@/packages/expo-screen-orientation";
+import { Platform } from "react-native";
import { ScreenOrientationEnum, useSettings } from "@/utils/atoms/settings";
+import { BitrateSelector, BITRATES } from "@/components/BitrateSelector";
+import {
+ BACKGROUND_FETCH_TASK,
+ registerBackgroundFetchAsync,
+ unregisterBackgroundFetchAsync,
+} from "@/utils/background-tasks";
+import { Ionicons } from "@expo/vector-icons";
+const BackgroundFetch = !Platform.isTV
+ ? require("expo-background-fetch")
+ : null;
+import * as ScreenOrientation from "@/packages/expo-screen-orientation";
+const TaskManager = !Platform.isTV ? require("expo-task-manager") : null;
+import { useRouter } from "expo-router";
+import React, { useEffect, useMemo } from "react";
+import { Linking, Switch, TouchableOpacity } from "react-native";
+import { toast } from "sonner-native";
import { Text } from "../common/Text";
import { ListGroup } from "../list/ListGroup";
import { ListItem } from "../list/ListItem";
+import { useTranslation } from "react-i18next";
+import DisabledSetting from "@/components/settings/DisabledSetting";
+import Dropdown from "@/components/common/Dropdown";
export const OtherSettings: React.FC = () => {
const router = useRouter();
- const { settings, updateSettings, pluginSettings } = useSettings();
+ const [settings, updateSettings, pluginSettings] = useSettings();
const { t } = useTranslation();
+ /********************
+ * Background task
+ *******************/
+ const checkStatusAsync = async () => {
+ if (Platform.isTV) return;
+
+ await BackgroundFetch.getStatusAsync();
+ return await TaskManager.isTaskRegisteredAsync(BACKGROUND_FETCH_TASK);
+ };
+
+ useEffect(() => {
+ (async () => {
+ const registered = await checkStatusAsync();
+
+ if (settings?.autoDownload === true && !registered) {
+ registerBackgroundFetchAsync();
+ toast.success("Background downloads enabled");
+ } else if (settings?.autoDownload === false && registered) {
+ unregisterBackgroundFetchAsync();
+ toast.info("Background downloads disabled");
+ } else if (settings?.autoDownload === true && registered) {
+ // Don't to anything
+ } else if (settings?.autoDownload === false && !registered) {
+ // Don't to anything
+ } else {
+ updateSettings({ autoDownload: false });
+ }
+ })();
+ }, [settings?.autoDownload]);
+ /**********************
+ *********************/
+
const disabled = useMemo(
() =>
+ pluginSettings?.autoRotate?.locked === true &&
pluginSettings?.defaultVideoOrientation?.locked === true &&
pluginSettings?.safeAreaInControlsEnabled?.locked === true &&
pluginSettings?.showCustomMenuLinks?.locked === true &&
pluginSettings?.hiddenLibraries?.locked === true &&
pluginSettings?.disableHapticFeedback?.locked === true,
- [pluginSettings],
+ [pluginSettings]
);
const orientations = [
@@ -37,94 +79,53 @@ export const OtherSettings: React.FC = () => {
ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT,
];
- const orientationTranslations = useMemo(
- () => ({
- [ScreenOrientation.OrientationLock.DEFAULT]:
- "home.settings.other.orientations.DEFAULT",
- [ScreenOrientation.OrientationLock.PORTRAIT_UP]:
- "home.settings.other.orientations.PORTRAIT_UP",
- [ScreenOrientation.OrientationLock.LANDSCAPE_LEFT]:
- "home.settings.other.orientations.LANDSCAPE_LEFT",
- [ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT]:
- "home.settings.other.orientations.LANDSCAPE_RIGHT",
- }),
- [],
- );
-
- const orientationOptions = useMemo(
- () => [
- {
- options: orientations.map((orientation) => ({
- type: "radio" as const,
- label: t(ScreenOrientationEnum[orientation]),
- value: String(orientation),
- selected: orientation === settings?.defaultVideoOrientation,
- onPress: () =>
- updateSettings({ defaultVideoOrientation: orientation }),
- })),
- },
- ],
- [orientations, settings?.defaultVideoOrientation, t, updateSettings],
- );
-
- const bitrateOptions = useMemo(
- () => [
- {
- options: BITRATES.map((bitrate) => ({
- type: "radio" as const,
- label: bitrate.key,
- value: bitrate.key,
- selected: bitrate.key === settings?.defaultBitrate?.key,
- onPress: () => updateSettings({ defaultBitrate: bitrate }),
- })),
- },
- ],
- [settings?.defaultBitrate?.key, t, updateSettings],
- );
-
- const autoPlayEpisodeOptions = useMemo(
- () => [
- {
- options: AUTOPLAY_EPISODES_COUNT(t).map((item) => ({
- type: "radio" as const,
- label: item.key,
- value: item.key,
- selected: item.key === settings?.maxAutoPlayEpisodeCount?.key,
- onPress: () => updateSettings({ maxAutoPlayEpisodeCount: item }),
- })),
- },
- ],
- [settings?.maxAutoPlayEpisodeCount?.key, t, updateSettings],
- );
-
if (!settings) return null;
return (
-
+
+
+ updateSettings({ autoRotate: value })}
+ />
+
+
-
-
- {t(
- orientationTranslations[
- settings.defaultVideoOrientation as keyof typeof orientationTranslations
- ],
- ) || "Unknown Orientation"}
+ ScreenOrientationEnum[item]}
+ title={
+
+
+ {t(ScreenOrientationEnum[settings.defaultVideoOrientation])}
-
+
+ }
+ label={t("home.settings.other.orientation")}
+ onSelected={(defaultVideoOrientation) =>
+ updateSettings({ defaultVideoOrientation })
}
- title={t("home.settings.other.orientation")}
/>
@@ -146,7 +147,7 @@ export const OtherSettings: React.FC = () => {
disabled={pluginSettings?.showCustomMenuLinks?.locked}
onPress={() =>
Linking.openURL(
- "https://jellyfin.org/docs/general/clients/web-config/#custom-menu-links",
+ "https://jellyfin.org/docs/general/clients/web-config/#custom-menu-links"
)
}
>
@@ -158,14 +159,6 @@ export const OtherSettings: React.FC = () => {
}
/>
-
-
- updateSettings({ showLargeHomeCarousel: value })
- }
- />
-
router.push("/settings/hide-libraries/page")}
title={t("home.settings.other.hide_libraries")}
@@ -175,21 +168,26 @@ export const OtherSettings: React.FC = () => {
title={t("home.settings.other.default_quality")}
disabled={pluginSettings?.defaultBitrate?.locked}
>
-
-
+ item.key}
+ titleExtractor={(item) => item.key}
+ selected={settings.defaultBitrate}
+ title={
+
+
{settings.defaultBitrate?.key}
-
+
}
- title={t("home.settings.other.default_quality")}
+ label={t("home.settings.other.default_quality")}
+ onSelected={(defaultBitrate) => updateSettings({ defaultBitrate })}
/>
{
}
/>
-
-
-
- {t(settings?.maxAutoPlayEpisodeCount.key)}
-
-
-
- }
- title={t("home.settings.other.max_auto_play_episode_count")}
- />
-
);
};
-
-const AUTOPLAY_EPISODES_COUNT = (
- t: TFunction<"translation", undefined>,
-): {
- key: string;
- value: number;
-}[] => [
- { key: t("home.settings.other.disabled"), value: -1 },
- { key: "1", value: 1 },
- { key: "2", value: 2 },
- { key: "3", value: 3 },
- { key: "4", value: 4 },
- { key: "5", value: 5 },
- { key: "6", value: 6 },
- { key: "7", value: 7 },
-];
diff --git a/components/video-player/controls/Controls.tsx b/components/video-player/controls/Controls.tsx
index 96dfad6b..17bd2028 100644
--- a/components/video-player/controls/Controls.tsx
+++ b/components/video-player/controls/Controls.tsx
@@ -1,80 +1,99 @@
-import { Api } from "@jellyfin/sdk";
-import type {
- BaseItemDto,
- MediaSourceInfo,
-} from "@jellyfin/sdk/lib/generated-client";
-import { useLocalSearchParams } from "expo-router";
-import { type FC, useCallback, useEffect, useState } from "react";
-import { StyleSheet, useWindowDimensions, View } from "react-native";
-import Animated, {
- Easing,
- type SharedValue,
- useAnimatedReaction,
- useAnimatedStyle,
- useSharedValue,
- withTiming,
-} from "react-native-reanimated";
-import ContinueWatchingOverlay from "@/components/video-player/controls/ContinueWatchingOverlay";
-import useRouter from "@/hooks/useAppRouter";
+import { Text } from "@/components/common/Text";
+import { Loader } from "@/components/Loader";
+import { useAdjacentItems } from "@/hooks/useAdjacentEpisodes";
import { useCreditSkipper } from "@/hooks/useCreditSkipper";
import { useHaptic } from "@/hooks/useHaptic";
import { useIntroSkipper } from "@/hooks/useIntroSkipper";
-import { usePlaybackManager } from "@/hooks/usePlaybackManager";
import { useTrickplay } from "@/hooks/useTrickplay";
-import type { TechnicalInfo } from "@/modules/mpv-player";
-import { DownloadedItem } from "@/providers/Downloads/types";
-import { useOfflineMode } from "@/providers/OfflineModeProvider";
+import {
+ TrackInfo,
+ VlcPlayerViewRef,
+} from "@/modules/vlc-player/src/VlcPlayer.types";
+import { apiAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings";
-import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
-import { ticksToMs } from "@/utils/time";
-import { BottomControls } from "./BottomControls";
-import { CenterControls } from "./CenterControls";
-import { CONTROLS_CONSTANTS } from "./constants";
+import {
+ getDefaultPlaySettings,
+ previousIndexes,
+} from "@/utils/jellyfin/getDefaultPlaySettings";
+import { getItemById } from "@/utils/jellyfin/user-library/getItemById";
+import { writeToLog } from "@/utils/log";
+import {
+ formatTimeString,
+ msToTicks,
+ secondsToMs,
+ ticksToMs,
+ ticksToSeconds,
+} from "@/utils/time";
+import { Ionicons, MaterialIcons } from "@expo/vector-icons";
+import {
+ BaseItemDto,
+ MediaSourceInfo,
+} from "@jellyfin/sdk/lib/generated-client";
+import { Image } from "expo-image";
+import { useLocalSearchParams, useRouter } from "expo-router";
+import * as ScreenOrientation from "@/packages/expo-screen-orientation";
+import { useAtom } from "jotai";
+import { debounce } from "lodash";
+import React, { useCallback, useEffect, useRef, useState } from "react";
+import {
+ Platform,
+ TouchableOpacity,
+ useWindowDimensions,
+ View,
+} from "react-native";
+import { Slider } from "react-native-awesome-slider";
+import {
+ runOnJS,
+ SharedValue,
+ useAnimatedReaction,
+ useSharedValue,
+} from "react-native-reanimated";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+import { VideoRef } from "react-native-video";
+import AudioSlider from "./AudioSlider";
+import BrightnessSlider from "./BrightnessSlider";
+import { ControlProvider } from "./contexts/ControlContext";
+import { VideoProvider } from "./contexts/VideoContext";
+import DropdownView from "./dropdown/DropdownView";
import { EpisodeList } from "./EpisodeList";
-import { GestureOverlay } from "./GestureOverlay";
-import { HeaderControls } from "./HeaderControls";
-import { useRemoteControl } from "./hooks/useRemoteControl";
-import { useVideoNavigation } from "./hooks/useVideoNavigation";
-import { useVideoSlider } from "./hooks/useVideoSlider";
-import { useVideoTime } from "./hooks/useVideoTime";
-import { TechnicalInfoOverlay } from "./TechnicalInfoOverlay";
+import NextEpisodeCountDownButton from "./NextEpisodeCountDownButton";
+import SkipButton from "./SkipButton";
import { useControlsTimeout } from "./useControlsTimeout";
-import { PlaybackSpeedScope } from "./utils/playback-speed-settings";
-import { type AspectRatio } from "./VideoScalingModeSelector";
+import { VideoTouchOverlay } from "./VideoTouchOverlay";
interface Props {
item: BaseItemDto;
+ videoRef: React.MutableRefObject;
isPlaying: boolean;
isSeeking: SharedValue;
cacheProgress: SharedValue;
progress: SharedValue;
isBuffering: boolean;
showControls: boolean;
+ ignoreSafeAreas?: boolean;
+ setIgnoreSafeAreas: React.Dispatch>;
enableTrickplay?: boolean;
togglePlay: () => void;
setShowControls: (shown: boolean) => void;
+ offline?: boolean;
+ isVideoLoaded?: boolean;
mediaSource?: MediaSourceInfo | null;
seek: (ticks: number) => void;
- startPictureInPicture?: () => Promise;
- play: () => void;
+ startPictureInPicture: () => Promise;
+ play: (() => Promise) | (() => void);
pause: () => void;
- aspectRatio?: AspectRatio;
- isZoomedToFill?: boolean;
- onZoomToggle?: () => void;
- api?: Api | null;
- downloadedFiles?: DownloadedItem[];
- // Playback speed props
- playbackSpeed?: number;
- setPlaybackSpeed?: (speed: number, scope: PlaybackSpeedScope) => void;
- // Technical info props
- showTechnicalInfo?: boolean;
- onToggleTechnicalInfo?: () => void;
- getTechnicalInfo?: () => Promise;
- playMethod?: "DirectPlay" | "DirectStream" | "Transcode";
- transcodeReasons?: string[];
+ getAudioTracks?: (() => Promise) | (() => TrackInfo[]);
+ getSubtitleTracks?: (() => Promise) | (() => TrackInfo[]);
+ setSubtitleURL?: (url: string, customName: string) => void;
+ setSubtitleTrack?: (index: number) => void;
+ setAudioTrack?: (index: number) => void;
+ stop: (() => Promise) | (() => void);
+ isVlc?: boolean;
}
-export const Controls: FC = ({
+const CONTROLS_TIMEOUT = 4000;
+
+export const Controls: React.FC = ({
item,
seek,
startPictureInPicture,
@@ -88,211 +107,65 @@ export const Controls: FC = ({
cacheProgress,
showControls,
setShowControls,
+ ignoreSafeAreas,
+ setIgnoreSafeAreas,
mediaSource,
- aspectRatio = "default",
- isZoomedToFill = false,
- onZoomToggle,
- api = null,
- downloadedFiles = undefined,
- playbackSpeed = 1.0,
- setPlaybackSpeed,
- showTechnicalInfo = false,
- onToggleTechnicalInfo,
- getTechnicalInfo,
- playMethod,
- transcodeReasons,
+ isVideoLoaded,
+ getAudioTracks,
+ getSubtitleTracks,
+ setSubtitleURL,
+ setSubtitleTrack,
+ setAudioTrack,
+ stop,
+ offline = false,
+ enableTrickplay = true,
+ isVlc = false,
}) => {
- const offline = useOfflineMode();
- const { settings, updateSettings } = useSettings();
+ const [settings] = useSettings();
const router = useRouter();
- const lightHapticFeedback = useHaptic("light");
+ const insets = useSafeAreaInsets();
+ const [api] = useAtom(apiAtom);
const [episodeView, setEpisodeView] = useState(false);
+ const [isSliding, setIsSliding] = useState(false);
+
+ // Used when user changes audio through audio button on device.
const [showAudioSlider, setShowAudioSlider] = useState(false);
const { height: screenHeight, width: screenWidth } = useWindowDimensions();
- const { previousItem, nextItem } = usePlaybackManager({
- item,
- isOffline: offline,
- });
-
+ const { previousItem, nextItem } = useAdjacentItems({ item });
const {
trickPlayUrl,
calculateTrickplayUrl,
trickplayInfo,
prefetchAllTrickplayImages,
- } = useTrickplay(item);
+ } = useTrickplay(item, !offline && enableTrickplay);
+
+ const [currentTime, setCurrentTime] = useState(0);
+ const [remainingTime, setRemainingTime] = useState(Infinity);
const min = useSharedValue(0);
- // Regular value for use during render (avoids Reanimated warning)
- const maxMs = ticksToMs(item.RunTimeTicks || 0);
- const max = useSharedValue(maxMs);
+ const max = useSharedValue(item.RunTimeTicks || 0);
- // Animation values for controls
- const controlsOpacity = useSharedValue(showControls ? 1 : 0);
- const headerTranslateY = useSharedValue(showControls ? 0 : -50);
- const bottomTranslateY = useSharedValue(showControls ? 0 : 50);
+ const wasPlayingRef = useRef(false);
+ const lastProgressRef = useRef(0);
+
+ const lightHapticFeedback = useHaptic("light");
useEffect(() => {
prefetchAllTrickplayImages();
- }, [prefetchAllTrickplayImages]);
+ }, []);
- // Animate controls visibility
- useEffect(() => {
- const animationConfig = {
- duration: 300,
- easing: Easing.out(Easing.quad),
- };
-
- controlsOpacity.value = withTiming(showControls ? 1 : 0, animationConfig);
- headerTranslateY.value = withTiming(
- showControls ? 0 : -10,
- animationConfig,
- );
- bottomTranslateY.value = withTiming(showControls ? 0 : 10, animationConfig);
- }, [showControls, controlsOpacity, headerTranslateY, bottomTranslateY]);
-
- // Create animated styles
- const headerAnimatedStyle = useAnimatedStyle(() => ({
- opacity: controlsOpacity.value,
- transform: [{ translateY: headerTranslateY.value }],
- position: "absolute" as const,
- top: 0,
- left: 0,
- right: 0,
- zIndex: 10,
- }));
-
- const centerAnimatedStyle = useAnimatedStyle(() => ({
- opacity: controlsOpacity.value,
- position: "absolute" as const,
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- zIndex: 5,
- }));
-
- const bottomAnimatedStyle = useAnimatedStyle(() => ({
- opacity: controlsOpacity.value,
- transform: [{ translateY: bottomTranslateY.value }],
- position: "absolute" as const,
- bottom: 0,
- left: 0,
- right: 0,
- zIndex: 10,
- }));
-
- // Initialize progress values - MPV uses milliseconds
useEffect(() => {
if (item) {
- progress.value = ticksToMs(item?.UserData?.PlaybackPositionTicks);
- max.value = ticksToMs(item.RunTimeTicks || 0);
+ progress.value = isVlc
+ ? ticksToMs(item?.UserData?.PlaybackPositionTicks)
+ : item?.UserData?.PlaybackPositionTicks || 0;
+ max.value = isVlc
+ ? ticksToMs(item.RunTimeTicks || 0)
+ : item.RunTimeTicks || 0;
}
- }, [item, progress, max]);
-
- // Navigation hooks
- const {
- handleSeekBackward,
- handleSeekForward,
- handleSkipBackward,
- handleSkipForward,
- } = useVideoNavigation({
- progress,
- isPlaying,
- seek,
- play,
- });
-
- // Time management hook
- const { currentTime, remainingTime } = useVideoTime({
- progress,
- max,
- isSeeking,
- });
-
- const toggleControls = useCallback(() => {
- if (showControls) {
- setShowAudioSlider(false);
- setShowControls(false);
- } else {
- setShowControls(true);
- }
- }, [showControls, setShowControls]);
-
- // Remote control hook
- const {
- remoteScrubProgress,
- isRemoteScrubbing,
- showRemoteBubble,
- isSliding: isRemoteSliding,
- time: remoteTime,
- } = useRemoteControl({
- progress,
- min,
- max,
- showControls,
- isPlaying,
- seek,
- play,
- togglePlay,
- toggleControls,
- calculateTrickplayUrl,
- handleSeekForward,
- handleSeekBackward,
- });
-
- // Slider hook
- const {
- isSliding,
- time,
- handleSliderStart,
- handleTouchStart,
- handleTouchEnd,
- handleSliderComplete,
- handleSliderChange,
- } = useVideoSlider({
- progress,
- isSeeking,
- isPlaying,
- seek,
- play,
- pause,
- calculateTrickplayUrl,
- showControls,
- });
-
- const effectiveProgress = useSharedValue(0);
-
- // Recompute progress whenever remote scrubbing is active or when progress significantly changes
- useAnimatedReaction(
- () => ({
- isScrubbing: isRemoteScrubbing.value,
- scrub: remoteScrubProgress.value,
- actual: progress.value,
- }),
- (current, previous) => {
- // Always update if scrubbing state changed or we're currently scrubbing
- if (
- current.isScrubbing !== previous?.isScrubbing ||
- current.isScrubbing
- ) {
- effectiveProgress.value =
- current.isScrubbing && current.scrub != null
- ? current.scrub
- : current.actual;
- } else {
- // When not scrubbing, only update if progress changed significantly (1 second)
- // MPV uses milliseconds
- const progressUnit = CONTROLS_CONSTANTS.PROGRESS_UNIT_MS;
- const progressDiff = Math.abs(current.actual - effectiveProgress.value);
- if (progressDiff >= progressUnit) {
- effectiveProgress.value = current.actual;
- }
- }
- },
- [],
- );
+ }, [item, isVlc]);
const { bitrateValue, subtitleIndex, audioIndex } = useLocalSearchParams<{
bitrateValue: string;
@@ -301,276 +174,675 @@ export const Controls: FC = ({
}>();
const { showSkipButton, skipIntro } = useIntroSkipper(
- item.Id!,
+ offline ? undefined : item.Id,
currentTime,
seek,
play,
- offline,
- api,
- downloadedFiles,
+ isVlc
);
- const { showSkipCreditButton, skipCredit, hasContentAfterCredits } =
- useCreditSkipper(
- item.Id!,
- currentTime,
- seek,
- play,
- offline,
- api,
- downloadedFiles,
- maxMs,
- );
-
- const goToItemCommon = useCallback(
- (item: BaseItemDto) => {
- if (!item || !settings) {
- return;
- }
- lightHapticFeedback();
- const previousIndexes = {
- subtitleIndex: subtitleIndex
- ? Number.parseInt(subtitleIndex, 10)
- : undefined,
- audioIndex: audioIndex ? Number.parseInt(audioIndex, 10) : undefined,
- };
-
- const {
- mediaSource: newMediaSource,
- audioIndex: defaultAudioIndex,
- subtitleIndex: defaultSubtitleIndex,
- } = getDefaultPlaySettings(item, settings, {
- indexes: previousIndexes,
- source: mediaSource ?? undefined,
- });
-
- const queryParams = new URLSearchParams({
- ...(offline && { offline: "true" }),
- itemId: item.Id ?? "",
- audioIndex: defaultAudioIndex?.toString() ?? "",
- subtitleIndex: defaultSubtitleIndex?.toString() ?? "",
- mediaSourceId: newMediaSource?.Id ?? "",
- bitrateValue: bitrateValue?.toString(),
- playbackPosition:
- item.UserData?.PlaybackPositionTicks?.toString() ?? "",
- }).toString();
-
- router.replace(`player/direct-player?${queryParams}` as any);
- },
- [settings, subtitleIndex, audioIndex, mediaSource, bitrateValue, router],
+ const { showSkipCreditButton, skipCredit } = useCreditSkipper(
+ offline ? undefined : item.Id,
+ currentTime,
+ seek,
+ play,
+ isVlc
);
const goToPreviousItem = useCallback(() => {
- if (!previousItem) {
- return;
- }
- goToItemCommon(previousItem);
- }, [previousItem, goToItemCommon]);
+ if (!previousItem || !settings) return;
- const goToNextItem = useCallback(
- ({
- isAutoPlay,
- resetWatchCount,
- }: {
- isAutoPlay?: boolean;
- resetWatchCount?: boolean;
- }) => {
- if (!nextItem) {
- return;
- }
+ lightHapticFeedback();
- if (!isAutoPlay) {
- // if we are not autoplaying, we won't update anything, we just go to the next item
- goToItemCommon(nextItem);
- if (resetWatchCount) {
- updateSettings({
- autoPlayEpisodeCount: 0,
- });
- }
- return;
- }
+ const previousIndexes: previousIndexes = {
+ subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined,
+ audioIndex: audioIndex ? parseInt(audioIndex) : undefined,
+ };
- // Skip autoplay logic if maxAutoPlayEpisodeCount is -1
- if (settings.maxAutoPlayEpisodeCount.value === -1) {
- goToItemCommon(nextItem);
- return;
- }
+ const {
+ mediaSource: newMediaSource,
+ audioIndex: defaultAudioIndex,
+ subtitleIndex: defaultSubtitleIndex,
+ } = getDefaultPlaySettings(
+ previousItem,
+ settings,
+ previousIndexes,
+ mediaSource ?? undefined
+ );
- if (
- settings.autoPlayEpisodeCount + 1 <
- settings.maxAutoPlayEpisodeCount.value
- ) {
- goToItemCommon(nextItem);
- }
+ const queryParams = new URLSearchParams({
+ itemId: previousItem.Id ?? "", // Ensure itemId is a string
+ audioIndex: defaultAudioIndex?.toString() ?? "",
+ subtitleIndex: defaultSubtitleIndex?.toString() ?? "",
+ mediaSourceId: newMediaSource?.Id ?? "", // Ensure mediaSourceId is a string
+ bitrateValue: bitrateValue.toString(),
+ }).toString();
- // Check if the autoPlayEpisodeCount is less than maxAutoPlayEpisodeCount for the autoPlay
- if (
- settings.autoPlayEpisodeCount < settings.maxAutoPlayEpisodeCount.value
- ) {
- // update the autoPlayEpisodeCount in settings
- updateSettings({
- autoPlayEpisodeCount: settings.autoPlayEpisodeCount + 1,
- });
- }
+ stop();
+
+ // @ts-expect-error
+ router.replace(`player/direct-player?${queryParams}`);
+ }, [previousItem, settings, subtitleIndex, audioIndex]);
+
+ const goToNextItem = useCallback(() => {
+ if (!nextItem || !settings) return;
+
+ lightHapticFeedback();
+
+ const previousIndexes: previousIndexes = {
+ subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined,
+ audioIndex: audioIndex ? parseInt(audioIndex) : undefined,
+ };
+
+ const {
+ mediaSource: newMediaSource,
+ audioIndex: defaultAudioIndex,
+ subtitleIndex: defaultSubtitleIndex,
+ } = getDefaultPlaySettings(
+ nextItem,
+ settings,
+ previousIndexes,
+ mediaSource ?? undefined
+ );
+
+ const queryParams = new URLSearchParams({
+ itemId: nextItem.Id ?? "", // Ensure itemId is a string
+ audioIndex: defaultAudioIndex?.toString() ?? "",
+ subtitleIndex: defaultSubtitleIndex?.toString() ?? "",
+ mediaSourceId: newMediaSource?.Id ?? "", // Ensure mediaSourceId is a string
+ bitrateValue: bitrateValue.toString(),
+ }).toString();
+
+ stop();
+
+ // @ts-expect-error
+ router.replace(`player/direct-player?${queryParams}`);
+ }, [nextItem, settings, subtitleIndex, audioIndex]);
+
+ const updateTimes = useCallback(
+ (currentProgress: number, maxValue: number) => {
+ const current = isVlc ? currentProgress : ticksToSeconds(currentProgress);
+ const remaining = isVlc
+ ? maxValue - currentProgress
+ : ticksToSeconds(maxValue - currentProgress);
+
+ setCurrentTime(current);
+ setRemainingTime(remaining);
},
- [nextItem, goToItemCommon],
+ [goToNextItem, isVlc]
);
- // Add a memoized handler for autoplay next episode
- const handleNextEpisodeAutoPlay = useCallback(() => {
- goToNextItem({ isAutoPlay: true });
- }, [goToNextItem]);
-
- // Add a memoized handler for manual next episode
- const handleNextEpisodeManual = useCallback(() => {
- goToNextItem({ isAutoPlay: false });
- }, [goToNextItem]);
-
- // Add a memoized handler for ContinueWatchingOverlay
- const handleContinueWatching = useCallback(
- (options: { isAutoPlay?: boolean; resetWatchCount?: boolean }) => {
- goToNextItem(options);
+ useAnimatedReaction(
+ () => ({
+ progress: progress.value,
+ max: max.value,
+ isSeeking: isSeeking.value,
+ }),
+ (result) => {
+ if (result.isSeeking === false) {
+ runOnJS(updateTimes)(result.progress, result.max);
+ }
},
- [goToNextItem],
+ [updateTimes]
);
const hideControls = useCallback(() => {
setShowControls(false);
setShowAudioSlider(false);
- }, [setShowControls]);
+ }, []);
const { handleControlsInteraction } = useControlsTimeout({
showControls,
- isSliding: isSliding || isRemoteSliding,
+ isSliding,
episodeView,
onHideControls: hideControls,
- timeout: CONTROLS_CONSTANTS.TIMEOUT,
- disabled: true,
+ timeout: CONTROLS_TIMEOUT,
});
+ const toggleControls = () => {
+ if (showControls) {
+ setShowAudioSlider(false);
+ setShowControls(false);
+ } else {
+ setShowControls(true);
+ }
+ };
+
+ const handleSliderStart = useCallback(() => {
+ if (showControls === false) return;
+
+ setIsSliding(true);
+ wasPlayingRef.current = isPlaying;
+ lastProgressRef.current = progress.value;
+
+ pause();
+ isSeeking.value = true;
+ }, [showControls, isPlaying]);
+
+ const handleSliderComplete = useCallback(
+ async (value: number) => {
+ isSeeking.value = false;
+ progress.value = value;
+ setIsSliding(false);
+
+ seek(Math.max(0, Math.floor(isVlc ? value : ticksToSeconds(value))));
+ if (wasPlayingRef.current === true) play();
+ },
+ [isVlc]
+ );
+
+ const [time, setTime] = useState({ hours: 0, minutes: 0, seconds: 0 });
+ const handleSliderChange = useCallback(
+ debounce((value: number) => {
+ const progressInTicks = isVlc ? msToTicks(value) : value;
+ calculateTrickplayUrl(progressInTicks);
+ const progressInSeconds = Math.floor(ticksToSeconds(progressInTicks));
+ const hours = Math.floor(progressInSeconds / 3600);
+ const minutes = Math.floor((progressInSeconds % 3600) / 60);
+ const seconds = progressInSeconds % 60;
+ setTime({ hours, minutes, seconds });
+ }, 3),
+ []
+ );
+
+ const handleSkipBackward = useCallback(async () => {
+ if (!settings?.rewindSkipTime) return;
+ wasPlayingRef.current = isPlaying;
+ lightHapticFeedback();
+ try {
+ const curr = progress.value;
+ if (curr !== undefined) {
+ const newTime = isVlc
+ ? Math.max(0, curr - secondsToMs(settings.rewindSkipTime))
+ : Math.max(0, ticksToSeconds(curr) - settings.rewindSkipTime);
+ seek(newTime);
+ if (wasPlayingRef.current === true) play();
+ }
+ } catch (error) {
+ writeToLog("ERROR", "Error seeking video backwards", error);
+ }
+ }, [settings, isPlaying, isVlc]);
+
+ const handleSkipForward = useCallback(async () => {
+ if (!settings?.forwardSkipTime) return;
+ wasPlayingRef.current = isPlaying;
+ lightHapticFeedback();
+ try {
+ const curr = progress.value;
+ if (curr !== undefined) {
+ const newTime = isVlc
+ ? curr + secondsToMs(settings.forwardSkipTime)
+ : ticksToSeconds(curr) + settings.forwardSkipTime;
+ seek(Math.max(0, newTime));
+ if (wasPlayingRef.current === true) play();
+ }
+ } catch (error) {
+ writeToLog("ERROR", "Error seeking video forwards", error);
+ }
+ }, [settings, isPlaying, isVlc]);
+
+ const goToItem = useCallback(
+ async (itemId: string) => {
+ try {
+ const gotoItem = await getItemById(api, itemId);
+ if (!settings || !gotoItem) return;
+
+ lightHapticFeedback();
+
+ const previousIndexes: previousIndexes = {
+ subtitleIndex: subtitleIndex ? parseInt(subtitleIndex) : undefined,
+ audioIndex: audioIndex ? parseInt(audioIndex) : undefined,
+ };
+
+ const {
+ mediaSource: newMediaSource,
+ audioIndex: defaultAudioIndex,
+ subtitleIndex: defaultSubtitleIndex,
+ } = getDefaultPlaySettings(
+ gotoItem,
+ settings,
+ previousIndexes,
+ mediaSource ?? undefined
+ );
+
+ const queryParams = new URLSearchParams({
+ itemId: gotoItem.Id ?? "", // Ensure itemId is a string
+ audioIndex: defaultAudioIndex?.toString() ?? "",
+ subtitleIndex: defaultSubtitleIndex?.toString() ?? "",
+ mediaSourceId: newMediaSource?.Id ?? "", // Ensure mediaSourceId is a string
+ bitrateValue: bitrateValue.toString(),
+ }).toString();
+
+ stop();
+
+ // @ts-expect-error
+ router.replace(`player/direct-player?${queryParams}`);
+ } catch (error) {
+ console.error("Error in gotoEpisode:", error);
+ }
+ },
+ [settings, subtitleIndex, audioIndex]
+ );
+
+ const toggleIgnoreSafeAreas = useCallback(() => {
+ setIgnoreSafeAreas((prev) => !prev);
+ lightHapticFeedback();
+ }, []);
+
const switchOnEpisodeMode = useCallback(() => {
setEpisodeView(true);
- if (isPlaying) {
- togglePlay();
- }
+ if (isPlaying) togglePlay();
}, [isPlaying, togglePlay]);
+ const memoizedRenderBubble = useCallback(() => {
+ if (!trickPlayUrl || !trickplayInfo) {
+ return null;
+ }
+ const { x, y, url } = trickPlayUrl;
+ const tileWidth = 150;
+ const tileHeight = 150 / trickplayInfo.aspectRatio!;
+
+ return (
+
+
+
+
+
+ {`${time.hours > 0 ? `${time.hours}:` : ""}${
+ time.minutes < 10 ? `0${time.minutes}` : time.minutes
+ }:${time.seconds < 10 ? `0${time.seconds}` : time.seconds}`}
+
+
+ );
+ }, [trickPlayUrl, trickplayInfo, time]);
+
+ const onClose = async () => {
+ stop();
+ lightHapticFeedback();
+ await ScreenOrientation.lockAsync(
+ ScreenOrientation.OrientationLock.PORTRAIT_UP
+ );
+ router.back();
+ };
+
return (
-
+
{episodeView ? (
setEpisodeView(false)}
- goToItem={goToItemCommon}
+ goToItem={goToItem}
/>
) : (
<>
-
- {/* Technical Info Overlay - rendered outside animated views to stay visible */}
- {getTechnicalInfo && (
-
- )}
-
-
-
-
+
+
+
+
+ )}
+
+
+ {!Platform.isTV && (
+
+
+
+ )}
+
+ {item?.Type === "Episode" && !offline && (
+ {
+ switchOnEpisodeMode();
+ }}
+ className="aspect-square flex flex-col rounded-xl items-center justify-center p-2"
+ >
+
+
+ )}
+ {previousItem && !offline && (
+
+
+
+ )}
+
+ {nextItem && !offline && (
+
+
+
+ )}
+
+ {/* {mediaSource?.TranscodingUrl && ( */}
+
+
+
+ {/* )} */}
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+ {settings?.rewindSkipTime}
+
+
+
+
+ {
+ togglePlay();
+ }}
+ >
+ {!isBuffering ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ {settings?.forwardSkipTime}
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+ {item?.Type === "Episode" && (
+
+ {`${item.SeriesName} - ${item.SeasonName} Episode ${item.IndexNumber}`}
+
+ )}
+ {item?.Name}
+ {item?.Type === "Movie" && (
+
+ {item?.ProductionYear}
+
+ )}
+ {item?.Type === "Audio" && (
+ {item?.Album}
+ )}
+
+
+
+
+
+
+
+
+
+ null}
+ cache={cacheProgress}
+ onSlidingStart={handleSliderStart}
+ onSlidingComplete={handleSliderComplete}
+ onValueChange={handleSliderChange}
+ containerStyle={{
+ borderRadius: 100,
+ }}
+ renderBubble={() => isSliding && memoizedRenderBubble()}
+ sliderHeight={10}
+ thumbWidth={0}
+ progress={progress}
+ minimumValue={min}
+ maximumValue={max}
+ />
+
+
+ {formatTimeString(currentTime, isVlc ? "ms" : "s")}
+
+
+ -{formatTimeString(remainingTime, isVlc ? "ms" : "s")}
+
+
+
+
+
>
)}
- {settings.maxAutoPlayEpisodeCount.value !== -1 && (
-
- )}
-
+
);
};
-
-const styles = StyleSheet.create({
- controlsContainer: {
- position: "absolute",
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- },
-});
diff --git a/components/video-player/controls/contexts/VideoContext.tsx b/components/video-player/controls/contexts/VideoContext.tsx
index ec9ca995..7a4e5161 100644
--- a/components/video-player/controls/contexts/VideoContext.tsx
+++ b/components/video-player/controls/contexts/VideoContext.tsx
@@ -1,361 +1,226 @@
-/**
- * VideoContext.tsx
- *
- * Manages subtitle and audio track state for the video player UI.
- *
- * ============================================================================
- * ARCHITECTURE
- * ============================================================================
- *
- * - Jellyfin is source of truth for subtitle list (embedded + external)
- * - MPV only knows about:
- * - Embedded subs it finds in the video stream
- * - External subs we explicitly add via addSubtitleFile()
- * - UI shows Jellyfin's complete list
- * - On selection: either select embedded track or load external URL
- *
- * ============================================================================
- * INDEX TYPES
- * ============================================================================
- *
- * 1. SERVER INDEX (sub.Index / track.index)
- * - Jellyfin's server-side stream index
- * - Used to report playback state to Jellyfin server
- * - Value of -1 means disabled/none
- *
- * 2. MPV INDEX (track.mpvIndex)
- * - MPV's internal track ID
- * - MPV orders tracks as: [all embedded, then all external]
- * - IDs: 1..embeddedCount for embedded, embeddedCount+1.. for external
- * - Value of -1 means track needs replacePlayer() (e.g., burned-in sub)
- *
- * ============================================================================
- * SUBTITLE HANDLING
- * ============================================================================
- *
- * Embedded (DeliveryMethod.Embed):
- * - Already in MPV's track list
- * - Select via setSubtitleTrack(mpvId)
- *
- * External (DeliveryMethod.External):
- * - Loaded into MPV on video start
- * - Select via setSubtitleTrack(embeddedCount + externalPosition + 1)
- *
- * Image-based during transcoding:
- * - Burned into video by Jellyfin, not in MPV
- * - Requires replacePlayer() to change
- */
-
-import { SubtitleDeliveryMethod } from "@jellyfin/sdk/lib/generated-client";
-import { useLocalSearchParams } from "expo-router";
-import type React from "react";
+import { TrackInfo } from "@/modules/vlc-player";
import {
+ BaseItemDto,
+ MediaSourceInfo,
+} from "@jellyfin/sdk/lib/generated-client";
+import React, {
createContext,
- type ReactNode,
useContext,
+ useState,
+ ReactNode,
useEffect,
useMemo,
- useState,
} from "react";
-import useRouter from "@/hooks/useAppRouter";
-import type { MpvAudioTrack } from "@/modules";
-import { useOfflineMode } from "@/providers/OfflineModeProvider";
-import { isImageBasedSubtitle } from "@/utils/jellyfin/subtitleUtils";
-import type { Track } from "../types";
-import { usePlayerContext, usePlayerControls } from "./PlayerContext";
+import { useControlContext } from "./ControlContext";
+import { Track } from "../types";
+import { router, useLocalSearchParams } from "expo-router";
interface VideoContextProps {
- subtitleTracks: Track[] | null;
audioTracks: Track[] | null;
+ subtitleTracks: Track[] | null;
+ setAudioTrack: ((index: number) => void) | undefined;
+ setSubtitleTrack: ((index: number) => void) | undefined;
+ setSubtitleURL: ((url: string, customName: string) => void) | undefined;
}
const VideoContext = createContext(undefined);
-export const VideoProvider: React.FC<{ children: ReactNode }> = ({
+interface VideoProviderProps {
+ children: ReactNode;
+ getAudioTracks:
+ | (() => Promise)
+ | (() => TrackInfo[])
+ | undefined;
+ getSubtitleTracks:
+ | (() => Promise)
+ | (() => TrackInfo[])
+ | undefined;
+ setAudioTrack: ((index: number) => void) | undefined;
+ setSubtitleTrack: ((index: number) => void) | undefined;
+ setSubtitleURL: ((url: string, customName: string) => void) | undefined;
+}
+
+export const VideoProvider: React.FC = ({
children,
+ getSubtitleTracks,
+ getAudioTracks,
+ setSubtitleTrack,
+ setSubtitleURL,
+ setAudioTrack,
}) => {
- const [subtitleTracks, setSubtitleTracks] = useState(null);
const [audioTracks, setAudioTracks] = useState(null);
+ const [subtitleTracks, setSubtitleTracks] = useState(null);
- const { tracksReady, mediaSource, downloadedItem } = usePlayerContext();
- const playerControls = usePlayerControls();
- const offline = useOfflineMode();
- const router = useRouter();
+ const ControlContext = useControlContext();
+ const isVideoLoaded = ControlContext?.isVideoLoaded;
+ const mediaSource = ControlContext?.mediaSource;
- const { itemId, audioIndex, bitrateValue, subtitleIndex, playbackPosition } =
+ const allSubs =
+ mediaSource?.MediaStreams?.filter((s) => s.Type === "Subtitle") || [];
+
+ const { itemId, audioIndex, bitrateValue, subtitleIndex } =
useLocalSearchParams<{
itemId: string;
audioIndex: string;
subtitleIndex: string;
mediaSourceId: string;
bitrateValue: string;
- playbackPosition: string;
}>();
- const allSubs =
- mediaSource?.MediaStreams?.filter((s) => s.Type === "Subtitle") || [];
- const allAudio =
- mediaSource?.MediaStreams?.filter((s) => s.Type === "Audio") || [];
+ const onTextBasedSubtitle = useMemo(
+ () =>
+ allSubs.find(
+ (s) => s.Index?.toString() === subtitleIndex && s.IsTextSubtitleStream
+ ) || subtitleIndex === "-1",
+ [allSubs, subtitleIndex]
+ );
- const isTranscoding = Boolean(mediaSource?.TranscodingUrl);
-
- /**
- * Check if the currently selected subtitle is image-based.
- * Used to determine if we need to refresh the player when changing subs.
- */
- const isCurrentSubImageBased = useMemo(() => {
- if (subtitleIndex === "-1") return false;
- const currentSub = allSubs.find(
- (s) => s.Index?.toString() === subtitleIndex,
- );
- return currentSub ? isImageBasedSubtitle(currentSub) : false;
- }, [allSubs, subtitleIndex]);
-
- /**
- * Refresh the player with new parameters.
- * This triggers Jellyfin to re-process the stream (e.g., burn in image subs).
- */
- const replacePlayer = (params: {
- audioIndex?: string;
- subtitleIndex?: string;
+ const setPlayerParams = ({
+ chosenAudioIndex = audioIndex,
+ chosenSubtitleIndex = subtitleIndex,
+ }: {
+ chosenAudioIndex?: string;
+ chosenSubtitleIndex?: string;
}) => {
+ console.log("chosenSubtitleIndex", chosenSubtitleIndex);
const queryParams = new URLSearchParams({
itemId: itemId ?? "",
- audioIndex: params.audioIndex ?? audioIndex,
- subtitleIndex: params.subtitleIndex ?? subtitleIndex,
+ audioIndex: chosenAudioIndex,
+ subtitleIndex: chosenSubtitleIndex,
mediaSourceId: mediaSource?.Id ?? "",
bitrateValue: bitrateValue,
- playbackPosition: playbackPosition,
}).toString();
- router.replace(`player/direct-player?${queryParams}` as any);
+
+ //@ts-ignore
+ router.replace(`player/direct-player?${queryParams}`);
+ };
+
+ const setTrackParams = (
+ type: "audio" | "subtitle",
+ index: number,
+ serverIndex: number
+ ) => {
+ const setTrack = type === "audio" ? setAudioTrack : setSubtitleTrack;
+ const paramKey = type === "audio" ? "audioIndex" : "subtitleIndex";
+
+ // If we're transcoding and we're going from a image based subtitle
+ // to a text based subtitle, we need to change the player params.
+
+ const shouldChangePlayerParams =
+ type === "subtitle" &&
+ mediaSource?.TranscodingUrl &&
+ !onTextBasedSubtitle;
+
+ console.log("Set player params", index, serverIndex);
+ if (shouldChangePlayerParams) {
+ setPlayerParams({
+ chosenSubtitleIndex: serverIndex.toString(),
+ });
+ return;
+ }
+ setTrack && setTrack(index);
+ router.setParams({
+ [paramKey]: serverIndex.toString(),
+ });
};
- // Fetch tracks when ready
useEffect(() => {
- if (!tracksReady) return;
-
const fetchTracks = async () => {
- // Check if this is offline transcoded content
- // For transcoded offline content, only ONE audio track exists in the file
- const isOfflineTranscoded =
- offline && downloadedItem?.userData?.isTranscoded === true;
+ if (getSubtitleTracks) {
+ const subtitleData = await getSubtitleTracks();
- if (isOfflineTranscoded) {
- // Build single audio track entry - only the downloaded track exists
- const downloadedAudioIndex = downloadedItem.userData.audioStreamIndex;
- const downloadedTrack = allAudio.find(
- (a) => a.Index === downloadedAudioIndex,
- );
+ let textSubIndex = 0;
+ const subtitles: Track[] = allSubs?.map((sub) => {
+ // Always increment for non-transcoding subtitles
+ // Only increment for text-based subtitles when transcoding
+ const shouldIncrement =
+ !mediaSource?.TranscodingUrl || sub.IsTextSubtitleStream;
- if (downloadedTrack) {
- const audio: Track[] = [
- {
- name: downloadedTrack.DisplayTitle || "Audio",
- index: downloadedTrack.Index ?? 0,
- mpvIndex: 1, // Only track in file (MPV uses 1-based indexing)
- setTrack: () => {
- // Track is already selected (only one available)
- router.setParams({ audioIndex: String(downloadedTrack.Index) });
- },
- },
- ];
- setAudioTracks(audio);
- } else {
- // Fallback: show no audio tracks if the stored track wasn't found
- setAudioTracks([]);
- }
+ const displayTitle = sub.DisplayTitle || "Undefined Subtitle";
+ const vlcIndex = subtitleData?.at(textSubIndex)?.index ?? -1;
- // For subtitles in transcoded offline content:
- // - Text-based subs may still be embedded
- // - Image-based subs were burned in during transcoding
- const downloadedSubtitleIndex =
- downloadedItem.userData.subtitleStreamIndex;
- const subs: Track[] = [];
+ const finalIndex = shouldIncrement ? vlcIndex : sub.Index ?? -1;
- // Add "Disable" option
- subs.push({
+ if (shouldIncrement) textSubIndex++;
+ return {
+ name: displayTitle,
+ index: sub.Index ?? -1,
+ originalIndex: finalIndex,
+ setTrack: () =>
+ shouldIncrement
+ ? setTrackParams("subtitle", finalIndex, sub.Index ?? -1)
+ : setPlayerParams({
+ chosenSubtitleIndex: sub.Index?.toString(),
+ }),
+ };
+ });
+
+ // Add a "Disable Subtitles" option
+ subtitles.unshift({
name: "Disable",
index: -1,
- mpvIndex: -1,
- setTrack: () => {
- playerControls.setSubtitleTrack(-1);
- router.setParams({ subtitleIndex: "-1" });
- },
+ setTrack: () =>
+ !mediaSource?.TranscodingUrl || onTextBasedSubtitle
+ ? setTrackParams("subtitle", -1, -1)
+ : setPlayerParams({ chosenSubtitleIndex: "-1" }),
});
- // For text-based subs, they should still be available in the file
- let subIdx = 1;
- for (const sub of allSubs) {
- if (sub.IsTextSubtitleStream) {
- subs.push({
- name: sub.DisplayTitle || "Unknown",
- index: sub.Index ?? -1,
- mpvIndex: subIdx,
- setTrack: () => {
- playerControls.setSubtitleTrack(subIdx);
- router.setParams({ subtitleIndex: String(sub.Index) });
- },
- });
- subIdx++;
- } else if (sub.Index === downloadedSubtitleIndex) {
- // This image-based sub was burned in - show it but indicate it's active
- subs.push({
- name: `${sub.DisplayTitle || "Unknown"} (burned in)`,
- index: sub.Index ?? -1,
- mpvIndex: -1, // Can't be changed
- setTrack: () => {
- // Already burned in, just update params
- router.setParams({ subtitleIndex: String(sub.Index) });
- },
- });
- }
- }
-
- setSubtitleTracks(subs.sort((a, b) => a.index - b.index));
- return;
+ setSubtitleTracks(subtitles);
}
+ if (
+ getAudioTracks &&
+ (audioTracks === null || audioTracks.length === 0)
+ ) {
+ const audioData = await getAudioTracks();
+ if (!audioData) return;
- // MPV track handling
- const audioData = await playerControls.getAudioTracks().catch(() => null);
- const playerAudio = (audioData as MpvAudioTrack[]) ?? [];
+ console.log("audioData", audioData);
- // Separate embedded vs external subtitles from Jellyfin's list
- // MPV orders tracks as: [all embedded, then all external]
- const embeddedSubs = allSubs.filter(
- (s) => s.DeliveryMethod === SubtitleDeliveryMethod.Embed,
- );
- const externalSubs = allSubs.filter(
- (s) => s.DeliveryMethod === SubtitleDeliveryMethod.External,
- );
+ const allAudio =
+ mediaSource?.MediaStreams?.filter((s) => s.Type === "Audio") || [];
- // Count embedded subs that will be in MPV
- // (excludes image-based subs during transcoding as they're burned in)
- const embeddedInPlayer = embeddedSubs.filter(
- (s) => !isTranscoding || !isImageBasedSubtitle(s),
- );
-
- const subs: Track[] = [];
-
- // Process all Jellyfin subtitles
- for (const sub of allSubs) {
- const isEmbedded = sub.DeliveryMethod === SubtitleDeliveryMethod.Embed;
- const isExternal =
- sub.DeliveryMethod === SubtitleDeliveryMethod.External;
-
- // For image-based subs during transcoding, need to refresh player
- if (isTranscoding && isImageBasedSubtitle(sub)) {
- subs.push({
- name: sub.DisplayTitle || "Unknown",
- index: sub.Index ?? -1,
- mpvIndex: -1,
- setTrack: () => {
- replacePlayer({ subtitleIndex: String(sub.Index) });
- },
- });
- continue;
- }
-
- // Calculate MPV track ID based on type
- // MPV IDs: [1..embeddedCount] for embedded, [embeddedCount+1..] for external
- let mpvId = -1;
-
- if (isEmbedded) {
- // Find position among embedded subs that are in player
- const embeddedPosition = embeddedInPlayer.findIndex(
- (s) => s.Index === sub.Index,
- );
- if (embeddedPosition !== -1) {
- mpvId = embeddedPosition + 1; // 1-based ID
+ const audioTracks: Track[] = allAudio?.map((audio, idx) => {
+ if (!mediaSource?.TranscodingUrl) {
+ const vlcIndex = audioData?.at(idx)?.index ?? -1;
+ return {
+ name: audio.DisplayTitle ?? "Undefined Audio",
+ index: audio.Index ?? -1,
+ setTrack: () =>
+ setTrackParams("audio", vlcIndex, audio.Index ?? -1),
+ };
}
- } else if (isExternal) {
- // Find position among external subs, offset by embedded count
- const externalPosition = externalSubs.findIndex(
- (s) => s.Index === sub.Index,
- );
- if (externalPosition !== -1) {
- mpvId = embeddedInPlayer.length + externalPosition + 1;
- }
- }
-
- subs.push({
- name: sub.DisplayTitle || "Unknown",
- index: sub.Index ?? -1,
- mpvIndex: mpvId,
- setTrack: () => {
- // Transcoding + switching to/from image-based sub
- if (
- isTranscoding &&
- (isImageBasedSubtitle(sub) || isCurrentSubImageBased)
- ) {
- replacePlayer({ subtitleIndex: String(sub.Index) });
- return;
- }
-
- // Direct switch in player
- if (mpvId !== -1) {
- playerControls.setSubtitleTrack(mpvId);
- router.setParams({ subtitleIndex: String(sub.Index) });
- return;
- }
-
- // Fallback - refresh player
- replacePlayer({ subtitleIndex: String(sub.Index) });
- },
+ return {
+ name: audio.DisplayTitle ?? "Undefined Audio",
+ index: audio.Index ?? -1,
+ setTrack: () =>
+ setPlayerParams({ chosenAudioIndex: audio.Index?.toString() }),
+ };
});
+ setAudioTracks(audioTracks);
}
-
- // Add "Disable" option at the beginning
- subs.unshift({
- name: "Disable",
- index: -1,
- mpvIndex: -1,
- setTrack: () => {
- if (isTranscoding && isCurrentSubImageBased) {
- replacePlayer({ subtitleIndex: "-1" });
- } else {
- playerControls.setSubtitleTrack(-1);
- router.setParams({ subtitleIndex: "-1" });
- }
- },
- });
-
- // Process audio tracks
- const audio: Track[] = allAudio.map((a, idx) => {
- const playerTrack = playerAudio[idx];
- const mpvId = playerTrack?.id ?? idx + 1;
-
- return {
- name: a.DisplayTitle || "Unknown",
- index: a.Index ?? -1,
- mpvIndex: mpvId,
- setTrack: () => {
- if (isTranscoding) {
- replacePlayer({ audioIndex: String(a.Index) });
- return;
- }
- playerControls.setAudioTrack(mpvId);
- router.setParams({ audioIndex: String(a.Index) });
- },
- };
- });
-
- setSubtitleTracks(subs.sort((a, b) => a.index - b.index));
- setAudioTracks(audio);
};
-
fetchTracks();
- }, [tracksReady, mediaSource, offline, downloadedItem]);
+ }, [isVideoLoaded, getAudioTracks, getSubtitleTracks]);
return (
-
+
{children}
);
};
export const useVideoContext = () => {
- const ctx = useContext(VideoContext);
- if (!ctx)
- throw new Error("useVideoContext must be used within VideoProvider");
- return ctx;
+ const context = useContext(VideoContext);
+ if (context === undefined) {
+ throw new Error("useVideoContext must be used within a VideoProvider");
+ }
+ return context;
};
diff --git a/components/video-player/controls/dropdown/DropdownView.tsx b/components/video-player/controls/dropdown/DropdownView.tsx
index 5b631ec4..0ee51dc1 100644
--- a/components/video-player/controls/dropdown/DropdownView.tsx
+++ b/components/video-player/controls/dropdown/DropdownView.tsx
@@ -1,228 +1,96 @@
+import React from "react";
+import { TouchableOpacity, Platform } from "react-native";
import { Ionicons } from "@expo/vector-icons";
-import { useLocalSearchParams } from "expo-router";
-import { useCallback, useMemo, useRef } from "react";
-import { Platform, View } from "react-native";
-import { BITRATES } from "@/components/BitrateSelector";
-import {
- type OptionGroup,
- PlatformDropdown,
-} from "@/components/PlatformDropdown";
-import { PLAYBACK_SPEEDS } from "@/components/PlaybackSpeedSelector";
-import useRouter from "@/hooks/useAppRouter";
-import { useOfflineMode } from "@/providers/OfflineModeProvider";
-import { useSettings } from "@/utils/atoms/settings";
-import { usePlayerContext } from "../contexts/PlayerContext";
+const DropdownMenu = !Platform.isTV ? require("zeego/dropdown-menu") : null;
import { useVideoContext } from "../contexts/VideoContext";
-import { PlaybackSpeedScope } from "../utils/playback-speed-settings";
-
-// Subtitle size presets (stored as scale * 100, so 1.0 = 100)
-const SUBTITLE_SIZE_PRESETS = [
- { label: "0.5", value: 50 },
- { label: "0.6", value: 60 },
- { label: "0.7", value: 70 },
- { label: "0.8", value: 80 },
- { label: "0.9", value: 90 },
- { label: "1.0", value: 100 },
- { label: "1.1", value: 110 },
- { label: "1.2", value: 120 },
-] as const;
+import { useLocalSearchParams } from "expo-router";
interface DropdownViewProps {
- playbackSpeed?: number;
- setPlaybackSpeed?: (speed: number, scope: PlaybackSpeedScope) => void;
- showTechnicalInfo?: boolean;
- onToggleTechnicalInfo?: () => void;
+ showControls: boolean;
+ offline?: boolean; // used to disable external subs for downloads
}
-const DropdownView = ({
- playbackSpeed = 1.0,
- setPlaybackSpeed,
- showTechnicalInfo = false,
- onToggleTechnicalInfo,
-}: DropdownViewProps) => {
- const { subtitleTracks, audioTracks } = useVideoContext();
- const { item, mediaSource } = usePlayerContext();
- const { settings, updateSettings } = useSettings();
- const router = useRouter();
- const isOffline = useOfflineMode();
+const DropdownView: React.FC = ({
+ showControls,
+ offline = false,
+}) => {
+ const videoContext = useVideoContext();
+ const { subtitleTracks, audioTracks } = videoContext;
- const { subtitleIndex, audioIndex, bitrateValue, playbackPosition } =
- useLocalSearchParams<{
- itemId: string;
- audioIndex: string;
- subtitleIndex: string;
- mediaSourceId: string;
- bitrateValue: string;
- playbackPosition: string;
- }>();
-
- // Use ref to track playbackPosition without causing re-renders
- const playbackPositionRef = useRef(playbackPosition);
- playbackPositionRef.current = playbackPosition;
-
- // Stabilize IDs to prevent unnecessary recalculations
- const itemIdRef = useRef(item.Id);
- const mediaSourceIdRef = useRef(mediaSource?.Id);
- itemIdRef.current = item.Id;
- mediaSourceIdRef.current = mediaSource?.Id;
-
- const changeBitrate = useCallback(
- (bitrate: string) => {
- const queryParams = new URLSearchParams({
- itemId: itemIdRef.current ?? "",
- audioIndex: audioIndex?.toString() ?? "",
- subtitleIndex: subtitleIndex?.toString() ?? "",
- mediaSourceId: mediaSourceIdRef.current ?? "",
- bitrateValue: bitrate.toString(),
- playbackPosition: playbackPositionRef.current,
- }).toString();
- router.replace(`player/direct-player?${queryParams}` as any);
- },
- [audioIndex, subtitleIndex, router],
- );
-
- // Create stable identifiers for tracks
- const subtitleTracksKey = useMemo(
- () => subtitleTracks?.map((t) => `${t.index}-${t.name}`).join(",") ?? "",
- [subtitleTracks],
- );
-
- const audioTracksKey = useMemo(
- () => audioTracks?.map((t) => `${t.index}-${t.name}`).join(",") ?? "",
- [audioTracks],
- );
-
- // Transform sections into OptionGroup format
- const optionGroups = useMemo(() => {
- const groups: OptionGroup[] = [];
-
- // Quality Section
- if (!isOffline) {
- groups.push({
- title: "Quality",
- options:
- BITRATES?.map((bitrate) => ({
- type: "radio" as const,
- label: bitrate.key,
- value: bitrate.value?.toString() ?? "",
- selected: bitrateValue === (bitrate.value?.toString() ?? ""),
- onPress: () => changeBitrate(bitrate.value?.toString() ?? ""),
- })) || [],
- });
- }
-
- // Subtitle Section
- if (subtitleTracks && subtitleTracks.length > 0) {
- groups.push({
- title: "Subtitles",
- options: subtitleTracks.map((sub) => ({
- type: "radio" as const,
- label: sub.name,
- value: sub.index.toString(),
- selected: subtitleIndex === sub.index.toString(),
- onPress: () => sub.setTrack(),
- })),
- });
-
- // Subtitle Size Section
- groups.push({
- title: "Subtitle Size",
- options: SUBTITLE_SIZE_PRESETS.map((preset) => ({
- type: "radio" as const,
- label: preset.label,
- value: preset.value.toString(),
- selected: settings.subtitleSize === preset.value,
- onPress: () => updateSettings({ subtitleSize: preset.value }),
- })),
- });
- }
-
- // Audio Section
- if (audioTracks && audioTracks.length > 0) {
- groups.push({
- title: "Audio",
- options: audioTracks.map((track) => ({
- type: "radio" as const,
- label: track.name,
- value: track.index.toString(),
- selected: audioIndex === track.index.toString(),
- onPress: () => track.setTrack(),
- })),
- });
- }
-
- // Speed Section
- if (setPlaybackSpeed) {
- groups.push({
- title: "Speed",
- options: PLAYBACK_SPEEDS.map((speed) => ({
- type: "radio" as const,
- label: speed.label,
- value: speed.value.toString(),
- selected: playbackSpeed === speed.value,
- onPress: () => setPlaybackSpeed(speed.value, PlaybackSpeedScope.All),
- })),
- });
- }
-
- // Technical Info (at bottom)
- if (onToggleTechnicalInfo) {
- groups.push({
- options: [
- {
- type: "action" as const,
- label: showTechnicalInfo
- ? "Hide Technical Info"
- : "Show Technical Info",
- onPress: onToggleTechnicalInfo,
- },
- ],
- });
- }
-
- return groups;
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [
- isOffline,
- bitrateValue,
- changeBitrate,
- subtitleTracksKey,
- audioTracksKey,
- subtitleIndex,
- audioIndex,
- settings.subtitleSize,
- updateSettings,
- playbackSpeed,
- setPlaybackSpeed,
- showTechnicalInfo,
- onToggleTechnicalInfo,
- // Note: subtitleTracks and audioTracks are intentionally excluded
- // because we use subtitleTracksKey and audioTracksKey for stability
- ]);
-
- // Memoize the trigger to prevent re-renders
- const trigger = useMemo(
- () => (
-
-
-
- ),
- [],
- );
-
- // Hide on TV platforms
- if (Platform.isTV) return null;
+ const { subtitleIndex, audioIndex } = useLocalSearchParams<{
+ itemId: string;
+ audioIndex: string;
+ subtitleIndex: string;
+ mediaSourceId: string;
+ bitrateValue: string;
+ }>();
return (
-
+
+
+
+
+
+
+
+
+
+ Subtitle
+
+
+ {subtitleTracks?.map((sub, idx: number) => (
+ sub.setTrack()}
+ >
+
+ {sub.name}
+
+
+ ))}
+
+
+
+
+ Audio
+
+
+ {audioTracks?.map((track, idx: number) => (
+ track.setTrack()}
+ >
+
+ {track.name}
+
+
+ ))}
+
+
+
+
);
};
diff --git a/components/video-player/controls/types.ts b/components/video-player/controls/types.ts
index 5ec03edd..8040f6d3 100644
--- a/components/video-player/controls/types.ts
+++ b/components/video-player/controls/types.ts
@@ -20,8 +20,7 @@ type TranscodedSubtitle = {
type Track = {
name: string;
index: number;
- mpvIndex?: number;
setTrack: () => void;
};
-export type { EmbeddedSubtitle, ExternalSubtitle, TranscodedSubtitle, Track };
+export { EmbeddedSubtitle, ExternalSubtitle, TranscodedSubtitle, Track };
diff --git a/eas.json b/eas.json
index 8a9736d3..0df93411 100644
--- a/eas.json
+++ b/eas.json
@@ -1,66 +1,51 @@
{
"cli": {
- "version": ">= 9.1.0"
+ "version": ">= 9.1.0",
+ "appVersionSource": "local"
},
"build": {
"development": {
- "environment": "development",
"developmentClient": true,
"distribution": "internal",
"android": {
"buildType": "apk"
- },
- "env": {
- "EXPO_PUBLIC_WRITE_DEBUG": "1"
}
},
"development_tv": {
- "environment": "development",
"developmentClient": true,
"distribution": "internal",
"android": {
"buildType": "apk"
},
"env": {
- "EXPO_TV": "1",
- "EXPO_PUBLIC_WRITE_DEBUG": "1"
+ "EXPO_TV": "1"
}
},
+ "preview": {
+ "distribution": "internal"
+ },
"development-simulator": {
- "environment": "development",
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
- },
- "env": {
- "EXPO_PUBLIC_WRITE_DEBUG": "1"
- }
- },
- "preview": {
- "distribution": "internal",
- "env": {
- "EXPO_PUBLIC_WRITE_DEBUG": "1"
}
},
"production": {
- "environment": "production",
- "channel": "0.52.0",
+ "channel": "0.27.0",
"android": {
"image": "latest"
}
},
"production-apk": {
- "environment": "production",
- "channel": "0.52.0",
+ "channel": "0.27.0",
"android": {
"buildType": "apk",
"image": "latest"
}
},
"production-apk-tv": {
- "environment": "production",
- "channel": "0.52.0",
+ "channel": "0.27.0",
"android": {
"buildType": "apk",
"image": "latest"
diff --git a/hooks/useDefaultPlaySettings.ts b/hooks/useDefaultPlaySettings.ts
index 51b0e9ee..9e0424fe 100644
--- a/hooks/useDefaultPlaySettings.ts
+++ b/hooks/useDefaultPlaySettings.ts
@@ -1,23 +1,50 @@
-import { type BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
+import { Bitrate, BITRATES } from "@/components/BitrateSelector";
+import { Settings } from "@/utils/atoms/settings";
+import {
+ BaseItemDto,
+ MediaSourceInfo,
+} from "@jellyfin/sdk/lib/generated-client";
import { useMemo } from "react";
-import type { Settings } from "@/utils/atoms/settings";
-import { getDefaultPlaySettings } from "@/utils/jellyfin/getDefaultPlaySettings";
-/**
- * React hook wrapper for getDefaultPlaySettings.
- * Used in UI components for initial playback (no previous track state).
- */
-const useDefaultPlaySettings = (item: BaseItemDto, settings: Settings | null) =>
- useMemo(() => {
- const { mediaSource, audioIndex, subtitleIndex, bitrate } =
- getDefaultPlaySettings(item, settings);
+// Used only for initial play settings.
+const useDefaultPlaySettings = (
+ item: BaseItemDto,
+ settings: Settings | null
+) => {
+ const playSettings = useMemo(() => {
+ // 1. Get first media source
+ const mediaSource = item.MediaSources?.[0];
+
+ // 2. Get default or preferred audio
+ const defaultAudioIndex = mediaSource?.DefaultAudioStreamIndex;
+ const preferedAudioIndex = mediaSource?.MediaStreams?.find(
+ (x) =>
+ x.Type === "Audio" &&
+ x.Language ===
+ settings?.defaultAudioLanguage?.ThreeLetterISOLanguageName
+ )?.Index;
+
+ const firstAudioIndex = mediaSource?.MediaStreams?.find(
+ (x) => x.Type === "Audio"
+ )?.Index;
+
+ // 4. Get default bitrate from settings or fallback to max
+ const bitrate = settings?.defaultBitrate ?? BITRATES[0];
return {
- defaultMediaSource: mediaSource,
- defaultAudioIndex: audioIndex,
- defaultSubtitleIndex: subtitleIndex,
- defaultBitrate: bitrate,
+ defaultAudioIndex:
+ preferedAudioIndex || defaultAudioIndex || firstAudioIndex || undefined,
+ defaultSubtitleIndex: mediaSource?.DefaultSubtitleStreamIndex || -1,
+ defaultMediaSource: mediaSource || undefined,
+ defaultBitrate: bitrate || undefined,
};
- }, [item, settings]);
+ }, [
+ item.MediaSources,
+ settings?.defaultAudioLanguage,
+ settings?.defaultSubtitleLanguage,
+ ]);
+
+ return playSettings;
+};
export default useDefaultPlaySettings;
diff --git a/hooks/useFavorite.ts b/hooks/useFavorite.ts
index 77af77ee..437d290e 100644
--- a/hooks/useFavorite.ts
+++ b/hooks/useFavorite.ts
@@ -1,140 +1,109 @@
-import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
-import { useMutation, useQueryClient } from "@tanstack/react-query";
-import { atom, useAtom } from "jotai";
-import { useCallback, useEffect, useMemo, useRef } from "react";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
-
-// Shared atom to store favorite status across all components
-// Maps itemId -> isFavorite
-const favoritesAtom = atom>({});
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
+import { getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { useAtom } from "jotai";
+import { useEffect, useState, useMemo } from "react";
export const useFavorite = (item: BaseItemDto) => {
const queryClient = useQueryClient();
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
- const [favorites, setFavorites] = useAtom(favoritesAtom);
+ const type = "item";
+ const [isFavorite, setIsFavorite] = useState(item.UserData?.IsFavorite);
- const itemId = item.Id ?? "";
-
- // Get current favorite status from shared state, falling back to item data
- const isFavorite = itemId
- ? (favorites[itemId] ?? item.UserData?.IsFavorite)
- : item.UserData?.IsFavorite;
-
- // Update shared state when item data changes
useEffect(() => {
- if (itemId && item.UserData?.IsFavorite !== undefined) {
- setFavorites((prev) => ({
- ...prev,
- [itemId]: item.UserData!.IsFavorite!,
- }));
- }
- }, [itemId, item.UserData?.IsFavorite, setFavorites]);
+ setIsFavorite(item.UserData?.IsFavorite);
+ }, [item.UserData?.IsFavorite]);
- // Helper to update favorite status in shared state
- const setIsFavorite = useCallback(
- (value: boolean | undefined) => {
- if (itemId && value !== undefined) {
- setFavorites((prev) => ({ ...prev, [itemId]: value }));
+ const updateItemInQueries = (newData: Partial) => {
+ queryClient.setQueryData(
+ [type, item.Id],
+ (old) => {
+ if (!old) return old;
+ return {
+ ...old,
+ ...newData,
+ UserData: { ...old.UserData, ...newData.UserData },
+ };
+ }
+ );
+ };
+
+ const markFavoriteMutation = useMutation({
+ mutationFn: async () => {
+ if (api && user) {
+ await getUserLibraryApi(api).markFavoriteItem({
+ userId: user.Id,
+ itemId: item.Id!,
+ });
}
},
- [itemId, setFavorites],
- );
+ onMutate: async () => {
+ await queryClient.cancelQueries({ queryKey: [type, item.Id] });
+ const previousItem = queryClient.getQueryData([
+ type,
+ item.Id,
+ ]);
+ updateItemInQueries({ UserData: { IsFavorite: true } });
- // Use refs to avoid stale closure issues in mutationFn
- const itemRef = useRef(item);
- const apiRef = useRef(api);
- const userRef = useRef(user);
-
- // Keep refs updated
- useEffect(() => {
- itemRef.current = item;
- }, [item]);
-
- useEffect(() => {
- apiRef.current = api;
- }, [api]);
-
- useEffect(() => {
- userRef.current = user;
- }, [user]);
-
- const itemQueryKeyPrefix = useMemo(
- () => ["item", item.Id] as const,
- [item.Id],
- );
-
- const updateItemInQueries = useCallback(
- (newData: Partial) => {
- queryClient.setQueriesData(
- { queryKey: itemQueryKeyPrefix },
- (old) => {
- if (!old) return old;
- return {
- ...old,
- ...newData,
- UserData: { ...old.UserData, ...newData.UserData },
- };
- },
- );
+ return { previousItem };
},
- [itemQueryKeyPrefix, queryClient],
- );
-
- const favoriteMutation = useMutation({
- mutationFn: async (nextIsFavorite: boolean) => {
- const currentApi = apiRef.current;
- const currentUser = userRef.current;
- const currentItem = itemRef.current;
-
- if (!currentApi || !currentUser?.Id || !currentItem?.Id) {
- return;
+ onError: (err, variables, context) => {
+ if (context?.previousItem) {
+ queryClient.setQueryData([type, item.Id], context.previousItem);
}
-
- // Use the same endpoint format as the web client:
- // POST /Users/{userId}/FavoriteItems/{itemId} - add favorite
- // DELETE /Users/{userId}/FavoriteItems/{itemId} - remove favorite
- const path = `/Users/${currentUser.Id}/FavoriteItems/${currentItem.Id}`;
-
- const response = nextIsFavorite
- ? await currentApi.post(path, {}, {})
- : await currentApi.delete(path, {});
- return response.data;
- },
- onMutate: async (nextIsFavorite: boolean) => {
- await queryClient.cancelQueries({ queryKey: itemQueryKeyPrefix });
-
- const previousIsFavorite = isFavorite;
- const previousQueries = queryClient.getQueriesData({
- queryKey: itemQueryKeyPrefix,
- });
-
- setIsFavorite(nextIsFavorite);
- updateItemInQueries({ UserData: { IsFavorite: nextIsFavorite } });
-
- return { previousIsFavorite, previousQueries };
- },
- onError: (_err, _nextIsFavorite, context) => {
- if (context?.previousQueries) {
- for (const [queryKey, data] of context.previousQueries) {
- queryClient.setQueryData(queryKey, data);
- }
- }
- setIsFavorite(context?.previousIsFavorite);
},
onSettled: () => {
- queryClient.invalidateQueries({ queryKey: itemQueryKeyPrefix });
+ queryClient.invalidateQueries({ queryKey: [type, item.Id] });
queryClient.invalidateQueries({ queryKey: ["home", "favorites"] });
+ setIsFavorite(true);
},
});
- const toggleFavorite = useCallback(() => {
- favoriteMutation.mutate(!isFavorite);
- }, [favoriteMutation, isFavorite]);
+ const unmarkFavoriteMutation = useMutation({
+ mutationFn: async () => {
+ if (api && user) {
+ await getUserLibraryApi(api).unmarkFavoriteItem({
+ userId: user.Id,
+ itemId: item.Id!,
+ });
+ }
+ },
+ onMutate: async () => {
+ await queryClient.cancelQueries({ queryKey: [type, item.Id] });
+ const previousItem = queryClient.getQueryData([
+ type,
+ item.Id,
+ ]);
+ updateItemInQueries({ UserData: { IsFavorite: false } });
+
+ return { previousItem };
+ },
+ onError: (err, variables, context) => {
+ if (context?.previousItem) {
+ queryClient.setQueryData([type, item.Id], context.previousItem);
+ }
+ },
+ onSettled: () => {
+ queryClient.invalidateQueries({ queryKey: [type, item.Id] });
+ queryClient.invalidateQueries({ queryKey: ["home", "favorites"] });
+ setIsFavorite(false);
+ },
+ });
+
+ const toggleFavorite = () => {
+ if (isFavorite) {
+ unmarkFavoriteMutation.mutate();
+ } else {
+ markFavoriteMutation.mutate();
+ }
+ };
return {
isFavorite,
toggleFavorite,
- favoriteMutation,
+ markFavoriteMutation,
+ unmarkFavoriteMutation,
};
};
diff --git a/hooks/useJellyseerr.ts b/hooks/useJellyseerr.ts
index 4ae918d8..9d3b37c7 100644
--- a/hooks/useJellyseerr.ts
+++ b/hooks/useJellyseerr.ts
@@ -1,58 +1,50 @@
-import axios, { type AxiosError, type AxiosInstance } from "axios";
+import axios, { AxiosError, AxiosInstance } from "axios";
+import { Results } from "@/utils/jellyseerr/server/models/Search";
+import { storage } from "@/utils/mmkv";
+import { inRange } from "lodash";
+import { User as JellyseerrUser } from "@/utils/jellyseerr/server/entity/User";
import { atom } from "jotai";
import { useAtom } from "jotai/index";
-import { inRange } from "lodash";
-import type { User as JellyseerrUser } from "@/utils/jellyseerr/server/entity/User";
-import type {
- MovieResult,
- Results,
- TvResult,
-} from "@/utils/jellyseerr/server/models/Search";
-import { storage } from "@/utils/mmkv";
import "@/augmentations";
-import { t } from "i18next";
import { useCallback, useMemo } from "react";
-import { toast } from "sonner-native";
-import { useNetworkAwareQueryClient } from "@/hooks/useNetworkAwareQueryClient";
import { useSettings } from "@/utils/atoms/settings";
-import type { RTRating } from "@/utils/jellyseerr/server/api/rating/rottentomatoes";
-import {
- IssueStatus,
- type IssueType,
-} from "@/utils/jellyseerr/server/constants/issue";
+import { toast } from "sonner-native";
import {
MediaRequestStatus,
MediaType,
} from "@/utils/jellyseerr/server/constants/media";
-import type DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
-import type Issue from "@/utils/jellyseerr/server/entity/Issue";
-import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
-import type { GenreSliderItem } from "@/utils/jellyseerr/server/interfaces/api/discoverInterfaces";
-import type {
- MediaRequestBody,
- RequestResultsResponse,
-} from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
-import type {
- ServiceCommonServer,
- ServiceCommonServerWithDetails,
-} from "@/utils/jellyseerr/server/interfaces/api/serviceInterfaces";
-import type { UserResultsResponse } from "@/utils/jellyseerr/server/interfaces/api/userInterfaces";
-import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
-import type {
- CombinedCredit,
- PersonCreditCast,
- PersonDetails,
-} from "@/utils/jellyseerr/server/models/Person";
-import type {
+import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
+import { MediaRequestBody } from "@/utils/jellyseerr/server/interfaces/api/requestInterfaces";
+import { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
+import {
SeasonWithEpisodes,
TvDetails,
} from "@/utils/jellyseerr/server/models/Tv";
+import {
+ IssueStatus,
+ IssueType,
+} from "@/utils/jellyseerr/server/constants/issue";
+import Issue from "@/utils/jellyseerr/server/entity/Issue";
+import { RTRating } from "@/utils/jellyseerr/server/api/rating/rottentomatoes";
import { writeErrorLog } from "@/utils/log";
+import DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
+import { t } from "i18next";
+import {
+ CombinedCredit,
+ PersonDetails,
+} from "@/utils/jellyseerr/server/models/Person";
+import { useQueryClient } from "@tanstack/react-query";
+import {GenreSliderItem} from "@/utils/jellyseerr/server/interfaces/api/discoverInterfaces";
+import {UserResultsResponse} from "@/utils/jellyseerr/server/interfaces/api/userInterfaces";
+import {
+ ServiceCommonServer,
+ ServiceCommonServerWithDetails
+} from "@/utils/jellyseerr/server/interfaces/api/serviceInterfaces";
interface SearchParams {
query: string;
page: number;
- // language: string;
+ language: string;
}
interface SearchResults {
@@ -66,8 +58,8 @@ const JELLYSEERR_USER = "JELLYSEERR_USER";
const JELLYSEERR_COOKIES = "JELLYSEERR_COOKIES";
export const clearJellyseerrStorageData = () => {
- storage.remove(JELLYSEERR_USER);
- storage.remove(JELLYSEERR_COOKIES);
+ storage.delete(JELLYSEERR_USER);
+ storage.delete(JELLYSEERR_COOKIES);
};
export enum Endpoints {
@@ -88,11 +80,11 @@ export enum Endpoints {
STUDIO = "/studio",
GENRE_SLIDER = "/genreslider",
DISCOVER = "/discover",
- DISCOVER_TRENDING = `${DISCOVER}/trending`,
- DISCOVER_MOVIES = `${DISCOVER}/movies`,
+ DISCOVER_TRENDING = DISCOVER + "/trending",
+ DISCOVER_MOVIES = DISCOVER + "/movies",
DISCOVER_TV = DISCOVER + TV,
DISCOVER_TV_NETWORK = DISCOVER + TV + NETWORK,
- DISCOVER_MOVIES_STUDIO = `${DISCOVER}${MOVIE}s${STUDIO}`,
+ DISCOVER_MOVIES_STUDIO = DISCOVER + `${MOVIE}s` + STUDIO,
AUTH_JELLYFIN = "/auth/jellyfin",
}
@@ -142,16 +134,15 @@ export class JellyseerrApi {
const { status, headers, data } = response;
if (inRange(status, 200, 299)) {
if (data.version < "2.0.0") {
- const error = t(
- "jellyseerr.toasts.jellyseer_does_not_meet_requirements",
- );
+ const error =
+ t("jellyseerr.toasts.jellyseer_does_not_meet_requirements");
toast.error(error);
throw Error(error);
}
storage.setAny(
JELLYSEERR_COOKIES,
- headers["set-cookie"]?.flatMap((c) => c.split("; ")) ?? [],
+ headers["set-cookie"]?.flatMap((c) => c.split("; ")) ?? []
);
return {
isValid: true,
@@ -160,8 +151,10 @@ export class JellyseerrApi {
}
toast.error(t("jellyseerr.toasts.jellyseerr_test_failed"));
writeErrorLog(
- `Jellyseerr returned a ${status} for url:\n${response.config.url}`,
- response.data,
+ `Jellyseerr returned a ${status} for url:\n` +
+ response.config.url +
+ "\n" +
+ JSON.stringify(response.data)
);
return {
isValid: false,
@@ -197,14 +190,14 @@ export class JellyseerrApi {
async discoverSettings(): Promise {
return this.axios
?.get(
- Endpoints.API_V1 + Endpoints.SETTINGS + Endpoints.DISCOVER,
+ Endpoints.API_V1 + Endpoints.SETTINGS + Endpoints.DISCOVER
)
.then(({ data }) => data);
}
async discover(
endpoint: DiscoverEndpoint | string,
- params: any,
+ params: any
): Promise {
return this.axios
?.get(Endpoints.API_V1 + endpoint, { params })
@@ -213,23 +206,19 @@ export class JellyseerrApi {
async getGenreSliders(
endpoint: Endpoints.TV | Endpoints.MOVIE,
- params: any = undefined,
+ params: any = undefined
): Promise {
return this.axios
- ?.get(
- Endpoints.API_V1 +
- Endpoints.DISCOVER +
- Endpoints.GENRE_SLIDER +
- endpoint,
- { params },
- )
+ ?.get(Endpoints.API_V1 + Endpoints.DISCOVER + Endpoints.GENRE_SLIDER + endpoint, { params })
.then(({ data }) => data);
}
async search(params: SearchParams): Promise {
- return this.axios
- ?.get(Endpoints.API_V1 + Endpoints.SEARCH, { params })
- .then(({ data }) => data);
+ const response = await this.axios?.get(
+ Endpoints.API_V1 + Endpoints.SEARCH,
+ { params }
+ );
+ return response?.data;
}
async request(request: MediaRequestBody): Promise {
@@ -238,46 +227,9 @@ export class JellyseerrApi {
.then(({ data }) => data);
}
- async getRequest(id: number): Promise {
- return this.axios
- ?.get(`${Endpoints.API_V1 + Endpoints.REQUEST}/${id}`)
- .then(({ data }) => data);
- }
-
- async approveRequest(requestId: number): Promise {
- return this.axios
- ?.post(
- `${Endpoints.API_V1 + Endpoints.REQUEST}/${requestId}/approve`,
- )
- .then(({ data }) => data);
- }
-
- async declineRequest(requestId: number): Promise {
- return this.axios
- ?.post(
- `${Endpoints.API_V1 + Endpoints.REQUEST}/${requestId}/decline`,
- )
- .then(({ data }) => data);
- }
-
- async requests(
- params = {
- filter: "all",
- take: 10,
- sort: "modified",
- skip: 0,
- },
- ): Promise {
- return this.axios
- ?.get(Endpoints.API_V1 + Endpoints.REQUEST, {
- params,
- })
- .then(({ data }) => data);
- }
-
async movieDetails(id: number) {
return this.axios
- ?.get(`${Endpoints.API_V1 + Endpoints.MOVIE}/${id}`)
+ ?.get(Endpoints.API_V1 + Endpoints.MOVIE + `/${id}`)
.then((response) => {
return response?.data;
});
@@ -285,7 +237,7 @@ export class JellyseerrApi {
async personDetails(id: number | string): Promise {
return this.axios
- ?.get(`${Endpoints.API_V1 + Endpoints.PERSON}/${id}`)
+ ?.get(Endpoints.API_V1 + Endpoints.PERSON + `/${id}`)
.then((response) => {
return response?.data;
});
@@ -294,9 +246,10 @@ export class JellyseerrApi {
async personCombinedCredits(id: number | string): Promise {
return this.axios
?.get(
- `${
- Endpoints.API_V1 + Endpoints.PERSON
- }/${id}${Endpoints.COMBINED_CREDITS}`,
+ Endpoints.API_V1 +
+ Endpoints.PERSON +
+ `/${id}` +
+ Endpoints.COMBINED_CREDITS
)
.then((response) => {
return response?.data;
@@ -306,7 +259,7 @@ export class JellyseerrApi {
async movieRatings(id: number) {
return this.axios
?.get(
- `${Endpoints.API_V1}${Endpoints.MOVIE}/${id}${Endpoints.RATINGS}`,
+ `${Endpoints.API_V1}${Endpoints.MOVIE}/${id}${Endpoints.RATINGS}`
)
.then(({ data }) => data);
}
@@ -322,7 +275,7 @@ export class JellyseerrApi {
async tvRatings(id: number) {
return this.axios
?.get(
- `${Endpoints.API_V1}${Endpoints.TV}/${id}${Endpoints.RATINGS}`,
+ `${Endpoints.API_V1}${Endpoints.TV}/${id}${Endpoints.RATINGS}`
)
.then(({ data }) => data);
}
@@ -330,7 +283,7 @@ export class JellyseerrApi {
async tvSeason(id: number, seasonId: number) {
return this.axios
?.get(
- `${Endpoints.API_V1}${Endpoints.TV}/${id}/season/${seasonId}`,
+ `${Endpoints.API_V1}${Endpoints.TV}/${id}/season/${seasonId}`
)
.then((response) => {
return response?.data;
@@ -339,18 +292,24 @@ export class JellyseerrApi {
async user(params: any) {
return this.axios
- ?.get(`${Endpoints.API_V1}${Endpoints.USER}`, {
- params,
- })
- .then(({ data }) => data.results);
+ ?.get(`${Endpoints.API_V1}${Endpoints.USER}`, { params })
+ .then(({data}) => data.results)
}
- imageProxy(path?: string, filter = "original", width = 1920, quality = 75) {
+ imageProxy(
+ path?: string,
+ filter: string = "original",
+ width: number = 1920,
+ quality: number = 75
+ ) {
return path
- ? `${this.axios.defaults.baseURL}/_next/image?${new URLSearchParams(
- `url=https://image.tmdb.org/t/p/${filter}/${path}&w=${width}&q=${quality}`,
- ).toString()}`
- : `${this.axios?.defaults.baseURL}/images/overseerr_poster_not_found_logo_top.png`;
+ ? this.axios.defaults.baseURL +
+ `/_next/image?` +
+ new URLSearchParams(
+ `url=https://image.tmdb.org/t/p/${filter}/${path}&w=${width}&q=${quality}`
+ ).toString()
+ : this.axios?.defaults.baseURL +
+ `/images/overseerr_poster_not_found_logo_top.png`;
}
async submitIssue(mediaId: number, issueType: IssueType, message: string) {
@@ -370,20 +329,16 @@ export class JellyseerrApi {
});
}
- async service(type: "radarr" | "sonarr") {
+ async service(type: 'radarr' | 'sonarr') {
return this.axios
- ?.get(
- `${Endpoints.API_V1 + Endpoints.SERVICE}/${type}`,
- )
- .then(({ data }) => data);
+ ?.get(Endpoints.API_V1 + Endpoints.SERVICE + `/${type}`)
+ .then(({data}) => data);
}
- async serviceDetails(type: "radarr" | "sonarr", id: number) {
+ async serviceDetails(type: 'radarr' | 'sonarr', id: number) {
return this.axios
- ?.get(
- `${Endpoints.API_V1 + Endpoints.SERVICE}/${type}/${id}`,
- )
- .then(({ data }) => data);
+ ?.get(Endpoints.API_V1 + Endpoints.SERVICE + `/${type}` + `/${id}`)
+ .then(({data}) => data);
}
private setInterceptors() {
@@ -393,31 +348,37 @@ export class JellyseerrApi {
if (cookies) {
storage.setAny(
JELLYSEERR_COOKIES,
- response.headers["set-cookie"]?.flatMap((c) => c.split("; ")),
+ response.headers["set-cookie"]?.flatMap((c) => c.split("; "))
);
}
return response;
},
(error: AxiosError) => {
+ const errorMsg = "Jellyseerr response error";
+ console.error(errorMsg, error, error.response?.data);
writeErrorLog(
- `Jellyseerr response error\nerror: ${error.toString()}\nurl: ${error?.config?.url}`,
- error.response?.data,
+ errorMsg +
+ `\n` +
+ `error: ${error.toString()}\n` +
+ `url: ${error?.config?.url}\n` +
+ `data:\n` +
+ JSON.stringify(error.response?.data)
);
- if (error.response?.status === 403) {
+ if (error.status === 403) {
clearJellyseerrStorageData();
}
return Promise.reject(error);
- },
+ }
);
this.axios.interceptors.request.use(
async (config) => {
const cookies = storage.get(JELLYSEERR_COOKIES);
if (cookies) {
- const headerName = this.axios.defaults.xsrfHeaderName!;
+ const headerName = this.axios.defaults.xsrfHeaderName!!;
const xsrfToken = cookies
.find((c) => c.includes(headerName))
- ?.split(`${headerName}=`)?.[1];
+ ?.split(headerName + "=")?.[1];
if (xsrfToken) {
config.headers[headerName] = xsrfToken;
}
@@ -426,7 +387,7 @@ export class JellyseerrApi {
},
(error) => {
console.error("Jellyseerr request error", error);
- },
+ }
);
}
}
@@ -434,9 +395,9 @@ export class JellyseerrApi {
const jellyseerrUserAtom = atom(storage.get(JELLYSEERR_USER));
export const useJellyseerr = () => {
- const { settings, updateSettings } = useSettings();
const [jellyseerrUser, setJellyseerrUser] = useAtom(jellyseerrUserAtom);
- const queryClient = useNetworkAwareQueryClient();
+ const [settings, updateSettings] = useSettings();
+ const queryClient = useQueryClient();
const jellyseerrApi = useMemo(() => {
const cookies = storage.get(JELLYSEERR_COOKIES);
@@ -462,75 +423,35 @@ export const useJellyseerr = () => {
switch (mediaRequest.status) {
case MediaRequestStatus.PENDING:
case MediaRequestStatus.APPROVED:
- toast.success(
- t("jellyseerr.toasts.requested_item", { item: title }),
- );
- onSuccess?.();
+ toast.success(t("jellyseerr.toasts.requested_item", {item: title}));
+ onSuccess?.()
break;
case MediaRequestStatus.DECLINED:
- toast.error(
- t("jellyseerr.toasts.you_dont_have_permission_to_request"),
- );
+ toast.error(t("jellyseerr.toasts.you_dont_have_permission_to_request"));
break;
case MediaRequestStatus.FAILED:
- toast.error(
- t("jellyseerr.toasts.something_went_wrong_requesting_media"),
- );
+ toast.error(t("jellyseerr.toasts.something_went_wrong_requesting_media"));
break;
}
});
},
- [jellyseerrApi],
+ [jellyseerrApi]
);
- const isJellyseerrMovieOrTvResult = (
- items: any | null | undefined,
- ): items is MovieResult | TvResult => {
+ const isJellyseerrResult = (
+ items: any[] | null | undefined
+ ): items is Results[] => {
return (
- items &&
- Object.hasOwn(items, "mediaType") &&
- (items.mediaType === MediaType.MOVIE || items.mediaType === MediaType.TV)
+ !items ||
+ (items.length >= 0 &&
+ Object.hasOwn(items[0], "mediaType") &&
+ Object.values(MediaType).includes(items[0]["mediaType"]))
);
};
- const getTitle = (
- item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
- ) => {
- return isJellyseerrMovieOrTvResult(item)
- ? item.mediaType === MediaType.MOVIE
- ? item?.title
- : item?.name
- : item?.mediaInfo?.mediaType === MediaType.MOVIE
- ? (item as MovieDetails)?.title
- : (item as TvDetails)?.name;
- };
-
- const getYear = (
- item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
- ) => {
- return new Date(
- (isJellyseerrMovieOrTvResult(item)
- ? item.mediaType === MediaType.MOVIE
- ? item?.releaseDate
- : item?.firstAirDate
- : item?.mediaInfo?.mediaType === MediaType.MOVIE
- ? (item as MovieDetails)?.releaseDate
- : (item as TvDetails)?.firstAirDate) || "",
- )?.getFullYear?.();
- };
-
- const getMediaType = (
- item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
- ): MediaType => {
- return isJellyseerrMovieOrTvResult(item)
- ? (item.mediaType as MediaType)
- : item?.mediaInfo?.mediaType;
- };
-
const jellyseerrRegion = useMemo(
- // streamingRegion and discoverRegion exists. region doesn't
- () => jellyseerrUser?.settings?.discoverRegion || "US",
- [jellyseerrUser],
+ () => jellyseerrUser?.settings?.region || "US",
+ [jellyseerrUser]
);
const jellyseerrLocale = useMemo(() => {
@@ -542,10 +463,7 @@ export const useJellyseerr = () => {
jellyseerrUser,
setJellyseerrUser,
clearAllJellyseerData,
- isJellyseerrMovieOrTvResult,
- getTitle,
- getYear,
- getMediaType,
+ isJellyseerrResult,
jellyseerrRegion,
jellyseerrLocale,
requestMedia,
diff --git a/hooks/useSessions.ts b/hooks/useSessions.ts
index 5aba6515..ec8d3189 100644
--- a/hooks/useSessions.ts
+++ b/hooks/useSessions.ts
@@ -1,10 +1,8 @@
-import { getSessionApi } from "@jellyfin/sdk/lib/utils/api/session-api";
import { useQuery } from "@tanstack/react-query";
+import { apiAtom } from "@/providers/JellyfinProvider";
import { useAtom } from "jotai";
-import { Platform } from "react-native";
-import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
-
-const _Notifications = !Platform.isTV ? require("expo-notifications") : null;
+import { getSessionApi } from "@jellyfin/sdk/lib/utils/api/session-api";
+import { userAtom } from "@/providers/JellyfinProvider";
export interface useSessionsProps {
refetchInterval: number;
@@ -18,7 +16,7 @@ export const useSessions = ({
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
- const { data, isLoading } = useQuery({
+ const { data, isLoading, error } = useQuery({
queryKey: ["sessions"],
queryFn: async () => {
if (!api || !user || !user.Policy?.IsAdministrator) {
@@ -27,43 +25,11 @@ export const useSessions = ({
const response = await getSessionApi(api).getSessions({
activeWithinSeconds: activeWithinSeconds,
});
-
- const result = response.data
- .filter((s) => s.NowPlayingItem)
- .sort((a, b) =>
- (b.NowPlayingItem?.Name ?? "").localeCompare(
- a.NowPlayingItem?.Name ?? "",
- ),
- );
-
- // Notifications.setBadgeCountAsync(result.length);
- return result;
- },
- refetchInterval: refetchInterval,
- });
-
- return { sessions: data, isLoading };
-};
-
-export const useAllSessions = ({
- refetchInterval = 5 * 1000,
- activeWithinSeconds = 360,
-}: useSessionsProps) => {
- const [api] = useAtom(apiAtom);
- const [user] = useAtom(userAtom);
-
- const { data, isLoading } = useQuery({
- queryKey: ["allSessions"],
- queryFn: async () => {
- if (!api || !user || !user.Policy?.IsAdministrator) {
- return [];
- }
- const response = await getSessionApi(api).getSessions({
- activeWithinSeconds: activeWithinSeconds,
- });
- return response.data;
+ return response.data.filter((s) => s.NowPlayingItem);
},
refetchInterval: refetchInterval,
+ //enabled: !!user || !!user.Policy?.IsAdministrator,
+ //cacheTime: 0
});
return { sessions: data, isLoading };
diff --git a/i18n.ts b/i18n.ts
index d462efdf..973b1268 100644
--- a/i18n.ts
+++ b/i18n.ts
@@ -1,92 +1,42 @@
-import { getLocales } from "expo-localization";
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
-import ar from "./translations/ar.json";
-import ca from "./translations/ca.json";
-import da from "./translations/da.json";
+
import de from "./translations/de.json";
import en from "./translations/en.json";
-import eo from "./translations/eo.json";
import es from "./translations/es.json";
-import fi from "./translations/fi.json";
import fr from "./translations/fr.json";
-import hu from "./translations/hu.json";
import it from "./translations/it.json";
import ja from "./translations/ja.json";
-import nb from "./translations/nb.json";
import nl from "./translations/nl.json";
-import nn from "./translations/nn.json";
-import pl from "./translations/pl.json";
-import ptBR from "./translations/pt-BR.json";
-import ro from "./translations/ro.json";
-import ru from "./translations/ru.json";
-import sq from "./translations/sq.json";
import sv from "./translations/sv.json";
-import tlh from "./translations/tlh.json";
-import tr from "./translations/tr.json";
-import uk from "./translations/uk.json";
-import vi from "./translations/vi.json";
-import zhCN from "./translations/zh-CN.json";
-import zhTW from "./translations/zh-TW.json";
+import zhCN from './translations/zh-CN.json';
+import zhTW from './translations/zh-TW.json';
+import { getLocales } from "expo-localization";
export const APP_LANGUAGES = [
- { label: "Catalan", value: "ca" },
- { label: "العربية", value: "ar" },
- { label: "Dansk", value: "da" },
{ label: "Deutsch", value: "de" },
{ label: "English", value: "en" },
{ label: "Español", value: "es" },
- { label: "Esperanto", value: "eo" },
{ label: "Français", value: "fr" },
{ label: "Italiano", value: "it" },
{ label: "日本語", value: "ja" },
- { label: "Klingon", value: "tlh" },
- { label: "Türkçe", value: "tr" },
- { label: "Magyar", value: "hu" },
{ label: "Nederlands", value: "nl" },
- { label: "Polski", value: "pl" },
- { label: "Português (Brasil)", value: "pt-BR" },
- { label: "Română", value: "ro" },
{ label: "Svenska", value: "sv" },
- { label: "Norsk Bokmål", value: "nb" },
- { label: "Norsk Nynorsk", value: "nn" },
- { label: "Suomi", value: "fi" },
- { label: "Shqip", value: "sq" },
- { label: "Русский", value: "ru" },
- { label: "Українська", value: "uk" },
{ label: "简体中文", value: "zh-CN" },
{ label: "繁體中文", value: "zh-TW" },
- { label: "Tiếng Việt", value: "vi" },
];
i18n.use(initReactI18next).init({
compatibilityJSON: "v4",
resources: {
- ca: { translation: ca },
- ar: { translation: ar },
- da: { translation: da },
de: { translation: de },
en: { translation: en },
es: { translation: es },
- eo: { translation: eo },
fr: { translation: fr },
- hu: { translation: hu },
it: { translation: it },
ja: { translation: ja },
nl: { translation: nl },
- pl: { translation: pl },
- "pt-BR": { translation: ptBR },
- ro: { translation: ro },
sv: { translation: sv },
- nb: { translation: nb },
- nn: { translation: nn },
- fi: { translation: fi },
- sq: { translation: sq },
- ru: { translation: ru },
- tr: { translation: tr },
- tlh: { translation: tlh },
- uk: { translation: uk },
- vi: { translation: vi },
"zh-CN": { translation: zhCN },
"zh-TW": { translation: zhTW },
},
diff --git a/login.yaml b/login.yaml
new file mode 100644
index 00000000..54418a9b
--- /dev/null
+++ b/login.yaml
@@ -0,0 +1,6 @@
+# login.yaml
+
+appId: your.app.id
+---
+- launchApp
+- tapOn: "Text on the screen"
diff --git a/modules/vlc-player/android/src/main/java/expo/modules/vlcplayer/VLCManager.kt b/modules/vlc-player/android/src/main/java/expo/modules/vlcplayer/VLCManager.kt
new file mode 100644
index 00000000..e65d98e4
--- /dev/null
+++ b/modules/vlc-player/android/src/main/java/expo/modules/vlcplayer/VLCManager.kt
@@ -0,0 +1,38 @@
+package expo.modules.vlcplayer
+
+import expo.modules.core.interfaces.ReactActivityLifecycleListener
+
+// TODO: Creating a separate package class and adding this as a lifecycle listener did not work...
+// https://docs.expo.dev/modules/android-lifecycle-listeners/
+object VLCManager: ReactActivityLifecycleListener {
+ val listeners: MutableList = mutableListOf()
+// override fun onCreate(activity: Activity?, savedInstanceState: Bundle?) {
+// listeners.forEach {
+// it.onCreate(activity, savedInstanceState)
+// }
+// }
+//
+// override fun onResume(activity: Activity?) {
+// listeners.forEach {
+// it.onResume(activity)
+// }
+// }
+//
+// override fun onPause(activity: Activity?) {
+// listeners.forEach {
+// it.onPause(activity)
+// }
+// }
+//
+// override fun onUserLeaveHint(activity: Activity?) {
+// listeners.forEach {
+// it.onUserLeaveHint(activity)
+// }
+// }
+//
+// override fun onDestroy(activity: Activity?) {
+// listeners.forEach {
+// it.onDestroy(activity)
+// }
+// }
+}
\ No newline at end of file
diff --git a/modules/vlc-player/android/src/main/java/expo/modules/vlcplayer/VlcPlayerModule.kt b/modules/vlc-player/android/src/main/java/expo/modules/vlcplayer/VlcPlayerModule.kt
new file mode 100644
index 00000000..91d48fc5
--- /dev/null
+++ b/modules/vlc-player/android/src/main/java/expo/modules/vlcplayer/VlcPlayerModule.kt
@@ -0,0 +1,87 @@
+package expo.modules.vlcplayer
+
+import androidx.core.os.bundleOf
+import expo.modules.kotlin.modules.Module
+import expo.modules.kotlin.modules.ModuleDefinition
+
+class VlcPlayerModule : Module() {
+ override fun definition() = ModuleDefinition {
+ Name("VlcPlayer")
+
+ OnActivityEntersForeground {
+ VLCManager.listeners.forEach {
+ it.onResume(appContext.currentActivity)
+ }
+ }
+
+ OnActivityEntersBackground {
+ VLCManager.listeners.forEach {
+ it.onPause(appContext.currentActivity)
+ }
+ }
+
+ View(VlcPlayerView::class) {
+ Prop("source") { view: VlcPlayerView, source: Map ->
+ view.setSource(source)
+ }
+
+ Prop("paused") { view: VlcPlayerView, paused: Boolean ->
+ if (paused) {
+ view.pause()
+ } else {
+ view.play()
+ }
+ }
+
+ Events(
+ "onPlaybackStateChanged",
+ "onVideoStateChange",
+ "onVideoLoadStart",
+ "onVideoLoadEnd",
+ "onVideoProgress",
+ "onVideoError",
+ "onPipStarted"
+ )
+
+ AsyncFunction("startPictureInPicture") { view: VlcPlayerView ->
+ view.startPictureInPicture()
+ }
+
+ AsyncFunction("play") { view: VlcPlayerView ->
+ view.play()
+ }
+
+ AsyncFunction("pause") { view: VlcPlayerView ->
+ view.pause()
+ }
+
+ AsyncFunction("stop") { view: VlcPlayerView ->
+ view.stop()
+ }
+
+ AsyncFunction("seekTo") { view: VlcPlayerView, time: Int ->
+ view.seekTo(time)
+ }
+
+ AsyncFunction("setAudioTrack") { view: VlcPlayerView, trackIndex: Int ->
+ view.setAudioTrack(trackIndex)
+ }
+
+ AsyncFunction("getAudioTracks") { view: VlcPlayerView ->
+ view.getAudioTracks()
+ }
+
+ AsyncFunction("setSubtitleTrack") { view: VlcPlayerView, trackIndex: Int ->
+ view.setSubtitleTrack(trackIndex)
+ }
+
+ AsyncFunction("getSubtitleTracks") { view: VlcPlayerView ->
+ view.getSubtitleTracks()
+ }
+
+ AsyncFunction("setSubtitleURL") { view: VlcPlayerView, url: String, name: String ->
+ view.setSubtitleURL(url, name)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/vlc-player/android/src/main/java/expo/modules/vlcplayer/VlcPlayerView.kt b/modules/vlc-player/android/src/main/java/expo/modules/vlcplayer/VlcPlayerView.kt
new file mode 100644
index 00000000..97d1d7aa
--- /dev/null
+++ b/modules/vlc-player/android/src/main/java/expo/modules/vlcplayer/VlcPlayerView.kt
@@ -0,0 +1,466 @@
+package expo.modules.vlcplayer
+
+import android.R
+import android.app.Activity
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_IMMUTABLE
+import android.app.PendingIntent.FLAG_UPDATE_CURRENT
+import android.app.PictureInPictureParams
+import android.app.RemoteAction
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.content.IntentFilter
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.os.Build
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import android.view.View
+import androidx.annotation.RequiresApi
+import androidx.core.app.PictureInPictureModeChangedInfo
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.OnLifecycleEvent
+import expo.modules.core.interfaces.ReactActivityLifecycleListener
+import expo.modules.core.logging.LogHandlers
+import expo.modules.core.logging.Logger
+import expo.modules.kotlin.AppContext
+import expo.modules.kotlin.viewevent.EventDispatcher
+import expo.modules.kotlin.views.ExpoView
+import org.videolan.libvlc.LibVLC
+import org.videolan.libvlc.Media
+import org.videolan.libvlc.MediaPlayer
+import org.videolan.libvlc.interfaces.IMedia
+import org.videolan.libvlc.util.VLCVideoLayout
+
+
+class VlcPlayerView(context: Context, appContext: AppContext) : ExpoView(context, appContext), LifecycleObserver, MediaPlayer.EventListener, ReactActivityLifecycleListener {
+ private val log = Logger(listOf(LogHandlers.createOSLogHandler(this::class.simpleName!!)))
+ private val PIP_PLAY_PAUSE_ACTION = "PIP_PLAY_PAUSE_ACTION"
+ private val PIP_REWIND_ACTION = "PIP_REWIND_ACTION"
+ private val PIP_FORWARD_ACTION = "PIP_FORWARD_ACTION"
+
+ private var libVLC: LibVLC? = null
+ private var mediaPlayer: MediaPlayer? = null
+ private lateinit var videoLayout: VLCVideoLayout
+ private var isPaused: Boolean = false
+ private var lastReportedState: Int? = null
+ private var lastReportedIsPlaying: Boolean? = null
+ private var media : Media? = null
+ private var timeLeft: Long? = null
+
+ private val onVideoProgress by EventDispatcher()
+ private val onVideoStateChange by EventDispatcher()
+ private val onVideoLoadEnd by EventDispatcher()
+ private val onPipStarted by EventDispatcher()
+
+ private var startPosition: Int? = 0
+ private var isMediaReady: Boolean = false
+ private var externalTrack: Map? = null
+ var hasSource: Boolean = false
+
+ private val handler = Handler(Looper.getMainLooper())
+ private val updateInterval = 1000L // 1 second
+ private val updateProgressRunnable = object : Runnable {
+ override fun run() {
+ updateVideoProgress()
+ handler.postDelayed(this, updateInterval)
+ }
+ }
+ private val currentActivity get() = context.findActivity()
+ private val actions: MutableList = mutableListOf()
+ private val remoteActionFilter = IntentFilter()
+ private val playPauseIntent: Intent = Intent(PIP_PLAY_PAUSE_ACTION).setPackage(context.packageName)
+ private val forwardIntent: Intent = Intent(PIP_FORWARD_ACTION).setPackage(context.packageName)
+ private val rewindIntent: Intent = Intent(PIP_REWIND_ACTION).setPackage(context.packageName)
+ private var actionReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ when (intent?.action) {
+ PIP_PLAY_PAUSE_ACTION -> {
+ if (isPaused) play() else pause()
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ setupPipActions()
+ currentActivity.setPictureInPictureParams(getPipParams()!!)
+ }
+ }
+ PIP_FORWARD_ACTION -> seekTo((mediaPlayer?.time?.toInt() ?: 0) + 15_000)
+ PIP_REWIND_ACTION -> seekTo((mediaPlayer?.time?.toInt() ?: 0) - 15_000)
+ }
+ }
+ }
+
+ private var pipChangeListener: (PictureInPictureModeChangedInfo) -> Unit = { info ->
+ if (!info.isInPictureInPictureMode && mediaPlayer?.isPlaying == true) {
+ log.debug("Exiting PiP")
+ timeLeft = mediaPlayer?.time
+ pause()
+
+ // Setting the media after reattaching the view allows for a fast video view render
+ if (mediaPlayer?.vlcVout?.areViewsAttached() == false) {
+ mediaPlayer?.attachViews(videoLayout, null, false, false)
+ mediaPlayer?.media = media
+ mediaPlayer?.play()
+ timeLeft?.let { mediaPlayer?.time = it }
+ mediaPlayer?.pause()
+
+ }
+ }
+ onPipStarted(mapOf(
+ "pipStarted" to info.isInPictureInPictureMode
+ ))
+ }
+
+ init {
+ VLCManager.listeners.add(this)
+ setupView()
+ setupPiP()
+ }
+
+ private fun setupView() {
+ log.debug("Setting up view")
+ setBackgroundColor(android.graphics.Color.WHITE)
+ videoLayout = VLCVideoLayout(context).apply {
+ layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
+ }
+ videoLayout.keepScreenOn = true
+ addView(videoLayout)
+ log.debug("View setup complete")
+ }
+
+ private fun setupPiP() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ remoteActionFilter.addAction(PIP_PLAY_PAUSE_ACTION)
+ remoteActionFilter.addAction(PIP_FORWARD_ACTION)
+ remoteActionFilter.addAction(PIP_REWIND_ACTION)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ currentActivity.registerReceiver(
+ actionReceiver,
+ remoteActionFilter,
+ Context.RECEIVER_NOT_EXPORTED
+ )
+ }
+ setupPipActions()
+ currentActivity.apply {
+ setPictureInPictureParams(getPipParams()!!)
+ addOnPictureInPictureModeChangedListener(pipChangeListener)
+ }
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private fun setupPipActions() {
+ actions.clear()
+ actions.addAll(
+ listOf(
+ RemoteAction(
+ Icon.createWithResource(context, R.drawable.ic_media_rew),
+ "Rewind",
+ "Rewind Video",
+ PendingIntent.getBroadcast(
+ context,
+ 0,
+ rewindIntent,
+ FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
+ ),
+ RemoteAction(
+ if (isPaused) Icon.createWithResource(context, R.drawable.ic_media_play)
+ else Icon.createWithResource(context, R.drawable.ic_media_pause),
+ "Play",
+ "Play Video",
+ PendingIntent.getBroadcast(
+ context,
+ if (isPaused) 0 else 1,
+ playPauseIntent,
+ FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
+ ),
+ RemoteAction(
+ Icon.createWithResource(context, R.drawable.ic_media_ff),
+ "Skip",
+ "Skip Forward",
+ PendingIntent.getBroadcast(
+ context,
+ 0,
+ forwardIntent,
+ FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
+ )
+ )
+ )
+ }
+
+ private fun getPipParams(): PictureInPictureParams? {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ var builder = PictureInPictureParams.Builder()
+ .setActions(actions)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ builder = builder.setAutoEnterEnabled(true)
+ }
+ return builder.build()
+ }
+ return null
+ }
+
+ fun setSource(source: Map) {
+ log.debug("setting source $source")
+ if (hasSource) {
+ log.debug("Source already set. Resuming")
+ mediaPlayer?.attachViews(videoLayout, null, false, false)
+ play()
+ return
+ }
+ val mediaOptions = source["mediaOptions"] as? Map ?: emptyMap()
+ val autoplay = source["autoplay"] as? Boolean ?: false
+ val isNetwork = source["isNetwork"] as? Boolean ?: false
+ externalTrack = source["externalTrack"] as? Map
+ startPosition = (source["startPosition"] as? Double)?.toInt() ?: 0
+
+ val initOptions = source["initOptions"] as? MutableList ?: mutableListOf()
+ initOptions.add("--start-time=$startPosition")
+
+
+ val uri = source["uri"] as? String
+
+ // Handle video load start event
+ // onVideoLoadStart?.invoke(mapOf("target" to reactTag ?: "null"))
+
+ libVLC = LibVLC(context, initOptions)
+ mediaPlayer = MediaPlayer(libVLC)
+ mediaPlayer?.attachViews(videoLayout, null, false, false)
+ mediaPlayer?.setEventListener(this)
+
+ log.debug("Loading network file: $uri")
+ media = Media(libVLC, Uri.parse(uri))
+ mediaPlayer?.media = media
+
+
+ log.debug("Debug: Media options: $mediaOptions")
+ // media.addOptions(mediaOptions)
+
+ // Apply subtitle options
+ // val subtitleTrackIndex = source["subtitleTrackIndex"] as? Int ?: -1
+ // Log.d("VlcPlayerView", "Debug: Subtitle track index from source: $subtitleTrackIndex")
+
+ // if (subtitleTrackIndex >= -1) {
+ // setSubtitleTrack(subtitleTrackIndex)
+ // Log.d("VlcPlayerView", "Debug: Set subtitle track to index: $subtitleTrackIndex")
+ // } else {
+ // Log.d("VlcPlayerView", "Debug: Subtitle track index is less than -1, not setting")
+ // }
+
+ hasSource = true
+
+ if (autoplay) {
+ log.debug("Playing...")
+ play()
+ }
+ }
+
+ fun startPictureInPicture() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ currentActivity.enterPictureInPictureMode(getPipParams()!!)
+ }
+ }
+
+ fun play() {
+ mediaPlayer?.play()
+ isPaused = false
+ handler.post(updateProgressRunnable) // Start updating progress
+ }
+
+ fun pause() {
+ mediaPlayer?.pause()
+ isPaused = true
+ handler.removeCallbacks(updateProgressRunnable) // Stop updating progress
+ }
+
+ fun stop() {
+ mediaPlayer?.stop()
+ handler.removeCallbacks(updateProgressRunnable) // Stop updating progress
+ }
+
+ fun seekTo(time: Int) {
+ mediaPlayer?.let { player ->
+ val wasPlaying = player.isPlaying
+ if (wasPlaying) {
+ player.pause()
+ }
+
+ val duration = player.length.toInt()
+ val seekTime = if (time > duration) duration - 1000 else time
+ player.time = seekTime.toLong()
+
+ if (wasPlaying) {
+ player.play()
+ }
+ }
+ }
+
+ fun setAudioTrack(trackIndex: Int) {
+ mediaPlayer?.setAudioTrack(trackIndex)
+ }
+
+ fun getAudioTracks(): List>? {
+ log.debug("getAudioTracks ${mediaPlayer?.audioTracks}")
+ val trackDescriptions = mediaPlayer?.audioTracks ?: return null
+
+ return trackDescriptions.map { trackDescription ->
+ mapOf("name" to trackDescription.name, "index" to trackDescription.id)
+ }
+ }
+
+ fun setSubtitleTrack(trackIndex: Int) {
+ mediaPlayer?.setSpuTrack(trackIndex)
+ }
+
+ // fun getSubtitleTracks(): List>? {
+ // return mediaPlayer?.getSpuTracks()?.map { trackDescription ->
+ // mapOf("name" to trackDescription.name, "index" to trackDescription.id)
+ // }
+ // }
+
+ fun getSubtitleTracks(): List>? {
+ val subtitleTracks = mediaPlayer?.spuTracks?.map { trackDescription ->
+ mapOf("name" to trackDescription.name, "index" to trackDescription.id)
+ }
+
+ // Debug statement to print the result
+ log.debug("Subtitle Tracks: $subtitleTracks")
+
+ return subtitleTracks
+ }
+
+ fun setSubtitleURL(subtitleURL: String, name: String) {
+ log.debug("Setting subtitle URL: $subtitleURL, name: $name")
+ mediaPlayer?.addSlave(IMedia.Slave.Type.Subtitle, Uri.parse(subtitleURL), true)
+ }
+
+ override fun onDetachedFromWindow() {
+ log.debug("onDetachedFromWindow")
+ super.onDetachedFromWindow()
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ currentActivity.setPictureInPictureParams(
+ PictureInPictureParams.Builder()
+ .setAutoEnterEnabled(false)
+ .build()
+ )
+ }
+
+ currentActivity.unregisterReceiver(actionReceiver)
+ currentActivity.removeOnPictureInPictureModeChangedListener(pipChangeListener)
+ VLCManager.listeners.clear()
+
+ mediaPlayer?.stop()
+ handler.removeCallbacks(updateProgressRunnable) // Stop updating progress
+
+ media?.release()
+ mediaPlayer?.release()
+ libVLC?.release()
+ mediaPlayer = null
+ media = null
+ libVLC = null
+ }
+
+ override fun onEvent(event: MediaPlayer.Event) {
+ keepScreenOn = event.type == MediaPlayer.Event.Playing || event.type == MediaPlayer.Event.Buffering
+ when (event.type) {
+ MediaPlayer.Event.Playing,
+ MediaPlayer.Event.Paused,
+ MediaPlayer.Event.Stopped,
+ MediaPlayer.Event.Buffering,
+ MediaPlayer.Event.EndReached,
+ MediaPlayer.Event.EncounteredError -> updatePlayerState(event)
+ MediaPlayer.Event.TimeChanged -> {
+ // Do nothing here, as we are updating progress every 1 second
+ }
+ }
+ }
+
+ private fun updatePlayerState(event: MediaPlayer.Event) {
+ val player = mediaPlayer ?: return
+ val currentState = event.type
+
+ val stateInfo = mutableMapOf(
+ "target" to "null", // Replace with actual target if needed
+ "currentTime" to player.time.toInt(),
+ "duration" to (player.media?.duration?.toInt() ?: 0),
+ "error" to false,
+ "isPlaying" to (currentState == MediaPlayer.Event.Playing),
+ "isBuffering" to (!player.isPlaying && currentState == MediaPlayer.Event.Buffering)
+ )
+
+ // Todo: make enum - string to prevent this when statement from becoming exhaustive
+ when (currentState) {
+ MediaPlayer.Event.Playing ->
+ stateInfo["state"] = "Playing"
+ MediaPlayer.Event.Paused ->
+ stateInfo["state"] = "Paused"
+ MediaPlayer.Event.Buffering ->
+ stateInfo["state"] = "Buffering"
+ MediaPlayer.Event.EncounteredError -> {
+ stateInfo["state"] = "Error"
+ onVideoLoadEnd(stateInfo);
+ }
+ MediaPlayer.Event.Opening ->
+ stateInfo["state"] = "Opening"
+ }
+
+ if (lastReportedState != currentState || lastReportedIsPlaying != player.isPlaying) {
+ lastReportedState = currentState
+ lastReportedIsPlaying = player.isPlaying
+ onVideoStateChange(stateInfo)
+ }
+ }
+
+
+ private fun updateVideoProgress() {
+ val player = mediaPlayer ?: return
+
+ val currentTimeMs = player.time.toInt()
+ val durationMs = player.media?.duration?.toInt() ?: 0
+ if (currentTimeMs >= 0 && currentTimeMs < durationMs) {
+ // Set subtitle URL if available
+ if (player.isPlaying && !isMediaReady) {
+ isMediaReady = true
+ externalTrack?.let {
+ val name = it["name"]
+ val deliveryUrl = it["DeliveryUrl"] ?: ""
+ if (!name.isNullOrEmpty() && !deliveryUrl.isNullOrEmpty()) {
+ setSubtitleURL(deliveryUrl, name)
+ }
+ }
+ }
+ onVideoProgress(mapOf(
+ "currentTime" to currentTimeMs,
+ "duration" to durationMs
+ ));
+ }
+ }
+
+ override fun onPause(activity: Activity?) {
+ log.debug("Pausing activity...")
+ }
+
+
+ override fun onResume(activity: Activity?) {
+ log.debug("Resuming activity...")
+ if (isPaused) play()
+ }
+}
+
+internal fun Context.findActivity(): androidx.activity.ComponentActivity {
+ var context = this
+ while (context is ContextWrapper) {
+ if (context is androidx.activity.ComponentActivity) return context
+ context = context.baseContext
+ }
+ throw IllegalStateException("Failed to find ComponentActivity")
+}
\ No newline at end of file
diff --git a/modules/vlc-player/expo-module.config.json b/modules/vlc-player/expo-module.config.json
new file mode 100644
index 00000000..c7159245
--- /dev/null
+++ b/modules/vlc-player/expo-module.config.json
@@ -0,0 +1,10 @@
+{
+ "platforms": ["ios", "tvos", "android", "web"],
+ "ios": {
+ "modules": ["VlcPlayerModule"],
+ "appDelegateSubscribers": ["AppLifecycleDelegate"]
+ },
+ "android": {
+ "modules": ["expo.modules.vlcplayer.VlcPlayerModule"]
+ }
+}
diff --git a/modules/vlc-player/index.ts b/modules/vlc-player/index.ts
new file mode 100644
index 00000000..d4b089cf
--- /dev/null
+++ b/modules/vlc-player/index.ts
@@ -0,0 +1,68 @@
+import {
+ EventEmitter,
+ EventSubscription,
+} from "expo-modules-core";
+
+import VlcPlayerModule from "./src/VlcPlayerModule";
+import VlcPlayerView from "./src/VlcPlayerView";
+import {
+ PlaybackStatePayload,
+ ProgressUpdatePayload,
+ VideoLoadStartPayload,
+ VideoStateChangePayload,
+ VideoProgressPayload,
+ VlcPlayerSource,
+ TrackInfo,
+ ChapterInfo,
+ VlcPlayerViewProps,
+ VlcPlayerViewRef,
+} from "./src/VlcPlayer.types";
+
+const emitter = new EventEmitter(VlcPlayerModule);
+
+export function addPlaybackStateListener(
+ listener: (event: PlaybackStatePayload) => void
+): EventSubscription {
+ return emitter.addListener(
+ "onPlaybackStateChanged",
+ listener
+ );
+}
+
+export function addVideoLoadStartListener(
+ listener: (event: VideoLoadStartPayload) => void
+): EventSubscription {
+ return emitter.addListener(
+ "onVideoLoadStart",
+ listener
+ );
+}
+
+export function addVideoStateChangeListener(
+ listener: (event: VideoStateChangePayload) => void
+): EventSubscription {
+ return emitter.addListener(
+ "onVideoStateChange",
+ listener
+ );
+}
+
+export function addVideoProgressListener(
+ listener: (event: VideoProgressPayload) => void
+): EventSubscription {
+ return emitter.addListener("onVideoProgress", listener);
+}
+
+export {
+ VlcPlayerView,
+ VlcPlayerViewProps,
+ VlcPlayerViewRef,
+ PlaybackStatePayload,
+ ProgressUpdatePayload,
+ VideoLoadStartPayload,
+ VideoStateChangePayload,
+ VideoProgressPayload,
+ VlcPlayerSource,
+ TrackInfo,
+ ChapterInfo,
+};
diff --git a/modules/vlc-player/ios/VlcPlayerModule.swift b/modules/vlc-player/ios/VlcPlayerModule.swift
new file mode 100644
index 00000000..38299392
--- /dev/null
+++ b/modules/vlc-player/ios/VlcPlayerModule.swift
@@ -0,0 +1,71 @@
+import ExpoModulesCore
+
+public class VlcPlayerModule: Module {
+ public func definition() -> ModuleDefinition {
+ Name("VlcPlayer")
+ View(VlcPlayerView.self) {
+ Prop("source") { (view: VlcPlayerView, source: [String: Any]) in
+ view.setSource(source)
+ }
+
+ Prop("paused") { (view: VlcPlayerView, paused: Bool) in
+ if paused {
+ view.pause()
+ } else {
+ view.play()
+ }
+ }
+
+ Events(
+ "onPlaybackStateChanged",
+ "onVideoStateChange",
+ "onVideoLoadStart",
+ "onVideoLoadEnd",
+ "onVideoProgress",
+ "onVideoError",
+ "onPipStarted"
+ )
+
+ AsyncFunction("startPictureInPicture") { (view: VlcPlayerView) in
+ view.startPictureInPicture()
+ }
+
+ AsyncFunction("play") { (view: VlcPlayerView) in
+ view.play()
+ }
+
+ AsyncFunction("pause") { (view: VlcPlayerView) in
+ view.pause()
+ }
+
+ AsyncFunction("stop") { (view: VlcPlayerView) in
+ view.stop()
+ }
+
+ AsyncFunction("seekTo") { (view: VlcPlayerView, time: Int32) in
+ view.seekTo(time)
+ }
+
+ AsyncFunction("setAudioTrack") { (view: VlcPlayerView, trackIndex: Int) in
+ view.setAudioTrack(trackIndex)
+ }
+
+ AsyncFunction("getAudioTracks") { (view: VlcPlayerView) -> [[String: Any]]? in
+ return view.getAudioTracks()
+ }
+
+ AsyncFunction("setSubtitleTrack") { (view: VlcPlayerView, trackIndex: Int) in
+ view.setSubtitleTrack(trackIndex)
+ }
+
+ AsyncFunction("getSubtitleTracks") { (view: VlcPlayerView) -> [[String: Any]]? in
+ return view.getSubtitleTracks()
+ }
+
+ AsyncFunction("setSubtitleURL") {
+ (view: VlcPlayerView, url: String, name: String) in
+ view.setSubtitleURL(url, name: name)
+ }
+ }
+ }
+}
diff --git a/modules/vlc-player/ios/VlcPlayerView.swift b/modules/vlc-player/ios/VlcPlayerView.swift
new file mode 100644
index 00000000..1c6dd9ea
--- /dev/null
+++ b/modules/vlc-player/ios/VlcPlayerView.swift
@@ -0,0 +1,483 @@
+import ExpoModulesCore
+import UIKit
+import VLCKit
+import os
+
+
+public class VLCPlayerView: UIView {
+ func setupView(parent: UIView) {
+ self.backgroundColor = .black
+ self.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ self.leadingAnchor.constraint(equalTo: parent.leadingAnchor),
+ self.trailingAnchor.constraint(equalTo: parent.trailingAnchor),
+ self.topAnchor.constraint(equalTo: parent.topAnchor),
+ self.bottomAnchor.constraint(equalTo: parent.bottomAnchor),
+ ])
+ }
+
+ public override func layoutSubviews() {
+ super.layoutSubviews()
+
+ for subview in subviews {
+ subview.frame = bounds
+ }
+ }
+}
+
+class VLCPlayerWrapper: NSObject {
+ private var lastProgressCall = Date().timeIntervalSince1970
+ public var player: VLCMediaPlayer = VLCMediaPlayer()
+ private var updatePlayerState: (() -> Void)?
+ private var updateVideoProgress: (() -> Void)?
+ private var playerView: VLCPlayerView = VLCPlayerView()
+ public weak var pipController: VLCPictureInPictureWindowControlling?
+
+ override public init() {
+ super.init()
+ player.delegate = self
+ player.drawable = self
+ player.scaleFactor = 0
+ }
+
+ public func setup(
+ parent: UIView,
+ updatePlayerState: (() -> Void)?,
+ updateVideoProgress: (() -> Void)?
+ ) {
+ self.updatePlayerState = updatePlayerState
+ self.updateVideoProgress = updateVideoProgress
+
+ player.delegate = self
+ parent.addSubview(playerView)
+ playerView.setupView(parent: parent)
+ }
+
+ public func getPlayerView() -> UIView {
+ return playerView
+ }
+}
+
+// MARK: - VLCPictureInPictureDrawable
+extension VLCPlayerWrapper: VLCPictureInPictureDrawable {
+ public func mediaController() -> (any VLCPictureInPictureMediaControlling)! {
+ return self
+ }
+
+ public func pictureInPictureReady() -> (((any VLCPictureInPictureWindowControlling)?) -> Void)!
+ {
+ return { [weak self] controller in
+ self?.pipController = controller
+ }
+ }
+}
+
+// MARK: - VLCPictureInPictureMediaControlling
+extension VLCPlayerWrapper: VLCPictureInPictureMediaControlling {
+ func mediaTime() -> Int64 {
+ return player.time.value?.int64Value ?? 0
+ }
+
+ func mediaLength() -> Int64 {
+ return player.media?.length.value?.int64Value ?? 0
+ }
+
+ func play() {
+ player.play()
+ }
+
+ func pause() {
+ player.pause()
+ }
+
+ func seek(by offset: Int64, completion: @escaping () -> Void) {
+ player.jump(withOffset: Int32(offset), completion: completion)
+ }
+
+ func isMediaSeekable() -> Bool {
+ return player.isSeekable
+ }
+
+ func isMediaPlaying() -> Bool {
+ return player.isPlaying
+ }
+}
+
+// MARK: - VLCDrawable
+extension VLCPlayerWrapper: VLCDrawable {
+ public func addSubview(_ view: UIView) {
+ playerView.addSubview(view)
+ }
+
+ public func bounds() -> CGRect {
+ return playerView.bounds
+ }
+}
+
+// MARK: - VLCMediaPlayerDelegate
+extension VLCPlayerWrapper: VLCMediaPlayerDelegate {
+ func mediaPlayerTimeChanged(_ aNotification: Notification) {
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self else { return }
+ let timeNow = Date().timeIntervalSince1970
+ if timeNow - self.lastProgressCall >= 1 {
+ self.lastProgressCall = timeNow
+ self.updateVideoProgress?()
+ }
+ }
+ }
+
+ func mediaPlayerStateChanged(_ state: VLCMediaPlayerState) {
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self else { return }
+ self.updatePlayerState?()
+
+ guard let pipController = self.pipController else { return }
+ pipController.invalidatePlaybackState()
+ }
+ }
+}
+
+// MARK: - VLCMediaDelegate
+extension VLCPlayerWrapper: VLCMediaDelegate {
+ // Implement VLCMediaDelegate methods if needed
+}
+
+class VlcPlayerView: ExpoView {
+ let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "VlcPlayerView")
+
+ private var vlc: VLCPlayerWrapper = VLCPlayerWrapper()
+ private var progressUpdateInterval: TimeInterval = 1.0 // Update interval set to 1 second
+ private var isPaused: Bool = false
+ private var customSubtitles: [(internalName: String, originalName: String)] = []
+ private var startPosition: Int32 = 0
+ private var externalTrack: [String: String]?
+ private var isStopping: Bool = false // Define isStopping here
+ private var externalSubtitles: [[String: String]]?
+ var hasSource = false
+
+ // MARK: - Initialization
+ required init(appContext: AppContext? = nil) {
+ super.init(appContext: appContext)
+ setupVLC()
+ setupNotifications()
+ VLCManager.shared.listeners.append(self)
+ }
+
+ // MARK: - Setup
+ private func setupVLC() {
+ vlc.setup(
+ parent: self,
+ updatePlayerState: updatePlayerState,
+ updateVideoProgress: updateVideoProgress
+ )
+ }
+
+ private func setupNotifications() {
+ NotificationCenter.default.addObserver(
+ self, selector: #selector(applicationWillResignActive),
+ name: UIApplication.willResignActiveNotification, object: nil)
+ NotificationCenter.default.addObserver(
+ self, selector: #selector(applicationDidBecomeActive),
+ name: UIApplication.didBecomeActiveNotification, object: nil)
+ }
+
+ // MARK: - Public Methods
+ func startPictureInPicture() {
+ self.vlc.pipController?.stateChangeEventHandler = { (isStarted: Bool) in
+ self.onPipStarted?(["pipStarted": isStarted])
+ }
+ self.vlc.pipController?.startPictureInPicture()
+ }
+
+ @objc func play() {
+ self.vlc.player.play()
+ self.isPaused = false
+ logger.debug("Play")
+ }
+
+ @objc func pause() {
+ self.vlc.player.pause()
+ self.isPaused = true
+ }
+
+ @objc func seekTo(_ time: Int32) {
+ let wasPlaying = vlc.player.isPlaying
+ if wasPlaying {
+ self.pause()
+ }
+
+ if let duration = vlc.player.media?.length.intValue {
+ logger.debug("Seeking to time: \(time) Video Duration \(duration)")
+
+ // If the specified time is greater than the duration, seek to the end
+ let seekTime = time > duration ? duration - 1000 : time
+ vlc.player.time = VLCTime(int: seekTime)
+ self.updatePlayerState()
+
+ // Let mediaPlayerStateChanged handle play state change
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
+ if wasPlaying {
+ self.play()
+ }
+ }
+ } else {
+ logger.error("Unable to retrieve video duration")
+ }
+ }
+
+ @objc func setSource(_ source: [String: Any]) {
+ logger.debug("Setting source...")
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self else { return }
+ if self.hasSource {
+ return
+ }
+
+ var mediaOptions = source["mediaOptions"] as? [String: Any] ?? [:]
+ self.externalTrack = source["externalTrack"] as? [String: String]
+ let initOptions: [String] = source["initOptions"] as? [String] ?? []
+ self.startPosition = source["startPosition"] as? Int32 ?? 0
+ self.externalSubtitles = source["externalSubtitles"] as? [[String: String]]
+
+ for item in initOptions {
+ let option = item.components(separatedBy: "=")
+ mediaOptions.updateValue(
+ option[1], forKey: option[0].replacingOccurrences(of: "--", with: ""))
+ }
+
+ guard let uri = source["uri"] as? String, !uri.isEmpty else {
+ logger.error("Invalid or empty URI")
+ self.onVideoError?(["error": "Invalid or empty URI"])
+ return
+ }
+
+ let autoplay = source["autoplay"] as? Bool ?? false
+ let isNetwork = source["isNetwork"] as? Bool ?? false
+
+ self.onVideoLoadStart?(["target": self.reactTag ?? NSNull()])
+
+ let media: VLCMedia!
+ if isNetwork {
+ logger.debug("Loading network file: \(uri)")
+ media = VLCMedia(url: URL(string: uri)!)
+ } else {
+ logger.debug("Loading local file: \(uri)")
+ if uri.starts(with: "file://"), let url = URL(string: uri) {
+ media = VLCMedia(url: url)
+ } else {
+ media = VLCMedia(path: uri)
+ }
+ }
+
+ logger.debug("Media options: \(mediaOptions)")
+ media.addOptions(mediaOptions)
+
+ self.vlc.player.media = media
+ self.setInitialExternalSubtitles()
+ self.hasSource = true
+ if autoplay {
+ logger.info("Playing...")
+ self.play()
+ self.vlc.player.time = VLCTime(number: NSNumber(value: self.startPosition * 1000))
+ }
+ }
+ }
+
+ @objc func setAudioTrack(_ trackIndex: Int) {
+ print("Setting audio track: \(trackIndex)")
+ let track = self.vlc.player.audioTracks[trackIndex]
+ track.isSelectedExclusively = true
+ }
+
+ @objc func getAudioTracks() -> [[String: Any]]? {
+ return vlc.player.audioTracks.enumerated().map {
+ return ["name": $1.trackName, "index": $0]
+ }
+ }
+
+ @objc func setSubtitleTrack(_ trackIndex: Int) {
+ logger.debug("Attempting to set subtitle track to index: \(trackIndex)")
+ if trackIndex == -1 {
+ logger.debug("Disabling all subtitles")
+ for track in self.vlc.player.textTracks {
+ track.isSelected = false
+ }
+ return
+ }
+ let track = self.vlc.player.textTracks[trackIndex]
+ track.isSelectedExclusively = true;
+ logger.debug("Current subtitle track index after setting: \(track.trackName)")
+ }
+
+ @objc func setSubtitleURL(_ subtitleURL: String, name: String) {
+ guard let url = URL(string: subtitleURL) else {
+ logger.error("Invalid subtitle URL")
+ return
+ }
+ let result = self.vlc.player.addPlaybackSlave(url, type: .subtitle, enforce: false)
+ if result == 0 {
+ let internalName = "Track \(self.customSubtitles.count)"
+ self.customSubtitles.append((internalName: internalName, originalName: name))
+ logger.debug("Subtitle added with result: \(result) \(internalName)")
+ } else {
+ logger.debug("Failed to add subtitle")
+ }
+ }
+
+ @objc func getSubtitleTracks() -> [[String: Any]]? {
+ if self.vlc.player.textTracks.count == 0 {
+ return nil
+ }
+
+ logger.debug("Number of subtitle tracks: \(self.vlc.player.textTracks.count)")
+
+ let tracks = self.vlc.player.textTracks.enumerated().map { (index, track) in
+ if let customSubtitle = customSubtitles.first(where: {
+ $0.internalName == track.trackName
+ }) {
+ return ["name": customSubtitle.originalName, "index": index]
+ } else {
+ return ["name": track.trackName, "index": index]
+ }
+ }
+
+ logger.debug("Subtitle tracks: \(tracks)")
+ return tracks
+ }
+
+ @objc func stop(completion: (() -> Void)? = nil) {
+ logger.debug("Stopping media...")
+ guard !isStopping else {
+ completion?()
+ return
+ }
+ isStopping = true
+
+ // If we're not on the main thread, dispatch to main thread
+ if !Thread.isMainThread {
+ DispatchQueue.main.async { [weak self] in
+ self?.performStop(completion: completion)
+ }
+ } else {
+ performStop(completion: completion)
+ }
+ }
+
+ // MARK: - Private Methods
+
+ @objc private func applicationWillResignActive() {
+
+ }
+
+ @objc private func applicationDidBecomeActive() {
+
+ }
+
+ private func setInitialExternalSubtitles() {
+ if let externalSubtitles = self.externalSubtitles {
+ for subtitle in externalSubtitles {
+ if let subtitleName = subtitle["name"],
+ let subtitleURL = subtitle["DeliveryUrl"]
+ {
+ print("Setting external subtitle: \(subtitleName) \(subtitleURL)")
+ self.setSubtitleURL(subtitleURL, name: subtitleName)
+ }
+ }
+ }
+ }
+
+ private func performStop(completion: (() -> Void)? = nil) {
+ // Stop the media player
+ vlc.player.stop()
+
+ // Remove observer
+ NotificationCenter.default.removeObserver(self)
+
+ // Clear the video view
+ vlc.getPlayerView().removeFromSuperview()
+
+ isStopping = false
+ completion?()
+ }
+
+ private func updateVideoProgress() {
+ guard let media = self.vlc.player.media else { return }
+
+ let currentTimeMs = self.vlc.player.time.intValue
+ let durationMs = self.vlc.player.media?.length.intValue ?? 0
+
+ logger.debug("Current time: \(currentTimeMs)")
+ self.onVideoProgress?([
+ "currentTime": currentTimeMs,
+ "duration": durationMs,
+ ])
+ }
+
+ private func updatePlayerState() {
+ let player = self.vlc.player
+ self.onVideoStateChange?([
+ "target": self.reactTag ?? NSNull(),
+ "currentTime": player.time.intValue,
+ "duration": player.media?.length.intValue ?? 0,
+ "error": false,
+ "isPlaying": player.isPlaying,
+ "isBuffering": !player.isPlaying && player.state == VLCMediaPlayerState.buffering,
+ "state": player.state.description,
+ ])
+ }
+
+ // MARK: - Expo Events
+ @objc var onPlaybackStateChanged: RCTDirectEventBlock?
+ @objc var onVideoLoadStart: RCTDirectEventBlock?
+ @objc var onVideoStateChange: RCTDirectEventBlock?
+ @objc var onVideoProgress: RCTDirectEventBlock?
+ @objc var onVideoLoadEnd: RCTDirectEventBlock?
+ @objc var onVideoError: RCTDirectEventBlock?
+ @objc var onPipStarted: RCTDirectEventBlock?
+
+ // MARK: - Deinitialization
+
+ deinit {
+ logger.debug("Deinitialization")
+ performStop()
+ VLCManager.shared.listeners.removeAll()
+ }
+}
+
+// MARK: - SimpleAppLifecycleListener
+extension VlcPlayerView: SimpleAppLifecycleListener {
+ func applicationDidEnterBackground() {
+ logger.debug("Entering background")
+ }
+
+ func applicationDidEnterForeground() {
+ logger.debug("Entering foreground, is player visible? \(self.vlc.getPlayerView().superview != nil)")
+ if !self.vlc.getPlayerView().isDescendant(of: self) {
+ logger.debug("Player view is missing. Adding back as subview")
+ self.addSubview(self.vlc.getPlayerView())
+ }
+
+ // Current solution to fixing black screen when re-entering application
+ if let videoTrack = self.vlc.player.videoTracks.first { $0.isSelected == true }, !self.vlc.isMediaPlaying() {
+ videoTrack.isSelected = false
+ videoTrack.isSelectedExclusively = true
+ self.vlc.player.play()
+ self.vlc.player.pause()
+ }
+ }
+}
+
+extension VLCMediaPlayerState {
+ var description: String {
+ switch self {
+ case .opening: return "Opening"
+ case .buffering: return "Buffering"
+ case .playing: return "Playing"
+ case .paused: return "Paused"
+ case .stopped: return "Stopped"
+ case .error: return "Error"
+ @unknown default: return "Unknown"
+ }
+ }
+}
diff --git a/modules/vlc-player/src/VlcPlayer.types.ts b/modules/vlc-player/src/VlcPlayer.types.ts
new file mode 100644
index 00000000..e1c37797
--- /dev/null
+++ b/modules/vlc-player/src/VlcPlayer.types.ts
@@ -0,0 +1,96 @@
+export type PlaybackStatePayload = {
+ nativeEvent: {
+ target: number;
+ state: "Opening" | "Buffering" | "Playing" | "Paused" | "Error";
+ currentTime: number;
+ duration: number;
+ isBuffering: boolean;
+ isPlaying: boolean;
+ };
+};
+
+export type ProgressUpdatePayload = {
+ nativeEvent: {
+ currentTime: number;
+ duration: number;
+ isPlaying: boolean;
+ isBuffering: boolean;
+ };
+};
+
+export type VideoLoadStartPayload = {
+ nativeEvent: {
+ target: number;
+ };
+};
+
+export type PipStartedPayload = {
+ nativeEvent: {
+ pipStarted: boolean;
+ };
+};
+
+export type VideoStateChangePayload = PlaybackStatePayload;
+
+export type VideoProgressPayload = ProgressUpdatePayload;
+
+export type VlcPlayerSource = {
+ uri: string;
+ type?: string;
+ isNetwork?: boolean;
+ autoplay?: boolean;
+ externalSubtitles: { name: string; DeliveryUrl: string }[];
+ initOptions?: any[];
+ mediaOptions?: { [key: string]: any };
+ startPosition?: number;
+};
+
+export type TrackInfo = {
+ name: string;
+ index: number;
+ language?: string;
+};
+
+export type ChapterInfo = {
+ name: string;
+ timeOffset: number;
+ duration: number;
+};
+
+export type VlcPlayerViewProps = {
+ source: VlcPlayerSource;
+ style?: Object;
+ progressUpdateInterval?: number;
+ paused?: boolean;
+ muted?: boolean;
+ volume?: number;
+ videoAspectRatio?: string;
+ onVideoProgress?: (event: ProgressUpdatePayload) => void;
+ onVideoStateChange?: (event: PlaybackStatePayload) => void;
+ onVideoLoadStart?: (event: VideoLoadStartPayload) => void;
+ onVideoLoadEnd?: (event: VideoLoadStartPayload) => void;
+ onVideoError?: (event: PlaybackStatePayload) => void;
+ onPipStarted?: (event: PipStartedPayload) => void;
+};
+
+export interface VlcPlayerViewRef {
+ startPictureInPicture: () => Promise;
+ play: () => Promise;
+ pause: () => Promise;
+ stop: () => Promise;
+ seekTo: (time: number) => Promise;
+ setAudioTrack: (trackIndex: number) => Promise;
+ getAudioTracks: () => Promise;
+ setSubtitleTrack: (trackIndex: number) => Promise;
+ getSubtitleTracks: () => Promise;
+ setSubtitleDelay: (delay: number) => Promise;
+ setAudioDelay: (delay: number) => Promise;
+ takeSnapshot: (path: string, width: number, height: number) => Promise;
+ setRate: (rate: number) => Promise;
+ nextChapter: () => Promise;
+ previousChapter: () => Promise;
+ getChapters: () => Promise;
+ setVideoCropGeometry: (geometry: string | null) => Promise;
+ getVideoCropGeometry: () => Promise;
+ setSubtitleURL: (url: string, name: string) => Promise;
+}
diff --git a/modules/vlc-player/src/VlcPlayerView.tsx b/modules/vlc-player/src/VlcPlayerView.tsx
new file mode 100644
index 00000000..8195d6a9
--- /dev/null
+++ b/modules/vlc-player/src/VlcPlayerView.tsx
@@ -0,0 +1,135 @@
+import { requireNativeViewManager } from "expo-modules-core";
+import * as React from "react";
+
+import {
+ VlcPlayerViewProps,
+ VlcPlayerViewRef,
+ VlcPlayerSource,
+} from "./VlcPlayer.types";
+
+interface NativeViewRef extends VlcPlayerViewRef {
+ setNativeProps?: (props: Partial) => void;
+}
+
+const NativeViewManager = requireNativeViewManager("VlcPlayer");
+
+// Create a forwarded ref version of the native view
+const NativeView = React.forwardRef(
+ (props, ref) =>
+);
+
+const VlcPlayerView = React.forwardRef(
+ (props, ref) => {
+ const nativeRef = React.useRef(null);
+
+ React.useImperativeHandle(ref, () => ({
+ startPictureInPicture: async () => {
+ await nativeRef.current?.startPictureInPicture()
+ },
+ play: async () => {
+ await nativeRef.current?.play();
+ },
+ pause: async () => {
+ await nativeRef.current?.pause();
+ },
+ stop: async () => {
+ await nativeRef.current?.stop();
+ },
+ seekTo: async (time: number) => {
+ await nativeRef.current?.seekTo(time);
+ },
+ setAudioTrack: async (trackIndex: number) => {
+ await nativeRef.current?.setAudioTrack(trackIndex);
+ },
+ getAudioTracks: async () => {
+ const tracks = await nativeRef.current?.getAudioTracks();
+ return tracks ?? null;
+ },
+ setSubtitleTrack: async (trackIndex: number) => {
+ await nativeRef.current?.setSubtitleTrack(trackIndex);
+ },
+ getSubtitleTracks: async () => {
+ const tracks = await nativeRef.current?.getSubtitleTracks();
+ return tracks ?? null;
+ },
+ setSubtitleDelay: async (delay: number) => {
+ await nativeRef.current?.setSubtitleDelay(delay);
+ },
+ setAudioDelay: async (delay: number) => {
+ await nativeRef.current?.setAudioDelay(delay);
+ },
+ takeSnapshot: async (path: string, width: number, height: number) => {
+ await nativeRef.current?.takeSnapshot(path, width, height);
+ },
+ setRate: async (rate: number) => {
+ await nativeRef.current?.setRate(rate);
+ },
+ nextChapter: async () => {
+ await nativeRef.current?.nextChapter();
+ },
+ previousChapter: async () => {
+ await nativeRef.current?.previousChapter();
+ },
+ getChapters: async () => {
+ const chapters = await nativeRef.current?.getChapters();
+ return chapters ?? null;
+ },
+ setVideoCropGeometry: async (geometry: string | null) => {
+ await nativeRef.current?.setVideoCropGeometry(geometry);
+ },
+ getVideoCropGeometry: async () => {
+ const geometry = await nativeRef.current?.getVideoCropGeometry();
+ return geometry ?? null;
+ },
+ setSubtitleURL: async (url: string, name: string) => {
+ await nativeRef.current?.setSubtitleURL(url, name);
+ },
+ }));
+
+ const {
+ source,
+ style,
+ progressUpdateInterval = 500,
+ paused,
+ muted,
+ volume,
+ videoAspectRatio,
+ onVideoLoadStart,
+ onVideoStateChange,
+ onVideoProgress,
+ onVideoLoadEnd,
+ onVideoError,
+ onPipStarted,
+ ...otherProps
+ } = props;
+
+ const processedSource: VlcPlayerSource =
+ typeof source === "string" ? { uri: source } : source;
+
+ if (processedSource.startPosition !== undefined) {
+ processedSource.startPosition = Math.floor(processedSource.startPosition);
+ }
+
+ return (
+
+ );
+ }
+);
+
+export default VlcPlayerView;
diff --git a/modules/wifi-ssid/ios/AppLifecycleDelegate.swift b/modules/wifi-ssid/ios/AppLifecycleDelegate.swift
new file mode 100644
index 00000000..d5069b48
--- /dev/null
+++ b/modules/wifi-ssid/ios/AppLifecycleDelegate.swift
@@ -0,0 +1,32 @@
+import ExpoModulesCore
+
+protocol SimpleAppLifecycleListener {
+ func applicationDidEnterBackground() -> Void
+ func applicationDidEnterForeground() -> Void
+}
+
+public class AppLifecycleDelegate: ExpoAppDelegateSubscriber {
+ public func applicationDidBecomeActive(_ application: UIApplication) {
+ // The app has become active.
+ }
+
+ public func applicationWillResignActive(_ application: UIApplication) {
+ // The app is about to become inactive.
+ }
+
+ public func applicationDidEnterBackground(_ application: UIApplication) {
+ VLCManager.shared.listeners.forEach { listener in
+ listener.applicationDidEnterBackground()
+ }
+ }
+
+ public func applicationWillEnterForeground(_ application: UIApplication) {
+ VLCManager.shared.listeners.forEach { listener in
+ listener.applicationDidEnterForeground()
+ }
+ }
+
+ public func applicationWillTerminate(_ application: UIApplication) {
+ // The app is about to terminate.
+ }
+}
diff --git a/modules/wifi-ssid/ios/VLCManager.swift b/modules/wifi-ssid/ios/VLCManager.swift
new file mode 100644
index 00000000..8f2b84b7
--- /dev/null
+++ b/modules/wifi-ssid/ios/VLCManager.swift
@@ -0,0 +1,4 @@
+class VLCManager {
+ static let shared = VLCManager()
+ var listeners: [SimpleAppLifecycleListener] = []
+}
\ No newline at end of file
diff --git a/modules/wifi-ssid/ios/WifiSsid.podspec b/modules/wifi-ssid/ios/WifiSsid.podspec
index 427a477d..25f2d73e 100644
--- a/modules/wifi-ssid/ios/WifiSsid.podspec
+++ b/modules/wifi-ssid/ios/WifiSsid.podspec
@@ -1,18 +1,19 @@
Pod::Spec.new do |s|
- s.name = 'WifiSsid'
- s.version = '1.0.0'
- s.summary = 'Get WiFi SSID on iOS'
- s.description = 'Native iOS module to get current WiFi SSID using NEHotspotNetwork'
+ s.name = 'VlcPlayer'
+ s.version = '4.0.0a10'
+ s.summary = 'A sample project summary'
+ s.description = 'A sample project description'
s.author = ''
s.homepage = 'https://docs.expo.dev/modules/'
- s.platforms = { :ios => '15.6', :tvos => '15.0' }
+ s.platforms = { :ios => '13.4', :tvos => '16' }
s.source = { git: '' }
s.static_framework = true
s.dependency 'ExpoModulesCore'
+ s.ios.dependency 'VLCKit', s.version
+ s.tvos.dependency 'VLCKit', s.version
- s.frameworks = 'NetworkExtension', 'SystemConfiguration'
-
+ # Swift/Objective-C compatibility
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
'SWIFT_COMPILATION_MODE' => 'wholemodule'
diff --git a/package.json b/package.json
index 2679912c..074e60bd 100644
--- a/package.json
+++ b/package.json
@@ -6,160 +6,131 @@
"submodule-reload": "git submodule update --init --remote --recursive",
"clean": "echo y | expo prebuild --clean",
"start": "bun run submodule-reload && expo start",
- "prebuild": "cross-env EXPO_TV=0 bun run clean",
- "prebuild:tv": "cross-env EXPO_TV=1 bun run clean",
- "ios:install-metal-toolchain": "bash scripts/ios/install-metal-toolchain.sh",
- "ios": "cross-env EXPO_TV=0 expo run:ios",
- "ios:tv": "cross-env EXPO_TV=1 expo run:ios",
- "android": "cross-env EXPO_TV=0 expo run:android",
- "android:tv": "cross-env EXPO_TV=1 expo run:android",
- "build:android:local": "cd android && cross-env NODE_ENV=production ./gradlew assembleRelease",
- "ios:unsigned-build": "cross-env EXPO_TV=0 bun scripts/ios/build-ios.ts --production",
- "prepare": "husky",
- "typecheck": "node scripts/typecheck.js",
- "check": "biome check . --max-diagnostics 1000",
- "lint": "biome check --write --unsafe --max-diagnostics 1000",
- "format": "biome format --write .",
- "doctor": "expo-doctor",
- "test": "bun run typecheck && bun run lint && bun run format && bun run doctor",
+ "ios": "EXPO_TV=0 expo run:ios",
+ "ios:tv": "EXPO_TV=1 expo run:ios",
+ "android": "EXPO_TV=0 expo run:android",
+ "android:tv": "EXPO_TV=1 expo run:android",
+ "prebuild": "EXPO_TV=0 bun run clean",
+ "prebuild:tv": "EXPO_TV=1 bun run clean",
+ "prebuild:tv-new": "EXPO_TV=1 node ./scripts/symlink-native-dirs.js; bun run prebuild:tv",
+ "test": "jest --watchAll",
+ "lint": "expo lint",
"postinstall": "patch-package"
},
"dependencies": {
- "@bottom-tabs/react-navigation": "1.1.0",
- "@douglowder/expo-av-route-picker-view": "^0.0.5",
- "@expo/metro-runtime": "~6.1.1",
- "@expo/react-native-action-sheet": "^4.1.1",
- "@expo/ui": "0.2.0-beta.9",
- "@expo/vector-icons": "^15.0.3",
- "@gorhom/bottom-sheet": "5.2.8",
- "@jellyfin/sdk": "^0.13.0",
- "@react-native-community/netinfo": "^11.4.1",
- "@react-navigation/material-top-tabs": "7.4.9",
+ "@bottom-tabs/react-navigation": "0.8.6",
+ "@config-plugins/ffmpeg-kit-react-native": "^9.0.0",
+ "@expo/config-plugins": "~9.0.15",
+ "@expo/react-native-action-sheet": "^4.1.0",
+ "@expo/vector-icons": "^14.0.4",
+ "@futurejj/react-native-visibility-sensor": "^1.3.10",
+ "@gorhom/bottom-sheet": "^5.1.0",
+ "@jellyfin/sdk": "^0.11.0",
+ "@kesha-antonov/react-native-background-downloader": "3.2.6",
+ "@react-native-community/netinfo": "11.4.1",
+ "@react-native-menu/menu": "^1.2.2",
+ "@react-navigation/bottom-tabs": "^7.2.0",
+ "@react-navigation/material-top-tabs": "^7.1.0",
"@react-navigation/native": "^7.0.14",
- "@shopify/flash-list": "2.0.2",
- "@tanstack/query-sync-storage-persister": "^5.90.18",
- "@tanstack/react-pacer": "^0.19.1",
- "@tanstack/react-query": "5.90.17",
- "@tanstack/react-query-persist-client": "^5.90.18",
+ "@shopify/flash-list": "1.7.3",
+ "@tanstack/react-query": "^5.66.0",
+ "add": "^2.0.6",
"axios": "^1.7.9",
- "expo": "~54.0.31",
- "expo-application": "~7.0.8",
- "expo-asset": "~12.0.12",
- "expo-background-task": "~1.0.10",
- "expo-blur": "~15.0.8",
- "expo-brightness": "~14.0.8",
- "expo-build-properties": "~1.0.10",
- "expo-constants": "18.0.13",
- "expo-crypto": "^15.0.8",
- "expo-dev-client": "~6.0.20",
- "expo-device": "~8.0.10",
- "expo-font": "~14.0.10",
- "expo-haptics": "~15.0.8",
- "expo-image": "~3.0.11",
- "expo-linear-gradient": "~15.0.8",
- "expo-linking": "~8.0.11",
- "expo-localization": "~17.0.8",
- "expo-location": "^19.0.8",
- "expo-notifications": "~0.32.16",
- "expo-router": "~6.0.21",
- "expo-screen-orientation": "~9.0.8",
- "expo-secure-store": "^15.0.8",
- "expo-sharing": "~14.0.8",
- "expo-splash-screen": "~31.0.13",
- "expo-status-bar": "~3.0.9",
- "expo-system-ui": "~6.0.9",
- "expo-task-manager": "14.0.9",
- "expo-web-browser": "~15.0.10",
- "i18next": "^25.0.0",
- "jotai": "2.16.2",
- "lodash": "4.17.21",
+ "expo": "^52.0.31",
+ "expo-asset": "~11.0.3",
+ "expo-background-fetch": "~13.0.5",
+ "expo-blur": "~14.0.3",
+ "expo-brightness": "~13.0.3",
+ "expo-build-properties": "~0.13.2",
+ "expo-constants": "~17.0.5",
+ "expo-crypto": "~14.0.2",
+ "expo-dev-client": "~5.0.11",
+ "expo-device": "~7.0.2",
+ "expo-font": "~13.0.3",
+ "expo-haptics": "~14.0.1",
+ "expo-image": "~2.0.4",
+ "expo-keep-awake": "~14.0.2",
+ "expo-linear-gradient": "~14.0.2",
+ "expo-linking": "~7.0.5",
+ "expo-localization": "~16.0.1",
+ "expo-network": "~7.0.5",
+ "expo-notifications": "~0.29.13",
+ "expo-router": "~4.0.17",
+ "expo-screen-orientation": "~8.0.4",
+ "expo-sensors": "~14.0.2",
+ "expo-splash-screen": "~0.29.22",
+ "expo-status-bar": "~2.0.1",
+ "expo-system-ui": "~4.0.8",
+ "expo-task-manager": "~12.0.5",
+ "expo-updates": "~0.26.17",
+ "expo-web-browser": "~14.0.2",
+ "ffmpeg-kit-react-native": "^6.0.2",
+ "i18next": "^24.2.2",
+ "jotai": "^2.11.3",
+ "lodash": "^4.17.21",
"nativewind": "^2.0.11",
- "patch-package": "^8.0.0",
- "react": "19.1.0",
- "react-dom": "19.1.0",
- "react-i18next": "16.5.3",
- "react-native": "0.81.5",
+ "react": "18.3.1",
+ "react-dom": "18.3.1",
+ "react-i18next": "^15.4.0",
+ "react-native": "npm:react-native-tvos@~0.77.0-0",
"react-native-awesome-slider": "^2.9.0",
- "react-native-bottom-tabs": "1.1.0",
+ "react-native-bottom-tabs": "0.8.6",
"react-native-circular-progress": "^1.4.1",
- "react-native-collapsible": "^1.6.2",
+ "react-native-compressor": "^1.10.3",
"react-native-country-flag": "^2.0.2",
- "react-native-device-info": "^15.0.0",
- "react-native-draggable-flatlist": "^4.0.3",
- "react-native-edge-to-edge": "^1.7.0",
- "react-native-gesture-handler": "2.28.0",
- "react-native-glass-effect-view": "^1.0.0",
- "react-native-google-cast": "^4.9.1",
+ "react-native-device-info": "^14.0.4",
+ "react-native-edge-to-edge": "^1.4.3",
+ "react-native-gesture-handler": "2.22.0",
+ "react-native-get-random-values": "^1.11.0",
+ "react-native-google-cast": "^4.8.3",
"react-native-image-colors": "^2.4.0",
- "react-native-ios-context-menu": "^3.2.1",
- "react-native-ios-utilities": "5.2.0",
- "react-native-mmkv": "4.1.1",
- "react-native-nitro-modules": "0.33.1",
- "react-native-pager-view": "^6.9.1",
- "react-native-reanimated": "~4.1.1",
- "react-native-reanimated-carousel": "4.0.3",
- "react-native-safe-area-context": "~5.6.0",
- "react-native-screens": "~4.18.0",
- "react-native-svg": "15.12.1",
- "react-native-text-ticker": "^1.15.0",
- "react-native-track-player": "github:lovegaoshi/react-native-track-player#APM",
+ "react-native-ios-context-menu": "^3.1.0",
+ "react-native-ios-utilities": "5.1.1",
+ "react-native-mmkv": "^2.12.2",
+ "react-native-pager-view": "6.5.1",
+ "react-native-progress": "^5.0.1",
+ "react-native-reanimated": "~3.16.7",
+ "react-native-reanimated-carousel": "3.5.1",
+ "react-native-safe-area-context": "5.1.0",
+ "react-native-screens": "~4.5.0",
+ "react-native-svg": "15.11.1",
+ "react-native-tab-view": "^4.0.5",
"react-native-udp": "^4.1.7",
+ "react-native-uitextview": "^1.4.0",
"react-native-url-polyfill": "^2.0.0",
"react-native-uuid": "^2.0.3",
+ "react-native-video": "6.10.0",
"react-native-volume-manager": "^2.0.8",
- "react-native-web": "^0.21.0",
- "react-native-worklets": "0.5.1",
- "sonner-native": "0.21.2",
+ "react-native-web": "~0.19.13",
+ "react-native-webview": "13.13.2",
+ "sonner-native": "^0.17.0",
"tailwindcss": "3.3.2",
"use-debounce": "^10.0.4",
- "zod": "4.1.13"
+ "uuid": "^11.0.5",
+ "zeego": "^2.0.4",
+ "zod": "^3.24.1"
},
"devDependencies": {
- "@babel/core": "7.28.6",
- "@biomejs/biome": "2.3.11",
- "@react-native-community/cli": "20.1.0",
- "@react-native-tvos/config-tv": "0.1.4",
- "@types/jest": "29.5.14",
- "@types/lodash": "4.17.23",
- "@types/react": "19.1.17",
- "@types/react-test-renderer": "19.1.0",
- "cross-env": "10.1.0",
- "expo-doctor": "1.17.14",
- "husky": "9.1.7",
- "lint-staged": "16.2.7",
- "react-test-renderer": "19.2.3",
- "typescript": "5.9.3"
- },
- "expo": {
- "doctor": {
- "reactNativeDirectoryCheck": {
- "exclude": [
- "react-native-google-cast",
- "react-native-udp",
- "@jellyfin/sdk"
- ],
- "listUnknownPackages": false
- }
- },
- "install": {
- "exclude": [
- "react-native-screens"
- ]
- }
+ "@babel/core": "^7.26.8",
+ "@react-native-community/cli": "15.1.3",
+ "@react-native-tvos/config-tv": "^0.1.1",
+ "@types/jest": "^29.5.14",
+ "@types/react": "~18.3.12",
+ "@types/react-test-renderer": "^19.0.0",
+ "patch-package": "^8.0.0",
+ "postinstall-postinstall": "^2.1.0",
+ "react-test-renderer": "19.0.0",
+ "typescript": "~5.7.3",
+ "@types/lodash": "^4.17.15",
+ "@types/react-native-vector-icons": "^6.4.18",
+ "@types/uuid": "^10.0.0"
},
"private": true,
- "lint-staged": {
- "*.{js,jsx,ts,tsx}": [
- "biome check --write --unsafe --no-errors-on-unmatched"
- ],
- "*.json": [
- "biome format --write"
- ]
- },
- "trustedDependencies": [
- "unrs-resolver"
- ],
- "resolutions": {
- "expo-constants": "18.0.13"
+ "expo": {
+ "install": {
+ "exclude": [
+ "react-native"
+ ]
+ }
}
}
diff --git a/patches/@expo+react-native-action-sheet+4.1.0.patch b/patches/@expo+react-native-action-sheet+4.1.0.patch
new file mode 100644
index 00000000..c2640492
--- /dev/null
+++ b/patches/@expo+react-native-action-sheet+4.1.0.patch
@@ -0,0 +1,18 @@
+diff --git a/node_modules/@expo/react-native-action-sheet/lib/commonjs/ActionSheet/CustomActionSheet.js b/node_modules/@expo/react-native-action-sheet/lib/commonjs/ActionSheet/CustomActionSheet.js
+index 2a6943f..42d40e0 100644
+--- a/node_modules/@expo/react-native-action-sheet/lib/commonjs/ActionSheet/CustomActionSheet.js
++++ b/node_modules/@expo/react-native-action-sheet/lib/commonjs/ActionSheet/CustomActionSheet.js
+@@ -1,2 +1,2 @@
+-var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");var _interopRequireWildcard=require("@babel/runtime/helpers/interopRequireWildcard");Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _classCallCheck2=_interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));var _createClass2=_interopRequireDefault(require("@babel/runtime/helpers/createClass"));var _assertThisInitialized2=_interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));var _inherits2=_interopRequireDefault(require("@babel/runtime/helpers/inherits"));var _possibleConstructorReturn2=_interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));var _getPrototypeOf2=_interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));var React=_interopRequireWildcard(require("react"));var _reactNative=require("react-native");var _ActionGroup=_interopRequireDefault(require("./ActionGroup"));var _jsxFileName="/home/runner/work/react-native-action-sheet/react-native-action-sheet/src/ActionSheet/CustomActionSheet.tsx";function _createSuper(Derived){var hasNativeReflectConstruct=_isNativeReflectConstruct();return function _createSuperInternal(){var Super=(0,_getPrototypeOf2.default)(Derived),result;if(hasNativeReflectConstruct){var NewTarget=(0,_getPrototypeOf2.default)(this).constructor;result=Reflect.construct(Super,arguments,NewTarget);}else{result=Super.apply(this,arguments);}return(0,_possibleConstructorReturn2.default)(this,result);};}function _isNativeReflectConstruct(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return true;}catch(e){return false;}}var OPACITY_ANIMATION_IN_TIME=225;var OPACITY_ANIMATION_OUT_TIME=195;var EASING_OUT=_reactNative.Easing.bezier(0.25,0.46,0.45,0.94);var EASING_IN=_reactNative.Easing.out(EASING_OUT);var ESCAPE_KEY='Escape';var CustomActionSheet=function(_React$Component){(0,_inherits2.default)(CustomActionSheet,_React$Component);var _super=_createSuper(CustomActionSheet);function CustomActionSheet(){var _this;(0,_classCallCheck2.default)(this,CustomActionSheet);for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++){args[_key]=arguments[_key];}_this=_super.call.apply(_super,[this].concat(args));_this._actionSheetHeight=360;_this.state={isVisible:false,isAnimating:false,options:null,onSelect:null,overlayOpacity:new _reactNative.Animated.Value(0),sheetOpacity:new _reactNative.Animated.Value(0)};_this._deferAfterAnimation=undefined;_this._handleWebKeyDown=function(event){if(event.key===ESCAPE_KEY&&_this.state.isVisible){event.preventDefault();_this._selectCancelButton();}};_this._setActionSheetHeight=function(_ref){var nativeEvent=_ref.nativeEvent;return _this._actionSheetHeight=nativeEvent.layout.height;};_this.showActionSheetWithOptions=function(options,onSelect){var _this$state=_this.state,isVisible=_this$state.isVisible,overlayOpacity=_this$state.overlayOpacity,sheetOpacity=_this$state.sheetOpacity;var _this$props$useNative=_this.props.useNativeDriver,useNativeDriver=_this$props$useNative===void 0?true:_this$props$useNative;if(isVisible){_this._deferAfterAnimation=_this.showActionSheetWithOptions.bind((0,_assertThisInitialized2.default)(_this),options,onSelect);return;}_this.setState({options:options,onSelect:onSelect,isVisible:true,isAnimating:true});overlayOpacity.setValue(0);sheetOpacity.setValue(0);_reactNative.Animated.parallel([_reactNative.Animated.timing(overlayOpacity,{toValue:0.32,easing:EASING_OUT,duration:OPACITY_ANIMATION_IN_TIME,useNativeDriver:useNativeDriver}),_reactNative.Animated.timing(sheetOpacity,{toValue:1,easing:EASING_OUT,duration:OPACITY_ANIMATION_IN_TIME,useNativeDriver:useNativeDriver})]).start(function(result){if(result.finished){_this.setState({isAnimating:false});_this._deferAfterAnimation=undefined;}});_reactNative.BackHandler.addEventListener('actionSheetHardwareBackPress',_this._selectCancelButton);};_this._selectCancelButton=function(){var options=_this.state.options;if(!options){return false;}if(typeof options.cancelButtonIndex==='undefined'){return false;}else if(typeof options.cancelButtonIndex==='number'){return _this._onSelect(options.cancelButtonIndex);}else{return _this._animateOut();}};_this._onSelect=function(index){var _this$state2=_this.state,isAnimating=_this$state2.isAnimating,onSelect=_this$state2.onSelect;if(isAnimating){return false;}if(onSelect){_this._deferAfterAnimation=onSelect.bind((0,_assertThisInitialized2.default)(_this),index);}return _this._animateOut();};_this._animateOut=function(){var _this$state3=_this.state,isAnimating=_this$state3.isAnimating,overlayOpacity=_this$state3.overlayOpacity,sheetOpacity=_this$state3.sheetOpacity;var _this$props$useNative2=_this.props.useNativeDriver,useNativeDriver=_this$props$useNative2===void 0?true:_this$props$useNative2;if(isAnimating){return false;}_reactNative.BackHandler.removeEventListener('actionSheetHardwareBackPress',_this._selectCancelButton);_this.setState({isAnimating:true});_reactNative.Animated.parallel([_reactNative.Animated.timing(overlayOpacity,{toValue:0,easing:EASING_IN,duration:OPACITY_ANIMATION_OUT_TIME,useNativeDriver:useNativeDriver}),_reactNative.Animated.timing(sheetOpacity,{toValue:0,easing:EASING_IN,duration:OPACITY_ANIMATION_OUT_TIME,useNativeDriver:useNativeDriver})]).start(function(result){if(result.finished){_this.setState({isVisible:false,isAnimating:false});if(_this._deferAfterAnimation){_this._deferAfterAnimation();}}});return true;};return _this;}(0,_createClass2.default)(CustomActionSheet,[{key:"componentDidMount",value:function componentDidMount(){if(_reactNative.Platform.OS==='web'){document.addEventListener('keydown',this._handleWebKeyDown);}}},{key:"componentWillUnmount",value:function componentWillUnmount(){if(_reactNative.Platform.OS==='web'){document.removeEventListener('keydown',this._handleWebKeyDown);}}},{key:"render",value:function render(){var _this$state4=this.state,isVisible=_this$state4.isVisible,overlayOpacity=_this$state4.overlayOpacity,options=_this$state4.options;var useModal=options?options.autoFocus||options.useModal===true:false;var overlay=isVisible?React.createElement(_reactNative.Animated.View,{style:[styles.overlay,{opacity:overlayOpacity}],__source:{fileName:_jsxFileName,lineNumber:79,columnNumber:7}}):null;var appContent=React.createElement(_reactNative.View,{style:styles.flexContainer,importantForAccessibility:isVisible?'no-hide-descendants':'auto',__source:{fileName:_jsxFileName,lineNumber:91,columnNumber:7}},React.Children.only(this.props.children));return React.createElement(_reactNative.View,{pointerEvents:this.props.pointerEvents,style:styles.flexContainer,__source:{fileName:_jsxFileName,lineNumber:99,columnNumber:7}},appContent,isVisible&&!useModal&&React.createElement(React.Fragment,null,overlay,this._renderSheet()),isVisible&&useModal&&React.createElement(_reactNative.Modal,{animationType:"none",transparent:true,onRequestClose:this._selectCancelButton,__source:{fileName:_jsxFileName,lineNumber:108,columnNumber:11}},overlay,this._renderSheet()));}},{key:"_renderSheet",value:function _renderSheet(){var _this$state5=this.state,options=_this$state5.options,isAnimating=_this$state5.isAnimating,sheetOpacity=_this$state5.sheetOpacity;if(!options){return null;}var optionsArray=options.options,icons=options.icons,tintIcons=options.tintIcons,destructiveButtonIndex=options.destructiveButtonIndex,disabledButtonIndices=options.disabledButtonIndices,destructiveColor=options.destructiveColor,textStyle=options.textStyle,tintColor=options.tintColor,title=options.title,titleTextStyle=options.titleTextStyle,message=options.message,messageTextStyle=options.messageTextStyle,autoFocus=options.autoFocus,showSeparators=options.showSeparators,containerStyle=options.containerStyle,separatorStyle=options.separatorStyle,cancelButtonIndex=options.cancelButtonIndex,cancelButtonTintColor=options.cancelButtonTintColor;return React.createElement(_reactNative.TouchableWithoutFeedback,{importantForAccessibility:"yes",onPress:this._selectCancelButton,__source:{fileName:_jsxFileName,lineNumber:145,columnNumber:7}},React.createElement(_reactNative.Animated.View,{needsOffscreenAlphaCompositing:isAnimating,style:[styles.sheetContainer,{opacity:sheetOpacity,transform:[{translateY:sheetOpacity.interpolate({inputRange:[0,1],outputRange:[this._actionSheetHeight,0]})}]}],__source:{fileName:_jsxFileName,lineNumber:146,columnNumber:9}},React.createElement(_reactNative.View,{style:styles.sheet,onLayout:this._setActionSheetHeight,__source:{fileName:_jsxFileName,lineNumber:162,columnNumber:11}},React.createElement(_ActionGroup.default,{options:optionsArray,icons:icons,tintIcons:tintIcons===undefined?true:tintIcons,cancelButtonIndex:cancelButtonIndex,cancelButtonTintColor:cancelButtonTintColor,destructiveButtonIndex:destructiveButtonIndex,destructiveColor:destructiveColor,disabledButtonIndices:disabledButtonIndices,onSelect:this._onSelect,startIndex:0,length:optionsArray.length,textStyle:textStyle||{},tintColor:tintColor,title:title||undefined,titleTextStyle:titleTextStyle,message:message||undefined,messageTextStyle:messageTextStyle,autoFocus:autoFocus,showSeparators:showSeparators,containerStyle:containerStyle,separatorStyle:separatorStyle,__source:{fileName:_jsxFileName,lineNumber:163,columnNumber:13}}))));}}]);return CustomActionSheet;}(React.Component);exports.default=CustomActionSheet;var styles=_reactNative.StyleSheet.create({flexContainer:{flex:1},overlay:{position:'absolute',top:0,right:0,bottom:0,left:0,backgroundColor:'black'},sheetContainer:{position:'absolute',left:0,right:0,bottom:0,top:0,backgroundColor:'transparent',alignItems:'flex-end',justifyContent:'center',flexDirection:'row'},sheet:{flex:1,backgroundColor:'transparent'}});
++var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");var _interopRequireWildcard=require("@babel/runtime/helpers/interopRequireWildcard");Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _classCallCheck2=_interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));var _createClass2=_interopRequireDefault(require("@babel/runtime/helpers/createClass"));var _assertThisInitialized2=_interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));var _inherits2=_interopRequireDefault(require("@babel/runtime/helpers/inherits"));var _possibleConstructorReturn2=_interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));var _getPrototypeOf2=_interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));var React=_interopRequireWildcard(require("react"));var _reactNative=require("react-native");var _ActionGroup=_interopRequireDefault(require("./ActionGroup"));var _jsxFileName="/home/runner/work/react-native-action-sheet/react-native-action-sheet/src/ActionSheet/CustomActionSheet.tsx";function _createSuper(Derived){var hasNativeReflectConstruct=_isNativeReflectConstruct();return function _createSuperInternal(){var Super=(0,_getPrototypeOf2.default)(Derived),result;if(hasNativeReflectConstruct){var NewTarget=(0,_getPrototypeOf2.default)(this).constructor;result=Reflect.construct(Super,arguments,NewTarget);}else{result=Super.apply(this,arguments);}return(0,_possibleConstructorReturn2.default)(this,result);};}function _isNativeReflectConstruct(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return true;}catch(e){return false;}}var OPACITY_ANIMATION_IN_TIME=225;var OPACITY_ANIMATION_OUT_TIME=195;var EASING_OUT=_reactNative.Easing.bezier(0.25,0.46,0.45,0.94);var EASING_IN=_reactNative.Easing.out(EASING_OUT);var ESCAPE_KEY='Escape';var CustomActionSheet=function(_React$Component){(0,_inherits2.default)(CustomActionSheet,_React$Component);var _super=_createSuper(CustomActionSheet);function CustomActionSheet(){var _this;(0,_classCallCheck2.default)(this,CustomActionSheet);for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++){args[_key]=arguments[_key];}_this=_super.call.apply(_super,[this].concat(args));_this._actionSheetHeight=360;_this._backHandlerListener=null;_this.state={isVisible:false,isAnimating:false,options:null,onSelect:null,overlayOpacity:new _reactNative.Animated.Value(0),sheetOpacity:new _reactNative.Animated.Value(0)};_this._deferAfterAnimation=undefined;_this._handleWebKeyDown=function(event){if(event.key===ESCAPE_KEY&&_this.state.isVisible){event.preventDefault();_this._selectCancelButton();}};_this._setActionSheetHeight=function(_ref){var nativeEvent=_ref.nativeEvent;return _this._actionSheetHeight=nativeEvent.layout.height;};_this.showActionSheetWithOptions=function(options,onSelect){var _this$state=_this.state,isVisible=_this$state.isVisible,overlayOpacity=_this$state.overlayOpacity,sheetOpacity=_this$state.sheetOpacity;var _this$props$useNative=_this.props.useNativeDriver,useNativeDriver=_this$props$useNative===void 0?true:_this$props$useNative;if(isVisible){_this._deferAfterAnimation=_this.showActionSheetWithOptions.bind((0,_assertThisInitialized2.default)(_this),options,onSelect);return;}_this.setState({options:options,onSelect:onSelect,isVisible:true,isAnimating:true});overlayOpacity.setValue(0);sheetOpacity.setValue(0);_reactNative.Animated.parallel([_reactNative.Animated.timing(overlayOpacity,{toValue:0.32,easing:EASING_OUT,duration:OPACITY_ANIMATION_IN_TIME,useNativeDriver:useNativeDriver}),_reactNative.Animated.timing(sheetOpacity,{toValue:1,easing:EASING_OUT,duration:OPACITY_ANIMATION_IN_TIME,useNativeDriver:useNativeDriver})]).start(function(result){if(result.finished){_this.setState({isAnimating:false});_this._deferAfterAnimation=undefined;}});_this._backHandlerListener=_reactNative.BackHandler.addEventListener('actionSheetHardwareBackPress',_this._selectCancelButton);};_this._selectCancelButton=function(){var options=_this.state.options;if(!options){return false;}if(typeof options.cancelButtonIndex==='undefined'){return false;}else if(typeof options.cancelButtonIndex==='number'){return _this._onSelect(options.cancelButtonIndex);}else{return _this._animateOut();}};_this._onSelect=function(index){var _this$state2=_this.state,isAnimating=_this$state2.isAnimating,onSelect=_this$state2.onSelect;if(isAnimating){return false;}if(onSelect){_this._deferAfterAnimation=onSelect.bind((0,_assertThisInitialized2.default)(_this),index);}return _this._animateOut();};_this._animateOut=function(){var _this$state3=_this.state,isAnimating=_this$state3.isAnimating,overlayOpacity=_this$state3.overlayOpacity,sheetOpacity=_this$state3.sheetOpacity;var _this$props$useNative2=_this.props.useNativeDriver,useNativeDriver=_this$props$useNative2===void 0?true:_this$props$useNative2;if(isAnimating){return false;}if(_this._backHandlerListener){_this._backHandlerListener.remove();};_this.setState({isAnimating:true});_reactNative.Animated.parallel([_reactNative.Animated.timing(overlayOpacity,{toValue:0,easing:EASING_IN,duration:OPACITY_ANIMATION_OUT_TIME,useNativeDriver:useNativeDriver}),_reactNative.Animated.timing(sheetOpacity,{toValue:0,easing:EASING_IN,duration:OPACITY_ANIMATION_OUT_TIME,useNativeDriver:useNativeDriver})]).start(function(result){if(result.finished){_this.setState({isVisible:false,isAnimating:false});if(_this._deferAfterAnimation){_this._deferAfterAnimation();}}});return true;};return _this;}(0,_createClass2.default)(CustomActionSheet,[{key:"componentDidMount",value:function componentDidMount(){if(_reactNative.Platform.OS==='web'){document.addEventListener('keydown',this._handleWebKeyDown);}}},{key:"componentWillUnmount",value:function componentWillUnmount(){if(_reactNative.Platform.OS==='web'){document.removeEventListener('keydown',this._handleWebKeyDown);}}},{key:"render",value:function render(){var _this$state4=this.state,isVisible=_this$state4.isVisible,overlayOpacity=_this$state4.overlayOpacity,options=_this$state4.options;var useModal=options?options.autoFocus||options.useModal===true:false;var overlay=isVisible?React.createElement(_reactNative.Animated.View,{style:[styles.overlay,{opacity:overlayOpacity}],__source:{fileName:_jsxFileName,lineNumber:79,columnNumber:7}}):null;var appContent=React.createElement(_reactNative.View,{style:styles.flexContainer,importantForAccessibility:isVisible?'no-hide-descendants':'auto',__source:{fileName:_jsxFileName,lineNumber:91,columnNumber:7}},React.Children.only(this.props.children));return React.createElement(_reactNative.View,{pointerEvents:this.props.pointerEvents,style:styles.flexContainer,__source:{fileName:_jsxFileName,lineNumber:99,columnNumber:7}},appContent,isVisible&&!useModal&&React.createElement(React.Fragment,null,overlay,this._renderSheet()),isVisible&&useModal&&React.createElement(_reactNative.Modal,{animationType:"none",transparent:true,onRequestClose:this._selectCancelButton,__source:{fileName:_jsxFileName,lineNumber:108,columnNumber:11}},overlay,this._renderSheet()));}},{key:"_renderSheet",value:function _renderSheet(){var _this$state5=this.state,options=_this$state5.options,isAnimating=_this$state5.isAnimating,sheetOpacity=_this$state5.sheetOpacity;if(!options){return null;}var optionsArray=options.options,icons=options.icons,tintIcons=options.tintIcons,destructiveButtonIndex=options.destructiveButtonIndex,disabledButtonIndices=options.disabledButtonIndices,destructiveColor=options.destructiveColor,textStyle=options.textStyle,tintColor=options.tintColor,title=options.title,titleTextStyle=options.titleTextStyle,message=options.message,messageTextStyle=options.messageTextStyle,autoFocus=options.autoFocus,showSeparators=options.showSeparators,containerStyle=options.containerStyle,separatorStyle=options.separatorStyle,cancelButtonIndex=options.cancelButtonIndex,cancelButtonTintColor=options.cancelButtonTintColor;return React.createElement(_reactNative.TouchableWithoutFeedback,{importantForAccessibility:"yes",onPress:this._selectCancelButton,__source:{fileName:_jsxFileName,lineNumber:145,columnNumber:7}},React.createElement(_reactNative.Animated.View,{needsOffscreenAlphaCompositing:isAnimating,style:[styles.sheetContainer,{opacity:sheetOpacity,transform:[{translateY:sheetOpacity.interpolate({inputRange:[0,1],outputRange:[this._actionSheetHeight,0]})}]}],__source:{fileName:_jsxFileName,lineNumber:146,columnNumber:9}},React.createElement(_reactNative.View,{style:styles.sheet,onLayout:this._setActionSheetHeight,__source:{fileName:_jsxFileName,lineNumber:162,columnNumber:11}},React.createElement(_ActionGroup.default,{options:optionsArray,icons:icons,tintIcons:tintIcons===undefined?true:tintIcons,cancelButtonIndex:cancelButtonIndex,cancelButtonTintColor:cancelButtonTintColor,destructiveButtonIndex:destructiveButtonIndex,destructiveColor:destructiveColor,disabledButtonIndices:disabledButtonIndices,onSelect:this._onSelect,startIndex:0,length:optionsArray.length,textStyle:textStyle||{},tintColor:tintColor,title:title||undefined,titleTextStyle:titleTextStyle,message:message||undefined,messageTextStyle:messageTextStyle,autoFocus:autoFocus,showSeparators:showSeparators,containerStyle:containerStyle,separatorStyle:separatorStyle,__source:{fileName:_jsxFileName,lineNumber:163,columnNumber:13}}))));}}]);return CustomActionSheet;}(React.Component);exports.default=CustomActionSheet;var styles=_reactNative.StyleSheet.create({flexContainer:{flex:1},overlay:{position:'absolute',top:0,right:0,bottom:0,left:0,backgroundColor:'black'},sheetContainer:{position:'absolute',left:0,right:0,bottom:0,top:0,backgroundColor:'transparent',alignItems:'flex-end',justifyContent:'center',flexDirection:'row'},sheet:{flex:1,backgroundColor:'transparent'}});
+ //# sourceMappingURL=CustomActionSheet.js.map
+\ No newline at end of file
+diff --git a/node_modules/@expo/react-native-action-sheet/lib/module/ActionSheet/CustomActionSheet.js b/node_modules/@expo/react-native-action-sheet/lib/module/ActionSheet/CustomActionSheet.js
+index 253c851..2eb2ba2 100644
+--- a/node_modules/@expo/react-native-action-sheet/lib/module/ActionSheet/CustomActionSheet.js
++++ b/node_modules/@expo/react-native-action-sheet/lib/module/ActionSheet/CustomActionSheet.js
+@@ -1,2 +1,2 @@
+-import _classCallCheck from"@babel/runtime/helpers/classCallCheck";import _createClass from"@babel/runtime/helpers/createClass";import _assertThisInitialized from"@babel/runtime/helpers/assertThisInitialized";import _inherits from"@babel/runtime/helpers/inherits";import _possibleConstructorReturn from"@babel/runtime/helpers/possibleConstructorReturn";import _getPrototypeOf from"@babel/runtime/helpers/getPrototypeOf";var _jsxFileName="/home/runner/work/react-native-action-sheet/react-native-action-sheet/src/ActionSheet/CustomActionSheet.tsx";function _createSuper(Derived){var hasNativeReflectConstruct=_isNativeReflectConstruct();return function _createSuperInternal(){var Super=_getPrototypeOf(Derived),result;if(hasNativeReflectConstruct){var NewTarget=_getPrototypeOf(this).constructor;result=Reflect.construct(Super,arguments,NewTarget);}else{result=Super.apply(this,arguments);}return _possibleConstructorReturn(this,result);};}function _isNativeReflectConstruct(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return true;}catch(e){return false;}}import*as React from'react';import{Animated,BackHandler,Easing,Modal,Platform,StyleSheet,TouchableWithoutFeedback,View}from'react-native';import ActionGroup from'./ActionGroup';var OPACITY_ANIMATION_IN_TIME=225;var OPACITY_ANIMATION_OUT_TIME=195;var EASING_OUT=Easing.bezier(0.25,0.46,0.45,0.94);var EASING_IN=Easing.out(EASING_OUT);var ESCAPE_KEY='Escape';var CustomActionSheet=function(_React$Component){_inherits(CustomActionSheet,_React$Component);var _super=_createSuper(CustomActionSheet);function CustomActionSheet(){var _this;_classCallCheck(this,CustomActionSheet);for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++){args[_key]=arguments[_key];}_this=_super.call.apply(_super,[this].concat(args));_this._actionSheetHeight=360;_this.state={isVisible:false,isAnimating:false,options:null,onSelect:null,overlayOpacity:new Animated.Value(0),sheetOpacity:new Animated.Value(0)};_this._deferAfterAnimation=undefined;_this._handleWebKeyDown=function(event){if(event.key===ESCAPE_KEY&&_this.state.isVisible){event.preventDefault();_this._selectCancelButton();}};_this._setActionSheetHeight=function(_ref){var nativeEvent=_ref.nativeEvent;return _this._actionSheetHeight=nativeEvent.layout.height;};_this.showActionSheetWithOptions=function(options,onSelect){var _this$state=_this.state,isVisible=_this$state.isVisible,overlayOpacity=_this$state.overlayOpacity,sheetOpacity=_this$state.sheetOpacity;var _this$props$useNative=_this.props.useNativeDriver,useNativeDriver=_this$props$useNative===void 0?true:_this$props$useNative;if(isVisible){_this._deferAfterAnimation=_this.showActionSheetWithOptions.bind(_assertThisInitialized(_this),options,onSelect);return;}_this.setState({options:options,onSelect:onSelect,isVisible:true,isAnimating:true});overlayOpacity.setValue(0);sheetOpacity.setValue(0);Animated.parallel([Animated.timing(overlayOpacity,{toValue:0.32,easing:EASING_OUT,duration:OPACITY_ANIMATION_IN_TIME,useNativeDriver:useNativeDriver}),Animated.timing(sheetOpacity,{toValue:1,easing:EASING_OUT,duration:OPACITY_ANIMATION_IN_TIME,useNativeDriver:useNativeDriver})]).start(function(result){if(result.finished){_this.setState({isAnimating:false});_this._deferAfterAnimation=undefined;}});BackHandler.addEventListener('actionSheetHardwareBackPress',_this._selectCancelButton);};_this._selectCancelButton=function(){var options=_this.state.options;if(!options){return false;}if(typeof options.cancelButtonIndex==='undefined'){return false;}else if(typeof options.cancelButtonIndex==='number'){return _this._onSelect(options.cancelButtonIndex);}else{return _this._animateOut();}};_this._onSelect=function(index){var _this$state2=_this.state,isAnimating=_this$state2.isAnimating,onSelect=_this$state2.onSelect;if(isAnimating){return false;}if(onSelect){_this._deferAfterAnimation=onSelect.bind(_assertThisInitialized(_this),index);}return _this._animateOut();};_this._animateOut=function(){var _this$state3=_this.state,isAnimating=_this$state3.isAnimating,overlayOpacity=_this$state3.overlayOpacity,sheetOpacity=_this$state3.sheetOpacity;var _this$props$useNative2=_this.props.useNativeDriver,useNativeDriver=_this$props$useNative2===void 0?true:_this$props$useNative2;if(isAnimating){return false;}BackHandler.removeEventListener('actionSheetHardwareBackPress',_this._selectCancelButton);_this.setState({isAnimating:true});Animated.parallel([Animated.timing(overlayOpacity,{toValue:0,easing:EASING_IN,duration:OPACITY_ANIMATION_OUT_TIME,useNativeDriver:useNativeDriver}),Animated.timing(sheetOpacity,{toValue:0,easing:EASING_IN,duration:OPACITY_ANIMATION_OUT_TIME,useNativeDriver:useNativeDriver})]).start(function(result){if(result.finished){_this.setState({isVisible:false,isAnimating:false});if(_this._deferAfterAnimation){_this._deferAfterAnimation();}}});return true;};return _this;}_createClass(CustomActionSheet,[{key:"componentDidMount",value:function componentDidMount(){if(Platform.OS==='web'){document.addEventListener('keydown',this._handleWebKeyDown);}}},{key:"componentWillUnmount",value:function componentWillUnmount(){if(Platform.OS==='web'){document.removeEventListener('keydown',this._handleWebKeyDown);}}},{key:"render",value:function render(){var _this$state4=this.state,isVisible=_this$state4.isVisible,overlayOpacity=_this$state4.overlayOpacity,options=_this$state4.options;var useModal=options?options.autoFocus||options.useModal===true:false;var overlay=isVisible?React.createElement(Animated.View,{style:[styles.overlay,{opacity:overlayOpacity}],__source:{fileName:_jsxFileName,lineNumber:79,columnNumber:7}}):null;var appContent=React.createElement(View,{style:styles.flexContainer,importantForAccessibility:isVisible?'no-hide-descendants':'auto',__source:{fileName:_jsxFileName,lineNumber:91,columnNumber:7}},React.Children.only(this.props.children));return React.createElement(View,{pointerEvents:this.props.pointerEvents,style:styles.flexContainer,__source:{fileName:_jsxFileName,lineNumber:99,columnNumber:7}},appContent,isVisible&&!useModal&&React.createElement(React.Fragment,null,overlay,this._renderSheet()),isVisible&&useModal&&React.createElement(Modal,{animationType:"none",transparent:true,onRequestClose:this._selectCancelButton,__source:{fileName:_jsxFileName,lineNumber:108,columnNumber:11}},overlay,this._renderSheet()));}},{key:"_renderSheet",value:function _renderSheet(){var _this$state5=this.state,options=_this$state5.options,isAnimating=_this$state5.isAnimating,sheetOpacity=_this$state5.sheetOpacity;if(!options){return null;}var optionsArray=options.options,icons=options.icons,tintIcons=options.tintIcons,destructiveButtonIndex=options.destructiveButtonIndex,disabledButtonIndices=options.disabledButtonIndices,destructiveColor=options.destructiveColor,textStyle=options.textStyle,tintColor=options.tintColor,title=options.title,titleTextStyle=options.titleTextStyle,message=options.message,messageTextStyle=options.messageTextStyle,autoFocus=options.autoFocus,showSeparators=options.showSeparators,containerStyle=options.containerStyle,separatorStyle=options.separatorStyle,cancelButtonIndex=options.cancelButtonIndex,cancelButtonTintColor=options.cancelButtonTintColor;return React.createElement(TouchableWithoutFeedback,{importantForAccessibility:"yes",onPress:this._selectCancelButton,__source:{fileName:_jsxFileName,lineNumber:145,columnNumber:7}},React.createElement(Animated.View,{needsOffscreenAlphaCompositing:isAnimating,style:[styles.sheetContainer,{opacity:sheetOpacity,transform:[{translateY:sheetOpacity.interpolate({inputRange:[0,1],outputRange:[this._actionSheetHeight,0]})}]}],__source:{fileName:_jsxFileName,lineNumber:146,columnNumber:9}},React.createElement(View,{style:styles.sheet,onLayout:this._setActionSheetHeight,__source:{fileName:_jsxFileName,lineNumber:162,columnNumber:11}},React.createElement(ActionGroup,{options:optionsArray,icons:icons,tintIcons:tintIcons===undefined?true:tintIcons,cancelButtonIndex:cancelButtonIndex,cancelButtonTintColor:cancelButtonTintColor,destructiveButtonIndex:destructiveButtonIndex,destructiveColor:destructiveColor,disabledButtonIndices:disabledButtonIndices,onSelect:this._onSelect,startIndex:0,length:optionsArray.length,textStyle:textStyle||{},tintColor:tintColor,title:title||undefined,titleTextStyle:titleTextStyle,message:message||undefined,messageTextStyle:messageTextStyle,autoFocus:autoFocus,showSeparators:showSeparators,containerStyle:containerStyle,separatorStyle:separatorStyle,__source:{fileName:_jsxFileName,lineNumber:163,columnNumber:13}}))));}}]);return CustomActionSheet;}(React.Component);export{CustomActionSheet as default};var styles=StyleSheet.create({flexContainer:{flex:1},overlay:{position:'absolute',top:0,right:0,bottom:0,left:0,backgroundColor:'black'},sheetContainer:{position:'absolute',left:0,right:0,bottom:0,top:0,backgroundColor:'transparent',alignItems:'flex-end',justifyContent:'center',flexDirection:'row'},sheet:{flex:1,backgroundColor:'transparent'}});
++import _classCallCheck from"@babel/runtime/helpers/classCallCheck";import _createClass from"@babel/runtime/helpers/createClass";import _assertThisInitialized from"@babel/runtime/helpers/assertThisInitialized";import _inherits from"@babel/runtime/helpers/inherits";import _possibleConstructorReturn from"@babel/runtime/helpers/possibleConstructorReturn";import _getPrototypeOf from"@babel/runtime/helpers/getPrototypeOf";var _jsxFileName="/home/runner/work/react-native-action-sheet/react-native-action-sheet/src/ActionSheet/CustomActionSheet.tsx";function _createSuper(Derived){var hasNativeReflectConstruct=_isNativeReflectConstruct();return function _createSuperInternal(){var Super=_getPrototypeOf(Derived),result;if(hasNativeReflectConstruct){var NewTarget=_getPrototypeOf(this).constructor;result=Reflect.construct(Super,arguments,NewTarget);}else{result=Super.apply(this,arguments);}return _possibleConstructorReturn(this,result);};}function _isNativeReflectConstruct(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return true;}catch(e){return false;}}import*as React from'react';import{Animated,BackHandler,Easing,Modal,Platform,StyleSheet,TouchableWithoutFeedback,View}from'react-native';import ActionGroup from'./ActionGroup';var OPACITY_ANIMATION_IN_TIME=225;var OPACITY_ANIMATION_OUT_TIME=195;var EASING_OUT=Easing.bezier(0.25,0.46,0.45,0.94);var EASING_IN=Easing.out(EASING_OUT);var ESCAPE_KEY='Escape';var CustomActionSheet=function(_React$Component){_inherits(CustomActionSheet,_React$Component);var _super=_createSuper(CustomActionSheet);function CustomActionSheet(){var _this;_classCallCheck(this,CustomActionSheet);for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++){args[_key]=arguments[_key];}_this=_super.call.apply(_super,[this].concat(args));_this._actionSheetHeight=360;_this._backHandlerListener=null;_this.state={isVisible:false,isAnimating:false,options:null,onSelect:null,overlayOpacity:new Animated.Value(0),sheetOpacity:new Animated.Value(0)};_this._deferAfterAnimation=undefined;_this._handleWebKeyDown=function(event){if(event.key===ESCAPE_KEY&&_this.state.isVisible){event.preventDefault();_this._selectCancelButton();}};_this._setActionSheetHeight=function(_ref){var nativeEvent=_ref.nativeEvent;return _this._actionSheetHeight=nativeEvent.layout.height;};_this.showActionSheetWithOptions=function(options,onSelect){var _this$state=_this.state,isVisible=_this$state.isVisible,overlayOpacity=_this$state.overlayOpacity,sheetOpacity=_this$state.sheetOpacity;var _this$props$useNative=_this.props.useNativeDriver,useNativeDriver=_this$props$useNative===void 0?true:_this$props$useNative;if(isVisible){_this._deferAfterAnimation=_this.showActionSheetWithOptions.bind(_assertThisInitialized(_this),options,onSelect);return;}_this.setState({options:options,onSelect:onSelect,isVisible:true,isAnimating:true});overlayOpacity.setValue(0);sheetOpacity.setValue(0);Animated.parallel([Animated.timing(overlayOpacity,{toValue:0.32,easing:EASING_OUT,duration:OPACITY_ANIMATION_IN_TIME,useNativeDriver:useNativeDriver}),Animated.timing(sheetOpacity,{toValue:1,easing:EASING_OUT,duration:OPACITY_ANIMATION_IN_TIME,useNativeDriver:useNativeDriver})]).start(function(result){if(result.finished){_this.setState({isAnimating:false});_this._deferAfterAnimation=undefined;}});_this._backHandlerListener=BackHandler.addEventListener('actionSheetHardwareBackPress',_this._selectCancelButton);};_this._selectCancelButton=function(){var options=_this.state.options;if(!options){return false;}if(typeof options.cancelButtonIndex==='undefined'){return false;}else if(typeof options.cancelButtonIndex==='number'){return _this._onSelect(options.cancelButtonIndex);}else{return _this._animateOut();}};_this._onSelect=function(index){var _this$state2=_this.state,isAnimating=_this$state2.isAnimating,onSelect=_this$state2.onSelect;if(isAnimating){return false;}if(onSelect){_this._deferAfterAnimation=onSelect.bind(_assertThisInitialized(_this),index);}return _this._animateOut();};_this._animateOut=function(){var _this$state3=_this.state,isAnimating=_this$state3.isAnimating,overlayOpacity=_this$state3.overlayOpacity,sheetOpacity=_this$state3.sheetOpacity;var _this$props$useNative2=_this.props.useNativeDriver,useNativeDriver=_this$props$useNative2===void 0?true:_this$props$useNative2;if(isAnimating){return false;}if(_this._backHandlerListener){_this._backHandlerListener.remove();};_this.setState({isAnimating:true});Animated.parallel([Animated.timing(overlayOpacity,{toValue:0,easing:EASING_IN,duration:OPACITY_ANIMATION_OUT_TIME,useNativeDriver:useNativeDriver}),Animated.timing(sheetOpacity,{toValue:0,easing:EASING_IN,duration:OPACITY_ANIMATION_OUT_TIME,useNativeDriver:useNativeDriver})]).start(function(result){if(result.finished){_this.setState({isVisible:false,isAnimating:false});if(_this._deferAfterAnimation){_this._deferAfterAnimation();}}});return true;};return _this;}_createClass(CustomActionSheet,[{key:"componentDidMount",value:function componentDidMount(){if(Platform.OS==='web'){document.addEventListener('keydown',this._handleWebKeyDown);}}},{key:"componentWillUnmount",value:function componentWillUnmount(){if(Platform.OS==='web'){document.removeEventListener('keydown',this._handleWebKeyDown);}}},{key:"render",value:function render(){var _this$state4=this.state,isVisible=_this$state4.isVisible,overlayOpacity=_this$state4.overlayOpacity,options=_this$state4.options;var useModal=options?options.autoFocus||options.useModal===true:false;var overlay=isVisible?React.createElement(Animated.View,{style:[styles.overlay,{opacity:overlayOpacity}],__source:{fileName:_jsxFileName,lineNumber:79,columnNumber:7}}):null;var appContent=React.createElement(View,{style:styles.flexContainer,importantForAccessibility:isVisible?'no-hide-descendants':'auto',__source:{fileName:_jsxFileName,lineNumber:91,columnNumber:7}},React.Children.only(this.props.children));return React.createElement(View,{pointerEvents:this.props.pointerEvents,style:styles.flexContainer,__source:{fileName:_jsxFileName,lineNumber:99,columnNumber:7}},appContent,isVisible&&!useModal&&React.createElement(React.Fragment,null,overlay,this._renderSheet()),isVisible&&useModal&&React.createElement(Modal,{animationType:"none",transparent:true,onRequestClose:this._selectCancelButton,__source:{fileName:_jsxFileName,lineNumber:108,columnNumber:11}},overlay,this._renderSheet()));}},{key:"_renderSheet",value:function _renderSheet(){var _this$state5=this.state,options=_this$state5.options,isAnimating=_this$state5.isAnimating,sheetOpacity=_this$state5.sheetOpacity;if(!options){return null;}var optionsArray=options.options,icons=options.icons,tintIcons=options.tintIcons,destructiveButtonIndex=options.destructiveButtonIndex,disabledButtonIndices=options.disabledButtonIndices,destructiveColor=options.destructiveColor,textStyle=options.textStyle,tintColor=options.tintColor,title=options.title,titleTextStyle=options.titleTextStyle,message=options.message,messageTextStyle=options.messageTextStyle,autoFocus=options.autoFocus,showSeparators=options.showSeparators,containerStyle=options.containerStyle,separatorStyle=options.separatorStyle,cancelButtonIndex=options.cancelButtonIndex,cancelButtonTintColor=options.cancelButtonTintColor;return React.createElement(TouchableWithoutFeedback,{importantForAccessibility:"yes",onPress:this._selectCancelButton,__source:{fileName:_jsxFileName,lineNumber:145,columnNumber:7}},React.createElement(Animated.View,{needsOffscreenAlphaCompositing:isAnimating,style:[styles.sheetContainer,{opacity:sheetOpacity,transform:[{translateY:sheetOpacity.interpolate({inputRange:[0,1],outputRange:[this._actionSheetHeight,0]})}]}],__source:{fileName:_jsxFileName,lineNumber:146,columnNumber:9}},React.createElement(View,{style:styles.sheet,onLayout:this._setActionSheetHeight,__source:{fileName:_jsxFileName,lineNumber:162,columnNumber:11}},React.createElement(ActionGroup,{options:optionsArray,icons:icons,tintIcons:tintIcons===undefined?true:tintIcons,cancelButtonIndex:cancelButtonIndex,cancelButtonTintColor:cancelButtonTintColor,destructiveButtonIndex:destructiveButtonIndex,destructiveColor:destructiveColor,disabledButtonIndices:disabledButtonIndices,onSelect:this._onSelect,startIndex:0,length:optionsArray.length,textStyle:textStyle||{},tintColor:tintColor,title:title||undefined,titleTextStyle:titleTextStyle,message:message||undefined,messageTextStyle:messageTextStyle,autoFocus:autoFocus,showSeparators:showSeparators,containerStyle:containerStyle,separatorStyle:separatorStyle,__source:{fileName:_jsxFileName,lineNumber:163,columnNumber:13}}))));}}]);return CustomActionSheet;}(React.Component);export{CustomActionSheet as default};var styles=StyleSheet.create({flexContainer:{flex:1},overlay:{position:'absolute',top:0,right:0,bottom:0,left:0,backgroundColor:'black'},sheetContainer:{position:'absolute',left:0,right:0,bottom:0,top:0,backgroundColor:'transparent',alignItems:'flex-end',justifyContent:'center',flexDirection:'row'},sheet:{flex:1,backgroundColor:'transparent'}});
+ //# sourceMappingURL=CustomActionSheet.js.map
+\ No newline at end of file
\ No newline at end of file
diff --git a/plugins/withAndroidManifest.js b/plugins/withAndroidManifest.js
index 883869fb..acc1192a 100644
--- a/plugins/withAndroidManifest.js
+++ b/plugins/withAndroidManifest.js
@@ -1,42 +1,38 @@
-const { withAndroidManifest } = require("expo/config-plugins");
+const { withAndroidManifest: NativeAndroidManifest } = require("@expo/config-plugins");
-const _withGoogleCastAndroidManifest = (config) =>
- withAndroidManifest(config, async (mod) => {
- const mainApplication = mod.modResults.manifest.application[0];
+const withAndroidManifest = (config) =>
+ NativeAndroidManifest(config, async (config) => {
+ const mainApplication = config.modResults.manifest.application[0];
// Initialize activity array if it doesn't exist
if (!mainApplication.activity) {
mainApplication.activity = [];
}
- const googleCastActivityExists = mainApplication.activity.some(
- (activity) =>
- activity.$?.["android:name"] ===
- "com.reactnative.googlecast.RNGCExpandedControllerActivity",
+ const googleCastActivityExists = mainApplication.activity.some(activity =>
+ activity.$?.["android:name"] === "com.reactnative.googlecast.RNGCExpandedControllerActivity"
);
// Only add the activity if it doesn't already exist
if (!googleCastActivityExists) {
mainApplication.activity.push({
$: {
- "android:name":
- "com.reactnative.googlecast.RNGCExpandedControllerActivity",
+ "android:name": "com.reactnative.googlecast.RNGCExpandedControllerActivity",
"android:theme": "@style/Theme.MaterialComponents.NoActionBar",
"android:launchMode": "singleTask",
- "android:exported": "false",
},
});
}
- const mainActivity = mainApplication.activity.find(
- (activity) => activity.$?.["android:name"] === ".MainActivity",
+ const mainActivity = mainApplication.activity.find(activity =>
+ activity.$?.["android:name"] === ".MainActivity"
);
- if (mainActivity?.$) {
- mainActivity.$["android:supportsPictureInPicture"] = "true";
+ if (mainActivity) {
+ mainActivity.$["android:supportsPictureInPicture"] = "true"
}
- return mod;
+ return config;
});
-module.exports = _withGoogleCastAndroidManifest;
+module.exports = withAndroidManifest;
diff --git a/providers/DownloadProvider.tsx b/providers/DownloadProvider.tsx
index 89d93374..d59ebdeb 100644
--- a/providers/DownloadProvider.tsx
+++ b/providers/DownloadProvider.tsx
@@ -1,199 +1,749 @@
-import * as Application from "expo-application";
-import { Directory, Paths } from "expo-file-system";
-import { atom, useAtom } from "jotai";
-import { createContext, useCallback, useContext, useMemo, useRef } from "react";
-import { Platform } from "react-native";
import { useHaptic } from "@/hooks/useHaptic";
+import useImageStorage from "@/hooks/useImageStorage";
+import { DownloadMethod, useSettings } from "@/utils/atoms/settings";
+import { getOrSetDeviceId } from "@/utils/device";
+import useDownloadHelper from "@/utils/download";
+import { getItemImage } from "@/utils/getItemImage";
+import { useLog, writeToLog } from "@/utils/log";
+import { storage } from "@/utils/mmkv";
import {
- getAllDownloadedItems,
- getDownloadedItemById,
- getDownloadsDatabase,
- updateDownloadedItem,
-} from "./Downloads/database";
-import { getDownloadedItemSize } from "./Downloads/fileOperations";
-import { useDownloadEventHandlers } from "./Downloads/hooks/useDownloadEventHandlers";
-import { useDownloadOperations } from "./Downloads/hooks/useDownloadOperations";
-import type { JobStatus } from "./Downloads/types";
+ cancelAllJobs,
+ cancelJobById,
+ deleteDownloadItemInfoFromDiskTmp,
+ getAllJobsByDeviceId,
+ getDownloadItemInfoFromDiskTmp,
+ JobStatus,
+} from "@/utils/optimize-server";
+import {
+ BaseItemDto,
+ MediaSourceInfo,
+} from "@jellyfin/sdk/lib/generated-client/models";
+import BackGroundDownloader from "@kesha-antonov/react-native-background-downloader";
+import { focusManager, useQuery, useQueryClient } from "@tanstack/react-query";
+import axios from "axios";
+import * as Application from "expo-application";
+import * as FileSystem from "expo-file-system";
+import { FileInfo } from "expo-file-system";
+import Notifications from "expo-notifications";
+import { useRouter } from "expo-router";
+import { atom, useAtom } from "jotai";
+import React, {
+ createContext,
+ useCallback,
+ useContext,
+ useEffect,
+ useMemo,
+} from "react";
+import { useTranslation } from "react-i18next";
+import { AppState, AppStateStatus, Platform } from "react-native";
+import { toast } from "sonner-native";
import { apiAtom } from "./JellyfinProvider";
+export type DownloadedItem = {
+ item: Partial;
+ mediaSource: MediaSourceInfo;
+};
+
export const processesAtom = atom([]);
-export const downloadsRefreshAtom = atom(0);
+
+function onAppStateChange(status: AppStateStatus) {
+ focusManager.setFocused(status === "active");
+}
const DownloadContext = createContext | null>(null);
function useDownloadProvider() {
+ const queryClient = useQueryClient();
+ const { t } = useTranslation();
+ const [settings] = useSettings();
+ const router = useRouter();
const [api] = useAtom(apiAtom);
+ const { logs } = useLog();
+
+ const { saveSeriesPrimaryImage } = useDownloadHelper();
+ const { saveImage } = useImageStorage();
+
const [processes, setProcesses] = useAtom(processesAtom);
- const [refreshKey, setRefreshKey] = useAtom(downloadsRefreshAtom);
+
const successHapticFeedback = useHaptic("success");
- // Track task ID to process ID mapping
- const taskMapRef = useRef>(new Map());
-
- // Reactive downloaded items that updates when refreshKey changes
- const downloadedItems = useMemo(() => {
- return getAllDownloadedItems();
- }, [refreshKey]);
-
- // Trigger refresh of download lists
- const triggerRefresh = useCallback(() => {
- setRefreshKey((prev) => prev + 1);
- }, [setRefreshKey]);
-
const authHeader = useMemo(() => {
return api?.accessToken;
}, [api]);
- const APP_CACHE_DOWNLOAD_DIRECTORY = new Directory(
- Paths.cache,
- `${Application.applicationId}/Downloads/`,
- );
+ const { data: downloadedFiles, refetch } = useQuery({
+ queryKey: ["downloadedItems"],
+ queryFn: getAllDownloadedItems,
+ staleTime: 0,
+ refetchOnMount: true,
+ refetchOnReconnect: true,
+ refetchOnWindowFocus: true,
+ });
- const updateProcess = useCallback(
- (
- processId: string,
- updater:
- | Partial
- | ((current: JobStatus) => Partial),
- ) => {
- setProcesses((prev) => {
- const processIndex = prev.findIndex((p) => p.id === processId);
- if (processIndex === -1) return prev;
+ useEffect(() => {
+ const subscription = AppState.addEventListener("change", onAppStateChange);
- const currentProcess = prev[processIndex];
- if (!currentProcess) return prev;
+ return () => subscription.remove();
+ }, []);
- const newStatus =
- typeof updater === "function" ? updater(currentProcess) : updater;
+ useQuery({
+ queryKey: ["jobs"],
+ queryFn: async () => {
+ const deviceId = await getOrSetDeviceId();
+ const url = settings?.optimizedVersionsServerUrl;
- // Create new array with updated process
- const newProcesses = [...prev];
- newProcesses[processIndex] = {
- ...currentProcess,
- ...newStatus,
- };
+ if (
+ settings?.downloadMethod !== DownloadMethod.Optimized ||
+ !url ||
+ !deviceId ||
+ !authHeader
+ )
+ return [];
- return newProcesses;
+ const jobs = await getAllJobsByDeviceId({
+ deviceId,
+ authHeader,
+ url,
});
+
+ const downloadingProcesses = processes
+ .filter((p) => p.status === "downloading")
+ .filter((p) => jobs.some((j) => j.id === p.id));
+
+ const updatedProcesses = jobs.filter(
+ (j) => !downloadingProcesses.some((p) => p.id === j.id)
+ );
+
+ setProcesses([...updatedProcesses, ...downloadingProcesses]);
+
+ for (let job of jobs) {
+ const process = processes.find((p) => p.id === job.id);
+ if (
+ process &&
+ process.status === "optimizing" &&
+ job.status === "completed"
+ ) {
+ if (settings.autoDownload) {
+ startDownload(job);
+ } else {
+ toast.info(
+ t("home.downloads.toasts.item_is_ready_to_be_downloaded", {
+ item: job.item.Name,
+ }),
+ {
+ action: {
+ label: t("home.downloads.toasts.go_to_downloads"),
+ onClick: () => {
+ router.push("/downloads");
+ toast.dismiss();
+ },
+ },
+ }
+ );
+ Notifications.scheduleNotificationAsync({
+ content: {
+ title: job.item.Name,
+ body: `${job.item.Name} is ready to be downloaded`,
+ data: {
+ url: `/downloads`,
+ },
+ },
+ trigger: null,
+ });
+ }
+ }
+ }
+
+ return jobs;
},
- [setProcesses],
- );
+ staleTime: 0,
+ refetchInterval: 2000,
+ enabled: settings?.downloadMethod === DownloadMethod.Optimized,
+ });
+
+ useEffect(() => {
+ const checkIfShouldStartDownload = async () => {
+ if (processes.length === 0) return;
+ await BackGroundDownloader?.checkForExistingDownloads();
+ };
+
+ checkIfShouldStartDownload();
+ }, [settings, processes]);
const removeProcess = useCallback(
- (id: string) => {
- // Use setTimeout to defer removal and avoid race conditions during rendering
- setTimeout(() => {
- setProcesses((prev) => prev.filter((process) => process.id !== id));
+ async (id: string) => {
+ const deviceId = await getOrSetDeviceId();
+ if (!deviceId || !authHeader || !settings?.optimizedVersionsServerUrl)
+ return;
- // Find and remove from task map
- taskMapRef.current.forEach((processId, taskId) => {
- if (processId === id) {
- taskMapRef.current.delete(taskId);
- }
+ try {
+ await cancelJobById({
+ authHeader,
+ id,
+ url: settings?.optimizedVersionsServerUrl,
});
- }, 0);
+ } catch (error) {
+ console.error(error);
+ }
},
- [setProcesses],
+ [settings?.optimizedVersionsServerUrl, authHeader]
);
- // Set up download event handlers
- useDownloadEventHandlers({
- taskMapRef,
- processes,
- updateProcess,
- removeProcess,
- onSuccess: successHapticFeedback,
- onDataChange: triggerRefresh,
- api: api || undefined,
- });
+ const APP_CACHE_DOWNLOAD_DIRECTORY = `${FileSystem.cacheDirectory}${Application.applicationId}/Downloads/`;
- // Get download operation functions
- const {
- startBackgroundDownload,
- cancelDownload,
- deleteFile,
- deleteItems,
- deleteAllFiles,
- deleteFileByType,
- appSizeUsage,
- } = useDownloadOperations({
- taskMapRef,
- processes,
- setProcesses,
- removeProcess,
- api,
- authHeader,
- onDataChange: triggerRefresh,
- });
+ const startDownload = useCallback(
+ async (process: JobStatus) => {
+ if (!process?.item.Id || !authHeader) throw new Error("No item id");
+
+ setProcesses((prev) =>
+ prev.map((p) =>
+ p.id === process.id
+ ? {
+ ...p,
+ speed: undefined,
+ status: "downloading",
+ progress: 0,
+ }
+ : p
+ )
+ );
+
+ BackGroundDownloader?.setConfig({
+ isLogsEnabled: true,
+ progressInterval: 500,
+ headers: {
+ Authorization: authHeader,
+ },
+ });
+
+ toast.info(
+ t("home.downloads.toasts.download_stated_for_item", {
+ item: process.item.Name,
+ }),
+ {
+ action: {
+ label: t("home.downloads.toasts.go_to_downloads"),
+ onClick: () => {
+ router.push("/downloads");
+ toast.dismiss();
+ },
+ },
+ }
+ );
+
+ const baseDirectory = FileSystem.documentDirectory;
+
+ BackGroundDownloader?.download({
+ id: process.id,
+ url: settings?.optimizedVersionsServerUrl + "download/" + process.id,
+ destination: `${baseDirectory}/${process.item.Id}.mp4`,
+ })
+ .begin(() => {
+ setProcesses((prev) =>
+ prev.map((p) =>
+ p.id === process.id
+ ? {
+ ...p,
+ speed: undefined,
+ status: "downloading",
+ progress: 0,
+ }
+ : p
+ )
+ );
+ })
+ .progress((data) => {
+ const percent = (data.bytesDownloaded / data.bytesTotal) * 100;
+ setProcesses((prev) =>
+ prev.map((p) =>
+ p.id === process.id
+ ? {
+ ...p,
+ speed: undefined,
+ status: "downloading",
+ progress: percent,
+ }
+ : p
+ )
+ );
+ })
+ .done(async (doneHandler) => {
+ await saveDownloadedItemInfo(
+ process.item,
+ doneHandler.bytesDownloaded
+ );
+ toast.success(
+ t("home.downloads.toasts.download_completed_for_item", {
+ item: process.item.Name,
+ }),
+ {
+ duration: 3000,
+ action: {
+ label: t("home.downloads.toasts.go_to_downloads"),
+ onClick: () => {
+ router.push("/downloads");
+ toast.dismiss();
+ },
+ },
+ }
+ );
+ setTimeout(() => {
+ BackGroundDownloader.completeHandler(process.id);
+ removeProcess(process.id);
+ }, 1000);
+ })
+ .error(async (error) => {
+ removeProcess(process.id);
+ BackGroundDownloader.completeHandler(process.id);
+ let errorMsg = "";
+ if (error.errorCode === 1000) {
+ errorMsg = "No space left";
+ }
+ if (error.errorCode === 404) {
+ errorMsg = "File not found on server";
+ }
+ toast.error(
+ t("home.downloads.toasts.download_failed_for_item", {
+ item: process.item.Name,
+ error: errorMsg,
+ })
+ );
+ writeToLog("ERROR", `Download failed for ${process.item.Name}`, {
+ error,
+ processDetails: {
+ id: process.id,
+ itemName: process.item.Name,
+ itemId: process.item.Id,
+ },
+ });
+ console.error("Error details:", {
+ errorCode: error.errorCode,
+ });
+ });
+ },
+ [queryClient, settings?.optimizedVersionsServerUrl, authHeader]
+ );
+
+ const startBackgroundDownload = useCallback(
+ async (url: string, item: BaseItemDto, mediaSource: MediaSourceInfo) => {
+ if (!api || !item.Id || !authHeader)
+ throw new Error("startBackgroundDownload ~ Missing required params");
+
+ try {
+ const fileExtension = mediaSource.TranscodingContainer;
+ const deviceId = await getOrSetDeviceId();
+
+ await saveSeriesPrimaryImage(item);
+ const itemImage = getItemImage({
+ item,
+ api,
+ variant: "Primary",
+ quality: 90,
+ width: 500,
+ });
+ await saveImage(item.Id, itemImage?.uri);
+
+ const response = await axios.post(
+ settings?.optimizedVersionsServerUrl + "optimize-version",
+ {
+ url,
+ fileExtension,
+ deviceId,
+ itemId: item.Id,
+ item,
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: authHeader,
+ },
+ }
+ );
+
+ if (response.status !== 201) {
+ throw new Error("Failed to start optimization job");
+ }
+
+ toast.success(
+ t("home.downloads.toasts.queued_item_for_optimization", {
+ item: item.Name,
+ }),
+ {
+ action: {
+ label: t("home.downloads.toasts.go_to_downloads"),
+ onClick: () => {
+ router.push("/downloads");
+ toast.dismiss();
+ },
+ },
+ }
+ );
+ } catch (error) {
+ writeToLog("ERROR", "Error in startBackgroundDownload", error);
+ console.error("Error in startBackgroundDownload:", error);
+ if (axios.isAxiosError(error)) {
+ console.error("Axios error details:", {
+ message: error.message,
+ response: error.response?.data,
+ status: error.response?.status,
+ headers: error.response?.headers,
+ });
+ toast.error(
+ t("home.downloads.toasts.failed_to_start_download_for_item", {
+ item: item.Name,
+ message: error.message,
+ })
+ );
+ if (error.response) {
+ toast.error(
+ t("home.downloads.toasts.server_responded_with_status", {
+ statusCode: error.response.status,
+ })
+ );
+ } else if (error.request) {
+ t("home.downloads.toasts.no_response_received_from_server");
+ } else {
+ toast.error("Error setting up the request");
+ }
+ } else {
+ console.error("Non-Axios error:", error);
+ toast.error(
+ t(
+ "home.downloads.toasts.failed_to_start_download_for_item_unexpected_error",
+ { item: item.Name }
+ )
+ );
+ }
+ }
+ },
+ [settings?.optimizedVersionsServerUrl, authHeader]
+ );
+
+ const deleteAllFiles = async (): Promise => {
+ Promise.all([
+ deleteLocalFiles(),
+ removeDownloadedItemsFromStorage(),
+ cancelAllServerJobs(),
+ queryClient.invalidateQueries({ queryKey: ["downloadedItems"] }),
+ ])
+ .then(() =>
+ toast.success(
+ t(
+ "home.downloads.toasts.all_files_folders_and_jobs_deleted_successfully"
+ )
+ )
+ )
+ .catch((reason) => {
+ console.error("Failed to delete all files, folders, and jobs:", reason);
+ toast.error(
+ t(
+ "home.downloads.toasts.an_error_occured_while_deleting_files_and_jobs"
+ )
+ );
+ });
+ };
+
+ const forEveryDocumentDirFile = async (
+ includeMMKV: boolean = true,
+ ignoreList: string[] = [],
+ callback: (file: FileInfo) => void
+ ) => {
+ const baseDirectory = FileSystem.documentDirectory;
+ if (!baseDirectory) {
+ throw new Error("Base directory not found");
+ }
+
+ const dirContents = await FileSystem.readDirectoryAsync(baseDirectory);
+ for (const item of dirContents) {
+ // Exclude mmkv directory.
+ // Deleting this deletes all user information as well. Logout should handle this.
+ if (
+ (item == "mmkv" && !includeMMKV) ||
+ ignoreList.some((i) => item.includes(i))
+ ) {
+ continue;
+ }
+ await FileSystem.getInfoAsync(`${baseDirectory}${item}`)
+ .then((itemInfo) => {
+ if (itemInfo.exists && !itemInfo.isDirectory) {
+ callback(itemInfo);
+ }
+ })
+ .catch((e) => console.error(e));
+ }
+ };
+
+ const deleteLocalFiles = async (): Promise => {
+ await forEveryDocumentDirFile(false, [], (file) => {
+ console.warn("Deleting file", file.uri);
+ FileSystem.deleteAsync(file.uri, { idempotent: true });
+ });
+ };
+
+ const removeDownloadedItemsFromStorage = async () => {
+ // delete any saved images first
+ Promise.all([deleteFileByType("Movie"), deleteFileByType("Episode")])
+ .then(() => storage.delete("downloadedItems"))
+ .catch((reason) => {
+ console.error("Failed to remove downloadedItems from storage:", reason);
+ throw reason;
+ });
+ };
+
+ const cancelAllServerJobs = async (): Promise => {
+ if (!authHeader) {
+ throw new Error("No auth header available");
+ }
+ if (!settings?.optimizedVersionsServerUrl) {
+ console.error("No server URL configured");
+ return;
+ }
+
+ const deviceId = await getOrSetDeviceId();
+ if (!deviceId) {
+ throw new Error("Failed to get device ID");
+ }
+
+ try {
+ await cancelAllJobs({
+ authHeader,
+ url: settings.optimizedVersionsServerUrl,
+ deviceId,
+ });
+ } catch (error) {
+ console.error("Failed to cancel all server jobs:", error);
+ throw error;
+ }
+ };
+
+ const deleteFile = async (id: string): Promise => {
+ if (!id) {
+ console.error("Invalid file ID");
+ return;
+ }
+
+ try {
+ const directory = FileSystem.documentDirectory;
+
+ if (!directory) {
+ console.error("Document directory not found");
+ return;
+ }
+ const dirContents = await FileSystem.readDirectoryAsync(directory);
+
+ for (const item of dirContents) {
+ const itemNameWithoutExtension = item.split(".")[0];
+ if (itemNameWithoutExtension === id) {
+ const filePath = `${directory}${item}`;
+ await FileSystem.deleteAsync(filePath, { idempotent: true });
+ break;
+ }
+ }
+
+ const downloadedItems = storage.getString("downloadedItems");
+ if (downloadedItems) {
+ let items = JSON.parse(downloadedItems) as DownloadedItem[];
+ items = items.filter((item) => item.item.Id !== id);
+ storage.set("downloadedItems", JSON.stringify(items));
+ }
+
+ queryClient.invalidateQueries({ queryKey: ["downloadedItems"] });
+ } catch (error) {
+ console.error(
+ `Failed to delete file and storage entry for ID ${id}:`,
+ error
+ );
+ }
+ };
+
+ const deleteItems = async (items: BaseItemDto[]) => {
+ Promise.all(
+ items.map((i) => {
+ if (i.Id) return deleteFile(i.Id);
+ return;
+ })
+ ).then(() => successHapticFeedback());
+ };
+
+ const cleanCacheDirectory = async () => {
+ const cacheDir = await FileSystem.getInfoAsync(
+ APP_CACHE_DOWNLOAD_DIRECTORY
+ );
+ if (cacheDir.exists) {
+ const cachedFiles = await FileSystem.readDirectoryAsync(
+ APP_CACHE_DOWNLOAD_DIRECTORY
+ );
+ let position = 0;
+ const batchSize = 3;
+
+ // batching promise.all to avoid OOM
+ while (position < cachedFiles.length) {
+ const itemsForBatch = cachedFiles.slice(position, position + batchSize);
+ await Promise.all(
+ itemsForBatch.map(async (file) => {
+ const info = await FileSystem.getInfoAsync(
+ `${APP_CACHE_DOWNLOAD_DIRECTORY}${file}`
+ );
+ if (info.exists) {
+ await FileSystem.deleteAsync(info.uri, { idempotent: true });
+ return Promise.resolve(file);
+ }
+ return Promise.reject();
+ })
+ );
+
+ position += batchSize;
+ }
+ }
+ };
+
+ const deleteFileByType = async (type: BaseItemDto["Type"]) => {
+ await Promise.all(
+ downloadedFiles
+ ?.filter((file) => file.item.Type == type)
+ ?.flatMap((file) => {
+ const promises = [];
+ if (type == "Episode" && file.item.SeriesId)
+ promises.push(deleteFile(file.item.SeriesId));
+ promises.push(deleteFile(file.item.Id!));
+ return promises;
+ }) || []
+ );
+ };
+
+ const appSizeUsage = useMemo(async () => {
+ const sizes: number[] =
+ downloadedFiles?.map((d) => {
+ return getDownloadedItemSize(d.item.Id!!);
+ }) || [];
+
+ await forEveryDocumentDirFile(
+ true,
+ getAllDownloadedItems().map((d) => d.item.Id!!),
+ (file) => {
+ if (file.exists) {
+ sizes.push(file.size);
+ }
+ }
+ ).catch((e) => {
+ console.error(e);
+ });
+
+ return sizes.reduce((sum, size) => sum + size, 0);
+ }, [logs, downloadedFiles, forEveryDocumentDirFile]);
+
+ function getDownloadedItem(itemId: string): DownloadedItem | null {
+ try {
+ const downloadedItems = storage.getString("downloadedItems");
+ if (downloadedItems) {
+ const items: DownloadedItem[] = JSON.parse(downloadedItems);
+ const item = items.find((i) => i.item.Id === itemId);
+ return item || null;
+ }
+ return null;
+ } catch (error) {
+ console.error(`Failed to retrieve item with ID ${itemId}:`, error);
+ return null;
+ }
+ }
+
+ function getAllDownloadedItems(): DownloadedItem[] {
+ try {
+ const downloadedItems = storage.getString("downloadedItems");
+ if (downloadedItems) {
+ return JSON.parse(downloadedItems) as DownloadedItem[];
+ } else {
+ return [];
+ }
+ } catch (error) {
+ console.error("Failed to retrieve downloaded items:", error);
+ return [];
+ }
+ }
+
+ function saveDownloadedItemInfo(item: BaseItemDto, size: number = 0) {
+ try {
+ const downloadedItems = storage.getString("downloadedItems");
+ let items: DownloadedItem[] = downloadedItems
+ ? JSON.parse(downloadedItems)
+ : [];
+
+ const existingItemIndex = items.findIndex((i) => i.item.Id === item.Id);
+
+ const data = getDownloadItemInfoFromDiskTmp(item.Id!);
+
+ if (!data?.mediaSource)
+ throw new Error(
+ "Media source not found in tmp storage. Did you forget to save it before starting download?"
+ );
+
+ const newItem = { item, mediaSource: data.mediaSource };
+
+ if (existingItemIndex !== -1) {
+ items[existingItemIndex] = newItem;
+ } else {
+ items.push(newItem);
+ }
+
+ deleteDownloadItemInfoFromDiskTmp(item.Id!);
+
+ storage.set("downloadedItems", JSON.stringify(items));
+ storage.set("downloadedItemSize-" + item.Id, size.toString());
+
+ queryClient.invalidateQueries({ queryKey: ["downloadedItems"] });
+ refetch();
+ } catch (error) {
+ console.error(
+ "Failed to save downloaded item information with media source:",
+ error
+ );
+ }
+ }
+
+ function getDownloadedItemSize(itemId: string): number {
+ const size = storage.getString("downloadedItemSize-" + itemId);
+ return size ? parseInt(size) : 0;
+ }
return {
processes,
startBackgroundDownload,
- downloadedItems, // Reactive value that auto-updates
- getDownloadedItems: getAllDownloadedItems, // Keep for backward compatibility
- getDownloadsDatabase,
+ downloadedFiles,
deleteAllFiles,
deleteFile,
deleteItems,
- deleteFileByType,
+ saveDownloadedItemInfo,
removeProcess,
- cancelDownload,
- getDownloadedItemSize,
- getDownloadedItemById,
- updateDownloadedItem,
- triggerRefresh,
- APP_CACHE_DOWNLOAD_DIRECTORY: APP_CACHE_DOWNLOAD_DIRECTORY.uri,
+ setProcesses,
+ startDownload,
+ getDownloadedItem,
+ deleteFileByType,
appSizeUsage,
- // Deprecated/not implemented in simple version
- startDownload: async () => {},
- cleanCacheDirectory: async () => {},
- dumpDownloadDiagnostics: async () => "",
+ getDownloadedItemSize,
+ APP_CACHE_DOWNLOAD_DIRECTORY,
+ cleanCacheDirectory,
};
}
-export function useDownload() {
- const context = useContext(DownloadContext);
-
- if (Platform.isTV) {
- return {
- processes: [],
- startBackgroundDownload: async () => {},
- downloadedItems: [],
- getDownloadedItems: () => [],
- getDownloadsDatabase: () => ({ movies: {}, series: {}, other: {} }),
- deleteAllFiles: async () => {},
- deleteFile: async () => {},
- deleteItems: async () => {},
- deleteFileByType: async () => {},
- removeProcess: () => {},
- cancelDownload: async () => {},
- triggerRefresh: () => {},
- startDownload: async () => {},
- getDownloadedItemSize: () => 0,
- getDownloadedItemById: () => undefined,
- updateDownloadedItem: () => {},
- APP_CACHE_DOWNLOAD_DIRECTORY: "",
- cleanCacheDirectory: async () => {},
- appSizeUsage: async () => ({ total: 0, remaining: 0, appSize: 0 }),
- dumpDownloadDiagnostics: async () => "",
- };
- }
-
- if (context === null) {
- throw new Error("useDownload must be used within a DownloadProvider");
- }
-
- return context;
-}
-
export function DownloadProvider({ children }: { children: React.ReactNode }) {
- const downloadUtils = useDownloadProvider();
+ const downloadProviderValue = useDownloadProvider();
return (
-
+
{children}
);
}
+
+export function useDownload() {
+ const context = useContext(DownloadContext);
+ if (context === null) {
+ throw new Error("useDownload must be used within a DownloadProvider");
+ }
+ if (Platform.isTV) {
+ throw new Error("useDownload is not supported on TVOS");
+ }
+ return context;
+}
diff --git a/providers/DownloadProvider.tv.tsx b/providers/DownloadProvider.tv.tsx
new file mode 100644
index 00000000..80d72026
--- /dev/null
+++ b/providers/DownloadProvider.tv.tsx
@@ -0,0 +1,107 @@
+import { storage } from "@/utils/mmkv";
+import { JobStatus } from "@/utils/optimize-server";
+import {
+ BaseItemDto,
+ MediaSourceInfo,
+} from "@jellyfin/sdk/lib/generated-client/models";
+import * as Application from "expo-application";
+import * as FileSystem from "expo-file-system";
+import { atom, useAtom } from "jotai";
+import React, { createContext, useCallback, useContext, useMemo } from "react";
+
+export type DownloadedItem = {
+ item: Partial;
+ mediaSource: MediaSourceInfo;
+};
+
+export const processesAtom = atom([]);
+
+const DownloadContext = createContext | null>(null);
+
+/**
+ * Dummy download provider for tvOS
+ */
+function useDownloadProvider() {
+ const [processes, setProcesses] = useAtom(processesAtom);
+
+ const downloadedFiles: DownloadedItem[] = [];
+
+ const removeProcess = useCallback(async (id: string) => {}, []);
+
+ const startDownload = useCallback(async (process: JobStatus) => {
+ return null;
+ }, []);
+
+ const startBackgroundDownload = useCallback(
+ async (url: string, item: BaseItemDto, mediaSource: MediaSourceInfo) => {
+ return null;
+ },
+ []
+ );
+
+ const deleteAllFiles = async (): Promise => {};
+
+ const deleteFile = async (id: string): Promise => {};
+
+ const deleteItems = async (items: BaseItemDto[]) => {};
+
+ const cleanCacheDirectory = async () => {};
+
+ const deleteFileByType = async (type: BaseItemDto["Type"]) => {};
+
+ const appSizeUsage = useMemo(async () => {
+ return 0;
+ }, []);
+
+ function getDownloadedItem(itemId: string): DownloadedItem | null {
+ return null;
+ }
+
+ function saveDownloadedItemInfo(item: BaseItemDto, size: number = 0) {}
+
+ function getDownloadedItemSize(itemId: string): number {
+ const size = storage.getString("downloadedItemSize-" + itemId);
+ return size ? parseInt(size) : 0;
+ }
+
+ const APP_CACHE_DOWNLOAD_DIRECTORY = `${FileSystem.cacheDirectory}${Application.applicationId}/Downloads/`;
+
+ return {
+ processes,
+ startBackgroundDownload,
+ downloadedFiles,
+ deleteAllFiles,
+ deleteFile,
+ deleteItems,
+ saveDownloadedItemInfo,
+ removeProcess,
+ setProcesses,
+ startDownload,
+ getDownloadedItem,
+ deleteFileByType,
+ appSizeUsage,
+ getDownloadedItemSize,
+ APP_CACHE_DOWNLOAD_DIRECTORY,
+ cleanCacheDirectory,
+ };
+}
+
+export function DownloadProvider({ children }: { children: React.ReactNode }) {
+ const downloadProviderValue = useDownloadProvider();
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useDownload() {
+ const context = useContext(DownloadContext);
+ if (context === null) {
+ throw new Error("useDownload must be used within a DownloadProvider");
+ }
+ return context;
+}
diff --git a/providers/JellyfinProvider.tsx b/providers/JellyfinProvider.tsx
index 0bce8439..ea473d43 100644
--- a/providers/JellyfinProvider.tsx
+++ b/providers/JellyfinProvider.tsx
@@ -1,16 +1,19 @@
import "@/augmentations";
-import { type Api, Jellyfin } from "@jellyfin/sdk";
-import type { UserDto } from "@jellyfin/sdk/lib/generated-client/models";
+import { useInterval } from "@/hooks/useInterval";
+import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
+import { useSettings } from "@/utils/atoms/settings";
+import { storage } from "@/utils/mmkv";
+import { Api, Jellyfin } from "@jellyfin/sdk";
+import { UserDto } from "@jellyfin/sdk/lib/generated-client/models";
import { getUserApi } from "@jellyfin/sdk/lib/utils/api";
-import { useMutation } from "@tanstack/react-query";
+import { useMutation, useQuery } from "@tanstack/react-query";
import axios, { AxiosError } from "axios";
-import { useSegments } from "expo-router";
+import { router, useSegments } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
import { atom, useAtom } from "jotai";
-import type React from "react";
-import {
+import React, {
createContext,
- type ReactNode,
+ ReactNode,
useCallback,
useContext,
useEffect,
@@ -18,26 +21,9 @@ import {
useState,
} from "react";
import { useTranslation } from "react-i18next";
-import { AppState, Platform } from "react-native";
+import { Platform } from "react-native";
import { getDeviceName } from "react-native-device-info";
import uuid from "react-native-uuid";
-import useRouter from "@/hooks/useAppRouter";
-import { useInterval } from "@/hooks/useInterval";
-import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
-import { useSettings } from "@/utils/atoms/settings";
-import { writeErrorLog, writeInfoLog } from "@/utils/log";
-import { storage } from "@/utils/mmkv";
-import {
- type AccountSecurityType,
- addServerToList,
- deleteAccountCredential,
- getAccountCredential,
- hashPIN,
- migrateToMultiAccount,
- saveAccountCredential,
- updateAccountToken,
-} from "@/utils/secureCredentials";
-import { store } from "@/utils/store";
interface Server {
address: string;
@@ -47,39 +33,17 @@ export const apiAtom = atom(null);
export const userAtom = atom(null);
export const wsAtom = atom(null);
-interface LoginOptions {
- saveAccount?: boolean;
- securityType?: AccountSecurityType;
- pinCode?: string;
-}
-
interface JellyfinContextValue {
discoverServers: (url: string) => Promise;
setServer: (server: Server) => Promise;
removeServer: () => void;
- login: (
- username: string,
- password: string,
- serverName?: string,
- options?: LoginOptions,
- ) => Promise;
+ login: (username: string, password: string) => Promise;
logout: () => Promise;
initiateQuickConnect: () => Promise;
- loginWithSavedCredential: (
- serverUrl: string,
- userId: string,
- ) => Promise;
- loginWithPassword: (
- serverUrl: string,
- username: string,
- password: string,
- ) => Promise;
- removeSavedCredential: (serverUrl: string, userId: string) => Promise;
- switchServerUrl: (newUrl: string) => void;
}
const JellyfinContext = createContext(
- undefined,
+ undefined
);
export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
@@ -97,12 +61,12 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
setJellyfin(
() =>
new Jellyfin({
- clientInfo: { name: "Streamyfin", version: "0.52.0" },
+ clientInfo: { name: "Streamyfin", version: "0.27.0" },
deviceInfo: {
name: deviceName,
id,
},
- }),
+ })
);
setDeviceId(id);
})();
@@ -112,7 +76,13 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
const [user, setUser] = useAtom(userAtom);
const [isPolling, setIsPolling] = useState(false);
const [secret, setSecret] = useState(null);
- const { setPluginSettings, refreshStreamyfinPluginSettings } = useSettings();
+ const [
+ settings,
+ updateSettings,
+ pluginSettings,
+ setPluginSettings,
+ refreshStreamyfinPluginSettings,
+ ] = useSettings();
const { clearAllJellyseerData, setJellyseerrUser } = useJellyseerr();
const headers = useMemo(() => {
@@ -120,7 +90,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
return {
authorization: `MediaBrowser Client="Streamyfin", Device=${
Platform.OS === "android" ? "Android" : "iOS"
- }, DeviceId="${deviceId}", Version="0.52.0"`,
+ }, DeviceId="${deviceId}", Version="0.27.0"`,
};
}, [deviceId]);
@@ -128,18 +98,19 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
if (!api || !deviceId) return;
try {
const response = await api.axiosInstance.post(
- `${api.basePath}/QuickConnect/Initiate`,
+ api.basePath + "/QuickConnect/Initiate",
null,
{
headers,
- },
+ }
);
if (response?.status === 200) {
setSecret(response?.data?.Secret);
setIsPolling(true);
return response.data?.Code;
+ } else {
+ throw new Error("Failed to initiate quick connect");
}
- throw new Error("Failed to initiate quick connect");
} catch (error) {
console.error(error);
throw error;
@@ -147,11 +118,11 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
}, [api, deviceId, headers]);
const pollQuickConnect = useCallback(async () => {
- if (!api || !secret || !jellyfin) return;
+ if (!api || !secret) return;
try {
const response = await api.axiosInstance.get(
- `${api.basePath}/QuickConnect/Connect?Secret=${secret}`,
+ `${api.basePath}/QuickConnect/Connect?Secret=${secret}`
);
if (response.status === 200) {
@@ -159,18 +130,18 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
setIsPolling(false);
const authResponse = await api.axiosInstance.post(
- `${api.basePath}/Users/AuthenticateWithQuickConnect`,
+ api.basePath + "/Users/AuthenticateWithQuickConnect",
{
secret,
},
{
headers,
- },
+ }
);
const { AccessToken, User } = authResponse.data;
+ api.accessToken = AccessToken;
setUser(User);
- setApi(jellyfin.createApi(api.basePath, AccessToken));
storage.set("token", AccessToken);
storage.set("user", JSON.stringify(User));
return true;
@@ -182,11 +153,12 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
setIsPolling(false);
setSecret(null);
throw new Error("The code has expired. Please try again.");
+ } else {
+ console.error("Error polling Quick Connect:", error);
+ throw error;
}
- console.error("Error polling Quick Connect:", error);
- throw error;
}
- }, [api, secret, headers, jellyfin]);
+ }, [api, secret, headers]);
useEffect(() => {
(async () => {
@@ -194,26 +166,13 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
})();
}, []);
- useEffect(() => {
- store.set(apiAtom, api);
- }, [api]);
-
useInterval(pollQuickConnect, isPolling ? 1000 : null);
-
- // Refresh plugin settings when app comes to foreground
- useEffect(() => {
- const subscription = AppState.addEventListener("change", (nextAppState) => {
- if (nextAppState === "active") {
- refreshStreamyfinPluginSettings();
- }
- });
-
- return () => subscription.remove();
- }, []);
+ useInterval(refreshStreamyfinPluginSettings, 60 * 5 * 1000); // 5 min
const discoverServers = async (url: string): Promise => {
- const servers =
- await jellyfin?.discovery.getRecommendedServerCandidates(url);
+ const servers = await jellyfin?.discovery.getRecommendedServerCandidates(
+ url
+ );
return servers?.map((server) => ({ address: server.address })) || [];
};
@@ -226,9 +185,18 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
setApi(apiInstance);
storage.set("serverUrl", server.address);
},
- onSuccess: async (_, server) => {
- // Add server to the list (will update existing or add new)
- addServerToList(server.address);
+ onSuccess: (_, server) => {
+ const previousServers = JSON.parse(
+ storage.getString("previousServers") || "[]"
+ );
+ const updatedServers = [
+ server,
+ ...previousServers.filter((s: Server) => s.address !== server.address),
+ ];
+ storage.set(
+ "previousServers",
+ JSON.stringify(updatedServers.slice(0, 5))
+ );
},
onError: (error) => {
console.error("Failed to set server:", error);
@@ -237,7 +205,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
const removeServerMutation = useMutation({
mutationFn: async () => {
- storage.remove("serverUrl");
+ storage.delete("serverUrl");
setApi(null);
},
onError: (error) => {
@@ -249,13 +217,9 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
mutationFn: async ({
username,
password,
- serverName,
- options,
}: {
username: string;
password: string;
- serverName?: string;
- options?: LoginOptions;
}) => {
if (!api || !jellyfin) throw new Error("API not initialized");
@@ -268,31 +232,10 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
setApi(jellyfin.createApi(api?.basePath, auth.data?.AccessToken));
storage.set("token", auth.data?.AccessToken);
- // Save credentials to secure storage if requested
- if (api.basePath && options?.saveAccount) {
- const securityType = options.securityType || "none";
- let pinHash: string | undefined;
-
- if (securityType === "pin" && options.pinCode) {
- pinHash = await hashPIN(options.pinCode);
- }
-
- await saveAccountCredential({
- serverUrl: api.basePath,
- serverName: serverName || "",
- token: auth.data.AccessToken,
- userId: auth.data.User.Id || "",
- username,
- savedAt: Date.now(),
- securityType,
- pinHash,
- });
- }
-
const recentPluginSettings = await refreshStreamyfinPluginSettings();
if (recentPluginSettings?.jellyseerrServerUrl?.value) {
const jellyseerrApi = new JellyseerrApi(
- recentPluginSettings.jellyseerrServerUrl.value,
+ recentPluginSettings.jellyseerrServerUrl.value
);
await jellyseerrApi.test().then((result) => {
if (result.isValid && result.requiresPass) {
@@ -308,23 +251,23 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
throw new Error(t("login.invalid_username_or_password"));
case 403:
throw new Error(
- t("login.user_does_not_have_permission_to_log_in"),
+ t("login.user_does_not_have_permission_to_log_in")
);
case 408:
throw new Error(
- t("login.server_is_taking_too_long_to_respond_try_again_later"),
+ t("login.server_is_taking_too_long_to_respond_try_again_later")
);
case 429:
throw new Error(
- t("login.server_received_too_many_requests_try_again_later"),
+ t("login.server_received_too_many_requests_try_again_later")
);
case 500:
throw new Error(t("login.there_is_a_server_error"));
default:
throw new Error(
t(
- "login.an_unexpected_error_occured_did_you_enter_the_correct_url",
- ),
+ "login.an_unexpected_error_occured_did_you_enter_the_correct_url"
+ )
);
}
}
@@ -338,148 +281,17 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
const logoutMutation = useMutation({
mutationFn: async () => {
- await api
- ?.delete(`/Streamyfin/device/${deviceId}`)
- .then((_r) => writeInfoLog("Deleted expo push token for device"))
- .catch((_e) =>
- writeErrorLog("Failed to delete expo push token for device"),
- );
-
- storage.remove("token");
+ storage.delete("token");
setUser(null);
setApi(null);
setPluginSettings(undefined);
await clearAllJellyseerData();
- // Note: We keep saved credentials for quick switching back
},
onError: (error) => {
console.error("Logout failed:", error);
},
});
- const loginWithSavedCredentialMutation = useMutation({
- mutationFn: async ({
- serverUrl,
- userId,
- }: {
- serverUrl: string;
- userId: string;
- }) => {
- if (!jellyfin) throw new Error("Jellyfin not initialized");
-
- const credential = await getAccountCredential(serverUrl, userId);
- if (!credential) {
- throw new Error("No saved credential found");
- }
-
- // Create API instance with saved token
- const apiInstance = jellyfin.createApi(serverUrl, credential.token);
- if (!apiInstance) {
- throw new Error("Failed to create API instance");
- }
-
- // Validate token by fetching current user
- try {
- const response = await getUserApi(apiInstance).getCurrentUser();
-
- // Token is valid, update state
- setApi(apiInstance);
- setUser(response.data);
- storage.set("serverUrl", serverUrl);
- storage.set("token", credential.token);
- storage.set("user", JSON.stringify(response.data));
-
- // Refresh plugin settings
- await refreshStreamyfinPluginSettings();
- } catch (error) {
- // Token is invalid/expired - remove it
- if (
- axios.isAxiosError(error) &&
- (error.response?.status === 401 || error.response?.status === 403)
- ) {
- await deleteAccountCredential(serverUrl, userId);
- throw new Error(t("server.session_expired"));
- }
- throw error;
- }
- },
- onError: (error) => {
- console.error("Quick login failed:", error);
- },
- });
-
- const loginWithPasswordMutation = useMutation({
- mutationFn: async ({
- serverUrl,
- username,
- password,
- }: {
- serverUrl: string;
- username: string;
- password: string;
- }) => {
- if (!jellyfin) throw new Error("Jellyfin not initialized");
-
- // Create API instance for the server
- const apiInstance = jellyfin.createApi(serverUrl);
- if (!apiInstance) {
- throw new Error("Failed to create API instance");
- }
-
- // Authenticate with password
- const auth = await apiInstance.authenticateUserByName(username, password);
-
- if (auth.data.AccessToken && auth.data.User) {
- setUser(auth.data.User);
- storage.set("user", JSON.stringify(auth.data.User));
- setApi(jellyfin.createApi(serverUrl, auth.data.AccessToken));
- storage.set("serverUrl", serverUrl);
- storage.set("token", auth.data.AccessToken);
-
- // Update the saved credential with new token
- await updateAccountToken(
- serverUrl,
- auth.data.User.Id || "",
- auth.data.AccessToken,
- );
-
- // Refresh plugin settings
- await refreshStreamyfinPluginSettings();
- }
- },
- onError: (error) => {
- console.error("Password login failed:", error);
- throw error;
- },
- });
-
- const removeSavedCredentialMutation = useMutation({
- mutationFn: async ({
- serverUrl,
- userId,
- }: {
- serverUrl: string;
- userId: string;
- }) => {
- await deleteAccountCredential(serverUrl, userId);
- },
- onError: (error) => {
- console.error("Failed to remove saved credential:", error);
- },
- });
-
- const switchServerUrl = useCallback(
- (newUrl: string) => {
- if (!jellyfin || !api?.accessToken) return;
-
- const newApi = jellyfin.createApi(newUrl, api.accessToken);
- setApi(newApi);
- // Note: We don't update storage.set("serverUrl") here
- // because we want to keep the original remote URL as the "primary" URL
- },
- [jellyfin, api?.accessToken],
- );
-
const [loaded, setLoaded] = useState(false);
const [initialLoaded, setInitialLoaded] = useState(false);
@@ -494,9 +306,6 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
if (!jellyfin) return;
try {
- // Run migration to multi-account format (once)
- await migrateToMultiAccount();
-
const token = getTokenFromStorage();
const serverUrl = getServerUrlFromStorage();
const storedUser = getUserFromStorage();
@@ -511,25 +320,6 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
const response = await getUserApi(apiInstance).getCurrentUser();
setUser(response.data);
-
- // Migrate current session to secure storage if not already saved
- if (storedUser?.Id && storedUser?.Name) {
- const existingCredential = await getAccountCredential(
- serverUrl,
- storedUser.Id,
- );
- if (!existingCredential) {
- await saveAccountCredential({
- serverUrl,
- serverName: "",
- token,
- userId: storedUser.Id,
- username: storedUser.Name,
- savedAt: Date.now(),
- securityType: "none",
- });
- }
- }
}
} catch (e) {
console.error(e);
@@ -545,17 +335,10 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
discoverServers,
setServer: (server) => setServerMutation.mutateAsync(server),
removeServer: () => removeServerMutation.mutateAsync(),
- login: (username, password, serverName, options) =>
- loginMutation.mutateAsync({ username, password, serverName, options }),
+ login: (username, password) =>
+ loginMutation.mutateAsync({ username, password }),
logout: () => logoutMutation.mutateAsync(),
initiateQuickConnect,
- loginWithSavedCredential: (serverUrl, userId) =>
- loginWithSavedCredentialMutation.mutateAsync({ serverUrl, userId }),
- loginWithPassword: (serverUrl, username, password) =>
- loginWithPasswordMutation.mutateAsync({ serverUrl, username, password }),
- removeSavedCredential: (serverUrl, userId) =>
- removeSavedCredentialMutation.mutateAsync({ serverUrl, userId }),
- switchServerUrl,
};
useEffect(() => {
@@ -582,16 +365,19 @@ export const useJellyfin = (): JellyfinContextValue => {
function useProtectedRoute(user: UserDto | null, loaded = false) {
const segments = useSegments();
- const router = useRouter();
useEffect(() => {
if (loaded === false) return;
- const inAuthGroup = segments.length > 1 && segments[0] === "(auth)";
+ console.log("Loaded", user);
+
+ const inAuthGroup = segments[0] === "(auth)";
if (!user?.Id && inAuthGroup) {
+ console.log("Redirected to login");
router.replace("/login");
} else if (user?.Id && !inAuthGroup) {
+ console.log("Redirected to home");
router.replace("/(auth)/(tabs)/(home)/");
}
}, [user, segments, loaded]);
diff --git a/translations/de.json b/translations/de.json
index bfb614b7..4e3c943f 100644
--- a/translations/de.json
+++ b/translations/de.json
@@ -17,12 +17,10 @@
"change_server": "Server wechseln",
"invalid_username_or_password": "Ungültiger Benutzername oder Passwort",
"user_does_not_have_permission_to_log_in": "Benutzer hat keine Berechtigung, um sich anzumelden",
- "server_is_taking_too_long_to_respond_try_again_later": "Der Server benötigt zu lange, um zu antworten. Bitte versuch es später erneut",
+ "server_is_taking_too_long_to_respond_try_again_later": "Der Server benötigt zu lange, um zu antworten. Bitte versuch es später erneut.",
"server_received_too_many_requests_try_again_later": "Der Server hat zu viele Anfragen erhalten. Bitte versuch es später erneut.",
"there_is_a_server_error": "Es gibt einen Serverfehler",
- "an_unexpected_error_occured_did_you_enter_the_correct_url": "Ein unerwarteter Fehler ist aufgetreten. Hast du die Server-URL korrekt eingegeben?",
- "too_old_server_text": "Nicht unterstützter Jellyfin Server entdeckt",
- "too_old_server_description": "Bitte aktualisiere Jellyfin auf die neueste Version"
+ "an_unexpected_error_occured_did_you_enter_the_correct_url": "Ein unerwarteter Fehler ist aufgetreten. Hast du die Server-URL korrekt eingegeben?"
},
"server": {
"enter_url_to_jellyfin_server": "Gib die URL zu deinem Jellyfin-Server ein",
@@ -30,64 +28,19 @@
"connect_button": "Verbinden",
"previous_servers": "Vorherige Server",
"clear_button": "Löschen",
- "swipe_to_remove": "Swipe to remove",
"search_for_local_servers": "Nach lokalen Servern suchen",
"searching": "Suche...",
- "servers": "Server",
- "saved": "Saved",
- "session_expired": "Session Expired",
- "please_login_again": "Your saved session has expired. Please log in again.",
- "remove_saved_login": "Remove Saved Login",
- "remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
- "accounts_count": "{{count}} accounts",
- "select_account": "Select Account",
- "add_account": "Add Account",
- "remove_account_description": "This will remove the saved credentials for {{username}}."
- },
- "save_account": {
- "title": "Save Account",
- "save_for_later": "Save this account",
- "security_option": "Security Option",
- "no_protection": "No protection",
- "no_protection_desc": "Quick login without authentication",
- "pin_code": "PIN code",
- "pin_code_desc": "4-digit PIN required when switching",
- "password": "Re-enter password",
- "password_desc": "Password required when switching",
- "save_button": "Save",
- "cancel_button": "Cancel"
- },
- "pin": {
- "enter_pin": "Enter PIN",
- "enter_pin_for": "Enter PIN for {{username}}",
- "enter_4_digits": "Enter 4 digits",
- "invalid_pin": "Invalid PIN",
- "setup_pin": "Set Up PIN",
- "confirm_pin": "Confirm PIN",
- "pins_dont_match": "PINs don't match",
- "forgot_pin": "Forgot PIN?",
- "forgot_pin_desc": "Your saved credentials will be removed"
- },
- "password": {
- "enter_password": "Enter Password",
- "enter_password_for": "Enter password for {{username}}",
- "invalid_password": "Invalid password"
+ "servers": "Server"
},
"home": {
- "checking_server_connection": "Überprüfe Serververbindung...",
"no_internet": "Kein Internet",
"no_items": "Keine Elemente",
"no_internet_message": "Keine Sorge, du kannst immer noch heruntergeladene Inhalte ansehen.",
- "checking_server_connection_message": "Überprüfe Verbindung zum Server",
"go_to_downloads": "Gehe zu den Downloads",
- "retry": "Wiederholen",
- "server_unreachable": "Server nicht erreichbar",
- "server_unreachable_message": "Server konnte nicht erreicht werden.\nBitte überprüfe deine Netzwerkverbindung.",
"oops": "Ups!",
"error_message": "Etwas ist schiefgelaufen.\nBitte melde dich ab und wieder an.",
"continue_watching": "Weiterschauen",
"next_up": "Als nächstes",
- "continue_and_next_up": "Continue & Next Up",
"recently_added_in": "Kürzlich hinzugefügt in {{libraryName}}",
"suggested_movies": "Empfohlene Filme",
"suggested_episodes": "Empfohlene Episoden",
@@ -100,7 +53,7 @@
"downloads_feature_title": "Downloads",
"downloads_feature_description": "Lade Filme und Serien herunter, um sie offline anzusehen. Nutze entweder die Standardmethode oder installiere den optimierten Server, um Dateien im Hintergrund herunterzuladen.",
"chromecast_feature_description": "Übertrage Filme und Serien auf deine Chromecast-Geräte.",
- "centralised_settings_plugin_title": "Zentralisiertes Einstellungsplugin",
+ "centralised_settings_plugin_title": "Zentralisiertes Einstellungs-Plugin",
"centralised_settings_plugin_description": "Konfiguriere Einstellungen an einem zentralen Ort auf deinem Jellyfin-Server. Alle Client-Einstellungen für alle Benutzer werden automatisch synchronisiert.",
"done_button": "Fertig",
"go_to_settings_button": "Gehe zu den Einstellungen",
@@ -109,48 +62,6 @@
"settings": {
"settings_title": "Einstellungen",
"log_out_button": "Abmelden",
- "categories": {
- "title": "Kategorien"
- },
- "playback_controls": {
- "title": "Wiedergabe & Steuerung"
- },
- "audio_subtitles": {
- "title": "Audio & Untertitel "
- },
- "appearance": {
- "title": "Aussehen",
- "merge_next_up_continue_watching": "Merge Continue Watching & Next Up",
- "hide_remote_session_button": "Hide Remote Session Button"
- },
- "network": {
- "title": "Network",
- "local_network": "Local Network",
- "auto_switch_enabled": "Auto-switch when at home",
- "auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
- "local_url": "Local URL",
- "local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
- "local_url_placeholder": "http://192.168.1.100:8096",
- "home_wifi_networks": "Home WiFi Networks",
- "add_current_network": "Add \"{{ssid}}\"",
- "not_connected_to_wifi": "Not connected to WiFi",
- "no_networks_configured": "No networks configured",
- "add_network_hint": "Add your home WiFi network to enable auto-switching",
- "current_wifi": "Current WiFi",
- "using_url": "Using",
- "local": "Local URL",
- "remote": "Remote URL",
- "not_connected": "Not connected",
- "current_server": "Current Server",
- "remote_url": "Remote URL",
- "active_url": "Active URL",
- "not_configured": "Not configured",
- "network_added": "Network added",
- "network_already_added": "Network already added",
- "no_wifi_connected": "Not connected to WiFi",
- "permission_denied": "Location permission denied",
- "permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
- },
"user_info": {
"user_info_title": "Benutzerinformationen",
"user": "Benutzer",
@@ -174,111 +85,35 @@
"rewind_length": "Rückspulzeit",
"seconds_unit": "s"
},
- "gesture_controls": {
- "gesture_controls_title": "Gestensteuerung",
- "horizontal_swipe_skip": "Horizontales Wischen zum Überspringen",
- "horizontal_swipe_skip_description": "Wische links/rechts, wenn Steuerelemente ausgeblendet werden um zu überspringen",
- "left_side_brightness": "Helligkeitskontrolle der linken Seite",
- "left_side_brightness_description": "Wischen Sie auf der linken Seite nach oben/runter, um die Helligkeit anzupassen",
- "right_side_volume": "Lautstärkeregelung der rechten Seite",
- "right_side_volume_description": "Auf der rechten Seite nach oben/unten wischen, um Lautstärke anzupassen",
- "hide_volume_slider": "Hide Volume Slider",
- "hide_volume_slider_description": "Hide the volume slider in the video player",
- "hide_brightness_slider": "Hide Brightness Slider",
- "hide_brightness_slider_description": "Hide the brightness slider in the video player"
- },
"audio": {
"audio_title": "Audio",
"set_audio_track": "Audiospur aus dem vorherigen Element festlegen",
"audio_language": "Audio-Sprache",
"audio_hint": "Wähl die Standardsprache für Audio aus.",
"none": "Keine",
- "language": "Sprache",
- "transcode_mode": {
- "title": "Audio Transcoding",
- "description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
- "auto": "Auto",
- "stereo": "Force Stereo",
- "5_1": "Allow 5.1",
- "passthrough": "Passthrough"
- }
+ "language": "Sprache"
},
"subtitles": {
"subtitle_title": "Untertitel",
- "subtitle_hint": "Konfigurier die Untertitel-Präferenzen.",
"subtitle_language": "Untertitel-Sprache",
"subtitle_mode": "Untertitel-Modus",
"set_subtitle_track": "Untertitel-Spur aus dem vorherigen Element festlegen",
"subtitle_size": "Untertitel-Größe",
+ "subtitle_hint": "Konfigurier die Untertitel-Präferenzen.",
"none": "Keine",
"language": "Sprache",
"loading": "Lädt",
"modes": {
"Default": "Standard",
- "Smart": "Intelligent",
+ "Smart": "Smart",
"Always": "Immer",
"None": "Keine",
"OnlyForced": "Nur erzwungen"
- },
- "text_color": "Textfarbe",
- "background_color": "Hintergrundfarbe",
- "outline_color": "Konturfarbe",
- "outline_thickness": "Umriss Dicke",
- "background_opacity": "Hintergrundtransparenz",
- "outline_opacity": "Kontur-Deckkraft",
- "bold_text": "Bold Text",
- "colors": {
- "Black": "Schwarz",
- "Gray": "Grau",
- "Silver": "Silber",
- "White": "Weiß",
- "Maroon": "Marotte",
- "Red": "Rot",
- "Fuchsia": "Fuchsia",
- "Yellow": "Gelb",
- "Olive": "Olivgrün",
- "Green": "Grün",
- "Teal": "Türkis",
- "Lime": "Hellgrün",
- "Purple": "Lila",
- "Navy": "Marineblau",
- "Blue": "Blau",
- "Aqua": "Himmelblau"
- },
- "thickness": {
- "None": "Keine",
- "Thin": "Dünn",
- "Normal": "Normal",
- "Thick": "Dick"
- },
- "subtitle_color": "Subtitle Color",
- "subtitle_background_color": "Background Color",
- "subtitle_font": "Subtitle Font",
- "ksplayer_title": "KSPlayer Settings",
- "hardware_decode": "Hardware Decoding",
- "hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues."
- },
- "vlc_subtitles": {
- "title": "VLC Subtitle Settings",
- "hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
- "text_color": "Text Color",
- "background_color": "Background Color",
- "background_opacity": "Background Opacity",
- "outline_color": "Outline Color",
- "outline_opacity": "Outline Opacity",
- "outline_thickness": "Outline Thickness",
- "bold": "Bold Text",
- "margin": "Bottom Margin"
- },
- "video_player": {
- "title": "Video Player",
- "video_player": "Video Player",
- "video_player_description": "Choose which video player to use on iOS.",
- "ksplayer": "KSPlayer",
- "vlc": "VLC"
+ }
},
"other": {
"other_title": "Sonstiges",
+ "auto_rotate": "Automatische Drehung",
"video_orientation": "Videoausrichtung",
"orientation": "Ausrichtung",
"orientations": {
@@ -294,45 +129,38 @@
"UNKNOWN": "Unbekannt"
},
"safe_area_in_controls": "Sicherer Bereich in den Steuerungen",
- "video_player": "Video player",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Experimentell + PiP)"
- },
"show_custom_menu_links": "Benutzerdefinierte Menülinks anzeigen",
- "show_large_home_carousel": "Zeige Großes Heimkarussell (Beta)",
"hide_libraries": "Bibliotheken ausblenden",
"select_liraries_you_want_to_hide": "Wähl die Bibliotheken aus, die du im Bibliothekstab und auf der Startseite ausblenden möchtest.",
"disable_haptic_feedback": "Haptisches Feedback deaktivieren",
- "default_quality": "Standardqualität",
- "default_playback_speed": "Default Playback Speed",
- "auto_play_next_episode": "Auto-play Next Episode",
- "max_auto_play_episode_count": "Max. automatische Wiedergabe Episodenanzahl",
- "disabled": "Deaktiviert"
+ "default_quality": "Standardqualität"
},
"downloads": {
- "downloads_title": "Downloads"
- },
- "music": {
- "title": "Music",
- "playback_title": "Playback",
- "playback_description": "Configure how music is played.",
- "prefer_downloaded": "Prefer Downloaded Songs",
- "caching_title": "Caching",
- "caching_description": "Automatically cache upcoming tracks for smoother playback.",
- "lookahead_enabled": "Enable Look-Ahead Caching",
- "lookahead_count": "Tracks to Pre-cache",
- "max_cache_size": "Max Cache Size"
+ "downloads_title": "Downloads",
+ "download_method": "Download-Methode",
+ "remux_max_download": "Maximaler Remux-Download",
+ "auto_download": "Automatischer Download",
+ "optimized_versions_server": "Optimierter Versions-Server",
+ "save_button": "Speichern",
+ "optimized_server": "Optimierter Server",
+ "optimized": "Optimiert",
+ "default": "Standard",
+ "optimized_version_hint": "Gib die URL für den optimierten Server ein. Die URL sollte http oder https enthalten und optional den Port.",
+ "read_more_about_optimized_server": "Mehr über den optimierten Server lesen.",
+ "url":"URL",
+ "server_url_placeholder": "http(s)://domain.org:port"
},
"plugins": {
- "plugins_title": "Erweiterungen",
+ "plugins_title": "Plugins",
"jellyseerr": {
"jellyseerr_warning": "Diese integration ist in einer frühen Entwicklungsphase. Erwarte Veränderungen.",
- "server_url": "Server Adresse",
+ "server_url": "Server URL",
"server_url_hint": "Beispiel: http(s)://your-host.url\n(Portnummer hinzufügen, falls erforderlich)",
"server_url_placeholder": "Jellyseerr URL...",
"password": "Passwort",
"password_placeholder": "Passwort für Jellyfin Benutzer {{username}} eingeben",
+ "save_button": "Speichern",
+ "clear_button": "Löschen",
"login_button": "Anmelden",
"total_media_requests": "Gesamtanfragen",
"movie_quota_limit": "Film-Anfragelimit",
@@ -340,13 +168,7 @@
"tv_quota_limit": "TV-Anfragelimit",
"tv_quota_days": "TV-Anfragetage",
"reset_jellyseerr_config_button": "Setze Jellyseerr-Konfiguration zurück",
- "unlimited": "Unlimitiert",
- "plus_n_more": "+{{n}} more",
- "order_by": {
- "DEFAULT": "Standard",
- "VOTE_COUNT_AND_AVERAGE": "Stimmenanzahl und Durchschnitt",
- "POPULARITY": "Beliebtheit"
- }
+ "unlimited": "Unlimitiert"
},
"marlin_search": {
"enable_marlin_search": "Aktiviere Marlin Search",
@@ -356,40 +178,8 @@
"read_more_about_marlin": "Erfahre mehr über Marlin.",
"save_button": "Speichern",
"toasts": {
- "saved": "Gespeichert",
- "refreshed": "Settings refreshed from server"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "streamystats": {
- "enable_streamystats": "Enable Streamystats",
- "disable_streamystats": "Disable Streamystats",
- "enable_search": "Use for Search",
- "url": "URL",
- "server_url_placeholder": "http(s)://streamystats.example.com",
- "streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
- "read_more_about_streamystats": "Read More About Streamystats.",
- "save_button": "Save",
- "save": "Save",
- "features_title": "Features",
- "home_sections_title": "Home Sections",
- "enable_movie_recommendations": "Movie Recommendations",
- "enable_series_recommendations": "Series Recommendations",
- "enable_promoted_watchlists": "Promoted Watchlists",
- "hide_watchlists_tab": "Hide Watchlists Tab",
- "home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
- "recommended_movies": "Recommended Movies",
- "recommended_series": "Recommended Series",
- "toasts": {
- "saved": "Saved",
- "refreshed": "Settings refreshed from server",
- "disabled": "Streamystats disabled"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "kefinTweaks": {
- "watchlist_enabler": "Enable our Watchlist integration",
- "watchlist_button": "Toggle Watchlist integration"
+ "saved": "Gespeichert"
+ }
}
},
"storage": {
@@ -397,58 +187,43 @@
"app_usage": "App {{usedSpace}}%",
"device_usage": "Gerät {{availableSpace}}%",
"size_used": "{{used}} von {{total}} benutzt",
- "delete_all_downloaded_files": "Alle Downloads löschen",
- "music_cache_title": "Music Cache",
- "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
- "enable_music_cache": "Enable Music Cache",
- "clear_music_cache": "Clear Music Cache",
- "music_cache_size": "{{size}} cached",
- "music_cache_cleared": "Music cache cleared",
- "delete_all_downloaded_songs": "Delete All Downloaded Songs",
- "downloaded_songs_size": "{{size}} downloaded",
- "downloaded_songs_deleted": "Downloaded songs deleted"
+ "delete_all_downloaded_files": "Alle Downloads löschen"
},
"intro": {
- "title": "Intro ",
"show_intro": "Show intro",
"reset_intro": "Reset intro"
},
"logs": {
"logs_title": "Logs",
- "export_logs": "Export logs",
- "click_for_more_info": "Click for more info",
- "level": "Level",
"no_logs_available": "Keine Logs verfügbar",
"delete_all_logs": "Alle Logs löschen"
},
"languages": {
"title": "Sprachen",
"app_language": "App-Sprache",
+ "app_language_description": "Wähle die Sprache für die App aus.",
"system": "System"
},
- "toasts": {
+ "toasts":{
"error_deleting_files": "Fehler beim Löschen von Dateien",
"background_downloads_enabled": "Hintergrunddownloads aktiviert",
- "background_downloads_disabled": "Hintergrunddownloads deaktiviert"
+ "background_downloads_disabled": "Hintergrunddownloads deaktiviert",
+ "connected": "Verbunden",
+ "could_not_connect": "Konnte keine Verbindung herstellen",
+ "invalid_url": "Ungültige URL"
}
},
- "sessions": {
- "title": "Sitzungen",
- "no_active_sessions": "Keine aktiven Sitzungen"
- },
"downloads": {
"downloads_title": "Downloads",
"tvseries": "TV-Serien",
"movies": "Filme",
"queue": "Warteschlange",
- "other_media": "Andere Medien",
"queue_hint": "Warteschlange und aktive Downloads gehen verloren bei App-Neustart",
"no_items_in_queue": "Keine Elemente in der Warteschlange",
"no_downloaded_items": "Keine heruntergeladenen Elemente",
"delete_all_movies_button": "Alle Filme löschen",
"delete_all_tvseries_button": "Alle TV-Serien löschen",
"delete_all_button": "Alles löschen",
- "delete_all_other_media_button": "Andere Medien löschen",
"active_download": "Aktiver Download",
"no_active_downloads": "Keine aktiven Downloads",
"active_downloads": "Aktive Downloads",
@@ -459,57 +234,35 @@
"something_went_wrong": "Etwas ist schiefgelaufen",
"could_not_get_stream_url_from_jellyfin": "Konnte keine Stream-URL von Jellyfin erhalten",
"eta": "ETA {{eta}}",
+ "methods": "Methoden",
"toasts": {
"you_are_not_allowed_to_download_files": "Du hast keine Berechtigung, Dateien herunterzuladen",
"deleted_all_movies_successfully": "Alle Filme erfolgreich gelöscht!",
"failed_to_delete_all_movies": "Fehler beim Löschen aller Filme",
"deleted_all_tvseries_successfully": "Alle TV-Serien erfolgreich gelöscht!",
"failed_to_delete_all_tvseries": "Fehler beim Löschen aller TV-Serien",
- "deleted_media_successfully": "Andere Medien erfolgreich gelöscht!",
- "failed_to_delete_media": "Fehler beim Löschen anderer Medien",
- "download_deleted": "Download gelöscht",
"download_cancelled": "Download abgebrochen",
- "could_not_delete_download": "Download konnte nicht gelöscht werden",
- "download_paused": "Download pausiert",
- "could_not_pause_download": "Download konnte nicht angehalten werden",
- "download_resumed": "Download fortgesetzt",
- "could_not_resume_download": "Download konnte nicht fortgesetzt werden",
+ "could_not_cancel_download": "Download konnte nicht abgebrochen werden",
"download_completed": "Download abgeschlossen",
- "download_failed": "Download fehlgeschlagen",
+ "download_started_for": "Download für {{item}} gestartet",
+ "item_is_ready_to_be_downloaded": "{{item}} ist bereit zum Herunterladen",
+ "download_stated_for_item": "Download für {{item}} gestartet",
"download_failed_for_item": "Download für {{item}} fehlgeschlagen - {{error}}",
"download_completed_for_item": "Download für {{item}} ",
- "download_started_for_item": "Download für {{item}} gestartet",
- "failed_to_start_download": "Download konnte nicht gestartet werden",
- "item_already_downloading": "{{item}} Lädt",
- "all_files_deleted": "Alle Downloads gelöscht",
- "files_deleted_by_type": "{{count}} {{type}} gelöscht",
+ "queued_item_for_optimization": "{{item}} für Optimierung in die Warteschlange gestellt",
+ "failed_to_start_download_for_item": "Download konnte für {{item}} nicht gestartet werden: {{message}}",
+ "server_responded_with_status_code": "Server hat mit Status {{statusCode}} geantwortet",
+ "no_response_received_from_server": "Keine Antwort vom Server erhalten",
+ "error_setting_up_the_request": "Fehler beim Einrichten der Anfrage",
+ "failed_to_start_download_for_item_unexpected_error": "Fehler beim Starten des Downloads für {{item}}: Unerwarteter Fehler",
"all_files_folders_and_jobs_deleted_successfully": "Alle Dateien, Ordner und Jobs erfolgreich gelöscht",
- "failed_to_clean_cache_directory": "Fehler beim Bereinigen des Cache-Verzeichnisses",
- "could_not_get_download_url_for_item": "Download-URL für {{itemName}} konnte nicht geladen werden",
- "go_to_downloads": "Gehe zu den Downloads",
- "file_deleted": "{{item}} gelöscht"
+ "an_error_occured_while_deleting_files_and_jobs": "Ein Fehler ist beim Löschen von Dateien und Jobs aufgetreten",
+ "go_to_downloads": "Gehe zu den Downloads"
}
}
},
- "common": {
- "select": "Auswählen",
- "no_trailer_available": "Kein Trailer verfügbar",
- "video": "Video",
- "audio": "Audio",
- "subtitle": "Untertitel",
- "play": "Abspielen",
- "none": "Keine",
- "track": "Track",
- "cancel": "Cancel",
- "delete": "Delete",
- "ok": "OK",
- "remove": "Remove",
- "next": "Next",
- "back": "Back",
- "continue": "Continue",
- "verifying": "Verifying..."
- },
"search": {
+ "search_here": "Hier Suchen...",
"search": "Suche...",
"x_items": "{{count}} Elemente",
"library": "Bibliothek",
@@ -521,10 +274,6 @@
"episodes": "Episoden",
"collections": "Sammlungen",
"actors": "Schauspieler",
- "artists": "Artists",
- "albums": "Albums",
- "songs": "Songs",
- "playlists": "Playlists",
"request_movies": "Film anfragen",
"request_series": "Serie anfragen",
"recently_added": "Kürzlich hinzugefügt",
@@ -550,6 +299,7 @@
"tmdb_tv_streaming_services": "TMDB TV-Serien-Streaming-Dienste"
},
"library": {
+ "no_items_found": "Keine Elemente gefunden",
"no_results": "Keine Ergebnisse",
"no_libraries_found": "Keine Bibliotheken gefunden",
"item_types": {
@@ -559,7 +309,7 @@
"items": "Elemente"
},
"options": {
- "display": "Anzeige",
+ "display": "Display",
"row": "Reihe",
"list": "Liste",
"image_style": "Bildstil",
@@ -572,7 +322,6 @@
"genres": "Genres",
"years": "Jahre",
"sort_by": "Sortieren nach",
- "filter_by": "Filter By",
"sort_order": "Sortierreihenfolge",
"tags": "Tags"
}
@@ -583,9 +332,7 @@
"episodes": "Episoden",
"videos": "Videos",
"boxsets": "Boxsets",
- "playlists": "Wiedergabelisten",
- "noDataTitle": "Noch keine Favoriten",
- "noData": "Markiere Elemente als Favoriten, damit sie hier für einen schnellen Zugriff angezeigt werden."
+ "playlists": "Playlists"
},
"custom_links": {
"no_links": "Keine Links"
@@ -597,18 +344,15 @@
"client_error": "Client-Fehler",
"could_not_create_stream_for_chromecast": "Konnte keinen Stream für Chromecast erstellen",
"message_from_server": "Nachricht vom Server: {{message}}",
+ "video_has_finished_playing": "Video wurde fertig abgespielt!",
+ "no_video_source": "Keine Videoquelle...",
"next_episode": "Nächste Episode",
"refresh_tracks": "Spuren aktualisieren",
+ "subtitle_tracks": "Untertitel-Spuren:",
"audio_tracks": "Audiospuren:",
"playback_state": "Wiedergabestatus:",
- "index": "Index:",
- "continue_watching": "Weiterschauen",
- "go_back": "Zurück",
- "downloaded_file_title": "Diese Datei wurde heruntergeladen",
- "downloaded_file_message": "Möchten Sie die heruntergeladene Datei abspielen?",
- "downloaded_file_yes": "Ja",
- "downloaded_file_no": "Nein",
- "downloaded_file_cancel": "Abbrechen"
+ "no_data_available": "Keine Daten verfügbar",
+ "index": "Index:"
},
"item_card": {
"next_up": "Als Nächstes",
@@ -624,7 +368,6 @@
"no_similar_items_found": "Keine ähnlichen Elemente gefunden",
"video": "Video",
"more_details": "Mehr Details",
- "media_options": "Medienoptionen",
"quality": "Qualität",
"audio": "Audio",
"subtitles": "Untertitel",
@@ -639,22 +382,24 @@
"download_episode": "Episode herunterladen",
"download_movie": "Film herunterladen",
"download_x_item": "{{item_count}} Elemente herunterladen",
- "download_unwatched_only": "Nur unbeobachtete",
- "download_button": "Herunterladen"
+ "download_button": "Herunterladen",
+ "using_optimized_server": "Verwende optimierten Server",
+ "using_default_method": "Verwende Standardmethode"
}
},
"live_tv": {
"next": "Nächster",
"previous": "Vorheriger",
+ "live_tv": "Live TV",
"coming_soon": "Demnächst",
"on_now": "Jetzt",
- "shows": "Serien",
+ "shows": "Shows",
"movies": "Filme",
"sports": "Sport",
"for_kids": "Für Kinder",
"news": "Nachrichten"
},
- "jellyseerr": {
+ "jellyseerr":{
"confirm": "Bestätigen",
"cancel": "Abbrechen",
"yes": "Ja",
@@ -688,15 +433,11 @@
"tags": "Tags",
"quality_profile": "Qualitätsprofil",
"root_folder": "Root-Ordner",
- "season_all": "Season (all)",
+ "season_x": "Staffel {{seasons}}",
"season_number": "Staffel {{season_number}}",
- "number_episodes": "{{episode_number}} Folgen",
+ "number_episodes": "{{episode_number}} Episodes",
"born": "Geboren",
"appearances": "Auftritte",
- "approve": "Genehmigen",
- "decline": "Ablehnen",
- "requested_by": "Angefragt von {{user}}",
- "unknown_user": "Unbekannter Nutzer",
"toasts": {
"jellyseer_does_not_meet_requirements": "Jellyseerr Server erfüllt nicht die Anforderungsversion. Bitte aktualisiere deinen Jellyseerr Server auf mindestens 2.0.0",
"jellyseerr_test_failed": "Jellyseerr-Test fehlgeschlagen. Bitte versuche es erneut.",
@@ -704,11 +445,7 @@
"issue_submitted": "Problem eingereicht!",
"requested_item": "{{item}} angefragt!",
"you_dont_have_permission_to_request": "Du hast keine Berechtigung Anfragen zu stellen",
- "something_went_wrong_requesting_media": "Etwas ist schiefgelaufen beim Anfragen von Medien",
- "request_approved": "Anfrage bestätigt!",
- "request_declined": "Anfrage abgelehnt!",
- "failed_to_approve_request": "Fehler beim Bestätigen der Anfrage",
- "failed_to_decline_request": "Fehler beim Ablehnen der Anfrage"
+ "something_went_wrong_requesting_media": "Etwas ist schiefgelaufen beim Anfragen von Medien"
}
},
"tabs": {
@@ -717,129 +454,5 @@
"library": "Bibliothek",
"custom_links": "Benutzerdefinierte Links",
"favorites": "Favoriten"
- },
- "music": {
- "title": "Music",
- "tabs": {
- "suggestions": "Suggestions",
- "albums": "Albums",
- "artists": "Artists",
- "playlists": "Playlists",
- "tracks": "tracks"
- },
- "filters": {
- "all": "All"
- },
- "recently_added": "Recently Added",
- "recently_played": "Recently Played",
- "frequently_played": "Frequently Played",
- "explore": "Explore",
- "top_tracks": "Top Tracks",
- "play": "Play",
- "shuffle": "Shuffle",
- "play_top_tracks": "Play Top Tracks",
- "no_suggestions": "No suggestions available",
- "no_albums": "No albums found",
- "no_artists": "No artists found",
- "no_playlists": "No playlists found",
- "album_not_found": "Album not found",
- "artist_not_found": "Artist not found",
- "playlist_not_found": "Playlist not found",
- "track_options": {
- "play_next": "Play Next",
- "add_to_queue": "Add to Queue",
- "add_to_playlist": "Add to Playlist",
- "download": "Download",
- "downloaded": "Downloaded",
- "downloading": "Downloading...",
- "cached": "Cached",
- "delete_download": "Delete Download",
- "delete_cache": "Remove from Cache",
- "go_to_artist": "Go to Artist",
- "go_to_album": "Go to Album",
- "add_to_favorites": "Add to Favorites",
- "remove_from_favorites": "Remove from Favorites",
- "remove_from_playlist": "Remove from Playlist"
- },
- "playlists": {
- "create_playlist": "Create Playlist",
- "playlist_name": "Playlist Name",
- "enter_name": "Enter playlist name",
- "create": "Create",
- "search_playlists": "Search playlists...",
- "added_to": "Added to {{name}}",
- "added": "Added to playlist",
- "removed_from": "Removed from {{name}}",
- "removed": "Removed from playlist",
- "created": "Playlist created",
- "create_new": "Create New Playlist",
- "failed_to_add": "Failed to add to playlist",
- "failed_to_remove": "Failed to remove from playlist",
- "failed_to_create": "Failed to create playlist",
- "delete_playlist": "Delete Playlist",
- "delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "deleted": "Playlist deleted",
- "failed_to_delete": "Failed to delete playlist"
- },
- "sort": {
- "title": "Sort By",
- "alphabetical": "Alphabetical",
- "date_created": "Date Created"
- }
- },
- "watchlists": {
- "title": "Watchlists",
- "my_watchlists": "My Watchlists",
- "public_watchlists": "Public Watchlists",
- "create_title": "Create Watchlist",
- "edit_title": "Edit Watchlist",
- "create_button": "Create Watchlist",
- "save_button": "Save Changes",
- "delete_button": "Delete",
- "remove_button": "Remove",
- "cancel_button": "Cancel",
- "name_label": "Name",
- "name_placeholder": "Enter watchlist name",
- "description_label": "Description",
- "description_placeholder": "Enter description (optional)",
- "is_public_label": "Public Watchlist",
- "is_public_description": "Allow others to view this watchlist",
- "allowed_type_label": "Content Type",
- "sort_order_label": "Default Sort Order",
- "empty_title": "No Watchlists",
- "empty_description": "Create your first watchlist to start organizing your media",
- "empty_watchlist": "This watchlist is empty",
- "empty_watchlist_hint": "Add items from your library to this watchlist",
- "not_configured_title": "Streamystats Not Configured",
- "not_configured_description": "Configure Streamystats in settings to use watchlists",
- "go_to_settings": "Go to Settings",
- "add_to_watchlist": "Add to Watchlist",
- "remove_from_watchlist": "Remove from Watchlist",
- "select_watchlist": "Select Watchlist",
- "create_new": "Create New Watchlist",
- "item": "item",
- "items": "items",
- "public": "Public",
- "private": "Private",
- "you": "You",
- "by_owner": "By another user",
- "not_found": "Watchlist not found",
- "delete_confirm_title": "Delete Watchlist",
- "delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "remove_item_title": "Remove from Watchlist",
- "remove_item_message": "Remove \"{{name}}\" from this watchlist?",
- "loading": "Loading watchlists...",
- "no_compatible_watchlists": "No compatible watchlists",
- "create_one_first": "Create a watchlist that accepts this content type"
- },
- "playback_speed": {
- "title": "Playback Speed",
- "apply_to": "Apply To",
- "speed": "Speed",
- "scope": {
- "media": "This media only",
- "show": "This show",
- "all": "All media (default)"
- }
}
}
diff --git a/translations/en.json b/translations/en.json
index 3fe9efb6..54ea665f 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -1,156 +1,67 @@
{
"login": {
- "username_required": "Username Is Required",
+ "username_required": "Username is required",
"error_title": "Error",
- "login_title": "Log In",
+ "login_title": "Log in",
"login_to_title": "Log in to",
"username_placeholder": "Username",
"password_placeholder": "Password",
- "login_button": "Log In",
+ "login_button": "Log in",
"quick_connect": "Quick Connect",
"enter_code_to_login": "Enter code {{code}} to login",
"failed_to_initiate_quick_connect": "Failed to initiate Quick Connect",
- "got_it": "Got It",
- "connection_failed": "Connection Failed",
+ "got_it": "Got it",
+ "connection_failed": "Connection failed",
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.",
- "an_unexpected_error_occured": "An Unexpected Error Occurred",
- "change_server": "Change Server",
- "invalid_username_or_password": "Invalid Username or Password",
+ "an_unexpected_error_occured": "An unexpected error occurred",
+ "change_server": "Change server",
+ "invalid_username_or_password": "Invalid username or password",
"user_does_not_have_permission_to_log_in": "User does not have permission to log in",
"server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later",
"server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.",
"there_is_a_server_error": "There is a server error",
- "an_unexpected_error_occured_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
- "too_old_server_text": "Unsupported Jellyfin Server Discovered",
- "too_old_server_description": "Please update Jellyfin to the latest version"
+ "an_unexpected_error_occured_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?"
},
"server": {
"enter_url_to_jellyfin_server": "Enter the URL to your Jellyfin server",
"server_url_placeholder": "http(s)://your-server.com",
"connect_button": "Connect",
- "previous_servers": "Previous Servers",
- "clear_button": "Clear all",
- "swipe_to_remove": "Swipe to remove",
- "search_for_local_servers": "Search for Local Servers",
+ "previous_servers": "previous servers",
+ "clear_button": "Clear",
+ "search_for_local_servers": "Search for local servers",
"searching": "Searching...",
- "servers": "Servers",
- "saved": "Saved",
- "session_expired": "Session Expired",
- "please_login_again": "Your saved session has expired. Please log in again.",
- "remove_saved_login": "Remove Saved Login",
- "remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
- "accounts_count": "{{count}} accounts",
- "select_account": "Select Account",
- "add_account": "Add Account",
- "remove_account_description": "This will remove the saved credentials for {{username}}."
- },
- "save_account": {
- "title": "Save Account",
- "save_for_later": "Save this account",
- "security_option": "Security Option",
- "no_protection": "No protection",
- "no_protection_desc": "Quick login without authentication",
- "pin_code": "PIN code",
- "pin_code_desc": "4-digit PIN required when switching",
- "password": "Re-enter password",
- "password_desc": "Password required when switching",
- "save_button": "Save",
- "cancel_button": "Cancel"
- },
- "pin": {
- "enter_pin": "Enter PIN",
- "enter_pin_for": "Enter PIN for {{username}}",
- "enter_4_digits": "Enter 4 digits",
- "invalid_pin": "Invalid PIN",
- "setup_pin": "Set Up PIN",
- "confirm_pin": "Confirm PIN",
- "pins_dont_match": "PINs don't match",
- "forgot_pin": "Forgot PIN?",
- "forgot_pin_desc": "Your saved credentials will be removed"
- },
- "password": {
- "enter_password": "Enter Password",
- "enter_password_for": "Enter password for {{username}}",
- "invalid_password": "Invalid password"
+ "servers": "Servers"
},
"home": {
- "checking_server_connection": "Checking server connection...",
"no_internet": "No Internet",
- "no_items": "No Items",
+ "no_items": "No items",
"no_internet_message": "No worries, you can still watch\ndownloaded content.",
- "checking_server_connection_message": "Checking connection to server",
- "go_to_downloads": "Go to Downloads",
- "retry": "Retry",
- "server_unreachable": "Server Unreachable",
- "server_unreachable_message": "Could not reach the server.\nPlease check your network connection.",
+ "go_to_downloads": "Go to downloads",
"oops": "Oops!",
"error_message": "Something went wrong.\nPlease log out and in again.",
"continue_watching": "Continue Watching",
"next_up": "Next Up",
- "continue_and_next_up": "Continue & Next Up",
"recently_added_in": "Recently Added in {{libraryName}}",
"suggested_movies": "Suggested Movies",
"suggested_episodes": "Suggested Episodes",
"intro": {
"welcome_to_streamyfin": "Welcome to Streamyfin",
- "a_free_and_open_source_client_for_jellyfin": "A Free and Open-Source Client for Jellyfin.",
+ "a_free_and_open_source_client_for_jellyfin": "A free and open-source client for Jellyfin.",
"features_title": "Features",
"features_description": "Streamyfin has a bunch of features and integrates with a wide array of software which you can find in the settings menu, these include:",
- "jellyseerr_feature_description": "Connect to your Seerr instance and request movies directly in the app.",
+ "jellyseerr_feature_description": "Connect to your Jellyseerr instance and request movies directly in the app.",
"downloads_feature_title": "Downloads",
"downloads_feature_description": "Download movies and tv-shows to view offline. Use either the default method or install the optimize server to download files in the background.",
"chromecast_feature_description": "Cast movies and tv-shows to your Chromecast devices.",
"centralised_settings_plugin_title": "Centralised Settings Plugin",
"centralised_settings_plugin_description": "Configure settings from a centralised location on your Jellyfin server. All client settings for all users will be synced automatically.",
"done_button": "Done",
- "go_to_settings_button": "Go to Settings",
- "read_more": "Read More"
+ "go_to_settings_button": "Go to settings",
+ "read_more": "Read more"
},
"settings": {
"settings_title": "Settings",
- "log_out_button": "Log Out",
- "categories": {
- "title": "Categories"
- },
- "playback_controls": {
- "title": "Playback & Controls"
- },
- "audio_subtitles": {
- "title": "Audio & Subtitles"
- },
- "appearance": {
- "title": "Appearance",
- "merge_next_up_continue_watching": "Merge Continue Watching & Next Up",
- "hide_remote_session_button": "Hide Remote Session Button"
- },
- "network": {
- "title": "Network",
- "local_network": "Local Network",
- "auto_switch_enabled": "Auto-switch when at home",
- "auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
- "local_url": "Local URL",
- "local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
- "local_url_placeholder": "http://192.168.1.100:8096",
- "home_wifi_networks": "Home WiFi Networks",
- "add_current_network": "Add \"{{ssid}}\"",
- "not_connected_to_wifi": "Not connected to WiFi",
- "no_networks_configured": "No networks configured",
- "add_network_hint": "Add your home WiFi network to enable auto-switching",
- "current_wifi": "Current WiFi",
- "using_url": "Using",
- "local": "Local URL",
- "remote": "Remote URL",
- "not_connected": "Not connected",
- "current_server": "Current Server",
- "remote_url": "Remote URL",
- "active_url": "Active URL",
- "not_configured": "Not configured",
- "network_added": "Network added",
- "network_already_added": "Network already added",
- "no_wifi_connected": "Not connected to WiFi",
- "permission_denied": "Location permission denied",
- "permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
- },
+ "log_out_button": "Log out",
"user_info": {
"user_info_title": "User Info",
"user": "User",
@@ -163,53 +74,32 @@
"authorize_button": "Authorize Quick Connect",
"enter_the_quick_connect_code": "Enter the quick connect code...",
"success": "Success",
- "quick_connect_autorized": "Quick Connect Authorized",
+ "quick_connect_autorized": "Quick Connect authorized",
"error": "Error",
- "invalid_code": "Invalid Code",
+ "invalid_code": "Invalid code",
"authorize": "Authorize"
},
"media_controls": {
"media_controls_title": "Media Controls",
- "forward_skip_length": "Forward Skip Length",
- "rewind_length": "Rewind Length",
+ "forward_skip_length": "Forward skip length",
+ "rewind_length": "Rewind length",
"seconds_unit": "s"
},
- "gesture_controls": {
- "gesture_controls_title": "Gesture Controls",
- "horizontal_swipe_skip": "Horizontal Swipe to Skip",
- "horizontal_swipe_skip_description": "Swipe left/right when controls are hidden to skip",
- "left_side_brightness": "Left Side Brightness Control",
- "left_side_brightness_description": "Swipe up/down on left side to adjust brightness",
- "right_side_volume": "Right Side Volume Control",
- "right_side_volume_description": "Swipe up/down on right side to adjust volume",
- "hide_volume_slider": "Hide Volume Slider",
- "hide_volume_slider_description": "Hide the volume slider in the video player",
- "hide_brightness_slider": "Hide Brightness Slider",
- "hide_brightness_slider_description": "Hide the brightness slider in the video player"
- },
"audio": {
"audio_title": "Audio",
"set_audio_track": "Set Audio Track From Previous Item",
- "audio_language": "Audio Language",
+ "audio_language": "Audio language",
"audio_hint": "Choose a default audio language.",
"none": "None",
- "language": "Language",
- "transcode_mode": {
- "title": "Audio Transcoding",
- "description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
- "auto": "Auto",
- "stereo": "Force Stereo",
- "5_1": "Allow 5.1",
- "passthrough": "Passthrough"
- }
+ "language": "Language"
},
"subtitles": {
"subtitle_title": "Subtitles",
- "subtitle_hint": "Configure how subtitles look and behave.",
"subtitle_language": "Subtitle language",
"subtitle_mode": "Subtitle Mode",
"set_subtitle_track": "Set Subtitle Track From Previous Item",
"subtitle_size": "Subtitle Size",
+ "subtitle_hint": "Configure subtitle preference.",
"none": "None",
"language": "Language",
"loading": "Loading",
@@ -219,110 +109,46 @@
"Always": "Always",
"None": "None",
"OnlyForced": "OnlyForced"
- },
- "text_color": "Text Color",
- "background_color": "Background Color",
- "outline_color": "Outline Color",
- "outline_thickness": "Outline Thickness",
- "background_opacity": "Background Opacity",
- "outline_opacity": "Outline Opacity",
- "bold_text": "Bold Text",
- "colors": {
- "Black": "Black",
- "Gray": "Gray",
- "Silver": "Silver",
- "White": "White",
- "Maroon": "Maroon",
- "Red": "Red",
- "Fuchsia": "Fuchsia",
- "Yellow": "Yellow",
- "Olive": "Olive",
- "Green": "Green",
- "Teal": "Teal",
- "Lime": "Lime",
- "Purple": "Purple",
- "Navy": "Navy",
- "Blue": "Blue",
- "Aqua": "Aqua"
- },
- "thickness": {
- "None": "None",
- "Thin": "Thin",
- "Normal": "Normal",
- "Thick": "Thick"
- },
- "subtitle_color": "Subtitle Color",
- "subtitle_background_color": "Background Color",
- "subtitle_font": "Subtitle Font",
- "ksplayer_title": "KSPlayer Settings",
- "hardware_decode": "Hardware Decoding",
- "hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues."
- },
- "vlc_subtitles": {
- "title": "VLC Subtitle Settings",
- "hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
- "text_color": "Text Color",
- "background_color": "Background Color",
- "background_opacity": "Background Opacity",
- "outline_color": "Outline Color",
- "outline_opacity": "Outline Opacity",
- "outline_thickness": "Outline Thickness",
- "bold": "Bold Text",
- "margin": "Bottom Margin"
- },
- "video_player": {
- "title": "Video Player",
- "video_player": "Video Player",
- "video_player_description": "Choose which video player to use on iOS.",
- "ksplayer": "KSPlayer",
- "vlc": "VLC"
+ }
},
"other": {
"other_title": "Other",
- "video_orientation": "Video Orientation",
+ "auto_rotate": "Auto rotate",
+ "video_orientation": "Video orientation",
"orientation": "Orientation",
"orientations": {
- "DEFAULT": "Follow Device Orientation",
+ "DEFAULT": "Default",
"ALL": "All",
- "PORTRAIT": "Portrait Auto",
+ "PORTRAIT": "Portrait",
"PORTRAIT_UP": "Portrait Up",
"PORTRAIT_DOWN": "Portrait Down",
- "LANDSCAPE": "Landscape Auto",
+ "LANDSCAPE": "Landscape",
"LANDSCAPE_LEFT": "Landscape Left",
"LANDSCAPE_RIGHT": "Landscape Right",
"OTHER": "Other",
"UNKNOWN": "Unknown"
},
- "safe_area_in_controls": "Safe Area in Controls",
- "video_player": "Video Player",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Experimental + PiP)"
- },
+ "safe_area_in_controls": "Safe area in controls",
"show_custom_menu_links": "Show Custom Menu Links",
- "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Hide Libraries",
"select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
"disable_haptic_feedback": "Disable Haptic Feedback",
- "default_quality": "Default Quality",
- "default_playback_speed": "Default Playback Speed",
- "auto_play_next_episode": "Auto-play Next Episode",
- "max_auto_play_episode_count": "Max Auto Play Episode Count",
- "disabled": "Disabled"
+ "default_quality": "Default quality"
},
"downloads": {
- "downloads_title": "Downloads"
- },
- "music": {
- "title": "Music",
- "playback_title": "Playback",
- "playback_description": "Configure how music is played.",
- "prefer_downloaded": "Prefer Downloaded Songs",
- "caching_title": "Caching",
- "caching_description": "Automatically cache upcoming tracks for smoother playback.",
- "lookahead_enabled": "Enable Look-Ahead Caching",
- "lookahead_count": "Tracks to Pre-cache",
- "max_cache_size": "Max Cache Size"
+ "downloads_title": "Downloads",
+ "download_method": "Download method",
+ "remux_max_download": "Remux max download",
+ "auto_download": "Auto download",
+ "optimized_versions_server": "Optimized versions server",
+ "save_button": "Save",
+ "optimized_server": "Optimized Server",
+ "optimized": "Optimized",
+ "default": "Default",
+ "optimized_version_hint": "Enter the URL for the optimize server. The URL should include http or https and optionally the port.",
+ "read_more_about_optimized_server": "Read more about the optimize server.",
+ "url": "URL",
+ "server_url_placeholder": "http(s)://domain.org:port"
},
"plugins": {
"plugins_title": "Plugins",
@@ -330,201 +156,128 @@
"jellyseerr_warning": "This integration is in its early stages. Expect things to change.",
"server_url": "Server URL",
"server_url_hint": "Example: http(s)://your-host.url\n(add port if required)",
- "server_url_placeholder": "Seerr URL",
+ "server_url_placeholder": "Jellyseerr URL...",
"password": "Password",
"password_placeholder": "Enter password for Jellyfin user {{username}}",
+ "save_button": "Save",
+ "clear_button": "Clear",
"login_button": "Login",
- "total_media_requests": "Total Media Requests",
- "movie_quota_limit": "Movie Quota Limit",
- "movie_quota_days": "Movie Quota Days",
- "tv_quota_limit": "TV Quota Limit",
- "tv_quota_days": "TV Quota Days",
- "reset_jellyseerr_config_button": "Reset Seerr Config",
- "unlimited": "Unlimited",
- "plus_n_more": "+{{n}} More",
- "order_by": {
- "DEFAULT": "Default",
- "VOTE_COUNT_AND_AVERAGE": "Vote count and average",
- "POPULARITY": "Popularity"
- }
+ "total_media_requests": "Total media requests",
+ "movie_quota_limit": "Movie quota limit",
+ "movie_quota_days": "Movie quota days",
+ "tv_quota_limit": "TV quota limit",
+ "tv_quota_days": "TV quota days",
+ "reset_jellyseerr_config_button": "Reset Jellyseerr config",
+ "unlimited": "Unlimited"
},
"marlin_search": {
- "enable_marlin_search": "Enable Marlin Search",
+ "enable_marlin_search": "Enable Marlin Search ",
"url": "URL",
"server_url_placeholder": "http(s)://domain.org:port",
"marlin_search_hint": "Enter the URL for the Marlin server. The URL should include http or https and optionally the port.",
- "read_more_about_marlin": "Read More About Marlin.",
+ "read_more_about_marlin": "Read more about Marlin.",
"save_button": "Save",
"toasts": {
- "saved": "Saved",
- "refreshed": "Settings refreshed from server"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "streamystats": {
- "enable_streamystats": "Enable Streamystats",
- "disable_streamystats": "Disable Streamystats",
- "enable_search": "Use for Search",
- "url": "URL",
- "server_url_placeholder": "http(s)://streamystats.example.com",
- "streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
- "read_more_about_streamystats": "Read More About Streamystats.",
- "save_button": "Save",
- "save": "Save",
- "features_title": "Features",
- "home_sections_title": "Home Sections",
- "enable_movie_recommendations": "Movie Recommendations",
- "enable_series_recommendations": "Series Recommendations",
- "enable_promoted_watchlists": "Promoted Watchlists",
- "hide_watchlists_tab": "Hide Watchlists Tab",
- "home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
- "recommended_movies": "Recommended Movies",
- "recommended_series": "Recommended Series",
- "toasts": {
- "saved": "Saved",
- "refreshed": "Settings refreshed from server",
- "disabled": "Streamystats disabled"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "kefinTweaks": {
- "watchlist_enabler": "Enable our Watchlist integration",
- "watchlist_button": "Toggle Watchlist integration"
+ "saved": "Saved"
+ }
}
},
"storage": {
"storage_title": "Storage",
"app_usage": "App {{usedSpace}}%",
"device_usage": "Device {{availableSpace}}%",
- "size_used": "{{used}} of {{total}} Used",
- "delete_all_downloaded_files": "Delete All Downloaded Files",
- "music_cache_title": "Music Cache",
- "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
- "enable_music_cache": "Enable Music Cache",
- "clear_music_cache": "Clear Music Cache",
- "music_cache_size": "{{size}} cached",
- "music_cache_cleared": "Music cache cleared",
- "delete_all_downloaded_songs": "Delete All Downloaded Songs",
- "downloaded_songs_size": "{{size}} downloaded",
- "downloaded_songs_deleted": "Downloaded songs deleted"
+ "size_used": "{{used}} of {{total}} used",
+ "delete_all_downloaded_files": "Delete All Downloaded Files"
},
"intro": {
- "title": "Intro",
- "show_intro": "Show Intro",
- "reset_intro": "Reset Intro"
+ "show_intro": "Show intro",
+ "reset_intro": "Reset intro"
},
"logs": {
"logs_title": "Logs",
- "export_logs": "Export Logs",
- "click_for_more_info": "Click for More Info",
- "level": "Level",
- "no_logs_available": "No Logs Available",
- "delete_all_logs": "Delete All Logs"
+ "no_logs_available": "No logs available",
+ "delete_all_logs": "Delete all logs"
},
"languages": {
"title": "Languages",
- "app_language": "App Language",
+ "app_language": "App language",
+ "app_language_description": "Select the language for the app.",
"system": "System"
},
"toasts": {
- "error_deleting_files": "Error Deleting Files",
+ "error_deleting_files": "Error deleting files",
"background_downloads_enabled": "Background downloads enabled",
- "background_downloads_disabled": "Background downloads disabled"
- }
+ "background_downloads_disabled": "Background downloads disabled",
+ "connected": "Connected",
+ "could_not_connect": "Could not connect",
+ "invalid_url": "Invalid URL"
+ },
},
"sessions": {
"title": "Sessions",
- "no_active_sessions": "No Active Sessions"
+ "no_active_sessions": "No active sessions"
},
"downloads": {
"downloads_title": "Downloads",
"tvseries": "TV-Series",
"movies": "Movies",
"queue": "Queue",
- "other_media": "Other media",
"queue_hint": "Queue and downloads will be lost on app restart",
- "no_items_in_queue": "No Items in Queue",
- "no_downloaded_items": "No Downloaded Items",
- "delete_all_movies_button": "Delete All Movies",
- "delete_all_tvseries_button": "Delete All TV-Series",
- "delete_all_button": "Delete All",
- "delete_all_other_media_button": "Delete other media",
- "active_download": "Active Download",
- "no_active_downloads": "No Active Downloads",
- "active_downloads": "Active Downloads",
+ "no_items_in_queue": "No items in queue",
+ "no_downloaded_items": "No downloaded items",
+ "delete_all_movies_button": "Delete all Movies",
+ "delete_all_tvseries_button": "Delete all TV-Series",
+ "delete_all_button": "Delete all",
+ "active_download": "Active download",
+ "no_active_downloads": "No active downloads",
+ "active_downloads": "Active downloads",
"new_app_version_requires_re_download": "New app version requires re-download",
"new_app_version_requires_re_download_description": "The new update requires content to be downloaded again. Please remove all downloaded content and try again.",
"back": "Back",
"delete": "Delete",
- "something_went_wrong": "Something Went Wrong",
+ "something_went_wrong": "Something went wrong",
"could_not_get_stream_url_from_jellyfin": "Could not get the stream URL from Jellyfin",
"eta": "ETA {{eta}}",
+ "methods": "Methods",
"toasts": {
"you_are_not_allowed_to_download_files": "You are not allowed to download files.",
- "deleted_all_movies_successfully": "Deleted All Movies Successfully!",
- "failed_to_delete_all_movies": "Failed to Delete All Movies",
- "deleted_all_tvseries_successfully": "Deleted All TV-Series Successfully!",
- "failed_to_delete_all_tvseries": "Failed to Delete All TV-Series",
- "deleted_media_successfully": "Deleted other media Successfully!",
- "failed_to_delete_media": "Failed to Delete other media",
- "download_deleted": "Download Deleted",
- "download_cancelled": "Download Cancelled",
- "could_not_delete_download": "Could Not Delete Download",
- "download_paused": "Download Paused",
- "could_not_pause_download": "Could Not Pause Download",
- "download_resumed": "Download Resumed",
- "could_not_resume_download": "Could Not Resume Download",
- "download_completed": "Download Completed",
- "download_failed": "Download Failed",
+ "deleted_all_movies_successfully": "Deleted all movies successfully!",
+ "failed_to_delete_all_movies": "Failed to delete all movies",
+ "deleted_all_tvseries_successfully": "Deleted all TV-Series successfully!",
+ "failed_to_delete_all_tvseries": "Failed to delete all TV-Series",
+ "download_cancelled": "Download cancelled",
+ "could_not_cancel_download": "Could not cancel download",
+ "download_completed": "Download completed",
+ "download_started_for": "Download started for {{item}}",
+ "item_is_ready_to_be_downloaded": "{{item}} is ready to be downloaded",
+ "download_stated_for_item": "Download started for {{item}}",
"download_failed_for_item": "Download failed for {{item}} - {{error}}",
- "download_completed_for_item": "Download Completed for {{item}}",
- "download_started_for_item": "Download Started for {{item}}",
- "failed_to_start_download": "Failed to start download",
- "item_already_downloading": "{{item}} is already downloading",
- "all_files_deleted": "All Downloads Deleted Successfully",
- "files_deleted_by_type": "{{count}} {{type}} deleted",
+ "download_completed_for_item": "Download completed for {{item}}",
+ "queued_item_for_optimization": "Queued {{item}} for optimization",
+ "failed_to_start_download_for_item": "Failed to start downloading for {{item}}: {{message}}",
+ "server_responded_with_status_code": "Server responded with status {{statusCode}}",
+ "no_response_received_from_server": "No response received from the server",
+ "error_setting_up_the_request": "Error setting up the request",
+ "failed_to_start_download_for_item_unexpected_error": "Failed to start downloading for {{item}}: Unexpected error",
"all_files_folders_and_jobs_deleted_successfully": "All files, folders, and jobs deleted successfully",
- "failed_to_clean_cache_directory": "Failed to clean cache directory",
- "could_not_get_download_url_for_item": "Could not get download URL for {{itemName}}",
- "go_to_downloads": "Go to Downloads",
- "file_deleted": "{{item}} deleted"
+ "an_error_occured_while_deleting_files_and_jobs": "An error occurred while deleting files and jobs",
+ "go_to_downloads": "Go to downloads"
}
}
},
- "common": {
- "select": "Select",
- "no_trailer_available": "No trailer available",
- "video": "Video",
- "audio": "Audio",
- "subtitle": "Subtitle",
- "play": "Play",
- "none": "None",
- "track": "Track",
- "cancel": "Cancel",
- "delete": "Delete",
- "ok": "OK",
- "remove": "Remove",
- "next": "Next",
- "back": "Back",
- "continue": "Continue",
- "verifying": "Verifying..."
- },
"search": {
+ "search_here": "Search here...",
"search": "Search...",
- "x_items": "{{count}} Items",
+ "x_items": "{{count}} items",
"library": "Library",
"discover": "Discover",
- "no_results": "No Results",
- "no_results_found_for": "No Results Found For",
+ "no_results": "No results",
+ "no_results_found_for": "No results found for",
"movies": "Movies",
"series": "Series",
"episodes": "Episodes",
"collections": "Collections",
"actors": "Actors",
- "artists": "Artists",
- "albums": "Albums",
- "songs": "Songs",
- "playlists": "Playlists",
"request_movies": "Request Movies",
"request_series": "Request Series",
"recently_added": "Recently Added",
@@ -550,29 +303,29 @@
"tmdb_tv_streaming_services": "TMDB TV Streaming Services"
},
"library": {
- "no_results": "No Results",
- "no_libraries_found": "No Libraries Found",
+ "no_items_found": "No items found",
+ "no_results": "No results",
+ "no_libraries_found": "No libraries found",
"item_types": {
- "movies": "Movies",
- "series": "Series",
- "boxsets": "Box Sets",
- "items": "Items"
+ "movies": "movies",
+ "series": "series",
+ "boxsets": "box sets",
+ "items": "items"
},
"options": {
"display": "Display",
"row": "Row",
"list": "List",
- "image_style": "Image Style",
+ "image_style": "Image style",
"poster": "Poster",
"cover": "Cover",
- "show_titles": "Show Titles",
- "show_stats": "Show Stats"
+ "show_titles": "Show titles",
+ "show_stats": "Show stats"
},
"filters": {
"genres": "Genres",
"years": "Years",
"sort_by": "Sort By",
- "filter_by": "Filter By",
"sort_order": "Sort Order",
"tags": "Tags"
}
@@ -582,37 +335,32 @@
"movies": "Movies",
"episodes": "Episodes",
"videos": "Videos",
- "boxsets": "Box Sets",
- "playlists": "Playlists",
- "noDataTitle": "No Favorites Yet",
- "noData": "Mark items as favorites to see them appear here for quick access."
+ "boxsets": "Boxsets",
+ "playlists": "Playlists"
},
"custom_links": {
- "no_links": "No Links"
+ "no_links": "No links"
},
"player": {
"error": "Error",
"failed_to_get_stream_url": "Failed to get the stream URL",
"an_error_occured_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
- "client_error": "Client Error",
+ "client_error": "Client error",
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
- "message_from_server": "Message from Server: {{message}}",
+ "message_from_server": "Message from server: {{message}}",
+ "video_has_finished_playing": "Video has finished playing!",
+ "no_video_source": "No video source...",
"next_episode": "Next Episode",
"refresh_tracks": "Refresh Tracks",
+ "subtitle_tracks": "Subtitle Tracks:",
"audio_tracks": "Audio Tracks:",
"playback_state": "Playback State:",
- "index": "Index:",
- "continue_watching": "Continue Watching",
- "go_back": "Go Back",
- "downloaded_file_title": "You have this file downloaded",
- "downloaded_file_message": "Do you want to play the downloaded file?",
- "downloaded_file_yes": "Yes",
- "downloaded_file_no": "No",
- "downloaded_file_cancel": "Cancel"
+ "no_data_available": "No data available",
+ "index": "Index:"
},
"item_card": {
- "next_up": "Next Up",
- "no_items_to_display": "No Items to Display",
+ "next_up": "Next up",
+ "no_items_to_display": "No items to display",
"cast_and_crew": "Cast & Crew",
"series": "Series",
"seasons": "Seasons",
@@ -620,34 +368,35 @@
"no_episodes_for_this_season": "No episodes for this season",
"overview": "Overview",
"more_with": "More with {{name}}",
- "similar_items": "Similar Items",
- "no_similar_items_found": "No Similar Items Found",
+ "similar_items": "Similar items",
+ "no_similar_items_found": "No similar items found",
"video": "Video",
- "more_details": "More Details",
- "media_options": "Media Options",
+ "more_details": "More details",
"quality": "Quality",
"audio": "Audio",
"subtitles": "Subtitle",
- "show_more": "Show More",
- "show_less": "Show Less",
- "appeared_in": "Appeared In",
- "could_not_load_item": "Could Not Load Item",
+ "show_more": "Show more",
+ "show_less": "Show less",
+ "appeared_in": "Appeared in",
+ "could_not_load_item": "Could not load item",
"none": "None",
"download": {
"download_season": "Download Season",
"download_series": "Download Series",
"download_episode": "Download Episode",
"download_movie": "Download Movie",
- "download_x_item": "Download {{item_count}} Items",
- "download_unwatched_only": "Unwatched Only",
- "download_button": "Download"
+ "download_x_item": "Download {{item_count}} items",
+ "download_button": "Download",
+ "using_optimized_server": "Using optimized server",
+ "using_default_method": "Using default method"
}
},
"live_tv": {
"next": "Next",
"previous": "Previous",
- "coming_soon": "Coming Soon",
- "on_now": "On Now",
+ "live_tv": "Live TV",
+ "coming_soon": "Coming soon",
+ "on_now": "On now",
"shows": "Shows",
"movies": "Movies",
"sports": "Sports",
@@ -658,16 +407,16 @@
"confirm": "Confirm",
"cancel": "Cancel",
"yes": "Yes",
- "whats_wrong": "What's Wrong?",
- "issue_type": "Issue Type",
- "select_an_issue": "Select an Issue",
+ "whats_wrong": "What's wrong?",
+ "issue_type": "Issue type",
+ "select_an_issue": "Select an issue",
"types": "Types",
- "describe_the_issue": "(Optional) Describe the Issue...",
+ "describe_the_issue": "(optional) Describe the issue...",
"submit_button": "Submit",
- "report_issue_button": "Report Issue",
+ "report_issue_button": "Report issue",
"request_button": "Request",
"are_you_sure_you_want_to_request_all_seasons": "Are you sure you want to request all seasons?",
- "failed_to_login": "Failed to Login",
+ "failed_to_login": "Failed to login",
"cast": "Cast",
"details": "Details",
"status": "Status",
@@ -682,33 +431,25 @@
"production_country": "Production Country",
"studios": "Studios",
"network": "Network",
- "currently_streaming_on": "Currently Streaming On",
+ "currently_streaming_on": "Currently Streaming on",
"advanced": "Advanced",
"request_as": "Request As",
"tags": "Tags",
"quality_profile": "Quality Profile",
"root_folder": "Root Folder",
- "season_all": "Season (All)",
+ "season_x": "Season {{seasons}}",
"season_number": "Season {{season_number}}",
"number_episodes": "{{episode_number}} Episodes",
"born": "Born",
"appearances": "Appearances",
- "approve": "Approve",
- "decline": "Decline",
- "requested_by": "Requested by {{user}}",
- "unknown_user": "Unknown User",
"toasts": {
- "jellyseer_does_not_meet_requirements": "Seerr server does not meet minimum version requirements! Please update to at least 2.0.0",
- "jellyseerr_test_failed": "Seerr test failed. Please try again.",
- "failed_to_test_jellyseerr_server_url": "Failed to test Seerr server url",
- "issue_submitted": "Issue Submitted!",
+ "jellyseer_does_not_meet_requirements": "Jellyseerr server does not meet minimum version requirements! Please update to at least 2.0.0",
+ "jellyseerr_test_failed": "Jellyseerr test failed. Please try again.",
+ "failed_to_test_jellyseerr_server_url": "Failed to test jellyseerr server url",
+ "issue_submitted": "Issue submitted!",
"requested_item": "Requested {{item}}!",
"you_dont_have_permission_to_request": "You don't have permission to request!",
- "something_went_wrong_requesting_media": "Something went wrong requesting media!",
- "request_approved": "Request Approved!",
- "request_declined": "Request Declined!",
- "failed_to_approve_request": "Failed to Approve Request",
- "failed_to_decline_request": "Failed to Decline Request"
+ "something_went_wrong_requesting_media": "Something went wrong requesting media!"
}
},
"tabs": {
@@ -718,128 +459,16 @@
"custom_links": "Custom Links",
"favorites": "Favorites"
},
- "music": {
- "title": "Music",
- "tabs": {
- "suggestions": "Suggestions",
- "albums": "Albums",
- "artists": "Artists",
- "playlists": "Playlists",
- "tracks": "tracks"
- },
- "filters": {
- "all": "All"
- },
- "recently_added": "Recently Added",
- "recently_played": "Recently Played",
- "frequently_played": "Frequently Played",
- "explore": "Explore",
- "top_tracks": "Top Tracks",
- "play": "Play",
- "shuffle": "Shuffle",
- "play_top_tracks": "Play Top Tracks",
- "no_suggestions": "No suggestions available",
- "no_albums": "No albums found",
- "no_artists": "No artists found",
- "no_playlists": "No playlists found",
- "album_not_found": "Album not found",
- "artist_not_found": "Artist not found",
- "playlist_not_found": "Playlist not found",
- "track_options": {
- "play_next": "Play Next",
- "add_to_queue": "Add to Queue",
- "add_to_playlist": "Add to Playlist",
- "download": "Download",
- "downloaded": "Downloaded",
- "downloading": "Downloading...",
- "cached": "Cached",
- "delete_download": "Delete Download",
- "delete_cache": "Remove from Cache",
- "go_to_artist": "Go to Artist",
- "go_to_album": "Go to Album",
- "add_to_favorites": "Add to Favorites",
- "remove_from_favorites": "Remove from Favorites",
- "remove_from_playlist": "Remove from Playlist"
- },
- "playlists": {
- "create_playlist": "Create Playlist",
- "playlist_name": "Playlist Name",
- "enter_name": "Enter playlist name",
- "create": "Create",
- "search_playlists": "Search playlists...",
- "added_to": "Added to {{name}}",
- "added": "Added to playlist",
- "removed_from": "Removed from {{name}}",
- "removed": "Removed from playlist",
- "created": "Playlist created",
- "create_new": "Create New Playlist",
- "failed_to_add": "Failed to add to playlist",
- "failed_to_remove": "Failed to remove from playlist",
- "failed_to_create": "Failed to create playlist",
- "delete_playlist": "Delete Playlist",
- "delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "deleted": "Playlist deleted",
- "failed_to_delete": "Failed to delete playlist"
- },
- "sort": {
- "title": "Sort By",
- "alphabetical": "Alphabetical",
- "date_created": "Date Created"
- }
- },
- "watchlists": {
- "title": "Watchlists",
- "my_watchlists": "My Watchlists",
- "public_watchlists": "Public Watchlists",
- "create_title": "Create Watchlist",
- "edit_title": "Edit Watchlist",
- "create_button": "Create Watchlist",
- "save_button": "Save Changes",
- "delete_button": "Delete",
- "remove_button": "Remove",
- "cancel_button": "Cancel",
- "name_label": "Name",
- "name_placeholder": "Enter watchlist name",
- "description_label": "Description",
- "description_placeholder": "Enter description (optional)",
- "is_public_label": "Public Watchlist",
- "is_public_description": "Allow others to view this watchlist",
- "allowed_type_label": "Content Type",
- "sort_order_label": "Default Sort Order",
- "empty_title": "No Watchlists",
- "empty_description": "Create your first watchlist to start organizing your media",
- "empty_watchlist": "This watchlist is empty",
- "empty_watchlist_hint": "Add items from your library to this watchlist",
- "not_configured_title": "Streamystats Not Configured",
- "not_configured_description": "Configure Streamystats in settings to use watchlists",
- "go_to_settings": "Go to Settings",
- "add_to_watchlist": "Add to Watchlist",
- "remove_from_watchlist": "Remove from Watchlist",
- "select_watchlist": "Select Watchlist",
- "create_new": "Create New Watchlist",
- "item": "item",
- "items": "items",
- "public": "Public",
- "private": "Private",
- "you": "You",
- "by_owner": "By another user",
- "not_found": "Watchlist not found",
- "delete_confirm_title": "Delete Watchlist",
- "delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "remove_item_title": "Remove from Watchlist",
- "remove_item_message": "Remove \"{{name}}\" from this watchlist?",
- "loading": "Loading watchlists...",
- "no_compatible_watchlists": "No compatible watchlists",
- "create_one_first": "Create a watchlist that accepts this content type"
- },
- "playback_speed": {
- "title": "Playback Speed",
- "apply_to": "Apply To",
- "speed": "Speed",
- "scope": {
- "media": "This media only",
- "show": "This show",
- "all": "All media (default)"
- }
+ "chromecast": {
+ "no_devices_available": "No Google Cast devices available.",
+ "are_you_on_same_network": "Are you on the same network?",
+ "no_device_selected": "No device selected",
+ "click_icon_to_connect": "Click icon to connect.",
+ "establishing_connection": "Establishing connection...",
+ "no_media_selected": "No media selected.",
+ "start_playing": "Start playing any media",
+ "go_home": "Go Home",
+ "error_loading_item": "An unexpected error occurred while loading the media.",
+ "retry_load_item": "Retry"
}
-}
+}
\ No newline at end of file
diff --git a/translations/es.json b/translations/es.json
index 9ab662eb..8883c2be 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -20,9 +20,7 @@
"server_is_taking_too_long_to_respond_try_again_later": "El servidor está tardando mucho en responder, inténtalo de nuevo más tarde.",
"server_received_too_many_requests_try_again_later": "El servidor está recibiendo muchas peticiones, inténtalo de nuevo más tarde.",
"there_is_a_server_error": "Hay un error en el servidor",
- "an_unexpected_error_occured_did_you_enter_the_correct_url": "Ha ocurrido un error inesperado. ¿Has introducido la URL correcta?",
- "too_old_server_text": "Servidor Jellyfin no soportado descubierto",
- "too_old_server_description": "Por favor, actualiza Jellyfin a la última versión"
+ "an_unexpected_error_occured_did_you_enter_the_correct_url": "Ha ocurrido un error inesperado. ¿Has introducido la URL correcta?"
},
"server": {
"enter_url_to_jellyfin_server": "Introduce la URL de tu servidor Jellyfin",
@@ -30,64 +28,19 @@
"connect_button": "Conectar",
"previous_servers": "Servidores previos",
"clear_button": "Limpiar",
- "swipe_to_remove": "Deslizar para eliminar",
"search_for_local_servers": "Buscar servidores locales",
"searching": "Buscando...",
- "servers": "Servidores",
- "saved": "Guardado",
- "session_expired": "Sesión Caducada",
- "please_login_again": "Su sesión guardada ha caducado. Por favor, inicie sesión de nuevo.",
- "remove_saved_login": "Eliminar inicio de sesión guardado",
- "remove_saved_login_description": "Esto eliminará tus credenciales guardadas para este servidor. Tendrás que volver a introducir tu nombre de usuario y contraseña la próxima vez.",
- "accounts_count": "{{count}} accounts",
- "select_account": "Select Account",
- "add_account": "Add Account",
- "remove_account_description": "This will remove the saved credentials for {{username}}."
- },
- "save_account": {
- "title": "Save Account",
- "save_for_later": "Save this account",
- "security_option": "Security Option",
- "no_protection": "No protection",
- "no_protection_desc": "Quick login without authentication",
- "pin_code": "PIN code",
- "pin_code_desc": "4-digit PIN required when switching",
- "password": "Re-enter password",
- "password_desc": "Password required when switching",
- "save_button": "Save",
- "cancel_button": "Cancel"
- },
- "pin": {
- "enter_pin": "Enter PIN",
- "enter_pin_for": "Enter PIN for {{username}}",
- "enter_4_digits": "Enter 4 digits",
- "invalid_pin": "Invalid PIN",
- "setup_pin": "Set Up PIN",
- "confirm_pin": "Confirm PIN",
- "pins_dont_match": "PINs don't match",
- "forgot_pin": "Forgot PIN?",
- "forgot_pin_desc": "Your saved credentials will be removed"
- },
- "password": {
- "enter_password": "Enter Password",
- "enter_password_for": "Enter password for {{username}}",
- "invalid_password": "Invalid password"
+ "servers": "Servidores"
},
"home": {
- "checking_server_connection": "Comprobando conexión con el servidor...",
"no_internet": "Sin internet",
"no_items": "No hay ítems",
"no_internet_message": "No te preocupes, todavía puedes\nver el contenido descargado.",
- "checking_server_connection_message": "Comprobando conexión con el servidor",
"go_to_downloads": "Ir a descargas",
- "retry": "Reintentar",
- "server_unreachable": "Servidor inaccesible",
- "server_unreachable_message": "No se pudo acceder al servidor.\nCompruebe su conexión de red.",
"oops": "¡Vaya!",
"error_message": "Algo ha salido mal.\nPor favor, cierra la sesión y vuelve a iniciar.",
"continue_watching": "Seguir viendo",
"next_up": "A continuación",
- "continue_and_next_up": "Continuar y siguiente",
"recently_added_in": "Recientemente añadido en {{libraryName}}",
"suggested_movies": "Películas sugeridas",
"suggested_episodes": "Episodios sugeridos",
@@ -109,48 +62,6 @@
"settings": {
"settings_title": "Configuración",
"log_out_button": "Cerrar sesión",
- "categories": {
- "title": "Categorías"
- },
- "playback_controls": {
- "title": "Reproducción y controles"
- },
- "audio_subtitles": {
- "title": "Audio y subtítulos"
- },
- "appearance": {
- "title": "Apariencia",
- "merge_next_up_continue_watching": "Fusionar continuar viendo y siguiente",
- "hide_remote_session_button": "Ocultar botón de sesión remota"
- },
- "network": {
- "title": "Network",
- "local_network": "Local Network",
- "auto_switch_enabled": "Auto-switch when at home",
- "auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
- "local_url": "Local URL",
- "local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
- "local_url_placeholder": "http://192.168.1.100:8096",
- "home_wifi_networks": "Home WiFi Networks",
- "add_current_network": "Add \"{{ssid}}\"",
- "not_connected_to_wifi": "Not connected to WiFi",
- "no_networks_configured": "No networks configured",
- "add_network_hint": "Add your home WiFi network to enable auto-switching",
- "current_wifi": "Current WiFi",
- "using_url": "Using",
- "local": "Local URL",
- "remote": "Remote URL",
- "not_connected": "Not connected",
- "current_server": "Current Server",
- "remote_url": "Remote URL",
- "active_url": "Active URL",
- "not_configured": "Not configured",
- "network_added": "Network added",
- "network_already_added": "Network already added",
- "no_wifi_connected": "Not connected to WiFi",
- "permission_denied": "Location permission denied",
- "permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
- },
"user_info": {
"user_info_title": "Información de usuario",
"user": "Usuario",
@@ -174,42 +85,21 @@
"rewind_length": "Longitud de retroceso",
"seconds_unit": "s"
},
- "gesture_controls": {
- "gesture_controls_title": "Controles de gestos",
- "horizontal_swipe_skip": "Deslizar horizontal para omitir",
- "horizontal_swipe_skip_description": "Desliza hacia izquierda/derecha cuando los controles están ocultos para omitir",
- "left_side_brightness": "Control de brillo lateral izquierdo",
- "left_side_brightness_description": "Desliza hacia arriba/abajo en el lado izquierdo para ajustar el brillo",
- "right_side_volume": "Control de volumen derecho",
- "right_side_volume_description": "Desliza hacia arriba/abajo en el lado derecho para ajustar el volumen",
- "hide_volume_slider": "Ocultar control deslizante de volumen",
- "hide_volume_slider_description": "Ocultar control deslizante de volumen en el reproductor de vídeo",
- "hide_brightness_slider": "Ocultar control deslizante de ajuste de brillo",
- "hide_brightness_slider_description": "Ocultar control deslizante de brillo en el reproductor de vídeo"
- },
"audio": {
"audio_title": "Audio",
"set_audio_track": "Establecer pista del elemento anterior",
"audio_language": "Idioma de audio",
"audio_hint": "Elige un idioma de audio por defecto.",
"none": "Ninguno",
- "language": "Idioma",
- "transcode_mode": {
- "title": "Audio Transcoding",
- "description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
- "auto": "Auto",
- "stereo": "Force Stereo",
- "5_1": "Allow 5.1",
- "passthrough": "Passthrough"
- }
+ "language": "Idioma"
},
"subtitles": {
"subtitle_title": "Subtítulos",
- "subtitle_hint": "Configurar preferencias de subtítulos.",
"subtitle_language": "Idioma de subtítulos",
"subtitle_mode": "Modo de subtítulos",
"set_subtitle_track": "Establecer pista del elemento anterior",
"subtitle_size": "Tamaño de subtítulos",
+ "subtitle_hint": "Configurar preferencias de subtítulos.",
"none": "Ninguno",
"language": "Idioma",
"loading": "Cargando",
@@ -219,66 +109,11 @@
"Always": "Siempre",
"None": "Nada",
"OnlyForced": "Solo forzados"
- },
- "text_color": "Color del texto",
- "background_color": "Color de fondo",
- "outline_color": "Color de salida",
- "outline_thickness": "Grosor exterior",
- "background_opacity": "Opacidad de fondo",
- "outline_opacity": "Opacidad exterior",
- "bold_text": "Texto en negrita",
- "colors": {
- "Black": "Negro",
- "Gray": "Gris",
- "Silver": "Plata",
- "White": "Blanco",
- "Maroon": "Granate",
- "Red": "Rojo",
- "Fuchsia": "Fucsia",
- "Yellow": "Amarillo",
- "Olive": "Oliva",
- "Green": "Verde",
- "Teal": "Cereal",
- "Lime": "Lima",
- "Purple": "Morado",
- "Navy": "Naval",
- "Blue": "Azul",
- "Aqua": "Agua"
- },
- "thickness": {
- "None": "Ninguno",
- "Thin": "Ligero",
- "Normal": "Normal",
- "Thick": "Grosor"
- },
- "subtitle_color": "Color de los Subtítulos",
- "subtitle_background_color": "Color del fondo",
- "subtitle_font": "Fuente de los subtítulos",
- "ksplayer_title": "Ajustes de KSPlayer",
- "hardware_decode": "Decodificación de hardware",
- "hardware_decode_description": "Utilizar la aceleración de hardware para la decodificación de vídeo. Deshabilite si experimenta problemas de reproducción."
- },
- "vlc_subtitles": {
- "title": "VLC Subtitle Settings",
- "hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
- "text_color": "Text Color",
- "background_color": "Background Color",
- "background_opacity": "Background Opacity",
- "outline_color": "Outline Color",
- "outline_opacity": "Outline Opacity",
- "outline_thickness": "Outline Thickness",
- "bold": "Bold Text",
- "margin": "Bottom Margin"
- },
- "video_player": {
- "title": "Reproductor de vídeo",
- "video_player": "Reproductor de vídeo",
- "video_player_description": "Elige qué reproductor de vídeo en iOS",
- "ksplayer": "KSPlayer",
- "vlc": "VLC"
+ }
},
"other": {
"other_title": "Otros",
+ "auto_rotate": "Rotación automática",
"video_orientation": "Orientación de vídeo",
"orientation": "Orientación",
"orientations": {
@@ -294,35 +129,26 @@
"UNKNOWN": "Desconocida"
},
"safe_area_in_controls": "Área segura en controles",
- "video_player": "Reproductor de vídeo",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Experimental + PiP)"
- },
"show_custom_menu_links": "Mostrar enlaces de menú personalizados",
- "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Ocultar bibliotecas",
"select_liraries_you_want_to_hide": "Selecciona las bibliotecas que quieres ocultar de la pestaña Bibliotecas y de Inicio.",
"disable_haptic_feedback": "Desactivar feedback háptico",
- "default_quality": "Calidad por defecto",
- "default_playback_speed": "Velocidad de reproducción predeterminada",
- "auto_play_next_episode": "Auto-play Next Episode",
- "max_auto_play_episode_count": "Máximo número de episodios de Auto Play",
- "disabled": "Deshabilitado"
+ "default_quality": "Calidad por defecto"
},
"downloads": {
- "downloads_title": "Descargas"
- },
- "music": {
- "title": "Música",
- "playback_title": "Reproducir",
- "playback_description": "Configurar cómo se reproduce la música.",
- "prefer_downloaded": "Prefer Downloaded Songs",
- "caching_title": "Almacenando en caché",
- "caching_description": "Automatically cache upcoming tracks for smoother playback.",
- "lookahead_enabled": "Enable Look-Ahead Caching",
- "lookahead_count": "",
- "max_cache_size": "Tamaño máximo del caché"
+ "downloads_title": "Descargas",
+ "download_method": "Método de descarga",
+ "remux_max_download": "Remux máx. descarga",
+ "auto_download": "Descarga automática",
+ "optimized_versions_server": "Servidor de versiones optimizadas",
+ "save_button": "Guardar",
+ "optimized_server": "Servidor optimizado",
+ "optimized": "Optimizado",
+ "default": "Por defecto",
+ "optimized_version_hint": "Introduce la URL del servidor de versiones optimizadas. La URL debe incluir http o https y opcionalmente el puerto.",
+ "read_more_about_optimized_server": "Leer más sobre el servidor de versiones optimizadas.",
+ "url": "URL",
+ "server_url_placeholder": "http(s)://dominio.org:puerto"
},
"plugins": {
"plugins_title": "Plugins",
@@ -331,8 +157,10 @@
"server_url": "URL del servidor",
"server_url_hint": "Ejemplo: http(s)://tu-dominio.url\n(añade el puerto si es necesario)",
"server_url_placeholder": "URL de Jellyseerr...",
- "password": "Contraseña",
+ "password": "Contrasñea",
"password_placeholder": "Introduce la contraseña de Jellyfin de {{username}}",
+ "save_button": "Guardar",
+ "clear_button": "Limpiar",
"login_button": "Iniciar sesión",
"total_media_requests": "Peticiones totales de medios",
"movie_quota_limit": "Límite de cuota de películas",
@@ -340,13 +168,7 @@
"tv_quota_limit": "Límite de cuota de series",
"tv_quota_days": "Días de cuota de series",
"reset_jellyseerr_config_button": "Restablecer configuración de Jellyseerr",
- "unlimited": "Ilimitado",
- "plus_n_more": "+{{n}} more",
- "order_by": {
- "DEFAULT": "Por defecto",
- "VOTE_COUNT_AND_AVERAGE": "Número de votos y promedio",
- "POPULARITY": "Popularidad"
- }
+ "unlimited": "Ilimitado"
},
"marlin_search": {
"enable_marlin_search": "Habilitar búsqueda de Marlin",
@@ -356,40 +178,8 @@
"read_more_about_marlin": "Leer más sobre Marlin.",
"save_button": "Guardar",
"toasts": {
- "saved": "Guardado",
- "refreshed": "Ajustes del servidor actualizados"
- },
- "refresh_from_server": "Actualizar ajustes del servidor"
- },
- "streamystats": {
- "enable_streamystats": "Habilitar Streamystats",
- "disable_streamystats": "Deshabilitar Streamystats",
- "enable_search": "Usar para la búsqueda",
- "url": "URL",
- "server_url_placeholder": "http(s)://streamystats.ejemplo.com",
- "streamystats_search_hint": "Introduzca la URL para su servidor Streamystats. La URL debe incluir http o https y opcionalmente el puerto.",
- "read_more_about_streamystats": "Leer más sobre Streamystats.",
- "save_button": "Guardar",
- "save": "Guardar",
- "features_title": "Características",
- "home_sections_title": "Secciones de inicio",
- "enable_movie_recommendations": "Recomendaciones de películas",
- "enable_series_recommendations": "Recomendaciones de series",
- "enable_promoted_watchlists": "Listas promocionadas",
- "hide_watchlists_tab": "Ocultar pestaña de listas",
- "home_sections_hint": "Mostrar recomendaciones personalizadas y listas promovidas de Streamystats en la página principal.",
- "recommended_movies": "Películas recomendadas",
- "recommended_series": "Series recomendadas",
- "toasts": {
- "saved": "Guardado",
- "refreshed": "Ajustes actualizados desde el servidor",
- "disabled": "Streamystats deshabilitado"
- },
- "refresh_from_server": "Actualizar ajustes desde el servidor"
- },
- "kefinTweaks": {
- "watchlist_enabler": "Habilitar la integración de la lista de seguimiento",
- "watchlist_button": "Activar o desactivar la integración de la lista de seguimiento"
+ "saved": "Guardado"
+ }
}
},
"storage": {
@@ -397,58 +187,43 @@
"app_usage": "App {{usedSpace}}%",
"device_usage": "Dispositivo {{availableSpace}}%",
"size_used": "{{used}} de {{total}} usado",
- "delete_all_downloaded_files": "Eliminar todos los archivos descargados",
- "music_cache_title": "Caché de música",
- "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
- "enable_music_cache": "Activar Caché de Música",
- "clear_music_cache": "Borrar Caché de Música",
- "music_cache_size": "Caché {{Tamaño}}",
- "music_cache_cleared": "Caché de música eliminado",
- "delete_all_downloaded_songs": "Eliminar todas las descargas",
- "downloaded_songs_size": "{{tamaño}} descargado",
- "downloaded_songs_deleted": "Canciones descargadas eliminadas"
+ "delete_all_downloaded_files": "Eliminar todos los archivos descargados"
},
"intro": {
- "title": "Intro",
"show_intro": "Mostrar intro",
"reset_intro": "Restablecer intro"
},
"logs": {
"logs_title": "Registros",
- "export_logs": "Export logs",
- "click_for_more_info": "Click for more info",
- "level": "Nivel",
"no_logs_available": "No hay registros disponibles",
"delete_all_logs": "Eliminar todos los registros"
},
"languages": {
"title": "Idiomas",
"app_language": "Idioma de la app",
+ "app_language_description": "Selecciona el idioma de la app.",
"system": "Sistema"
},
- "toasts": {
+ "toasts":{
"error_deleting_files": "Error al eliminar archivos",
"background_downloads_enabled": "Descargas en segundo plano habilitadas",
- "background_downloads_disabled": "Descargas en segundo plano deshabilitadas"
+ "background_downloads_disabled": "Descargas en segundo plano deshabilitadas",
+ "connected": "Conectado",
+ "could_not_connect": "No se pudo conectar",
+ "invalid_url": "URL inválida"
}
},
- "sessions": {
- "title": "Sesiones",
- "no_active_sessions": "No hay sesiones activas"
- },
"downloads": {
"downloads_title": "Descargas",
"tvseries": "Series",
"movies": "Películas",
"queue": "Cola",
- "other_media": "Otros medios",
"queue_hint": "La cola de series y películas se perderá al reiniciar la app",
"no_items_in_queue": "No hay ítems en la cola",
"no_downloaded_items": "No hay ítems descargados",
"delete_all_movies_button": "Eliminar todas las películas",
"delete_all_tvseries_button": "Eliminar todas las series",
"delete_all_button": "Eliminar todo",
- "delete_all_other_media_button": "Eliminar otros medios",
"active_download": "Descarga activa",
"no_active_downloads": "No hay descargas activas",
"active_downloads": "Descargas activas",
@@ -459,59 +234,37 @@
"something_went_wrong": "Algo ha salido mal",
"could_not_get_stream_url_from_jellyfin": "No se pudo obtener la URL del stream de Jellyfin",
"eta": "{{eta}} restante",
+ "methods": "Métodos",
"toasts": {
"you_are_not_allowed_to_download_files": "No tienes permiso para descargar archivos.",
"deleted_all_movies_successfully": "¡Todas las películas eliminadas con éxito!",
"failed_to_delete_all_movies": "Error al eliminar todas las películas",
"deleted_all_tvseries_successfully": "¡Todas las series eliminadas con éxito!",
"failed_to_delete_all_tvseries": "Error al eliminar todas las series",
- "deleted_media_successfully": "¡Otros medios eliminados con éxito!",
- "failed_to_delete_media": "Error al eliminar otros medios",
- "download_deleted": "Descarga eliminada",
"download_cancelled": "Descarga cancelada",
- "could_not_delete_download": "No se pudo eliminar la descarga",
- "download_paused": "Descarga pausada",
- "could_not_pause_download": "No se pudo pausar la descarga",
- "download_resumed": "Descarga rebatida",
- "could_not_resume_download": "No se pudo reiniciar la descarga",
+ "could_not_cancel_download": "No se pudo cancelar la descarga",
"download_completed": "Descarga completada",
- "download_failed": "Descarga fallida",
+ "download_started_for": "Descarga iniciada para {{item}}",
+ "item_is_ready_to_be_downloaded": "{{item}} está listo para ser descargado",
+ "download_stated_for_item": "Descarga iniciada para {{item}}",
"download_failed_for_item": "Descarga fallida para {{item}} - {{error}}",
"download_completed_for_item": "Descarga completada para {{item}}",
- "download_started_for_item": "Descarga iniciada para {{item}}",
- "failed_to_start_download": "Error al iniciar la descarga",
- "item_already_downloading": "{{item}} ya está descargando",
- "all_files_deleted": "Todas las descargas eliminadas correctamente",
- "files_deleted_by_type": "{{count}} {{type}} eliminado",
+ "queued_item_for_optimization": "{{item}} en cola para optimización",
+ "failed_to_start_download_for_item": "Error al iniciar la descarga para {{item}}: {{message}}",
+ "server_responded_with_status_code": "El servidor ha respondido con el estado {{statusCode}}",
+ "no_response_received_from_server": "No se ha recibido respuesta del servidor",
+ "error_setting_up_the_request": "Error al configurar la petición",
+ "failed_to_start_download_for_item_unexpected_error": "Error al iniciar la descarga para {{item}}: Error inesperado",
"all_files_folders_and_jobs_deleted_successfully": "Todos los archivos, carpetas y trabajos eliminados con éxito",
- "failed_to_clean_cache_directory": "Error al limpiar el directorio de caché",
- "could_not_get_download_url_for_item": "No se pudo obtener la URL de descarga para {{itemName}}",
- "go_to_downloads": "Ir a descargas",
- "file_deleted": "{{item}} eliminado"
+ "an_error_occured_while_deleting_files_and_jobs": "Ha ocurrido un error al eliminar archivos y trabajos",
+ "go_to_downloads": "Ir a descargas"
}
}
},
- "common": {
- "select": "Seleccionar",
- "no_trailer_available": "No hay tráiler disponible",
- "video": "Vídeo",
- "audio": "Audio",
- "subtitle": "Subtítulos",
- "play": "Jugar",
- "none": "Nada",
- "track": "Pista",
- "cancel": "Cancelar",
- "delete": "Borrar",
- "ok": "Aceptar",
- "remove": "Eliminar",
- "next": "Next",
- "back": "Back",
- "continue": "Continue",
- "verifying": "Verifying..."
- },
"search": {
+ "search_here": "Buscar aquí...",
"search": "Buscar...",
- "x_items": "{{count}} Elementos",
+ "x_items": "{{count}} ítems",
"library": "Biblioteca",
"discover": "Descubrir",
"no_results": "Sin resultados",
@@ -521,16 +274,12 @@
"episodes": "Episodios",
"collections": "Colecciones",
"actors": "Actores",
- "artists": "Artistas",
- "albums": "Álbumes",
- "songs": "Canciones",
- "playlists": "Listas de reproducción",
"request_movies": "Solicitar películas",
"request_series": "Solicitar series",
"recently_added": "Recientemente añadido",
"recent_requests": "Solicitudes recientes",
"plex_watchlist": "Lista de seguimiento de Plex",
- "trending": "Tendencias",
+ "trending": "Trending",
"popular_movies": "Películas populares",
"movie_genres": "Géneros de películas",
"upcoming_movies": "Próximas películas",
@@ -550,20 +299,21 @@
"tmdb_tv_streaming_services": "Servicios de streaming de series de TMDB"
},
"library": {
+ "no_items_found": "No se han encontrado ítems",
"no_results": "Sin resultados",
"no_libraries_found": "No se han encontrado bibliotecas",
"item_types": {
- "movies": "Películas",
- "series": "Series",
- "boxsets": "Colecciones",
- "items": "Elementos"
+ "movies": "películas",
+ "series": "series",
+ "boxsets": "colecciones",
+ "items": "ítems"
},
"options": {
"display": "Mostrar",
"row": "Fila",
"list": "Lista",
"image_style": "Estilo de imagen",
- "poster": "Póster",
+ "poster": "Poster",
"cover": "Portada",
"show_titles": "Mostrar títulos",
"show_stats": "Mostrar estadísticas"
@@ -572,7 +322,6 @@
"genres": "Géneros",
"years": "Años",
"sort_by": "Ordenar por",
- "filter_by": "Filtrar por",
"sort_order": "Ordenar",
"tags": "Etiquetas"
}
@@ -583,32 +332,27 @@
"episodes": "Episodios",
"videos": "Vídeos",
"boxsets": "Colecciones",
- "playlists": "Listas",
- "noDataTitle": "Aún no hay favoritos",
- "noData": "Marca elementos como favoritos para verlos aparecer aquí para un acceso rápido."
+ "playlists": "Playlists"
},
"custom_links": {
"no_links": "Sin enlaces"
},
"player": {
"error": "Error",
- "failed_to_get_stream_url": "Error al obtener la URL del Steam",
+ "failed_to_get_stream_url": "Error al obtener la URL del stream",
"an_error_occured_while_playing_the_video": "Ha ocurrido un error al reproducir el vídeo. Comprueba los registros en la configuración.",
"client_error": "Error del cliente",
- "could_not_create_stream_for_chromecast": "No se pudo crear el Steam para Chromecast",
+ "could_not_create_stream_for_chromecast": "No se pudo crear el stream para Chromecast",
"message_from_server": "Mensaje del servidor: {{message}}",
+ "video_has_finished_playing": "El vídeo ha terminado de reproducirse",
+ "no_video_source": "No hay fuente de vídeo...",
"next_episode": "Siguiente episodio",
"refresh_tracks": "Refrescar pistas",
+ "subtitle_tracks": "Pistas de subtítulos:",
"audio_tracks": "Pistas de audio:",
"playback_state": "Estado de la reproducción:",
- "index": "Índice:",
- "continue_watching": "Continuar viendo",
- "go_back": "Volver",
- "downloaded_file_title": "Ya tienes este archivo descargado",
- "downloaded_file_message": "¿Quieres reproducir el archivo descargado?",
- "downloaded_file_yes": "Sí",
- "downloaded_file_no": "No",
- "downloaded_file_cancel": "Cancelar"
+ "no_data_available": "No hay datos disponibles",
+ "index": "Índice:"
},
"item_card": {
"next_up": "A continuación",
@@ -624,7 +368,6 @@
"no_similar_items_found": "No se han encontrado ítems similares",
"video": "Vídeo",
"more_details": "Más detalles",
- "media_options": "Opciones de medios",
"quality": "Calidad",
"audio": "Audio",
"subtitles": "Subtítulos",
@@ -639,13 +382,15 @@
"download_episode": "Descargar episodio",
"download_movie": "Descargar película",
"download_x_item": "Descargar {{item_count}} ítems",
- "download_unwatched_only": "No visto",
- "download_button": "Descargar"
+ "download_button": "Descargar",
+ "using_optimized_server": "Usando servidor optimizado",
+ "using_default_method": "Usando método por defecto"
}
},
"live_tv": {
"next": "Siguiente",
"previous": "Anterior",
+ "live_tv": "TV en directo",
"coming_soon": "Próximamente",
"on_now": "En directo",
"shows": "Programas",
@@ -654,7 +399,7 @@
"for_kids": "Para niños",
"news": "Noticias"
},
- "jellyseerr": {
+ "jellyseerr":{
"confirm": "Confirmar",
"cancel": "Cancelar",
"yes": "Sí",
@@ -688,15 +433,11 @@
"tags": "Etiquetas",
"quality_profile": "Perfil de calidad",
"root_folder": "Carpeta raíz",
- "season_all": "Temporada (Todas)",
+ "season_x": "Temporada {{seasons}}",
"season_number": "Temporada {{season_number}}",
- "number_episodes": "{{episode_number}} Episodios",
+ "number_episodes": "{{episode_number}} episodios",
"born": "Nacido",
"appearances": "Apariciones",
- "approve": "Aprobar",
- "decline": "Rechazar",
- "requested_by": "Solicitado por {{user}}",
- "unknown_user": "Usuario desconocido",
"toasts": {
"jellyseer_does_not_meet_requirements": "¡Jellyseer no cumple con los requisitos! Por favor, actualízalo al menos a la versión 2.0.0.",
"jellyseerr_test_failed": "La prueba de Jellyseerr ha fallado. Por favor inténtalo de nuevo.",
@@ -704,11 +445,7 @@
"issue_submitted": "¡Problema enviado!",
"requested_item": "¡{{item}} solicitado!",
"you_dont_have_permission_to_request": "¡No tienes permiso para solicitar!",
- "something_went_wrong_requesting_media": "¡Algo ha salido mal solicitando los medios!",
- "request_approved": "¡Solicitud aprobada!",
- "request_declined": "¡Solicitud rechazada!",
- "failed_to_approve_request": "Error al aprobar la solicitud",
- "failed_to_decline_request": "Error al rechazar la solicitud"
+ "something_went_wrong_requesting_media": "¡Algo ha salido mal solicitando los medios!"
}
},
"tabs": {
@@ -717,129 +454,5 @@
"library": "Bibliotecas",
"custom_links": "Enlaces personalizados",
"favorites": "Favoritos"
- },
- "music": {
- "title": "Música",
- "tabs": {
- "suggestions": "Sugerencias",
- "albums": "Álbumes",
- "artists": "Artistas",
- "playlists": "Listas de reproducción",
- "tracks": "Canciones"
- },
- "filters": {
- "all": "Todas"
- },
- "recently_added": "Recientemente añadido",
- "recently_played": "Reproducidos Recientemente",
- "frequently_played": "Reproducido con frecuencia",
- "explore": "Explorar",
- "top_tracks": "Canciones Populares",
- "play": "Reproducir",
- "shuffle": "Aleatorio",
- "play_top_tracks": "Reproducir canciones populares",
- "no_suggestions": "No hay sugerencias",
- "no_albums": "No se encontraron álbumes",
- "no_artists": "No se han encontrado artistas",
- "no_playlists": "No se han encontrado listas de reproducción",
- "album_not_found": "Álbum no encontrado.",
- "artist_not_found": "Artista no encontrado",
- "playlist_not_found": "Lista no encontrada",
- "track_options": {
- "play_next": "Reproducir siguiente",
- "add_to_queue": "Añadir a la Cola",
- "add_to_playlist": "Añadir a la Lista de Reproducción",
- "download": "Descargar",
- "downloaded": "Descargado",
- "downloading": "Descargando...",
- "cached": "En caché",
- "delete_download": "Delete Download",
- "delete_cache": "Remove from Cache",
- "go_to_artist": "Ir al artista",
- "go_to_album": "Ir al álbum",
- "add_to_favorites": "Añadir a Favoritos",
- "remove_from_favorites": "Eliminar de Favoritos",
- "remove_from_playlist": "Eliminar de la lista de reproducción"
- },
- "playlists": {
- "create_playlist": "Crear una lista de reproducción",
- "playlist_name": "Nombre de la lista de reproducción",
- "enter_name": "Introducir nombre de la lista de reproducción",
- "create": "Crear",
- "search_playlists": "Buscar listas de reproducción...",
- "added_to": "Añadido a {{name}}",
- "added": "Añadido a la lista de reproducción",
- "removed_from": "Eliminado de {{name}}",
- "removed": "Quitar de la lista de reproducción",
- "created": "Lista de reproducción creada",
- "create_new": "Crear una nueva lista de reproducción",
- "failed_to_add": "Error al añadir a la lista de reproducción",
- "failed_to_remove": "Error al eliminar de la lista de reproducción",
- "failed_to_create": "Error al crear lista de reproducción",
- "delete_playlist": "Eliminar lista de reproducción",
- "delete_confirm": "¿Está seguro que desea eliminar \"{{name}}\"? Esta acción no se puede deshacer.",
- "deleted": "Lista de reproducción eliminada",
- "failed_to_delete": "Error al eliminar lista de reproducción"
- },
- "sort": {
- "title": "Ordenar por",
- "alphabetical": "Alfabéticamente",
- "date_created": "Fecha de creación"
- }
- },
- "watchlists": {
- "title": "Listas de seguimiento",
- "my_watchlists": "Mi lista de seguimiento",
- "public_watchlists": "Listas de seguimiento públicas",
- "create_title": "Crear lista de seguimiento",
- "edit_title": "Editar lista de seguimiento",
- "create_button": "Crear lista de seguimiento",
- "save_button": "Guardar cambios",
- "delete_button": "Borrar",
- "remove_button": "Eliminar",
- "cancel_button": "Cancelar",
- "name_label": "Nombre",
- "name_placeholder": "Introduce el nombre de la lista de seguimiento",
- "description_label": "Descripción",
- "description_placeholder": "Introduce una descripción (opcional)",
- "is_public_label": "Listas de seguimiento públicas",
- "is_public_description": "Permitir que otros vean esta lista de seguimiento",
- "allowed_type_label": "Tipo de contenido",
- "sort_order_label": "Ordenación por defecto",
- "empty_title": "No hay listas de seguimiento",
- "empty_description": "Crea tu primera lista de seguimiento para empezar a organizar tus medios",
- "empty_watchlist": "Esta lista de seguimiento está vacía",
- "empty_watchlist_hint": "Añadir elementos de tu biblioteca a esta lista de seguimiento",
- "not_configured_title": "Streamystats no configurado",
- "not_configured_description": "Configura Streamystats en ajustes para usar listas de seguimiento",
- "go_to_settings": "Ir a la configuración",
- "add_to_watchlist": "Añadir a Lista de Seguimiento",
- "remove_from_watchlist": "Quitar de la lista de seguimiento",
- "select_watchlist": "Seleccionar lista de seguimiento",
- "create_new": "Crear nueva lista de seguimiento",
- "item": "elemento",
- "items": "elementos",
- "public": "Público",
- "private": "Privado",
- "you": "Tú",
- "by_owner": "Por otro usuario",
- "not_found": "Lista de seguimiento no encontrada",
- "delete_confirm_title": "Eliminar lista de seguimiento",
- "delete_confirm_message": "¿Está seguro que desea eliminar\"{{name}}\"? Esta acción no se puede deshacer.",
- "remove_item_title": "Borrar de la lista de seguimiento",
- "remove_item_message": "¿Eliminar\"{{name}}\" de esta lista de seguimiento?",
- "loading": "Cargando listas de seguimiento...",
- "no_compatible_watchlists": "No hay listas de seguimiento compatibles",
- "create_one_first": "Crear una lista vigilada que acepte este tipo de contenido"
- },
- "playback_speed": {
- "title": "Velocidad de reproducción",
- "apply_to": "Aplicar a",
- "speed": "Velocidad",
- "scope": {
- "media": "Solo este medio",
- "show": "Este programa",
- "all": "Todos los medios (por defecto)"
- }
}
}
diff --git a/translations/fr.json b/translations/fr.json
index 21fef280..f197494d 100644
--- a/translations/fr.json
+++ b/translations/fr.json
@@ -7,22 +7,20 @@
"username_placeholder": "Nom d'utilisateur",
"password_placeholder": "Mot de passe",
"login_button": "Se connecter",
- "quick_connect": "Connexion rapide",
+ "quick_connect": "Connexion Rapide",
"enter_code_to_login": "Entrez le code {{code}} pour vous connecter",
- "failed_to_initiate_quick_connect": "Échec de l'initialisation de Connexion rapide",
+ "failed_to_initiate_quick_connect": "Échec de l'initialisation de Connexion Rapide",
"got_it": "D'accord",
"connection_failed": "La connexion a échoué",
- "could_not_connect_to_server": "Impossible de se connecter au serveur. Veuillez vérifier l’URL et votre connexion réseau.",
+ "could_not_connect_to_server": "Impossible de se connecter au serveur. Veuillez vérifier l'URL et votre connection réseau.",
"an_unexpected_error_occured": "Une erreur inattendue s'est produite",
"change_server": "Changer de serveur",
"invalid_username_or_password": "Nom d'utilisateur ou mot de passe invalide",
"user_does_not_have_permission_to_log_in": "L'utilisateur n'a pas la permission de se connecter",
"server_is_taking_too_long_to_respond_try_again_later": "Le serveur prend trop de temps à répondre, réessayez plus tard",
- "server_received_too_many_requests_try_again_later": "Le serveur a reçu trop de demandes, réessayez plus tard.",
+ "server_received_too_many_requests_try_again_later": "Le serveur a reçu trop de demandes, réessayez plus tard",
"there_is_a_server_error": "Il y a une erreur de serveur",
- "an_unexpected_error_occured_did_you_enter_the_correct_url": "Une erreur inattendue s’est produite. Avez-vous correctement saisi l’URL du serveur ?",
- "too_old_server_text": "Serveur Jellyfin non pris en charge découvert",
- "too_old_server_description": "Veuillez mettre à jour Jellyfin vers la dernière version"
+ "an_unexpected_error_occured_did_you_enter_the_correct_url": "Une erreur inattendue s'est produite. Avez-vous entré la bonne URL?"
},
"server": {
"enter_url_to_jellyfin_server": "Entrez l'URL du serveur Jellyfin",
@@ -30,127 +28,40 @@
"connect_button": "Connexion",
"previous_servers": "Serveurs précédents",
"clear_button": "Effacer",
- "swipe_to_remove": "Glisser pour supprimer",
"search_for_local_servers": "Rechercher des serveurs locaux",
"searching": "Recherche...",
- "servers": "Serveurs",
- "saved": "Enregistré",
- "session_expired": "Session expirée",
- "please_login_again": "Votre session enregistrée a expiré. Veuillez vous connecter à nouveau.",
- "remove_saved_login": "Supprimer l'identifiant enregistré",
- "remove_saved_login_description": "Cela supprimera vos identifiants enregistrés pour ce serveur. Vous devrez saisir à nouveau votre nom d’utilisateur et votre mot de passe la prochaine fois.",
- "accounts_count": "Comptes {{count}}",
- "select_account": "Sélectionnez un compte",
- "add_account": "Ajouter un compte",
- "remove_account_description": "Cela supprimera les identifiants enregistrés pour {{username}}."
- },
- "save_account": {
- "title": "Sauvegarder le compte",
- "save_for_later": "Enregistrer ce compte",
- "security_option": "Options de sécurité",
- "no_protection": "Aucune protection",
- "no_protection_desc": "Connexion rapide sans authentification",
- "pin_code": "Code PIN",
- "pin_code_desc": "Code PIN à 4 chiffres requis lors du changement",
- "password": "Confirmer le mot de passe",
- "password_desc": "Mot de passe requis lors du changement",
- "save_button": "Enregistrer",
- "cancel_button": "Annuler"
- },
- "pin": {
- "enter_pin": "Saisir le code PIN",
- "enter_pin_for": "Saisissez le code PIN pour {{username}}",
- "enter_4_digits": "Saisissez 4 chiffres",
- "invalid_pin": "Code PIN incorrect",
- "setup_pin": "Configurer le code PIN",
- "confirm_pin": "Confirmer le code PIN",
- "pins_dont_match": "Les codes PIN ne correspondent pas",
- "forgot_pin": "Code PIN oublié ?",
- "forgot_pin_desc": "Vos identifiants enregistrés seront supprimés"
- },
- "password": {
- "enter_password": "Veuillez saisir le mot de passe",
- "enter_password_for": "Entrez le mot de passe pour {{username}}",
- "invalid_password": "Mot de passe incorrect"
+ "servers": "Serveurs"
},
"home": {
- "checking_server_connection": "Vérification de la connexion au serveur...",
"no_internet": "Pas d'Internet",
"no_items": "Aucun média",
"no_internet_message": "Aucun problème, vous pouvez toujours regarder\nle contenu téléchargé.",
- "checking_server_connection_message": "Vérification de la connexion au serveur",
- "go_to_downloads": "Accédez aux téléchargements",
- "retry": "Réessayer",
- "server_unreachable": "Serveur injoignable",
- "server_unreachable_message": "Impossible d'accéder au serveur.\nVeuillez vérifier votre connexion réseau.",
- "oops": "Oups !",
+ "go_to_downloads": "Aller aux téléchargements",
+ "oops": "Oups!",
"error_message": "Quelque chose s'est mal passé.\nVeuillez vous reconnecter à nouveau.",
"continue_watching": "Continuer à regarder",
"next_up": "À suivre",
- "continue_and_next_up": "Continuer de regarder et à suivre",
"recently_added_in": "Ajoutés récemment dans {{libraryName}}",
"suggested_movies": "Films suggérés",
"suggested_episodes": "Épisodes suggérés",
"intro": {
"welcome_to_streamyfin": "Bienvenue sur Streamyfin",
- "a_free_and_open_source_client_for_jellyfin": "Un client gratuit et open-source pour Jellyfin.",
+ "a_free_and_open_source_client_for_jellyfin": "Un client gratuit et open source pour Jellyfin",
"features_title": "Fonctionnalités",
- "features_description": "Streamyfin offre de nombreuses fonctionnalités et s'intègre à de nombreux logiciels disponibles dans le menu des paramètres, notamment :",
- "jellyseerr_feature_description": "Connectez-vous à votre instance Seerr et demandez des films directement dans l'application.",
+ "features_description": "Streamyfin possède de nombreuses fonctionnalités et s'intègre à un large éventail de logiciels que vous pouvez trouver dans le menu des paramètres, notamment:",
+ "jellyseerr_feature_description": "Connectez-vous à votre instance Jellyseerr et demandez des films directement dans l'application.",
"downloads_feature_title": "Téléchargements",
- "downloads_feature_description": "Téléchargez des films et des séries pour les regarder hors ligne. Utilisez la méthode par défaut ou installez le serveur d'optimisation pour télécharger les fichiers en arrière-plan.",
- "chromecast_feature_description": "Diffusez des films et des séries sur vos appareils Chromecast.",
+ "downloads_feature_description": "Téléchargez des films et des émissions de télévision pour les regarder hors ligne. Utilisez la méthode par défaut ou installez le serveur d'optimisation pour télécharger les fichiers en arrière-plan.",
+ "chromecast_feature_description": "Diffusez des films et des émissions de télévision sur vos appareils Chromecast.",
"centralised_settings_plugin_title": "Plugin de paramètres centralisés",
"centralised_settings_plugin_description": "Configuration des paramètres d'un emplacement centralisé sur votre serveur Jellyfin. Tous les paramètres clients pour tous les utilisateurs seront synchronisés automatiquement.",
"done_button": "Terminé",
"go_to_settings_button": "Allez dans les paramètres",
- "read_more": "En savoir plus"
+ "read_more": "Lisez-en plus"
},
"settings": {
"settings_title": "Paramètres",
"log_out_button": "Déconnexion",
- "categories": {
- "title": "Catégories"
- },
- "playback_controls": {
- "title": "Lecture et commandes"
- },
- "audio_subtitles": {
- "title": "Audio et sous-titres"
- },
- "appearance": {
- "title": "Apparence",
- "merge_next_up_continue_watching": "Fusionner, continuer à regarder et à suivre",
- "hide_remote_session_button": "Masquer le bouton de session distante"
- },
- "network": {
- "title": "Réseau",
- "local_network": "Réseau local",
- "auto_switch_enabled": "Basculement automatique quand à la maison",
- "auto_switch_description": "Basculer automatiquement vers l'URL locale lorsque vous êtes connecté au Wi-Fi de la maison",
- "local_url": "URL locale",
- "local_url_hint": "Entrez l'adresse de votre serveur local (exemple, http://192.168.1.100:8096)",
- "local_url_placeholder": "http://192.168.1.100:8096",
- "home_wifi_networks": "Réseaux Wi-Fi domestiques",
- "add_current_network": "Ajouter \"{{ssid}}\"",
- "not_connected_to_wifi": "Non connecté au WiFi",
- "no_networks_configured": "Pas de réseau configuré",
- "add_network_hint": "Ajouter votre réseau Wi-Fi domestique pour activer la commutation automatique",
- "current_wifi": "WiFi actuel",
- "using_url": "Utilisant",
- "local": "URL locale",
- "remote": "URL à distance",
- "not_connected": "Non connecté",
- "current_server": "Serveur actuel",
- "remote_url": "URL à distance",
- "active_url": "URL active",
- "not_configured": "Non configuré",
- "network_added": "Réseau ajouté",
- "network_already_added": "Réseau déjà ajouté",
- "no_wifi_connected": "Non connecté au WiFi",
- "permission_denied": "Autorisation de localisation refusée",
- "permission_denied_explanation": "Une autorisation de localisation est requise pour détecter le réseau Wifi afin de changer automatiquement. Veuillez l’activer dans les paramètres."
- },
"user_info": {
"user_info_title": "Informations utilisateur",
"user": "Utilisateur",
@@ -160,8 +71,8 @@
},
"quick_connect": {
"quick_connect_title": "Connexion Rapide",
- "authorize_button": "Autoriser une Connexion Rapide",
- "enter_the_quick_connect_code": "Entrez le code de Connexion Rapide...",
+ "authorize_button": "Autoriser Connexion Rapide",
+ "enter_the_quick_connect_code": "Entrez le code Connexion Rapide...",
"success": "Succès",
"quick_connect_autorized": "Connexion Rapide autorisé",
"error": "Erreur",
@@ -174,42 +85,21 @@
"rewind_length": "Durée de retour en arrière",
"seconds_unit": "s"
},
- "gesture_controls": {
- "gesture_controls_title": "Commandes gestuelles",
- "horizontal_swipe_skip": "Glisser horizontalement pour passer",
- "horizontal_swipe_skip_description": "Glisser vers la gauche/droite lorsque les contrôles sont masqués pour passer",
- "left_side_brightness": "Contrôle de la luminosité du côté gauche",
- "left_side_brightness_description": "Glisser vers le haut/bas sur le côté gauche pour ajuster la luminosité",
- "right_side_volume": "Contrôle du volume du côté droit",
- "right_side_volume_description": "Glisser vers le haut/bas sur le côté droit pour ajuster le volume",
- "hide_volume_slider": "Masquer le curseur de volume",
- "hide_volume_slider_description": "Masquer le curseur de volume dans le lecteur vidéo",
- "hide_brightness_slider": "Cacher le curseur de luminosité",
- "hide_brightness_slider_description": "Masquer le curseur de volume dans le lecteur vidéo"
- },
"audio": {
"audio_title": "Audio",
"set_audio_track": "Piste audio de l'élément précédent",
"audio_language": "Langue audio",
"audio_hint": "Choisissez une langue audio par défaut.",
"none": "Aucune",
- "language": "Langage",
- "transcode_mode": {
- "title": "Transcodage audio",
- "description": "Contrôle la gestion de l'audio surround (7.1, TrueHD, DTS-HD)",
- "auto": "Auto",
- "stereo": "Forcer la stéréo",
- "5_1": "Autoriser 5.1",
- "passthrough": "Intercommunication"
- }
+ "language": "Langage"
},
"subtitles": {
"subtitle_title": "Sous-titres",
- "subtitle_hint": "Configurez les préférences des sous-titres.",
"subtitle_language": "Langue des sous-titres",
"subtitle_mode": "Mode des sous-titres",
"set_subtitle_track": "Piste de sous-titres de l'élément précédent",
"subtitle_size": "Taille des sous-titres",
+ "subtitle_hint": "Configurez les préférences des sous-titres.",
"none": "Aucune",
"language": "Langage",
"loading": "Chargement",
@@ -219,66 +109,11 @@
"Always": "Toujours",
"None": "Aucun",
"OnlyForced": "Forcés seulement"
- },
- "text_color": "Couleur du texte",
- "background_color": "Couleur d'arrière-plan",
- "outline_color": "Couleur du contour",
- "outline_thickness": "Épaisseur du contour",
- "background_opacity": "Opacité de l'arrière-plan",
- "outline_opacity": "Opacité du contour",
- "bold_text": "Texte en gras",
- "colors": {
- "Black": "Noir",
- "Gray": "Gris",
- "Silver": "Argent",
- "White": "Blanc",
- "Maroon": "Marron",
- "Red": "Rouge",
- "Fuchsia": "Fuchsia",
- "Yellow": "Jaune",
- "Olive": "Olive",
- "Green": "Vert",
- "Teal": "Bleu canard",
- "Lime": "Citron vert",
- "Purple": "Violet",
- "Navy": "Bleu marine",
- "Blue": "Bleu",
- "Aqua": "Bleu turquoise"
- },
- "thickness": {
- "None": "Aucun",
- "Thin": "Maigre",
- "Normal": "Normale",
- "Thick": "Épais"
- },
- "subtitle_color": "Couleur des sous-titres",
- "subtitle_background_color": "Couleur d'arrière-plan",
- "subtitle_font": "Police des sous-titres",
- "ksplayer_title": "Paramètres de KSPlayer",
- "hardware_decode": "Décodage matériel",
- "hardware_decode_description": "Utilisez l’accélération matérielle pour le décodage vidéo. Désactivez si vous rencontrez des problèmes de lecture."
- },
- "vlc_subtitles": {
- "title": "Paramètres des sous-titres VLC",
- "hint": "Personnaliser l'apparence des sous-titres pour le lecteur VLC. Les changements prennent effet lors de la lecture suivante.",
- "text_color": "Couleur du texte",
- "background_color": "Couleur d'arrière-plan",
- "background_opacity": "Opacité de l'arrière-plan",
- "outline_color": "Couleur du contour",
- "outline_opacity": "Opacité du contour",
- "outline_thickness": "Épaisseur du contour",
- "bold": "Texte en gras",
- "margin": "Marge inférieure"
- },
- "video_player": {
- "title": "Lecteur vidéo",
- "video_player": "Lecteur vidéo",
- "video_player_description": "Choisissez le lecteur vidéo à utiliser sur iOS.",
- "ksplayer": "KSPlayer",
- "vlc": "VLC"
+ }
},
"other": {
"other_title": "Autres",
+ "auto_rotate": "Rotation automatique",
"video_orientation": "Orientation vidéo",
"orientation": "Orientation",
"orientations": {
@@ -294,102 +129,58 @@
"UNKNOWN": "Inconnu"
},
"safe_area_in_controls": "Zone de sécurité dans les contrôles",
- "video_player": "Lecteur vidéo",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Expérimental + PiP)"
- },
"show_custom_menu_links": "Afficher les liens personnalisés",
- "show_large_home_carousel": "Afficher le grand carrousel d’accueil (bêta)",
"hide_libraries": "Cacher des bibliothèques",
- "select_liraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l'onglet Bibliothèque et les sections de la page d'accueil.",
+ "select_liraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l’onglet Bibliothèque et les sections de la page d’accueil.",
"disable_haptic_feedback": "Désactiver le retour haptique",
- "default_quality": "Qualité par défaut",
- "default_playback_speed": "Default Playback Speed",
- "auto_play_next_episode": "Auto-play Next Episode",
- "max_auto_play_episode_count": "Nombre d'épisodes en lecture automatique max",
- "disabled": "Désactivé"
+ "default_quality": "Qualité par défaut"
+
},
"downloads": {
- "downloads_title": "Téléchargements"
- },
- "music": {
- "title": "Music",
- "playback_title": "Playback",
- "playback_description": "Configure how music is played.",
- "prefer_downloaded": "Prefer Downloaded Songs",
- "caching_title": "Caching",
- "caching_description": "Automatically cache upcoming tracks for smoother playback.",
- "lookahead_enabled": "Enable Look-Ahead Caching",
- "lookahead_count": "Tracks to Pre-cache",
- "max_cache_size": "Max Cache Size"
+ "downloads_title": "Téléchargements",
+ "download_method": "Méthode de téléchargement",
+ "remux_max_download": "Téléchargement max remux",
+ "auto_download": "Téléchargement automatique",
+ "optimized_versions_server": "Serveur de versions optimisées",
+ "save_button": "Enregistrer",
+ "optimized_server": "Serveur optimisé",
+ "optimized": "Optimisé",
+ "default": "Par défaut",
+ "optimized_version_hint": "Entrez l'URL du serveur de versions optimisées. L'URL devrait inclure http ou https et optionnellement le port.",
+ "read_more_about_optimized_server": "Lisez-en plus sur le serveur de versions optimisées.",
+ "url": "URL",
+ "server_url_placeholder": "http(s)://domaine.org:port"
},
"plugins": {
"plugins_title": "Plugins",
"jellyseerr": {
"jellyseerr_warning": "Cette intégration est dans ses débuts. Attendez-vous à ce que des choses changent.",
"server_url": "URL du serveur",
- "server_url_hint": "Exemple : http(s)://votre-domaine.url\n(ajouter le port si nécessaire)",
- "server_url_placeholder": "URL de Seerr...",
+ "server_url_hint": "Exemple: http(s)://votre-domaine.url\n(ajouter le port si nécessaire)",
+ "server_url_placeholder": "URL de Jellyseerr...",
"password": "Mot de passe",
"password_placeholder": "Entrez le mot de passe pour l'utilisateur Jellyfin {{username}}",
+ "save_button": "Enregistrer",
+ "clear_button": "Effacer",
"login_button": "Connexion",
"total_media_requests": "Total de demandes de médias",
"movie_quota_limit": "Limite de quota de film",
"movie_quota_days": "Jours de quota de film",
- "tv_quota_limit": "Limite de quota de séries",
- "tv_quota_days": "Jours de quota de séries",
- "reset_jellyseerr_config_button": "Réinitialiser la configuration Seerr",
- "unlimited": "Illimité",
- "plus_n_more": "+{{n}} Plus",
- "order_by": {
- "DEFAULT": "Par défaut",
- "VOTE_COUNT_AND_AVERAGE": "Nombre de votes et moyenne",
- "POPULARITY": "Popularité"
- }
+ "tv_quota_limit": "Limite de quota TV",
+ "tv_quota_days": "Jours de quota TV",
+ "reset_jellyseerr_config_button": "Réinitialiser la configuration Jellyseerr",
+ "unlimited": "Illimité"
},
"marlin_search": {
- "enable_marlin_search": "Activer Marlin Search",
+ "enable_marlin_search": "Activer Marlin Search ",
"url": "URL",
"server_url_placeholder": "http(s)://domaine.org:port",
"marlin_search_hint": "Entrez l'URL du serveur Marlin. L'URL devrait inclure http ou https et optionnellement le port.",
- "read_more_about_marlin": "En savoir plus sur Marlin.",
+ "read_more_about_marlin": "Lisez-en plus sur Marlin.",
"save_button": "Enregistrer",
"toasts": {
- "saved": "Enregistré",
- "refreshed": "Settings refreshed from server"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "streamystats": {
- "enable_streamystats": "Enable Streamystats",
- "disable_streamystats": "Disable Streamystats",
- "enable_search": "Use for Search",
- "url": "URL",
- "server_url_placeholder": "http(s)://streamystats.example.com",
- "streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
- "read_more_about_streamystats": "Read More About Streamystats.",
- "save_button": "Save",
- "save": "Enregistrer",
- "features_title": "Fonctionnalités",
- "home_sections_title": "Sections de la page d´accueil",
- "enable_movie_recommendations": "Recommandations de films",
- "enable_series_recommendations": "Recommandations de séries",
- "enable_promoted_watchlists": "Listes de lecture promues",
- "hide_watchlists_tab": "Masquer l'onglet des listes de lecture",
- "home_sections_hint": "Afficher des recommandations personnalisées et des listes de lecture promues de Streamystats sur la page d’accueil.",
- "recommended_movies": "Films Recommandés",
- "recommended_series": "Séries recommandées",
- "toasts": {
- "saved": "Enregistré",
- "refreshed": "Paramètres actualisés depuis le serveur",
- "disabled": "Streamystats désactivé"
- },
- "refresh_from_server": "Rafraîchir les paramètres depuis le serveur"
- },
- "kefinTweaks": {
- "watchlist_enabler": "Activer l'intégration de notre liste de lecture",
- "watchlist_button": "Activer l'intégration de notre liste de lecture"
+ "saved": "Enregistré"
+ }
}
},
"storage": {
@@ -397,121 +188,84 @@
"app_usage": "App {{usedSpace}}%",
"device_usage": "Appareil {{availableSpace}}%",
"size_used": "{{used}} de {{total}} utilisés",
- "delete_all_downloaded_files": "Supprimer tous les fichiers téléchargés",
- "music_cache_title": "Mise en cache de la musique",
- "music_cache_description": "Mettez automatiquement en cache les chansons au fur et à mesure que vous écoutez pour une lecture plus fluide et une prise en charge hors ligne",
- "enable_music_cache": "Activer le cache sur la musique",
- "clear_music_cache": "Vider le cache de la musique",
- "music_cache_size": "{{size}} mis en cache",
- "music_cache_cleared": "Cache de musique effacé",
- "delete_all_downloaded_songs": "Supprimer toutes les musiques téléchargées",
- "downloaded_songs_size": "{{size}} téléchargé",
- "downloaded_songs_deleted": "Chansons téléchargées supprimées"
+ "delete_all_downloaded_files": "Supprimer tous les fichiers téléchargés"
},
"intro": {
- "title": "Introduction",
"show_intro": "Afficher l'intro",
"reset_intro": "Réinitialiser l'intro"
},
"logs": {
"logs_title": "Journaux",
- "export_logs": "Exporter les journaux",
- "click_for_more_info": "Cliquez pour plus d'informations",
- "level": "Niveau",
- "no_logs_available": "Pas de journaux disponibles",
+ "no_logs_available": "Aucun journal disponible",
"delete_all_logs": "Supprimer tous les journaux"
},
"languages": {
"title": "Langues",
"app_language": "Langue de l'application",
+ "app_language_description": "Sélectionnez la langue de l'application",
"system": "Système"
},
- "toasts": {
+ "toasts":{
"error_deleting_files": "Erreur lors de la suppression des fichiers",
"background_downloads_enabled": "Téléchargements en arrière-plan activés",
- "background_downloads_disabled": "Téléchargements en arrière-plan désactivés"
+ "background_downloads_disabled": "Téléchargements en arrière-plan désactivés",
+ "connected": "Connecté",
+ "could_not_connect": "Impossible de se connecter",
+ "invalid_url": "URL invalide"
}
},
- "sessions": {
- "title": "Appareils actifs",
- "no_active_sessions": "Aucun appareil actif"
- },
"downloads": {
"downloads_title": "Téléchargements",
- "tvseries": "Séries",
+ "tvseries": "Séries TV",
"movies": "Films",
"queue": "File d'attente",
- "other_media": "Autres médias",
"queue_hint": "La file d'attente et les téléchargements seront perdus au redémarrage de l'application",
"no_items_in_queue": "Aucun téléchargement de média dans la file d'attente",
"no_downloaded_items": "Aucun média téléchargé",
"delete_all_movies_button": "Supprimer tous les films",
"delete_all_tvseries_button": "Supprimer toutes les séries",
- "delete_all_button": "Supprimer tous les médias",
- "delete_all_other_media_button": "Supprimer un autre média",
+ "delete_all_button": "Supprimer tout les médias",
"active_download": "Téléchargement actif",
- "no_active_downloads": "Pas de téléchargements actifs",
+ "no_active_downloads": "Aucun téléchargements actifs",
"active_downloads": "Téléchargements actifs",
"new_app_version_requires_re_download": "La nouvelle version de l'application nécessite un nouveau téléchargement",
- "new_app_version_requires_re_download_description": "La nouvelle mise à jour nécessite que le contenu soit téléchargé à nouveau. Veuillez supprimer tout le contenu téléchargé et réessayer.",
+ "new_app_version_requires_re_download_description": "Une nouvelle version de l'application est disponible. Veuillez supprimer tous les téléchargements et redémarrer l'application pour télécharger à nouveau",
"back": "Retour",
"delete": "Supprimer",
"something_went_wrong": "Quelque chose s'est mal passé",
"could_not_get_stream_url_from_jellyfin": "Impossible d'obtenir l'URL du flux depuis Jellyfin",
"eta": "ETA {{eta}}",
+ "methods": "Méthodes",
"toasts": {
- "you_are_not_allowed_to_download_files": "Vous n'êtes pas autorisé à télécharger des fichiers.",
- "deleted_all_movies_successfully": "Tous les films ont été supprimés avec succès !",
+ "you_are_not_allowed_to_download_files": "Vous n'êtes pas autorisé à télécharger des fichiers",
+ "deleted_all_movies_successfully": "Tous les films ont été supprimés avec succès!",
"failed_to_delete_all_movies": "Échec de la suppression de tous les films",
- "deleted_all_tvseries_successfully": "Toutes les séries ont été supprimées avec succès !",
+ "deleted_all_tvseries_successfully": "Toutes les séries ont été supprimées avec succès!",
"failed_to_delete_all_tvseries": "Échec de la suppression de toutes les séries",
- "deleted_media_successfully": "Les autres médias ont été supprimés avec succès !",
- "failed_to_delete_media": "Échec de la suppression d'un autre média",
- "download_deleted": "Téléchargement supprimé",
"download_cancelled": "Téléchargement annulé",
- "could_not_delete_download": "Impossible de supprimer le téléchargement",
- "download_paused": "Téléchargement en pause",
- "could_not_pause_download": "Impossible de mettre en pause le téléchargement",
- "download_resumed": "Reprise du téléchargement",
- "could_not_resume_download": "Impossible de reprendre le téléchargement",
+ "could_not_cancel_download": "Impossible d'annuler le téléchargement",
"download_completed": "Téléchargement terminé",
- "download_failed": "Échec du téléchargement",
+ "download_started_for": "Téléchargement démarré pour {{item}}",
+ "item_is_ready_to_be_downloaded": "{{item}} est prêt à être téléchargé",
+ "download_stated_for_item": "Téléchargement démarré pour {{item}}",
"download_failed_for_item": "Échec du téléchargement pour {{item}} - {{error}}",
"download_completed_for_item": "Téléchargement terminé pour {{item}}",
- "download_started_for_item": "Téléchargement démarré pour {{item}}",
- "failed_to_start_download": "Impossible de démarrer le téléchargement",
- "item_already_downloading": "{{item}} est déjà en cours de téléchargement",
- "all_files_deleted": "Tous les téléchargements supprimés avec succès",
- "files_deleted_by_type": "{{count}} {{type}} supprimé",
+ "queued_item_for_optimization": "{{item}} mis en file d'attente pour l'optimisation",
+ "failed_to_start_download_for_item": "Échec du démarrage du téléchargement pour {{item}}: {{message}}",
+ "server_responded_with_status_code": "Le serveur a répondu avec le code de statut {{statusCode}}",
+ "no_response_received_from_server": "Aucune réponse reçue du serveur",
+ "error_setting_up_the_request": "Erreur lors de la configuration de la demande",
+ "failed_to_start_download_for_item_unexpected_error": "Échec du démarrage du téléchargement pour {{item}}: Erreur inattendue",
"all_files_folders_and_jobs_deleted_successfully": "Tous les fichiers, dossiers et tâches ont été supprimés avec succès",
- "failed_to_clean_cache_directory": "Échec du nettoyage du répertoire de cache",
- "could_not_get_download_url_for_item": "Échec d'obtention de l'URL de téléchargement pour {{itemName}}",
- "go_to_downloads": "Aller aux téléchargements",
- "file_deleted": "{{item}} supprimé"
+ "an_error_occured_while_deleting_files_and_jobs": "Une erreur s'est produite lors de la suppression des fichiers et des tâches",
+ "go_to_downloads": "Aller aux téléchargements"
}
}
},
- "common": {
- "select": "Sélectionner",
- "no_trailer_available": "Aucune bande-annonce disponible",
- "video": "Vidéo",
- "audio": "Audio",
- "subtitle": "Sous-titres",
- "play": "Lecture",
- "none": "Aucun",
- "track": "Suivre",
- "cancel": "Annuler",
- "delete": "Supprimer",
- "ok": "Ok",
- "remove": "Retirer",
- "next": "Suivant",
- "back": "Précédent",
- "continue": "Continuer",
- "verifying": "Vérification..."
- },
"search": {
+ "search_here": "Rechercher ici...",
"search": "Rechercher...",
- "x_items": "{{count}} Médias",
+ "x_items": "{{count}} médias",
"library": "Bibliothèque",
"discover": "Découvrir",
"no_results": "Aucun résultat",
@@ -521,10 +275,6 @@
"episodes": "Épisodes",
"collections": "Collections",
"actors": "Acteurs",
- "artists": "Artistes",
- "albums": "Albums",
- "songs": "Chansons",
- "playlists": "Playlists",
"request_movies": "Demander un film",
"request_series": "Demander une série",
"recently_added": "Ajoutés récemment",
@@ -535,28 +285,29 @@
"movie_genres": "Genres de films",
"upcoming_movies": "Films à venir",
"studios": "Studios",
- "popular_tv": "Séries populaires",
- "tv_genres": "Genres des séries",
- "upcoming_tv": "Séries à venir",
- "networks": "Studios",
- "tmdb_movie_keyword": "Mots-clés de film TMDB",
+ "popular_tv": "TV populaire",
+ "tv_genres": "Genres TV",
+ "upcoming_tv": "TV à venir",
+ "networks": "Réseaux",
+ "tmdb_movie_keyword": "Mot(s)-clé(s) Films TMDB",
"tmdb_movie_genre": "Genre de film TMDB",
- "tmdb_tv_keyword": "Mots-clés de séries TMDB",
- "tmdb_tv_genre": "Genre de séries TMDB",
+ "tmdb_tv_keyword": "Mot(s)-clé(s) TV TMDB",
+ "tmdb_tv_genre": "Genre TV TMDB",
"tmdb_search": "Recherche TMDB",
"tmdb_studio": "Studio TMDB",
"tmdb_network": "Réseau TMDB",
"tmdb_movie_streaming_services": "Services de streaming de films TMDB",
- "tmdb_tv_streaming_services": "Services de streaming de séries TMDB"
+ "tmdb_tv_streaming_services": "Services de streaming TV TMDB"
},
"library": {
+ "no_items_found": "Aucun média trouvé",
"no_results": "Aucun résultat",
"no_libraries_found": "Aucune bibliothèque trouvée",
"item_types": {
- "movies": "Films",
- "series": "Séries",
- "boxsets": "Coffrets ",
- "items": "Médias"
+ "movies": "films",
+ "series": "séries",
+ "boxsets": "coffrets",
+ "items": "médias"
},
"options": {
"display": "Affichage",
@@ -572,7 +323,6 @@
"genres": "Genres",
"years": "Années",
"sort_by": "Trier par",
- "filter_by": "Filter By",
"sort_order": "Ordre de tri",
"tags": "Tags"
}
@@ -583,9 +333,7 @@
"episodes": "Épisodes",
"videos": "Vidéos",
"boxsets": "Coffrets",
- "playlists": "Playlists",
- "noDataTitle": "Pas encore de favoris",
- "noData": "Marquez des éléments comme favoris pour les voir apparaître ici pour un accès rapide."
+ "playlists": "Listes de lecture"
},
"custom_links": {
"no_links": "Aucuns liens"
@@ -593,26 +341,23 @@
"player": {
"error": "Erreur",
"failed_to_get_stream_url": "Échec de l'obtention de l'URL du flux",
- "an_error_occured_while_playing_the_video": "Une erreur s’est produite lors de la lecture de la vidéo. Vérifiez les journaux dans les paramètres.",
- "client_error": "Erreur du client",
+ "an_error_occured_while_playing_the_video": "Une erreur s'est produite lors de la lecture de la vidéo",
+ "client_error": "Erreur client",
"could_not_create_stream_for_chromecast": "Impossible de créer un flux sur la Chromecast",
- "message_from_server": "Message du serveur : {{message}}",
+ "message_from_server": "Message du serveur: {{message}}",
+ "video_has_finished_playing": "La vidéo a fini de jouer!",
+ "no_video_source": "Aucune source vidéo...",
"next_episode": "Épisode suivant",
"refresh_tracks": "Rafraîchir les pistes",
- "audio_tracks": "Pistes audio :",
- "playback_state": "État de lecture :",
- "index": "Index :",
- "continue_watching": "Continuer à regarder",
- "go_back": "Retour",
- "downloaded_file_title": "Ce fichier est téléchargé",
- "downloaded_file_message": "Voulez-vous lire le fichier téléchargé ?",
- "downloaded_file_yes": "Oui",
- "downloaded_file_no": "Non",
- "downloaded_file_cancel": "Annuler"
+ "subtitle_tracks": "Pistes de sous-titres:",
+ "audio_tracks": "Pistes audio:",
+ "playback_state": "État de lecture:",
+ "no_data_available": "Aucune donnée disponible",
+ "index": "Index:"
},
"item_card": {
"next_up": "À suivre",
- "no_items_to_display": "Aucuns médias à afficher",
+ "no_items_to_display": "Aucun médias à afficher",
"cast_and_crew": "Distribution et équipe",
"series": "Séries",
"seasons": "Saisons",
@@ -621,10 +366,9 @@
"overview": "Aperçu",
"more_with": "Plus avec {{name}}",
"similar_items": "Médias similaires",
- "no_similar_items_found": "Aucuns médias similaires trouvés",
+ "no_similar_items_found": "Aucun média similaire trouvé",
"video": "Vidéo",
"more_details": "Plus de détails",
- "media_options": "Options média",
"quality": "Qualité",
"audio": "Audio",
"subtitles": "Sous-titres",
@@ -639,13 +383,15 @@
"download_episode": "Télécharger l'épisode",
"download_movie": "Télécharger le film",
"download_x_item": "Télécharger {{item_count}} médias",
- "download_unwatched_only": "Non visionné uniquement",
- "download_button": "Télécharger"
+ "download_button": "Télécharger",
+ "using_optimized_server": "Avec le serveur optimisées",
+ "using_default_method": "Avec la méthode par défaut"
}
},
"live_tv": {
"next": "Suivant",
"previous": "Précédent",
+ "live_tv": "TV en direct",
"coming_soon": "Bientôt",
"on_now": "En ce moment",
"shows": "Émissions",
@@ -654,23 +400,23 @@
"for_kids": "Pour enfants",
"news": "Actualités"
},
- "jellyseerr": {
+ "jellyseerr":{
"confirm": "Confirmer",
"cancel": "Annuler",
"yes": "Oui",
- "whats_wrong": "Quel est le problème ?",
+ "whats_wrong": "Quel est le problème?",
"issue_type": "Type de problème",
"select_an_issue": "Sélectionnez un problème",
- "types": "Types de fichiers",
+ "types": "Types",
"describe_the_issue": "(optionnel) Décrivez le problème...",
"submit_button": "Soumettre",
"report_issue_button": "Signaler un problème",
"request_button": "Demander",
- "are_you_sure_you_want_to_request_all_seasons": "Êtes-vous sûr de vouloir demander toutes les saisons ?",
+ "are_you_sure_you_want_to_request_all_seasons": "Êtes-vous sûr de vouloir demander toutes les saisons?",
"failed_to_login": "Échec de la connexion",
"cast": "Distribution",
"details": "Détails",
- "status": "Statuts",
+ "status": "Statut",
"original_title": "Titre original",
"series_type": "Type de série",
"release_dates": "Dates de sortie",
@@ -688,27 +434,19 @@
"tags": "Tags",
"quality_profile": "Profil de qualité",
"root_folder": "Dossier racine",
- "season_all": "Saison (Tous)",
+ "season_x": "Saison {{seasons}}",
"season_number": "Saison {{season_number}}",
- "number_episodes": "{{episode_number}} Épisodes",
+ "number_episodes": "{{episode_number}} épisodes",
"born": "Né(e) le",
"appearances": "Apparences",
- "approve": "Valider",
- "decline": "Refuser",
- "requested_by": "Demandé par {{user}}",
- "unknown_user": "Utilisateur inconnu",
"toasts": {
- "jellyseer_does_not_meet_requirements": "Seerr ne répond pas aux exigences ! Veuillez mettre à jour au moins vers la version 2.0.0.",
- "jellyseerr_test_failed": "Le test Seerr a échoué. Veuillez réessayer.",
- "failed_to_test_jellyseerr_server_url": "Échec du test de l'URL du serveur Seerr",
- "issue_submitted": "Problème soumis !",
- "requested_item": "{{item}}} demandé !",
- "you_dont_have_permission_to_request": "Vous n'avez pas la permission de demander !",
- "something_went_wrong_requesting_media": "Quelque chose s'est mal passé en demandant le média !",
- "request_approved": "Demande approuvée !",
- "request_declined": "Demande déclinée !",
- "failed_to_approve_request": "Échec d'approbation de la demande",
- "failed_to_decline_request": "Échec du refus de la demande"
+ "jellyseer_does_not_meet_requirements": "Jellyseer ne répond pas aux exigences! Veuillez mettre à jour au moins vers la version 2.0.0.",
+ "jellyseerr_test_failed": "Échec du test de Jellyseerr",
+ "failed_to_test_jellyseerr_server_url": "Échec du test de l'URL du serveur Jellyseerr",
+ "issue_submitted": "Problème soumis!",
+ "requested_item": "{{item}}} demandé!",
+ "you_dont_have_permission_to_request": "Vous n'avez pas la permission de demander {{item}}",
+ "something_went_wrong_requesting_media": "Quelque chose s'est mal passé en demandant le média!"
}
},
"tabs": {
@@ -717,129 +455,5 @@
"library": "Bibliothèque",
"custom_links": "Liens personnalisés",
"favorites": "Favoris"
- },
- "music": {
- "title": "Music",
- "tabs": {
- "suggestions": "Suggestions",
- "albums": "Albums",
- "artists": "Artists",
- "playlists": "Playlists",
- "tracks": "tracks"
- },
- "filters": {
- "all": "All"
- },
- "recently_added": "Recently Added",
- "recently_played": "Recently Played",
- "frequently_played": "Frequently Played",
- "explore": "Explore",
- "top_tracks": "Top Tracks",
- "play": "Play",
- "shuffle": "Shuffle",
- "play_top_tracks": "Play Top Tracks",
- "no_suggestions": "No suggestions available",
- "no_albums": "No albums found",
- "no_artists": "No artists found",
- "no_playlists": "No playlists found",
- "album_not_found": "Album not found",
- "artist_not_found": "Artist not found",
- "playlist_not_found": "Playlist not found",
- "track_options": {
- "play_next": "Play Next",
- "add_to_queue": "Add to Queue",
- "add_to_playlist": "Add to Playlist",
- "download": "Download",
- "downloaded": "Downloaded",
- "downloading": "Downloading...",
- "cached": "Cached",
- "delete_download": "Delete Download",
- "delete_cache": "Remove from Cache",
- "go_to_artist": "Go to Artist",
- "go_to_album": "Go to Album",
- "add_to_favorites": "Add to Favorites",
- "remove_from_favorites": "Remove from Favorites",
- "remove_from_playlist": "Remove from Playlist"
- },
- "playlists": {
- "create_playlist": "Create Playlist",
- "playlist_name": "Playlist Name",
- "enter_name": "Enter playlist name",
- "create": "Create",
- "search_playlists": "Search playlists...",
- "added_to": "Added to {{name}}",
- "added": "Added to playlist",
- "removed_from": "Removed from {{name}}",
- "removed": "Removed from playlist",
- "created": "Playlist created",
- "create_new": "Create New Playlist",
- "failed_to_add": "Failed to add to playlist",
- "failed_to_remove": "Failed to remove from playlist",
- "failed_to_create": "Failed to create playlist",
- "delete_playlist": "Delete Playlist",
- "delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "deleted": "Playlist deleted",
- "failed_to_delete": "Failed to delete playlist"
- },
- "sort": {
- "title": "Sort By",
- "alphabetical": "Alphabetical",
- "date_created": "Date Created"
- }
- },
- "watchlists": {
- "title": "Watchlists",
- "my_watchlists": "My Watchlists",
- "public_watchlists": "Public Watchlists",
- "create_title": "Create Watchlist",
- "edit_title": "Edit Watchlist",
- "create_button": "Create Watchlist",
- "save_button": "Save Changes",
- "delete_button": "Delete",
- "remove_button": "Remove",
- "cancel_button": "Cancel",
- "name_label": "Name",
- "name_placeholder": "Enter watchlist name",
- "description_label": "Description",
- "description_placeholder": "Enter description (optional)",
- "is_public_label": "Public Watchlist",
- "is_public_description": "Allow others to view this watchlist",
- "allowed_type_label": "Content Type",
- "sort_order_label": "Default Sort Order",
- "empty_title": "No Watchlists",
- "empty_description": "Create your first watchlist to start organizing your media",
- "empty_watchlist": "This watchlist is empty",
- "empty_watchlist_hint": "Add items from your library to this watchlist",
- "not_configured_title": "Streamystats Not Configured",
- "not_configured_description": "Configure Streamystats in settings to use watchlists",
- "go_to_settings": "Go to Settings",
- "add_to_watchlist": "Add to Watchlist",
- "remove_from_watchlist": "Remove from Watchlist",
- "select_watchlist": "Select Watchlist",
- "create_new": "Create New Watchlist",
- "item": "item",
- "items": "items",
- "public": "Public",
- "private": "Private",
- "you": "You",
- "by_owner": "By another user",
- "not_found": "Watchlist not found",
- "delete_confirm_title": "Delete Watchlist",
- "delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "remove_item_title": "Remove from Watchlist",
- "remove_item_message": "Remove \"{{name}}\" from this watchlist?",
- "loading": "Loading watchlists...",
- "no_compatible_watchlists": "No compatible watchlists",
- "create_one_first": "Create a watchlist that accepts this content type"
- },
- "playback_speed": {
- "title": "Playback Speed",
- "apply_to": "Apply To",
- "speed": "Speed",
- "scope": {
- "media": "This media only",
- "show": "This show",
- "all": "All media (default)"
- }
}
}
diff --git a/translations/it.json b/translations/it.json
index a6de11c1..314fc233 100644
--- a/translations/it.json
+++ b/translations/it.json
@@ -1,845 +1,458 @@
{
- "login": {
- "username_required": "Nome utente è obbligatorio",
- "error_title": "Errore",
- "login_title": "Accesso",
- "login_to_title": "Accedi a",
- "username_placeholder": "Nome utente",
- "password_placeholder": "Password",
- "login_button": "Accedi",
- "quick_connect": "Connessione Rapida",
- "enter_code_to_login": "Inserire {{code}} per accedere",
- "failed_to_initiate_quick_connect": "Impossibile avviare la Connessione Rapida",
- "got_it": "Capito",
- "connection_failed": "Connessione fallita",
- "could_not_connect_to_server": "Impossibile connettersi al server. Controllare l'URL e la connessione di rete.",
- "an_unexpected_error_occured": "Si è verificato un errore inaspettato",
- "change_server": "Cambiare il server",
- "invalid_username_or_password": "Nome utente o password non validi",
- "user_does_not_have_permission_to_log_in": "L'utente non ha il permesso di accedere",
- "server_is_taking_too_long_to_respond_try_again_later": "Il server sta impiegando troppo tempo per rispondere, riprovare più tardi",
- "server_received_too_many_requests_try_again_later": "Il server ha ricevuto troppe richieste, riprovare più tardi.",
- "there_is_a_server_error": "Si è verificato un errore del server",
- "an_unexpected_error_occured_did_you_enter_the_correct_url": "Si è verificato un errore imprevisto. L'URL del server è stato inserito correttamente?",
- "too_old_server_text": "Scoperto Server Jellyfin non supportato",
- "too_old_server_description": "Aggiorna Jellyfin all'ultima versione"
- },
- "server": {
- "enter_url_to_jellyfin_server": "Inserisci l'URL del tuo server Jellyfin",
- "server_url_placeholder": "http(s)://tuo-server.com",
- "connect_button": "Connetti",
- "previous_servers": "server precedente",
- "clear_button": "Cancella",
- "swipe_to_remove": "Swipe to remove",
- "search_for_local_servers": "Ricerca dei server locali",
- "searching": "Cercando...",
- "servers": "Server",
- "saved": "Saved",
- "session_expired": "Session Expired",
- "please_login_again": "Your saved session has expired. Please log in again.",
- "remove_saved_login": "Remove Saved Login",
- "remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
- "accounts_count": "{{count}} accounts",
- "select_account": "Select Account",
- "add_account": "Add Account",
- "remove_account_description": "This will remove the saved credentials for {{username}}."
- },
- "save_account": {
- "title": "Save Account",
- "save_for_later": "Save this account",
- "security_option": "Security Option",
- "no_protection": "No protection",
- "no_protection_desc": "Quick login without authentication",
- "pin_code": "PIN code",
- "pin_code_desc": "4-digit PIN required when switching",
- "password": "Re-enter password",
- "password_desc": "Password required when switching",
- "save_button": "Save",
- "cancel_button": "Cancel"
- },
- "pin": {
- "enter_pin": "Enter PIN",
- "enter_pin_for": "Enter PIN for {{username}}",
- "enter_4_digits": "Enter 4 digits",
- "invalid_pin": "Invalid PIN",
- "setup_pin": "Set Up PIN",
- "confirm_pin": "Confirm PIN",
- "pins_dont_match": "PINs don't match",
- "forgot_pin": "Forgot PIN?",
- "forgot_pin_desc": "Your saved credentials will be removed"
- },
- "password": {
- "enter_password": "Enter Password",
- "enter_password_for": "Enter password for {{username}}",
- "invalid_password": "Invalid password"
- },
- "home": {
- "checking_server_connection": "Controllo connessione server...",
- "no_internet": "Nessun Internet",
- "no_items": "Nessun oggetto",
- "no_internet_message": "Non c'è da preoccuparsi, è ancora possibile guardare\n i contenuti scaricati.",
- "checking_server_connection_message": "Controllo della connessione al server",
- "go_to_downloads": "Vai agli elementi scaricati",
- "retry": "Riprova",
- "server_unreachable": "Server Non Raggiunto",
- "server_unreachable_message": "Impossibile raggiungere il server.\nSi prega di controllare la connessione di rete.",
- "oops": "Ops!",
- "error_message": "Qualcosa è andato storto. \nEffetturare il logout e riaccedere.",
- "continue_watching": "Continua a guardare",
- "next_up": "Prossimo",
- "continue_and_next_up": "Continue & Next Up",
- "recently_added_in": "Aggiunti di recente a {{libraryName}}",
- "suggested_movies": "Film consigliati",
- "suggested_episodes": "Episodi consigliati",
- "intro": {
- "welcome_to_streamyfin": "Benvenuto a Streamyfin",
- "a_free_and_open_source_client_for_jellyfin": "Un client gratuito e open-source per Jellyfin.",
- "features_title": "Funzioni",
- "features_description": "Streamyfin dispone di numerose funzioni e si integra con un'ampia gamma di software che si possono trovare nel menu delle impostazioni:",
- "jellyseerr_feature_description": "Connettetevi alla vostra istanza Jellyseerr e richiedete i film direttamente nell'app.",
- "downloads_feature_title": "Scaricamento",
- "downloads_feature_description": "Scaricate film e serie tv da vedere offline. Utilizzate il metodo predefinito o installate il server di ottimizzazione per scaricare i file in background.",
- "chromecast_feature_description": "Trasmettete film e serie tv ai vostri dispositivi Chromecast.",
- "centralised_settings_plugin_title": "Impostazioni dei Plugin Centralizzate",
- "centralised_settings_plugin_description": "Configura le impostazioni da una posizione centralizzata sul server Jellyfin. Tutte le impostazioni del client per tutti gli utenti saranno sincronizzate automaticamente.",
- "done_button": "Fatto",
- "go_to_settings_button": "Vai alle impostazioni",
- "read_more": "Leggi di più"
+ "login": {
+ "username_required": "Nome utente è obbligatorio",
+ "error_title": "Errore",
+ "login_title": "Accesso",
+ "login_to_title": "Accedi a",
+ "username_placeholder": "Nome utente",
+ "password_placeholder": "Password",
+ "login_button": "Accedi",
+ "quick_connect": "Connessione Rapida",
+ "enter_code_to_login": "Inserire {{code}} per accedere",
+ "failed_to_initiate_quick_connect": "Impossibile avviare la Connessione Rapida",
+ "got_it": "Capito",
+ "connection_failed": "Connessione fallita",
+ "could_not_connect_to_server": "Impossibile connettersi al server. Controllare l'URL e la connessione di rete.",
+ "an_unexpected_error_occured": "Si è verificato un errore inaspettato",
+ "change_server": "Cambiare il server",
+ "invalid_username_or_password": "Nome utente o password non validi",
+ "user_does_not_have_permission_to_log_in": "L'utente non ha il permesso di accedere",
+ "server_is_taking_too_long_to_respond_try_again_later": "Il server sta impiegando troppo tempo per rispondere, riprovare più tardi",
+ "server_received_too_many_requests_try_again_later": "Il server ha ricevuto troppe richieste, riprovare più tardi.",
+ "there_is_a_server_error": "Si è verificato un errore del server",
+ "an_unexpected_error_occured_did_you_enter_the_correct_url": "Si è verificato un errore imprevisto. L'URL del server è stato inserito correttamente?"
},
- "settings": {
- "settings_title": "Impostazioni",
- "log_out_button": "Esci",
- "categories": {
- "title": "Categorie"
+ "server": {
+ "enter_url_to_jellyfin_server": "Inserisci l'URL del tuo server Jellyfin",
+ "server_url_placeholder": "http(s)://tuo-server.com",
+ "connect_button": "Connetti",
+ "previous_servers": "server precedente",
+ "clear_button": "Cancella",
+ "search_for_local_servers": "Ricerca dei server locali",
+ "searching": "Cercando...",
+ "servers": "Servers"
+ },
+ "home": {
+ "no_internet": "Nessun Internet",
+ "no_items": "Nessun oggetto",
+ "no_internet_message": "Non c'è da preoccuparsi, è ancora possibile guardare\n i contenuti scaricati.",
+ "go_to_downloads": "Vai agli elementi scaricati",
+ "oops": "Oops!",
+ "error_message": "Qualcosa è andato storto. \nEffetturare il logout e riaccedere.",
+ "continue_watching": "Continua a guardare",
+ "next_up": "Prossimo",
+ "recently_added_in": "Aggiunti di recente a {{libraryName}}",
+ "suggested_movies": "Film consigliati",
+ "suggested_episodes": "Episodi consigliati",
+ "intro": {
+ "welcome_to_streamyfin": "Benvenuto a Streamyfin",
+ "a_free_and_open_source_client_for_jellyfin": "Un client gratuito e open-source per Jellyfin.",
+ "features_title": "Funzioni",
+ "features_description": "Streamyfin dispone di numerose funzioni e si integra con un'ampia gamma di software che si possono trovare nel menu delle impostazioni:",
+ "jellyseerr_feature_description": "Connettetevi alla vostra istanza Jellyseerr e richiedete i film direttamente nell'app.",
+ "downloads_feature_title": "Scaricamento",
+ "downloads_feature_description": "Scaricate film e serie tv da vedere offline. Utilizzate il metodo predefinito o installate il server di ottimizzazione per scaricare i file in background.",
+ "chromecast_feature_description": "Trasmettete film e serie tv ai vostri dispositivi Chromecast.",
+ "centralised_settings_plugin_title": "Impostazioni dei Plugin Centralizzate",
+ "centralised_settings_plugin_description": "Configura le impostazioni da una posizione centralizzata sul server Jellyfin. Tutte le impostazioni del client per tutti gli utenti saranno sincronizzate automaticamente.",
+ "done_button": "Fatto",
+ "go_to_settings_button": "Vai alle impostazioni",
+ "read_more": "Leggi di più"
},
- "playback_controls": {
- "title": "Riproduzione & Controlli"
- },
- "audio_subtitles": {
- "title": "Audio & Sottotitoli"
- },
- "appearance": {
- "title": "Aspetto",
- "merge_next_up_continue_watching": "Merge Continue Watching & Next Up",
- "hide_remote_session_button": "Hide Remote Session Button"
- },
- "network": {
- "title": "Network",
- "local_network": "Local Network",
- "auto_switch_enabled": "Auto-switch when at home",
- "auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
- "local_url": "Local URL",
- "local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
- "local_url_placeholder": "http://192.168.1.100:8096",
- "home_wifi_networks": "Home WiFi Networks",
- "add_current_network": "Add \"{{ssid}}\"",
- "not_connected_to_wifi": "Not connected to WiFi",
- "no_networks_configured": "No networks configured",
- "add_network_hint": "Add your home WiFi network to enable auto-switching",
- "current_wifi": "Current WiFi",
- "using_url": "Using",
- "local": "Local URL",
- "remote": "Remote URL",
- "not_connected": "Not connected",
- "current_server": "Current Server",
- "remote_url": "Remote URL",
- "active_url": "Active URL",
- "not_configured": "Not configured",
- "network_added": "Network added",
- "network_already_added": "Network already added",
- "no_wifi_connected": "Not connected to WiFi",
- "permission_denied": "Location permission denied",
- "permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
- },
- "user_info": {
- "user_info_title": "Info utente",
- "user": "Utente",
- "server": "Server",
- "token": "Token",
- "app_version": "Versione dell'App"
- },
- "quick_connect": {
- "quick_connect_title": "Connessione Rapida",
- "authorize_button": "Autorizza Connessione Rapida",
- "enter_the_quick_connect_code": "Inserisci il codice per la Connessione Rapida...",
- "success": "Successo",
- "quick_connect_autorized": "Connessione Rapida autorizzata",
- "error": "Errore",
- "invalid_code": "Codice invalido",
- "authorize": "Autorizza"
- },
- "media_controls": {
- "media_controls_title": "Controlli multimediali",
- "forward_skip_length": "Lunghezza del salto in avanti",
- "rewind_length": "Lunghezza del riavvolgimento",
- "seconds_unit": "secondi"
- },
- "gesture_controls": {
- "gesture_controls_title": "Controlli Gesture",
- "horizontal_swipe_skip": "Scorrimento orizzontale per saltare",
- "horizontal_swipe_skip_description": "Scorri verso sinistra/destra quando i comandi sono nascosti per saltare",
- "left_side_brightness": "Controllo Luminosità Laterale Sinistra",
- "left_side_brightness_description": "Scorri verso l'alto/verso il basso sul lato sinistro per regolare la luminosità",
- "right_side_volume": "Controllo Volume Laterale Destro",
- "right_side_volume_description": "Scorri verso l'alto/verso il basso per regolare il volume",
- "hide_volume_slider": "Hide Volume Slider",
- "hide_volume_slider_description": "Hide the volume slider in the video player",
- "hide_brightness_slider": "Hide Brightness Slider",
- "hide_brightness_slider_description": "Hide the brightness slider in the video player"
- },
- "audio": {
- "audio_title": "Audio",
- "set_audio_track": "Imposta la traccia audio dall'elemento precedente",
- "audio_language": "Lingua Audio",
- "audio_hint": "Scegli la lingua audio predefinita.",
- "none": "Nessuno",
- "language": "Lingua",
- "transcode_mode": {
- "title": "Audio Transcoding",
- "description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
- "auto": "Auto",
- "stereo": "Force Stereo",
- "5_1": "Allow 5.1",
- "passthrough": "Passthrough"
- }
- },
- "subtitles": {
- "subtitle_title": "Sottotitoli",
- "subtitle_hint": "Configura la preferenza dei sottotitoli.",
- "subtitle_language": "Lingua dei sottotitoli",
- "subtitle_mode": "Modalità dei sottotitoli",
- "set_subtitle_track": "Imposta la traccia dei sottotitoli dall'elemento precedente",
- "subtitle_size": "Dimensione dei sottotitoli",
- "none": "Nessuno",
- "language": "Lingua",
- "loading": "Caricamento",
- "modes": {
- "Default": "Predefinito",
- "Smart": "Intelligente",
- "Always": "Sempre",
- "None": "Nessuno",
- "OnlyForced": "Solo forzati"
+ "settings": {
+ "settings_title": "Impostazioni",
+ "log_out_button": "Esci",
+ "user_info": {
+ "user_info_title": "Info utente",
+ "user": "Utente",
+ "server": "Server",
+ "token": "Token",
+ "app_version": "Versione dell'App"
},
- "text_color": "Colore Del Testo",
- "background_color": "Colore Di Sfondo",
- "outline_color": "Colore Contorno",
- "outline_thickness": "Spessore Contorno",
- "background_opacity": "Opacità Dello Sfondo",
- "outline_opacity": "Opacità Contorno",
- "bold_text": "Bold Text",
- "colors": {
- "Black": "Nero",
- "Gray": "Grigio",
- "Silver": "Argento",
- "White": "Bianco",
- "Maroon": "Maroon",
- "Red": "Rosso",
- "Fuchsia": "Fuchsia",
- "Yellow": "Giallo",
- "Olive": "Olive",
- "Green": "Verde",
- "Teal": "Teal",
- "Lime": "Lime",
- "Purple": "Viola",
- "Navy": "Marina",
- "Blue": "Blu",
- "Aqua": "Aqua"
+ "quick_connect": {
+ "quick_connect_title": "Connessione Rapida",
+ "authorize_button": "Autorizza Connessione Rapida",
+ "enter_the_quick_connect_code": "Inserisci il codice per la Connessione Rapida...",
+ "success": "Successo",
+ "quick_connect_autorized": "Connessione Rapida autorizzata",
+ "error": "Errore",
+ "invalid_code": "Codice invalido",
+ "authorize": "Autorizza"
},
- "thickness": {
- "None": "Nessuno",
- "Thin": "Sottile",
- "Normal": "Normale",
- "Thick": "Spessa"
+ "media_controls": {
+ "media_controls_title": "Controlli multimediali",
+ "forward_skip_length": "Lunghezza del salto in avanti",
+ "rewind_length": "Lunghezza del riavvolgimento",
+ "seconds_unit": "s"
},
- "subtitle_color": "Subtitle Color",
- "subtitle_background_color": "Background Color",
- "subtitle_font": "Subtitle Font",
- "ksplayer_title": "KSPlayer Settings",
- "hardware_decode": "Hardware Decoding",
- "hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues."
- },
- "vlc_subtitles": {
- "title": "VLC Subtitle Settings",
- "hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
- "text_color": "Text Color",
- "background_color": "Background Color",
- "background_opacity": "Background Opacity",
- "outline_color": "Outline Color",
- "outline_opacity": "Outline Opacity",
- "outline_thickness": "Outline Thickness",
- "bold": "Bold Text",
- "margin": "Bottom Margin"
- },
- "video_player": {
- "title": "Video Player",
- "video_player": "Video Player",
- "video_player_description": "Choose which video player to use on iOS.",
- "ksplayer": "KSPlayer",
- "vlc": "VLC"
- },
- "other": {
- "other_title": "Altro",
- "video_orientation": "Orientamento del video",
- "orientation": "Orientamento",
- "orientations": {
- "DEFAULT": "Predefinito",
- "ALL": "Tutto",
- "PORTRAIT": "Verticale",
- "PORTRAIT_UP": "Verticale sopra",
- "PORTRAIT_DOWN": "Verticale sotto",
- "LANDSCAPE": "Orizzontale",
- "LANDSCAPE_LEFT": "Orizzontale sinitra",
- "LANDSCAPE_RIGHT": "Orizzontale destra",
- "OTHER": "Altro",
- "UNKNOWN": "Sconosciuto"
+ "audio": {
+ "audio_title": "Audio",
+ "set_audio_track": "Imposta la traccia audio dall'elemento precedente",
+ "audio_language": "Lingua Audio",
+ "audio_hint": "Scegli la lingua audio predefinita.",
+ "none": "Nessuno",
+ "language": "Lingua"
},
- "safe_area_in_controls": "Area sicura per i controlli",
- "video_player": "Video player",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Sperimentale + PiP)"
- },
- "show_custom_menu_links": "Mostra i link del menu personalizzato",
- "show_large_home_carousel": "Mostra Carosello Grande nella Home (beta)",
- "hide_libraries": "Nascondi Librerie",
- "select_liraries_you_want_to_hide": "Selezionate le librerie che volete nascondere dalla scheda Libreria e dalle sezioni della pagina iniziale.",
- "disable_haptic_feedback": "Disabilita il feedback aptico",
- "default_quality": "Qualità predefinita",
- "default_playback_speed": "Default Playback Speed",
- "auto_play_next_episode": "Auto-play Next Episode",
- "max_auto_play_episode_count": "Numero Massimo Di Episodi Riproduzione Automatica",
- "disabled": "Disabilitato"
- },
- "downloads": {
- "downloads_title": "Scaricamento"
- },
- "music": {
- "title": "Music",
- "playback_title": "Playback",
- "playback_description": "Configure how music is played.",
- "prefer_downloaded": "Prefer Downloaded Songs",
- "caching_title": "Caching",
- "caching_description": "Automatically cache upcoming tracks for smoother playback.",
- "lookahead_enabled": "Enable Look-Ahead Caching",
- "lookahead_count": "Tracks to Pre-cache",
- "max_cache_size": "Max Cache Size"
- },
- "plugins": {
- "plugins_title": "Plugin",
- "jellyseerr": {
- "jellyseerr_warning": "Questa integrazione è in fase iniziale. Aspettarsi cambiamenti.",
- "server_url": "URL del Server",
- "server_url_hint": "Esempio: http(s)://tuo-host.url\n(aggiungere la porta se richiesto)",
- "server_url_placeholder": "URL di Jellyseerr...",
- "password": "Password",
- "password_placeholder": "Inserire la password per l'utente {{username}} di Jellyfin",
- "login_button": "Accedi",
- "total_media_requests": "Totale di richieste di media",
- "movie_quota_limit": "Limite di quota per i film",
- "movie_quota_days": "Giorni di quota per i film",
- "tv_quota_limit": "Limite di quota per le serie TV",
- "tv_quota_days": "Giorni di quota per le serie TV",
- "reset_jellyseerr_config_button": "Ripristina la configurazione di Jellyseerr",
- "unlimited": "Illimitato",
- "plus_n_more": "+{{n}} more",
- "order_by": {
- "DEFAULT": "Predefinito",
- "VOTE_COUNT_AND_AVERAGE": "Conteggio delle votazioni e media",
- "POPULARITY": "Popolarità"
+ "subtitles": {
+ "subtitle_title": "Sottotitoli",
+ "subtitle_language": "Lingua dei sottotitoli",
+ "subtitle_mode": "Modalità dei sottotitoli",
+ "set_subtitle_track": "Imposta la traccia dei sottotitoli dall'elemento precedente",
+ "subtitle_size": "Dimensione dei sottotitoli",
+ "subtitle_hint": "Configura la preferenza dei sottotitoli.",
+ "none": "Nessuno",
+ "language": "Lingua",
+ "loading": "Caricamento",
+ "modes": {
+ "Default": "Predefinito",
+ "Smart": "Intelligente",
+ "Always": "Sempre",
+ "None": "Nessuno",
+ "OnlyForced": "Solo forzati"
}
},
- "marlin_search": {
- "enable_marlin_search": "Abilita la ricerca Marlin ",
- "url": "URL",
- "server_url_placeholder": "http(s)://dominio.org:porta",
- "marlin_search_hint": "Inserire l'URL del server Marlin. L'URL deve includere http o https e, facoltativamente, la porta.",
- "read_more_about_marlin": "Leggi di più su Marlin.",
+ "other": {
+ "other_title": "Altro",
+ "auto_rotate": "Rotazione automatica",
+ "video_orientation": "Orientamento del video",
+ "orientation": "Orientamento",
+ "orientations": {
+ "DEFAULT": "Predefinito",
+ "ALL": "Tutto",
+ "PORTRAIT": "Verticale",
+ "PORTRAIT_UP": "Verticale sopra",
+ "PORTRAIT_DOWN": "Verticale sotto",
+ "LANDSCAPE": "Orizzontale",
+ "LANDSCAPE_LEFT": "Orizzontale sinitra",
+ "LANDSCAPE_RIGHT": "Orizzontale destra",
+ "OTHER": "Altro",
+ "UNKNOWN": "Sconosciuto"
+ },
+ "safe_area_in_controls": "Area sicura per i controlli",
+ "show_custom_menu_links": "Mostra i link del menu personalizzato",
+ "hide_libraries": "Nascondi Librerie",
+ "select_liraries_you_want_to_hide": "Selezionate le librerie che volete nascondere dalla scheda Libreria e dalle sezioni della pagina iniziale.",
+ "disable_haptic_feedback": "Disabilita il feedback aptico",
+ "default_quality": "Qualità predefinita"
+ },
+ "downloads": {
+ "downloads_title": "Scaricamento",
+ "download_method": "Metodo per lo scaricamento",
+ "remux_max_download": "Numero di Remux da scaricare al massimo",
+ "auto_download": "Scaricamento automatico",
+ "optimized_versions_server": "Versioni del server di ottimizzazione",
"save_button": "Salva",
- "toasts": {
- "saved": "Salvato",
- "refreshed": "Settings refreshed from server"
- },
- "refresh_from_server": "Refresh Settings from Server"
+ "optimized_server": "Server di ottimizzazione",
+ "optimized": "Ottimizzato",
+ "default": "Predefinito",
+ "optimized_version_hint": "Inserire l'URL del server di ottimizzazione. L'URL deve includere http o https e, facoltativamente, la porta.",
+ "read_more_about_optimized_server": "Per saperne di più sul server di ottimizzazione.",
+ "url":"URL",
+ "server_url_placeholder": "http(s)://dominio.org:porta"
},
- "streamystats": {
- "enable_streamystats": "Enable Streamystats",
- "disable_streamystats": "Disable Streamystats",
- "enable_search": "Use for Search",
- "url": "URL",
- "server_url_placeholder": "http(s)://streamystats.example.com",
- "streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
- "read_more_about_streamystats": "Read More About Streamystats.",
- "save_button": "Save",
- "save": "Save",
- "features_title": "Features",
- "home_sections_title": "Home Sections",
- "enable_movie_recommendations": "Movie Recommendations",
- "enable_series_recommendations": "Series Recommendations",
- "enable_promoted_watchlists": "Promoted Watchlists",
- "hide_watchlists_tab": "Hide Watchlists Tab",
- "home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
- "recommended_movies": "Recommended Movies",
- "recommended_series": "Recommended Series",
- "toasts": {
- "saved": "Saved",
- "refreshed": "Settings refreshed from server",
- "disabled": "Streamystats disabled"
+ "plugins": {
+ "plugins_title": "Plugin",
+ "jellyseerr": {
+ "jellyseerr_warning": "Questa integrazione è in fase iniziale. Aspettarsi cambiamenti.",
+ "server_url": "URL del Server",
+ "server_url_hint": "Esempio: http(s)://tuo-host.url\n(aggiungere la porta se richiesto)",
+ "server_url_placeholder": "URL di Jellyseerr...",
+ "password": "Password",
+ "password_placeholder": "Inserire la password per l'utente {{username}} di Jellyfin",
+ "save_button": "Salva",
+ "clear_button": "Cancella",
+ "login_button": "Accedi",
+ "total_media_requests": "Totale di richieste di media",
+ "movie_quota_limit": "Limite di quota per i film",
+ "movie_quota_days": "Giorni di quota per i film",
+ "tv_quota_limit": "Limite di quota per le serie TV",
+ "tv_quota_days": "Giorni di quota per le serie TV",
+ "reset_jellyseerr_config_button": "Ripristina la configurazione di Jellyseerr",
+ "unlimited": "Illimitato"
},
- "refresh_from_server": "Refresh Settings from Server"
+ "marlin_search": {
+ "enable_marlin_search": "Abilita la ricerca Marlin ",
+ "url": "URL",
+ "server_url_placeholder": "http(s)://dominio.org:porta",
+ "marlin_search_hint": "Inserire l'URL del server Marlin. L'URL deve includere http o https e, facoltativamente, la porta.",
+ "read_more_about_marlin": "Leggi di più su Marlin.",
+ "save_button": "Salva",
+ "toasts": {
+ "saved": "Salvato"
+ }
+ }
},
- "kefinTweaks": {
- "watchlist_enabler": "Enable our Watchlist integration",
- "watchlist_button": "Toggle Watchlist integration"
+ "storage": {
+ "storage_title": "Spazio",
+ "app_usage": "App {{usedSpace}}%",
+ "device_usage": "Dispositivo {{availableSpace}}%",
+ "size_used": "{{used}} di {{total}} usato",
+ "delete_all_downloaded_files": "Cancella Tutti i File Scaricati"
+ },
+ "intro": {
+ "show_intro": "Mostra intro",
+ "reset_intro": "Ripristina intro"
+ },
+ "logs": {
+ "logs_title": "Log",
+ "no_logs_available": "Nessun log disponibile",
+ "delete_all_logs": "Cancella tutti i log"
+ },
+ "languages": {
+ "title": "Lingue",
+ "app_language": "Lingua dell'App",
+ "app_language_description": "Selezione la lingua dell'app.",
+ "system": "Sistema"
+ },
+ "toasts":{
+ "error_deleting_files": "Errore nella cancellazione dei file",
+ "background_downloads_enabled": "Scaricamento in background abilitato",
+ "background_downloads_disabled": "Scaricamento in background disabilitato",
+ "connected": "Connesso",
+ "could_not_connect": "Non è stato possibile connettersi",
+ "invalid_url": "URL invalido"
}
},
- "storage": {
- "storage_title": "Spazio",
- "app_usage": "App {{usedSpace}}%",
- "device_usage": "Dispositivo {{availableSpace}}%",
- "size_used": "{{used}} di {{total}} usato",
- "delete_all_downloaded_files": "Cancella Tutti i File Scaricati",
- "music_cache_title": "Music Cache",
- "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
- "enable_music_cache": "Enable Music Cache",
- "clear_music_cache": "Clear Music Cache",
- "music_cache_size": "{{size}} cached",
- "music_cache_cleared": "Music cache cleared",
- "delete_all_downloaded_songs": "Delete All Downloaded Songs",
- "downloaded_songs_size": "{{size}} downloaded",
- "downloaded_songs_deleted": "Downloaded songs deleted"
- },
- "intro": {
- "title": "Intro",
- "show_intro": "Mostra intro",
- "reset_intro": "Ripristina intro"
- },
- "logs": {
- "logs_title": "Log",
- "export_logs": "Export logs",
- "click_for_more_info": "Click for more info",
- "level": "Livello",
- "no_logs_available": "Nessun log disponibile",
- "delete_all_logs": "Cancella tutti i log"
- },
- "languages": {
- "title": "Lingue",
- "app_language": "Lingua dell'App",
- "system": "Sistema"
- },
- "toasts": {
- "error_deleting_files": "Errore nella cancellazione dei file",
- "background_downloads_enabled": "Scaricamento in background abilitato",
- "background_downloads_disabled": "Scaricamento in background disabilitato"
+ "downloads": {
+ "downloads_title": "Scaricati",
+ "tvseries": "Serie TV",
+ "movies": "Film",
+ "queue": "Coda",
+ "queue_hint": "La coda e gli elementi scaricati saranno persi con il riavvio dell'app",
+ "no_items_in_queue": "Nessun elemento in coda",
+ "no_downloaded_items": "Nessun elemento scaricato",
+ "delete_all_movies_button": "Cancella tutti i film",
+ "delete_all_tvseries_button": "Cancella tutte le serie TV",
+ "delete_all_button": "Cancella tutti",
+ "active_download": "Scaricamento in corso",
+ "no_active_downloads": "Nessun scaricamento in corso",
+ "active_downloads": "Scaricamenti in corso",
+ "new_app_version_requires_re_download": "La nuova verione dell'app richiede di scaricare nuovamente i contenuti",
+ "new_app_version_requires_re_download_description": "Il nuovo aggiornamento richiede di scaricare nuovamente i contenuti. Rimuovere tutti i contenuti scaricati e riprovare.",
+ "back": "Indietro",
+ "delete": "Cancella",
+ "something_went_wrong": "Qualcosa è andato storto",
+ "could_not_get_stream_url_from_jellyfin": "Impossibile ottenere l'URL del flusso da Jellyfin",
+ "eta": "ETA {{eta}}",
+ "methods": "Metodi",
+ "toasts": {
+ "you_are_not_allowed_to_download_files": "Non è consentito scaricare file.",
+ "deleted_all_movies_successfully": "Cancellati tutti i film con successo!",
+ "failed_to_delete_all_movies": "Impossibile eliminare tutti i film",
+ "deleted_all_tvseries_successfully": "Eliminate tutte le serie TV con successo!",
+ "failed_to_delete_all_tvseries": "Impossibile eliminare tutte le serie TV",
+ "download_cancelled": "Scaricamento annullato",
+ "could_not_cancel_download": "Impossibile annullare lo scaricamento",
+ "download_completed": "Scaricamento completato",
+ "download_started_for": "Scaricamento iniziato per {{item}}",
+ "item_is_ready_to_be_downloaded": "{{item}} è pronto per essere scaricato",
+ "download_stated_for_item": "Scaricamento iniziato per {{item}}",
+ "download_failed_for_item": "Scaricamento fallito per {{item}} - {{error}}",
+ "download_completed_for_item": "Scaricamento completato per {{item}}",
+ "queued_item_for_optimization": "Messo in coda {{item}} per l'ottimizzazione",
+ "failed_to_start_download_for_item": "Failed to start downloading for {{item}}: {{message}}",
+ "server_responded_with_status_code": "Server responded with status {{statusCode}}",
+ "no_response_received_from_server": "No response received from the server",
+ "error_setting_up_the_request": "Error setting up the request",
+ "failed_to_start_download_for_item_unexpected_error": "Impossibile avviare il download per {{item}}: Errore imprevisto",
+ "all_files_folders_and_jobs_deleted_successfully": "Tutti i file, le cartelle e i processi sono stati eliminati con successo.",
+ "an_error_occured_while_deleting_files_and_jobs": "Si è verificato un errore durante l'eliminazione di file e processi",
+ "go_to_downloads": "Vai agli elementi scaricati"
+ }
}
},
- "sessions": {
- "title": "Sessioni",
- "no_active_sessions": "Nessuna Sessione Attiva"
- },
- "downloads": {
- "downloads_title": "Scaricati",
- "tvseries": "Serie TV",
+ "search": {
+ "search_here": "Cerca qui...",
+ "search": "Cerca...",
+ "x_items": "{{count}} elementi",
+ "library": "Libreria",
+ "discover": "Scopri",
+ "no_results": "Nessun risultato",
+ "no_results_found_for": "Nessun risultato trovato per",
"movies": "Film",
- "queue": "Coda",
- "other_media": "Altri supporti",
- "queue_hint": "La coda e gli elementi scaricati saranno persi con il riavvio dell'app",
- "no_items_in_queue": "Nessun elemento in coda",
- "no_downloaded_items": "Nessun elemento scaricato",
- "delete_all_movies_button": "Cancella tutti i film",
- "delete_all_tvseries_button": "Cancella tutte le serie TV",
- "delete_all_button": "Cancella tutti",
- "delete_all_other_media_button": "Elimina altri supporti",
- "active_download": "Scaricamento in corso",
- "no_active_downloads": "Nessun scaricamento in corso",
- "active_downloads": "Scaricamenti in corso",
- "new_app_version_requires_re_download": "La nuova verione dell'app richiede di scaricare nuovamente i contenuti",
- "new_app_version_requires_re_download_description": "Il nuovo aggiornamento richiede di scaricare nuovamente i contenuti. Rimuovere tutti i contenuti scaricati e riprovare.",
- "back": "Indietro",
- "delete": "Cancella",
- "something_went_wrong": "Qualcosa è andato storto",
- "could_not_get_stream_url_from_jellyfin": "Impossibile ottenere l'URL del flusso da Jellyfin",
- "eta": "Tempo stimato di completamento {{eta}}",
- "toasts": {
- "you_are_not_allowed_to_download_files": "Non è consentito scaricare file.",
- "deleted_all_movies_successfully": "Cancellati tutti i film con successo!",
- "failed_to_delete_all_movies": "Impossibile eliminare tutti i film",
- "deleted_all_tvseries_successfully": "Eliminate tutte le serie TV con successo!",
- "failed_to_delete_all_tvseries": "Impossibile eliminare tutte le serie TV",
- "deleted_media_successfully": "Eliminato altri supporti con successo!",
- "failed_to_delete_media": "Impossibile eliminare altri media",
- "download_deleted": "Download Eliminato",
- "download_cancelled": "Scaricamento annullato",
- "could_not_delete_download": "Impossibile Eliminare Il Download",
- "download_paused": "Download In Pausa",
- "could_not_pause_download": "Impossibile Sbloccare Il Download",
- "download_resumed": "Download Ripreso",
- "could_not_resume_download": "Impossibile Riprendere Il Download",
- "download_completed": "Scaricamento completato",
- "download_failed": "Scaricamento non riuscito",
- "download_failed_for_item": "Scaricamento fallito per {{item}} - {{error}}",
- "download_completed_for_item": "Scaricamento completato per {{item}}",
- "download_started_for_item": "Scarica Avviato per {{item}}",
- "failed_to_start_download": "Impossibile avviare il download",
- "item_already_downloading": "{{item}} è già in download",
- "all_files_deleted": "Tutti i Download Eliminati con Successo",
- "files_deleted_by_type": "{{count}} {{type}} cancellati",
- "all_files_folders_and_jobs_deleted_successfully": "Tutti i file, le cartelle e i processi sono stati eliminati con successo.",
- "failed_to_clean_cache_directory": "Pulizia della directory della cache non riuscita",
- "could_not_get_download_url_for_item": "Impossibile ottenere l'URL di download per {{itemName}}",
- "go_to_downloads": "Vai agli elementi scaricati",
- "file_deleted": "{{item}} cancellato"
+ "series": "Serie",
+ "episodes": "Episodi",
+ "collections": "Collezioni",
+ "actors": "Attori",
+ "request_movies": "Film Richiesti",
+ "request_series": "Serie Richieste",
+ "recently_added": "Aggiunti di Recente",
+ "recent_requests": "Richiesti di Recente",
+ "plex_watchlist": "Plex Watchlist",
+ "trending": "In tendenza",
+ "popular_movies": "Film Popolari",
+ "movie_genres": "Generi Film",
+ "upcoming_movies": "Film in arrivo",
+ "studios": "Studio",
+ "popular_tv": "Serie Popolari",
+ "tv_genres": "Generi Televisivi",
+ "upcoming_tv": "Serie in Arrivo",
+ "networks": "Network",
+ "tmdb_movie_keyword": "TMDB Parola chiave del film",
+ "tmdb_movie_genre": "TMDB Genere Film",
+ "tmdb_tv_keyword": "TMDB Parola chiave della serie",
+ "tmdb_tv_genre": "TMDB Genere Televisivo",
+ "tmdb_search": "TMDB Cerca",
+ "tmdb_studio": "TMDB Studio",
+ "tmdb_network": "TMDB Network",
+ "tmdb_movie_streaming_services": "TMDB Servizi di Streaming di Film",
+ "tmdb_tv_streaming_services": "TMDB Servizi di Streaming di Serie"
+ },
+ "library": {
+ "no_items_found": "Nessun elemento trovato",
+ "no_results": "Nessun risultato",
+ "no_libraries_found": "Nessuna libreria trovata",
+ "item_types": {
+ "movies": "film",
+ "series": "serie TV",
+ "boxsets": "cofanetti",
+ "items": "elementi"
+ },
+ "options": {
+ "display": "Display",
+ "row": "Fila",
+ "list": "Lista",
+ "image_style": "Stile dell'immagine",
+ "poster": "Poster",
+ "cover": "Cover",
+ "show_titles": "Mostra titoli",
+ "show_stats": "Mostra statistiche"
+ },
+ "filters": {
+ "genres": "Generi",
+ "years": "Anni",
+ "sort_by": "Ordina per",
+ "sort_order": "Criterio di ordinamento",
+ "tags": "Tag"
}
- }
- },
- "common": {
- "select": "Seleziona",
- "no_trailer_available": "Nessun trailer disponibile",
- "video": "Video",
- "audio": "Audio",
- "subtitle": "Sottotitoli",
- "play": "Gioca",
- "none": "Nulla",
- "track": "Traccia",
- "cancel": "Cancel",
- "delete": "Delete",
- "ok": "OK",
- "remove": "Remove",
- "next": "Next",
- "back": "Back",
- "continue": "Continue",
- "verifying": "Verifying..."
- },
- "search": {
- "search": "Cerca...",
- "x_items": "{{count}} elementi",
- "library": "Libreria",
- "discover": "Scopri",
- "no_results": "Nessun risultato",
- "no_results_found_for": "Nessun risultato trovato per",
- "movies": "Film",
- "series": "Serie",
- "episodes": "Episodi",
- "collections": "Collezioni",
- "actors": "Attori",
- "artists": "Artists",
- "albums": "Albums",
- "songs": "Songs",
- "playlists": "Playlists",
- "request_movies": "Film Richiesti",
- "request_series": "Serie Richieste",
- "recently_added": "Aggiunti di Recente",
- "recent_requests": "Richiesti di Recente",
- "plex_watchlist": "Watchlist di Plex",
- "trending": "In tendenza",
- "popular_movies": "Film Popolari",
- "movie_genres": "Generi Film",
- "upcoming_movies": "Film in arrivo",
- "studios": "Studio",
- "popular_tv": "Serie Popolari",
- "tv_genres": "Generi Televisivi",
- "upcoming_tv": "Serie in Arrivo",
- "networks": "Network",
- "tmdb_movie_keyword": "TMDB Parola chiave del film",
- "tmdb_movie_genre": "TMDB Genere Film",
- "tmdb_tv_keyword": "TMDB Parola chiave della serie",
- "tmdb_tv_genre": "TMDB Genere Televisivo",
- "tmdb_search": "TMDB Cerca",
- "tmdb_studio": "Studio TMDB",
- "tmdb_network": "Network TMDB",
- "tmdb_movie_streaming_services": "TMDB Servizi di Streaming di Film",
- "tmdb_tv_streaming_services": "TMDB Servizi di Streaming di Serie"
- },
- "library": {
- "no_results": "Nessun risultato",
- "no_libraries_found": "Nessuna libreria trovata",
- "item_types": {
- "movies": "film",
- "series": "serie TV",
- "boxsets": "cofanetti",
- "items": "elementi"
},
- "options": {
- "display": "Schermo",
- "row": "Fila",
- "list": "Lista",
- "image_style": "Stile dell'immagine",
- "poster": "Poster",
- "cover": "Copertina",
- "show_titles": "Mostra titoli",
- "show_stats": "Mostra statistiche"
+ "favorites": {
+ "series": "Serie TV",
+ "movies": "Film",
+ "episodes": "Episodi",
+ "videos": "Video",
+ "boxsets": "Boxset",
+ "playlists": "Playlist"
+ },
+ "custom_links": {
+ "no_links": "Nessun link"
+ },
+ "player": {
+ "error": "Errore",
+ "failed_to_get_stream_url": "Impossibile ottenere l'URL dello stream",
+ "an_error_occured_while_playing_the_video": "Si è verificato un errore durante la riproduzione del video. Controllare i log nelle impostazioni.",
+ "client_error": "Errore del client",
+ "could_not_create_stream_for_chromecast": "Impossibile creare uno stream per Chromecast",
+ "message_from_server": "Messaggio dal server: {{messagge}}",
+ "video_has_finished_playing": "La riproduzione del video è terminata!",
+ "no_video_source": "Nessuna sorgente video...",
+ "next_episode": "Prossimo Episodio",
+ "refresh_tracks": "Aggiorna tracce",
+ "subtitle_tracks": "Tracce di sottotitoli:",
+ "audio_tracks": "Tracce audio:",
+ "playback_state": "Stato della riproduzione:",
+ "no_data_available": "Nessun dato disponibile",
+ "index": "Indice:"
+ },
+ "item_card": {
+ "next_up": "Il prossimo",
+ "no_items_to_display": "Nessun elemento da visualizzare",
+ "cast_and_crew": "Cast e Equipaggio",
+ "series": "Serie",
+ "seasons": "Stagioni",
+ "season": "Stagione",
+ "no_episodes_for_this_season": "Nessun episodio per questa stagione",
+ "overview": "Panoramica",
+ "more_with": "Altri con {{name}}",
+ "similar_items": "Elementi simili",
+ "no_similar_items_found": "Non sono stati trovati elementi simili",
+ "video": "Video",
+ "more_details": "Più dettagli",
+ "quality": "Qualità",
+ "audio": "Audio",
+ "subtitles": "Sottotitoli",
+ "show_more": "Mostra di più",
+ "show_less": "Mostra di meno",
+ "appeared_in": "Apparso in",
+ "could_not_load_item": "Impossibile caricare l'elemento",
+ "none": "Nessuno",
+ "download": {
+ "download_season": "Scarica Stagione",
+ "download_series": "Scarica Serie",
+ "download_episode": "Scarica Episodio",
+ "download_movie": "Scarica Film",
+ "download_x_item": "Scarica {{item_count}} elementi",
+ "download_button": "Scarica",
+ "using_optimized_server": "Utilizzando il server di ottimizzazione",
+ "using_default_method": "Utilizzando il metodo predefinito"
+ }
+ },
+ "live_tv": {
+ "next": "Prossimo",
+ "previous": "Precedente",
+ "live_tv": "TV in diretta",
+ "coming_soon": "Prossimamente",
+ "on_now": "In onda ora",
+ "shows": "Programmi",
+ "movies": "Film",
+ "sports": "Sport",
+ "for_kids": "Per Bambini",
+ "news": "Notiziari"
+ },
+ "jellyseerr":{
+ "confirm": "Conferma",
+ "cancel": "Cancella",
+ "yes": "Si",
+ "whats_wrong": "Cosa c'è che non va?",
+ "issue_type": "Tipo di problema",
+ "select_an_issue": "Seleziona un problema",
+ "types": "Tipi",
+ "describe_the_issue": "(facoltativo) Descrivere il problema...",
+ "submit_button": "Invia",
+ "report_issue_button": "Segnalare il problema",
+ "request_button": "Richiedi",
+ "are_you_sure_you_want_to_request_all_seasons": "Sei sicuro di voler richiedere tutte le stagioni?",
+ "failed_to_login": "Accesso non riuscito",
+ "cast": "Cast",
+ "details": "Dettagli",
+ "status": "Stato",
+ "original_title": "Titolo originale",
+ "series_type": "Tipo di Serie",
+ "release_dates": "Date di Uscita",
+ "first_air_date": "Prima Data di Messa in Onda",
+ "next_air_date": "Prossima Data di Messa in Onda",
+ "revenue": "Ricavi",
+ "budget": "Budget",
+ "original_language": "Lingua Originale",
+ "production_country": "Paese di Produzione",
+ "studios": "Studio",
+ "network": "Network",
+ "currently_streaming_on": "Attualmente in streaming su",
+ "advanced": "Avanzate",
+ "request_as": "Richiedi Come",
+ "tags": "Tag",
+ "quality_profile": "Profilo qualità",
+ "root_folder": "Cartella radice",
+ "season_x": "Stagione {{seasons}}",
+ "season_number": "Stagione {{season_number}}",
+ "number_episodes": "{{episode_number}} Episodio",
+ "born": "Nato",
+ "appearances": "Aspetto",
+ "toasts": {
+ "jellyseer_does_not_meet_requirements": "Il server Jellyseerr non soddisfa i requisiti minimi di versione! Aggiornare almeno alla versione 2.0.0.",
+ "jellyseerr_test_failed": "Il test di Jellyseerr non è riuscito. Riprovare.",
+ "failed_to_test_jellyseerr_server_url": "Fallito il test dell'url del server jellyseerr",
+ "issue_submitted": "Problema inviato!",
+ "requested_item": "Richiesto {{item}}!",
+ "you_dont_have_permission_to_request": "Non hai il permesso di richiedere!",
+ "something_went_wrong_requesting_media": "Qualcosa è andato storto nella richiesta dei media!"
+ }
},
- "filters": {
- "genres": "Generi",
- "years": "Anni",
- "sort_by": "Ordina per",
- "filter_by": "Filter By",
- "sort_order": "Criterio di ordinamento",
- "tags": "Tag"
- }
- },
- "favorites": {
- "series": "Serie TV",
- "movies": "Film",
- "episodes": "Episodi",
- "videos": "Video",
- "boxsets": "Boxset",
- "playlists": "Playlist",
- "noDataTitle": "Ancora nessun preferito",
- "noData": "Contrassegna gli elementi come preferiti per vederli apparire qui per un accesso rapido."
- },
- "custom_links": {
- "no_links": "Nessun link"
- },
- "player": {
- "error": "Errore",
- "failed_to_get_stream_url": "Impossibile ottenere l'URL dello stream",
- "an_error_occured_while_playing_the_video": "Si è verificato un errore durante la riproduzione del video. Controllare i log nelle impostazioni.",
- "client_error": "Errore del client",
- "could_not_create_stream_for_chromecast": "Impossibile creare uno stream per Chromecast",
- "message_from_server": "Messaggio dal server",
- "next_episode": "Prossimo Episodio",
- "refresh_tracks": "Aggiorna tracce",
- "audio_tracks": "Tracce audio:",
- "playback_state": "Stato della riproduzione:",
- "index": "Indice:",
- "continue_watching": "Continua a guardare",
- "go_back": "Indietro",
- "downloaded_file_title": "You have this file downloaded",
- "downloaded_file_message": "Do you want to play the downloaded file?",
- "downloaded_file_yes": "Yes",
- "downloaded_file_no": "No",
- "downloaded_file_cancel": "Cancel"
- },
- "item_card": {
- "next_up": "Il prossimo",
- "no_items_to_display": "Nessun elemento da visualizzare",
- "cast_and_crew": "Cast e Equipaggio",
- "series": "Serie",
- "seasons": "Stagioni",
- "season": "Stagione",
- "no_episodes_for_this_season": "Nessun episodio per questa stagione",
- "overview": "Panoramica",
- "more_with": "Altri con {{name}}",
- "similar_items": "Elementi simili",
- "no_similar_items_found": "Non sono stati trovati elementi simili",
- "video": "Video",
- "more_details": "Più dettagli",
- "media_options": "Opzioni Media",
- "quality": "Qualità",
- "audio": "Audio",
- "subtitles": "Sottotitoli",
- "show_more": "Mostra di più",
- "show_less": "Mostra di meno",
- "appeared_in": "Apparso in",
- "could_not_load_item": "Impossibile caricare l'elemento",
- "none": "Nessuno",
- "download": {
- "download_season": "Scarica Stagione",
- "download_series": "Scarica Serie",
- "download_episode": "Scarica Episodio",
- "download_movie": "Scarica Film",
- "download_x_item": "Scarica {{item_count}} elementi",
- "download_unwatched_only": "Solo Non Visti",
- "download_button": "Scarica"
- }
- },
- "live_tv": {
- "next": "Prossimo",
- "previous": "Precedente",
- "coming_soon": "Prossimamente",
- "on_now": "In onda ora",
- "shows": "Programmi",
- "movies": "Film",
- "sports": "Sport",
- "for_kids": "Per Bambini",
- "news": "Notiziari"
- },
- "jellyseerr": {
- "confirm": "Conferma",
- "cancel": "Cancella",
- "yes": "Si",
- "whats_wrong": "Cosa c'è che non va?",
- "issue_type": "Tipo di problema",
- "select_an_issue": "Seleziona un problema",
- "types": "Tipi",
- "describe_the_issue": "(facoltativo) Descrivere il problema...",
- "submit_button": "Invia",
- "report_issue_button": "Segnalare il problema",
- "request_button": "Richiedi",
- "are_you_sure_you_want_to_request_all_seasons": "Sei sicuro di voler richiedere tutte le stagioni?",
- "failed_to_login": "Accesso non riuscito",
- "cast": "Cast",
- "details": "Dettagli",
- "status": "Stato",
- "original_title": "Titolo originale",
- "series_type": "Tipo di Serie",
- "release_dates": "Date di Uscita",
- "first_air_date": "Prima Data di Messa in Onda",
- "next_air_date": "Prossima Data di Messa in Onda",
- "revenue": "Ricavi",
- "budget": "Budget",
- "original_language": "Lingua Originale",
- "production_country": "Paese di Produzione",
- "studios": "Studio",
- "network": "Network",
- "currently_streaming_on": "Attualmente in streaming su",
- "advanced": "Avanzate",
- "request_as": "Richiedi Come",
- "tags": "Tag",
- "quality_profile": "Profilo qualità",
- "root_folder": "Cartella radice",
- "season_all": "Season (all)",
- "season_number": "Stagione {{season_number}}",
- "number_episodes": "{{episode_number}} Episodio",
- "born": "Nato",
- "appearances": "Aspetto",
- "approve": "Approva",
- "decline": "Rifiuta",
- "requested_by": "Richiesto da {{user}}",
- "unknown_user": "Utente Sconosciuto",
- "toasts": {
- "jellyseer_does_not_meet_requirements": "Il server Jellyseerr non soddisfa i requisiti minimi di versione! Aggiornare almeno alla versione 2.0.0.",
- "jellyseerr_test_failed": "Il test di Jellyseerr non è riuscito. Riprovare.",
- "failed_to_test_jellyseerr_server_url": "Fallito il test dell'url del server jellyseerr",
- "issue_submitted": "Problema inviato!",
- "requested_item": "Richiesto {{item}}!",
- "you_dont_have_permission_to_request": "Non hai il permesso di richiedere!",
- "something_went_wrong_requesting_media": "Qualcosa è andato storto nella richiesta dei media!",
- "request_approved": "Richiesta Approvata!",
- "request_declined": "Richiesta Rifiutata!",
- "failed_to_approve_request": "Impossibile Approvare la Richiesta",
- "failed_to_decline_request": "Rifiuto della Richiesta non Riuscito"
- }
- },
- "tabs": {
- "home": "Home",
- "search": "Cerca",
- "library": "Libreria",
- "custom_links": "Collegamenti personalizzati",
- "favorites": "Preferiti"
- },
- "music": {
- "title": "Music",
"tabs": {
- "suggestions": "Suggestions",
- "albums": "Albums",
- "artists": "Artists",
- "playlists": "Playlists",
- "tracks": "tracks"
- },
- "filters": {
- "all": "All"
- },
- "recently_added": "Recently Added",
- "recently_played": "Recently Played",
- "frequently_played": "Frequently Played",
- "explore": "Explore",
- "top_tracks": "Top Tracks",
- "play": "Play",
- "shuffle": "Shuffle",
- "play_top_tracks": "Play Top Tracks",
- "no_suggestions": "No suggestions available",
- "no_albums": "No albums found",
- "no_artists": "No artists found",
- "no_playlists": "No playlists found",
- "album_not_found": "Album not found",
- "artist_not_found": "Artist not found",
- "playlist_not_found": "Playlist not found",
- "track_options": {
- "play_next": "Play Next",
- "add_to_queue": "Add to Queue",
- "add_to_playlist": "Add to Playlist",
- "download": "Download",
- "downloaded": "Downloaded",
- "downloading": "Downloading...",
- "cached": "Cached",
- "delete_download": "Delete Download",
- "delete_cache": "Remove from Cache",
- "go_to_artist": "Go to Artist",
- "go_to_album": "Go to Album",
- "add_to_favorites": "Add to Favorites",
- "remove_from_favorites": "Remove from Favorites",
- "remove_from_playlist": "Remove from Playlist"
- },
- "playlists": {
- "create_playlist": "Create Playlist",
- "playlist_name": "Playlist Name",
- "enter_name": "Enter playlist name",
- "create": "Create",
- "search_playlists": "Search playlists...",
- "added_to": "Added to {{name}}",
- "added": "Added to playlist",
- "removed_from": "Removed from {{name}}",
- "removed": "Removed from playlist",
- "created": "Playlist created",
- "create_new": "Create New Playlist",
- "failed_to_add": "Failed to add to playlist",
- "failed_to_remove": "Failed to remove from playlist",
- "failed_to_create": "Failed to create playlist",
- "delete_playlist": "Delete Playlist",
- "delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "deleted": "Playlist deleted",
- "failed_to_delete": "Failed to delete playlist"
- },
- "sort": {
- "title": "Sort By",
- "alphabetical": "Alphabetical",
- "date_created": "Date Created"
+ "home": "Home",
+ "search": "Cerca",
+ "library": "Libreria",
+ "custom_links": "Collegamenti personalizzati",
+ "favorites": "Preferiti"
}
- },
- "watchlists": {
- "title": "Watchlists",
- "my_watchlists": "My Watchlists",
- "public_watchlists": "Public Watchlists",
- "create_title": "Create Watchlist",
- "edit_title": "Edit Watchlist",
- "create_button": "Create Watchlist",
- "save_button": "Save Changes",
- "delete_button": "Delete",
- "remove_button": "Remove",
- "cancel_button": "Cancel",
- "name_label": "Name",
- "name_placeholder": "Enter watchlist name",
- "description_label": "Description",
- "description_placeholder": "Enter description (optional)",
- "is_public_label": "Public Watchlist",
- "is_public_description": "Allow others to view this watchlist",
- "allowed_type_label": "Content Type",
- "sort_order_label": "Default Sort Order",
- "empty_title": "No Watchlists",
- "empty_description": "Create your first watchlist to start organizing your media",
- "empty_watchlist": "This watchlist is empty",
- "empty_watchlist_hint": "Add items from your library to this watchlist",
- "not_configured_title": "Streamystats Not Configured",
- "not_configured_description": "Configure Streamystats in settings to use watchlists",
- "go_to_settings": "Go to Settings",
- "add_to_watchlist": "Add to Watchlist",
- "remove_from_watchlist": "Remove from Watchlist",
- "select_watchlist": "Select Watchlist",
- "create_new": "Create New Watchlist",
- "item": "item",
- "items": "items",
- "public": "Public",
- "private": "Private",
- "you": "You",
- "by_owner": "By another user",
- "not_found": "Watchlist not found",
- "delete_confirm_title": "Delete Watchlist",
- "delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "remove_item_title": "Remove from Watchlist",
- "remove_item_message": "Remove \"{{name}}\" from this watchlist?",
- "loading": "Loading watchlists...",
- "no_compatible_watchlists": "No compatible watchlists",
- "create_one_first": "Create a watchlist that accepts this content type"
- },
- "playback_speed": {
- "title": "Playback Speed",
- "apply_to": "Apply To",
- "speed": "Speed",
- "scope": {
- "media": "This media only",
- "show": "This show",
- "all": "All media (default)"
- }
- }
-}
+ }
\ No newline at end of file
diff --git a/translations/ja.json b/translations/ja.json
index 76c02bdf..743f1e22 100644
--- a/translations/ja.json
+++ b/translations/ja.json
@@ -20,9 +20,7 @@
"server_is_taking_too_long_to_respond_try_again_later": "サーバーの応答に時間がかかりすぎています。しばらくしてからもう一度お試しください。",
"server_received_too_many_requests_try_again_later": "サーバーにリクエストが多すぎます。後でもう一度お試しください。",
"there_is_a_server_error": "サーバーエラーが発生しました",
- "an_unexpected_error_occured_did_you_enter_the_correct_url": "予期しないエラーが発生しました。サーバーのURLを正しく入力しましたか?",
- "too_old_server_text": "サポートされていないJellyfinサーバー発見",
- "too_old_server_description": "Jellyfinを最新バージョンにアップデートしてください"
+ "an_unexpected_error_occured_did_you_enter_the_correct_url": "予期しないエラーが発生しました。サーバーのURLを正しく入力しましたか?"
},
"server": {
"enter_url_to_jellyfin_server": "JellyfinサーバーのURLを入力してください",
@@ -30,64 +28,19 @@
"connect_button": "接続",
"previous_servers": "前のサーバー",
"clear_button": "クリア",
- "swipe_to_remove": "Swipe to remove",
"search_for_local_servers": "ローカルサーバーを検索",
"searching": "検索中...",
- "servers": "サーバー",
- "saved": "Saved",
- "session_expired": "Session Expired",
- "please_login_again": "Your saved session has expired. Please log in again.",
- "remove_saved_login": "Remove Saved Login",
- "remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
- "accounts_count": "{{count}} accounts",
- "select_account": "Select Account",
- "add_account": "Add Account",
- "remove_account_description": "This will remove the saved credentials for {{username}}."
- },
- "save_account": {
- "title": "Save Account",
- "save_for_later": "Save this account",
- "security_option": "Security Option",
- "no_protection": "No protection",
- "no_protection_desc": "Quick login without authentication",
- "pin_code": "PIN code",
- "pin_code_desc": "4-digit PIN required when switching",
- "password": "Re-enter password",
- "password_desc": "Password required when switching",
- "save_button": "Save",
- "cancel_button": "Cancel"
- },
- "pin": {
- "enter_pin": "Enter PIN",
- "enter_pin_for": "Enter PIN for {{username}}",
- "enter_4_digits": "Enter 4 digits",
- "invalid_pin": "Invalid PIN",
- "setup_pin": "Set Up PIN",
- "confirm_pin": "Confirm PIN",
- "pins_dont_match": "PINs don't match",
- "forgot_pin": "Forgot PIN?",
- "forgot_pin_desc": "Your saved credentials will be removed"
- },
- "password": {
- "enter_password": "Enter Password",
- "enter_password_for": "Enter password for {{username}}",
- "invalid_password": "Invalid password"
+ "servers": "サーバー"
},
"home": {
- "checking_server_connection": "サーバー接続を確認しています...",
"no_internet": "インターネット接続がありません",
"no_items": "アイテムはありません",
"no_internet_message": "心配しないでください。\nダウンロードしたコンテンツは引き続き視聴できます。",
- "checking_server_connection_message": "サーバーへの接続を確認しています",
"go_to_downloads": "ダウンロードに移動",
- "retry": "再試行する",
- "server_unreachable": "サーバーに到達できません",
- "server_unreachable_message": "サーバーに接続できませんでした。\nネットワーク接続を確認してください。",
"oops": "おっと!",
"error_message": "何か問題が発生しました。\nログアウトして再度ログインしてください。",
"continue_watching": "続きを見る",
"next_up": "次の動画",
- "continue_and_next_up": "Continue & Next Up",
"recently_added_in": "{{libraryName}}に最近追加された",
"suggested_movies": "おすすめ映画",
"suggested_episodes": "おすすめエピソード",
@@ -109,48 +62,6 @@
"settings": {
"settings_title": "設定",
"log_out_button": "ログアウト",
- "categories": {
- "title": "カテゴリ"
- },
- "playback_controls": {
- "title": "再生と操作"
- },
- "audio_subtitles": {
- "title": "音声と字幕"
- },
- "appearance": {
- "title": "Appearance",
- "merge_next_up_continue_watching": "Merge Continue Watching & Next Up",
- "hide_remote_session_button": "Hide Remote Session Button"
- },
- "network": {
- "title": "Network",
- "local_network": "Local Network",
- "auto_switch_enabled": "Auto-switch when at home",
- "auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
- "local_url": "Local URL",
- "local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
- "local_url_placeholder": "http://192.168.1.100:8096",
- "home_wifi_networks": "Home WiFi Networks",
- "add_current_network": "Add \"{{ssid}}\"",
- "not_connected_to_wifi": "Not connected to WiFi",
- "no_networks_configured": "No networks configured",
- "add_network_hint": "Add your home WiFi network to enable auto-switching",
- "current_wifi": "Current WiFi",
- "using_url": "Using",
- "local": "Local URL",
- "remote": "Remote URL",
- "not_connected": "Not connected",
- "current_server": "Current Server",
- "remote_url": "Remote URL",
- "active_url": "Active URL",
- "not_configured": "Not configured",
- "network_added": "Network added",
- "network_already_added": "Network already added",
- "no_wifi_connected": "Not connected to WiFi",
- "permission_denied": "Location permission denied",
- "permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
- },
"user_info": {
"user_info_title": "ユーザー情報",
"user": "ユーザー",
@@ -174,42 +85,21 @@
"rewind_length": "巻き戻しの長さ",
"seconds_unit": "s"
},
- "gesture_controls": {
- "gesture_controls_title": "ジェスチャーコントロール",
- "horizontal_swipe_skip": "水平方向にスワイプしてスキップ",
- "horizontal_swipe_skip_description": "コントロールが非表示の場合は左右にスワイプしてスキップします",
- "left_side_brightness": "左側の明るさコントロール",
- "left_side_brightness_description": "左側を上下にスワイプして明るさを調整する",
- "right_side_volume": "右側の音量制御",
- "right_side_volume_description": "右側の上下にスワイプして音量を調整します",
- "hide_volume_slider": "Hide Volume Slider",
- "hide_volume_slider_description": "Hide the volume slider in the video player",
- "hide_brightness_slider": "Hide Brightness Slider",
- "hide_brightness_slider_description": "Hide the brightness slider in the video player"
- },
"audio": {
"audio_title": "オーディオ",
"set_audio_track": "前のアイテムからオーディオトラックを設定",
"audio_language": "オーディオ言語",
"audio_hint": "デフォルトのオーディオ言語を選択します。",
"none": "なし",
- "language": "言語",
- "transcode_mode": {
- "title": "Audio Transcoding",
- "description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
- "auto": "Auto",
- "stereo": "Force Stereo",
- "5_1": "Allow 5.1",
- "passthrough": "Passthrough"
- }
+ "language": "言語"
},
"subtitles": {
"subtitle_title": "字幕",
- "subtitle_hint": "字幕設定を構成します。",
"subtitle_language": "字幕の言語",
"subtitle_mode": "字幕モード",
"set_subtitle_track": "前のアイテムから字幕トラックを設定",
"subtitle_size": "字幕サイズ",
+ "subtitle_hint": "字幕設定を構成します。",
"none": "なし",
"language": "言語",
"loading": "ロード中",
@@ -219,66 +109,11 @@
"Always": "常に",
"None": "なし",
"OnlyForced": "強制のみ"
- },
- "text_color": "テキストの色",
- "background_color": "背景色",
- "outline_color": "アウトラインの色",
- "outline_thickness": "概要 厚さ",
- "background_opacity": "背景の透明度",
- "outline_opacity": "アウトラインの透明度",
- "bold_text": "Bold Text",
- "colors": {
- "Black": "ブラック",
- "Gray": "グレー",
- "Silver": "シルバー",
- "White": "白",
- "Maroon": "Maroon",
- "Red": "赤",
- "Fuchsia": "Fuchsia",
- "Yellow": "黄色",
- "Olive": "オリーブ",
- "Green": "緑",
- "Teal": "ティール",
- "Lime": "黄緑",
- "Purple": "パープル",
- "Navy": "海軍format@@0",
- "Blue": "青",
- "Aqua": "Aqua"
- },
- "thickness": {
- "None": "なし",
- "Thin": "細いです",
- "Normal": "標準",
- "Thick": "濃厚な"
- },
- "subtitle_color": "Subtitle Color",
- "subtitle_background_color": "Background Color",
- "subtitle_font": "Subtitle Font",
- "ksplayer_title": "KSPlayer Settings",
- "hardware_decode": "Hardware Decoding",
- "hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues."
- },
- "vlc_subtitles": {
- "title": "VLC Subtitle Settings",
- "hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
- "text_color": "Text Color",
- "background_color": "Background Color",
- "background_opacity": "Background Opacity",
- "outline_color": "Outline Color",
- "outline_opacity": "Outline Opacity",
- "outline_thickness": "Outline Thickness",
- "bold": "Bold Text",
- "margin": "Bottom Margin"
- },
- "video_player": {
- "title": "Video Player",
- "video_player": "Video Player",
- "video_player_description": "Choose which video player to use on iOS.",
- "ksplayer": "KSPlayer",
- "vlc": "VLC"
+ }
},
"other": {
"other_title": "その他",
+ "auto_rotate": "画面の自動回転",
"video_orientation": "動画の向き",
"orientation": "向き",
"orientations": {
@@ -294,35 +129,25 @@
"UNKNOWN": "不明"
},
"safe_area_in_controls": "コントロールの安全エリア",
- "video_player": "Video player",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Experimental + PiP)"
- },
"show_custom_menu_links": "カスタムメニューのリンクを表示",
- "show_large_home_carousel": "大きなヒーロー(Beta)",
"hide_libraries": "ライブラリを非表示",
"select_liraries_you_want_to_hide": "ライブラリタブとホームページセクションから非表示にするライブラリを選択します。",
- "disable_haptic_feedback": "触覚フィードバックを無効にする",
- "default_quality": "デフォルトの品質",
- "default_playback_speed": "Default Playback Speed",
- "auto_play_next_episode": "Auto-play Next Episode",
- "max_auto_play_episode_count": "自動再生エピソードの最大数",
- "disabled": "無効"
+ "disable_haptic_feedback": "触覚フィードバックを無効にする"
},
"downloads": {
- "downloads_title": "ダウンロード"
- },
- "music": {
- "title": "Music",
- "playback_title": "Playback",
- "playback_description": "Configure how music is played.",
- "prefer_downloaded": "Prefer Downloaded Songs",
- "caching_title": "Caching",
- "caching_description": "Automatically cache upcoming tracks for smoother playback.",
- "lookahead_enabled": "Enable Look-Ahead Caching",
- "lookahead_count": "Tracks to Pre-cache",
- "max_cache_size": "Max Cache Size"
+ "downloads_title": "ダウンロード",
+ "download_method": "ダウンロード方法",
+ "remux_max_download": "Remux最大ダウンロード数",
+ "auto_download": "自動ダウンロード",
+ "optimized_versions_server": "Optimized versionsサーバー",
+ "save_button": "保存",
+ "optimized_server": "Optimizedサーバー",
+ "optimized": "最適化",
+ "default": "デフォルト",
+ "optimized_version_hint": "OptimizeサーバーのURLを入力します。URLにはhttpまたはhttpsを含め、オプションでポートを指定します。",
+ "read_more_about_optimized_server": "Optimizeサーバーの詳細をご覧ください。",
+ "url": "URL",
+ "server_url_placeholder": "http(s)://domain.org:ポート"
},
"plugins": {
"plugins_title": "プラグイン",
@@ -333,6 +158,8 @@
"server_url_placeholder": "Jellyseerr URL...",
"password": "パスワード",
"password_placeholder": "Jellyfinユーザー {{username}} のパスワードを入力してください",
+ "save_button": "保存",
+ "clear_button": "クリア",
"login_button": "ログイン",
"total_media_requests": "メディアリクエストの合計",
"movie_quota_limit": "映画のクオータ制限",
@@ -340,13 +167,7 @@
"tv_quota_limit": "テレビのクオータ制限",
"tv_quota_days": "テレビのクオータ日数",
"reset_jellyseerr_config_button": "Jellyseerrの設定をリセット",
- "unlimited": "無制限",
- "plus_n_more": "+{{n}} more",
- "order_by": {
- "DEFAULT": "デフォルト",
- "VOTE_COUNT_AND_AVERAGE": "投票数と平均",
- "POPULARITY": "人気"
- }
+ "unlimited": "無制限"
},
"marlin_search": {
"enable_marlin_search": "マーリン検索を有効にする ",
@@ -356,99 +177,52 @@
"read_more_about_marlin": "Marlinについて詳しく読む。",
"save_button": "保存",
"toasts": {
- "saved": "保存しました",
- "refreshed": "Settings refreshed from server"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "streamystats": {
- "enable_streamystats": "Enable Streamystats",
- "disable_streamystats": "Disable Streamystats",
- "enable_search": "Use for Search",
- "url": "URL",
- "server_url_placeholder": "http(s)://streamystats.example.com",
- "streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
- "read_more_about_streamystats": "Read More About Streamystats.",
- "save_button": "Save",
- "save": "Save",
- "features_title": "Features",
- "home_sections_title": "Home Sections",
- "enable_movie_recommendations": "Movie Recommendations",
- "enable_series_recommendations": "Series Recommendations",
- "enable_promoted_watchlists": "Promoted Watchlists",
- "hide_watchlists_tab": "Hide Watchlists Tab",
- "home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
- "recommended_movies": "Recommended Movies",
- "recommended_series": "Recommended Series",
- "toasts": {
- "saved": "Saved",
- "refreshed": "Settings refreshed from server",
- "disabled": "Streamystats disabled"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "kefinTweaks": {
- "watchlist_enabler": "Enable our Watchlist integration",
- "watchlist_button": "Toggle Watchlist integration"
+ "saved": "保存しました"
+ }
}
},
"storage": {
"storage_title": "ストレージ",
"app_usage": "アプリ {{usedSpace}}%",
- "device_usage": "デバイス {{availableSpace}}%",
+ "phone_usage": "電話 {{availableSpace}}%",
"size_used": "{{used}} / {{total}} 使用済み",
- "delete_all_downloaded_files": "すべてのダウンロードファイルを削除",
- "music_cache_title": "Music Cache",
- "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
- "enable_music_cache": "Enable Music Cache",
- "clear_music_cache": "Clear Music Cache",
- "music_cache_size": "{{size}} cached",
- "music_cache_cleared": "Music cache cleared",
- "delete_all_downloaded_songs": "Delete All Downloaded Songs",
- "downloaded_songs_size": "{{size}} downloaded",
- "downloaded_songs_deleted": "Downloaded songs deleted"
+ "delete_all_downloaded_files": "すべてのダウンロードファイルを削除"
},
"intro": {
- "title": "イントロ",
"show_intro": "イントロを表示",
"reset_intro": "イントロをリセット"
},
"logs": {
"logs_title": "ログ",
- "export_logs": "Export logs",
- "click_for_more_info": "Click for more info",
- "level": "レベル",
"no_logs_available": "ログがありません",
"delete_all_logs": "すべてのログを削除"
},
"languages": {
"title": "言語",
"app_language": "アプリの言語",
+ "app_language_description": "アプリの言語を選択。",
"system": "システム"
},
"toasts": {
"error_deleting_files": "ファイルの削除エラー",
"background_downloads_enabled": "バックグラウンドでのダウンロードは有効です",
- "background_downloads_disabled": "バックグラウンドでのダウンロードは無効です"
+ "background_downloads_disabled": "バックグラウンドでのダウンロードは無効です",
+ "connected": "接続済み",
+ "could_not_connect": "接続できません",
+ "invalid_url": "無効なURL"
}
},
- "sessions": {
- "title": "セッション",
- "no_active_sessions": "アクティブなセッションはありません"
- },
"downloads": {
"downloads_title": "ダウンロード",
"tvseries": "TVシリーズ",
"movies": "映画",
"queue": "キュー",
- "other_media": "その他のメディア",
"queue_hint": "アプリを再起動するとキューとダウンロードは失われます",
"no_items_in_queue": "キューにアイテムがありません",
"no_downloaded_items": "ダウンロードしたアイテムはありません",
"delete_all_movies_button": "すべての映画を削除",
"delete_all_tvseries_button": "すべてのシリーズを削除",
"delete_all_button": "すべて削除",
- "delete_all_other_media_button": "他のメディアを削除する",
"active_download": "アクティブなダウンロード",
"no_active_downloads": "アクティブなダウンロードはありません",
"active_downloads": "アクティブなダウンロード",
@@ -459,57 +233,35 @@
"something_went_wrong": "問題が発生しました",
"could_not_get_stream_url_from_jellyfin": "JellyfinからストリームURLを取得できませんでした",
"eta": "ETA {{eta}}",
+ "methods": "方法",
"toasts": {
"you_are_not_allowed_to_download_files": "ファイルをダウンロードする権限がありません。",
"deleted_all_movies_successfully": "すべての映画を正常に削除しました!",
"failed_to_delete_all_movies": "すべての映画を削除できませんでした",
"deleted_all_tvseries_successfully": "すべてのシリーズを正常に削除しました!",
"failed_to_delete_all_tvseries": "すべてのシリーズを削除できませんでした",
- "deleted_media_successfully": "他のメディアを削除しました!",
- "failed_to_delete_media": "他のメディアの削除に失敗しました",
- "download_deleted": "ダウンロードが削除されました",
"download_cancelled": "ダウンロードをキャンセルしました",
- "could_not_delete_download": "ダウンロードを削除できませんでした",
- "download_paused": "ダウンロードを一時停止しました",
- "could_not_pause_download": "ダウンロードを一時停止できませんでした",
- "download_resumed": "ダウンロード再開",
- "could_not_resume_download": "ダウンロードを再開できませんでした",
+ "could_not_cancel_download": "ダウンロードをキャンセルできませんでした",
"download_completed": "ダウンロードが完了しました",
- "download_failed": "ダウンロードに失敗しました",
+ "download_started_for": "{{item}}のダウンロードが開始されました",
+ "item_is_ready_to_be_downloaded": "{{item}}をダウンロードする準備ができました",
+ "download_stated_for_item": "{{item}}のダウンロードが開始されました",
"download_failed_for_item": "{{item}}のダウンロードに失敗しました - {{error}}",
"download_completed_for_item": "{{item}}のダウンロードが完了しました",
- "download_started_for_item": "{{item}} のダウンロードを開始しました",
- "failed_to_start_download": "ダウンロードの開始に失敗しました",
- "item_already_downloading": "{{item}} is already downloading",
- "all_files_deleted": "All Downloads Deleted Successfully",
- "files_deleted_by_type": "{{count}} {{type}} deleted",
+ "queued_item_for_optimization": "{{item}}をoptimizeのキューに追加しました",
+ "failed_to_start_download_for_item": "{{item}}のダウンロードを開始できませんでした: {{message}}",
+ "server_responded_with_status_code": "サーバーはステータス{{statusCode}}で応答しました",
+ "no_response_received_from_server": "サーバーからの応答がありません",
+ "error_setting_up_the_request": "リクエストの設定中にエラーが発生しました",
+ "failed_to_start_download_for_item_unexpected_error": "{{item}}のダウンロードを開始できませんでした: 予期しないエラーが発生しました",
"all_files_folders_and_jobs_deleted_successfully": "すべてのファイル、フォルダ、ジョブが正常に削除されました",
- "failed_to_clean_cache_directory": "キャッシュディレクトリのクリーンアップに失敗しました",
- "could_not_get_download_url_for_item": "{{itemName}} のダウンロードURLを取得できませんでした",
- "go_to_downloads": "ダウンロードに移動",
- "file_deleted": "{{item}} deleted"
+ "an_error_occured_while_deleting_files_and_jobs": "ファイルとジョブの削除中にエラーが発生しました",
+ "go_to_downloads": "ダウンロードに移動"
}
}
},
- "common": {
- "select": "選択",
- "no_trailer_available": "トレーラーがありません",
- "video": "映像",
- "audio": "音声",
- "subtitle": "字幕",
- "play": "再生",
- "none": "None",
- "track": "Track",
- "cancel": "Cancel",
- "delete": "Delete",
- "ok": "OK",
- "remove": "Remove",
- "next": "Next",
- "back": "Back",
- "continue": "Continue",
- "verifying": "Verifying..."
- },
"search": {
+ "search_here": "ここを検索...",
"search": "検索...",
"x_items": "{{count}}のアイテム",
"library": "ライブラリ",
@@ -521,10 +273,6 @@
"episodes": "エピソード",
"collections": "コレクション",
"actors": "俳優",
- "artists": "Artists",
- "albums": "Albums",
- "songs": "Songs",
- "playlists": "Playlists",
"request_movies": "映画をリクエスト",
"request_series": "シリーズをリクエスト",
"recently_added": "最近の追加",
@@ -550,6 +298,7 @@
"tmdb_tv_streaming_services": "TMDBシリーズストリーミングサービス"
},
"library": {
+ "no_items_found": "アイテムが見つかりません",
"no_results": "検索結果はありません",
"no_libraries_found": "ライブラリが見つかりません",
"item_types": {
@@ -572,7 +321,6 @@
"genres": "ジャンル",
"years": "年",
"sort_by": "ソート",
- "filter_by": "Filter By",
"sort_order": "ソート順",
"tags": "タグ"
}
@@ -583,9 +331,7 @@
"episodes": "エピソード",
"videos": "ビデオ",
"boxsets": "ボックスセット",
- "playlists": "プレイリスト",
- "noDataTitle": "お気に入りはまだありません",
- "noData": "アイテムをお気に入りとしてマークすると、ここに表示されクイックアクセスできるようになります。"
+ "playlists": "プレイリスト"
},
"custom_links": {
"no_links": "リンクがありません"
@@ -596,19 +342,16 @@
"an_error_occured_while_playing_the_video": "動画の再生中にエラーが発生しました。設定でログを確認してください。",
"client_error": "クライアントエラー",
"could_not_create_stream_for_chromecast": "Chromecastのストリームを作成できませんでした",
- "message_from_server": "サーバーからのメッセージ",
+ "message_from_server": "サーバーからのメッセージ: {{message}}",
+ "video_has_finished_playing": "ビデオの再生が終了しました!",
+ "no_video_source": "動画ソースがありません...",
"next_episode": "次のエピソード",
"refresh_tracks": "トラックを更新",
+ "subtitle_tracks": "字幕トラック:",
"audio_tracks": "音声トラック:",
"playback_state": "再生状態:",
- "index": "インデックス:",
- "continue_watching": "視聴を続ける",
- "go_back": "戻る",
- "downloaded_file_title": "You have this file downloaded",
- "downloaded_file_message": "Do you want to play the downloaded file?",
- "downloaded_file_yes": "Yes",
- "downloaded_file_no": "No",
- "downloaded_file_cancel": "Cancel"
+ "no_data_available": "データなし",
+ "index": "インデックス:"
},
"item_card": {
"next_up": "次",
@@ -624,7 +367,6 @@
"no_similar_items_found": "類似のアイテムは見つかりませんでした",
"video": "映像",
"more_details": "さらに詳細を表示",
- "media_options": "Media Options",
"quality": "画質",
"audio": "音声",
"subtitles": "字幕",
@@ -639,13 +381,15 @@
"download_episode": "エピソードをダウンロード",
"download_movie": "映画をダウンロード",
"download_x_item": "{{item_count}}のアイテムをダウンロード",
- "download_unwatched_only": "未視聴のみ",
- "download_button": "ダウンロード"
+ "download_button": "ダウンロード",
+ "using_optimized_server": "Optimizeサーバーを使用する",
+ "using_default_method": "デフォルトの方法を使用"
}
},
"live_tv": {
"next": "次",
"previous": "前",
+ "live_tv": "ライブTV",
"coming_soon": "近日公開",
"on_now": "現在",
"shows": "表示",
@@ -688,15 +432,11 @@
"tags": "タグ",
"quality_profile": "画質プロファイル",
"root_folder": "ルートフォルダ",
- "season_all": "Season (all)",
+ "season_x": "シーズン{{seasons}}",
"season_number": "シーズン{{season_number}}",
"number_episodes": "エピソード{{episode_number}}",
"born": "生まれ",
"appearances": "出演",
- "approve": "Approve",
- "decline": "Decline",
- "requested_by": "Requested by {{user}}",
- "unknown_user": "Unknown User",
"toasts": {
"jellyseer_does_not_meet_requirements": "Jellyseerrサーバーは最小バージョン要件を満たしていません。少なくとも 2.0.0 に更新してください。",
"jellyseerr_test_failed": "Jellyseerrテストに失敗しました。もう一度お試しください。",
@@ -704,11 +444,7 @@
"issue_submitted": "チケットを送信しました!",
"requested_item": "{{item}}をリクエスト!",
"you_dont_have_permission_to_request": "リクエストする権限がありません!",
- "something_went_wrong_requesting_media": "メディアのリクエスト中に問題が発生しました。",
- "request_approved": "Request Approved!",
- "request_declined": "Request Declined!",
- "failed_to_approve_request": "Failed to Approve Request",
- "failed_to_decline_request": "Failed to Decline Request"
+ "something_went_wrong_requesting_media": "メディアのリクエスト中に問題が発生しました。"
}
},
"tabs": {
@@ -717,129 +453,5 @@
"library": "ライブラリ",
"custom_links": "カスタムリンク",
"favorites": "お気に入り"
- },
- "music": {
- "title": "Music",
- "tabs": {
- "suggestions": "Suggestions",
- "albums": "Albums",
- "artists": "Artists",
- "playlists": "Playlists",
- "tracks": "tracks"
- },
- "filters": {
- "all": "All"
- },
- "recently_added": "Recently Added",
- "recently_played": "Recently Played",
- "frequently_played": "Frequently Played",
- "explore": "Explore",
- "top_tracks": "Top Tracks",
- "play": "Play",
- "shuffle": "Shuffle",
- "play_top_tracks": "Play Top Tracks",
- "no_suggestions": "No suggestions available",
- "no_albums": "No albums found",
- "no_artists": "No artists found",
- "no_playlists": "No playlists found",
- "album_not_found": "Album not found",
- "artist_not_found": "Artist not found",
- "playlist_not_found": "Playlist not found",
- "track_options": {
- "play_next": "Play Next",
- "add_to_queue": "Add to Queue",
- "add_to_playlist": "Add to Playlist",
- "download": "Download",
- "downloaded": "Downloaded",
- "downloading": "Downloading...",
- "cached": "Cached",
- "delete_download": "Delete Download",
- "delete_cache": "Remove from Cache",
- "go_to_artist": "Go to Artist",
- "go_to_album": "Go to Album",
- "add_to_favorites": "Add to Favorites",
- "remove_from_favorites": "Remove from Favorites",
- "remove_from_playlist": "Remove from Playlist"
- },
- "playlists": {
- "create_playlist": "Create Playlist",
- "playlist_name": "Playlist Name",
- "enter_name": "Enter playlist name",
- "create": "Create",
- "search_playlists": "Search playlists...",
- "added_to": "Added to {{name}}",
- "added": "Added to playlist",
- "removed_from": "Removed from {{name}}",
- "removed": "Removed from playlist",
- "created": "Playlist created",
- "create_new": "Create New Playlist",
- "failed_to_add": "Failed to add to playlist",
- "failed_to_remove": "Failed to remove from playlist",
- "failed_to_create": "Failed to create playlist",
- "delete_playlist": "Delete Playlist",
- "delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "deleted": "Playlist deleted",
- "failed_to_delete": "Failed to delete playlist"
- },
- "sort": {
- "title": "Sort By",
- "alphabetical": "Alphabetical",
- "date_created": "Date Created"
- }
- },
- "watchlists": {
- "title": "Watchlists",
- "my_watchlists": "My Watchlists",
- "public_watchlists": "Public Watchlists",
- "create_title": "Create Watchlist",
- "edit_title": "Edit Watchlist",
- "create_button": "Create Watchlist",
- "save_button": "Save Changes",
- "delete_button": "Delete",
- "remove_button": "Remove",
- "cancel_button": "Cancel",
- "name_label": "Name",
- "name_placeholder": "Enter watchlist name",
- "description_label": "Description",
- "description_placeholder": "Enter description (optional)",
- "is_public_label": "Public Watchlist",
- "is_public_description": "Allow others to view this watchlist",
- "allowed_type_label": "Content Type",
- "sort_order_label": "Default Sort Order",
- "empty_title": "No Watchlists",
- "empty_description": "Create your first watchlist to start organizing your media",
- "empty_watchlist": "This watchlist is empty",
- "empty_watchlist_hint": "Add items from your library to this watchlist",
- "not_configured_title": "Streamystats Not Configured",
- "not_configured_description": "Configure Streamystats in settings to use watchlists",
- "go_to_settings": "Go to Settings",
- "add_to_watchlist": "Add to Watchlist",
- "remove_from_watchlist": "Remove from Watchlist",
- "select_watchlist": "Select Watchlist",
- "create_new": "Create New Watchlist",
- "item": "item",
- "items": "items",
- "public": "Public",
- "private": "Private",
- "you": "You",
- "by_owner": "By another user",
- "not_found": "Watchlist not found",
- "delete_confirm_title": "Delete Watchlist",
- "delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "remove_item_title": "Remove from Watchlist",
- "remove_item_message": "Remove \"{{name}}\" from this watchlist?",
- "loading": "Loading watchlists...",
- "no_compatible_watchlists": "No compatible watchlists",
- "create_one_first": "Create a watchlist that accepts this content type"
- },
- "playback_speed": {
- "title": "Playback Speed",
- "apply_to": "Apply To",
- "speed": "Speed",
- "scope": {
- "media": "This media only",
- "show": "This show",
- "all": "All media (default)"
- }
}
}
diff --git a/translations/nl.json b/translations/nl.json
index 326f9a1e..929224c9 100644
--- a/translations/nl.json
+++ b/translations/nl.json
@@ -9,20 +9,18 @@
"login_button": "Aanmelden",
"quick_connect": "Snel Verbinden",
"enter_code_to_login": "Vul code {{code}} in om aan te melden",
- "failed_to_initiate_quick_connect": "Mislukt om Snel Verbinden op te starten",
+ "failed_to_initiate_quick_connect": "Gefaald om Snel Verbinden op te starten",
"got_it": "Begrepen",
- "connection_failed": "Verbinding mislukt",
+ "connection_failed": "Verbinding gefaald",
"could_not_connect_to_server": "Kon niet verbinden met de server. Controleer de URL en je netwerkverbinding.",
"an_unexpected_error_occured": "Er is een onverwachte fout opgetreden",
- "change_server": "Server wijzigen",
- "invalid_username_or_password": "Onjuiste gebruikersnaam of wachtwoord",
+ "change_server": "Verander server",
+ "invalid_username_or_password": "Ongeldige gebruikersnaam of wachtwoord",
"user_does_not_have_permission_to_log_in": "Gebruiker heeft geen rechten om aan te melden",
"server_is_taking_too_long_to_respond_try_again_later": "De server doet er te lang over om te antwoorden, probeer later opnieuw",
"server_received_too_many_requests_try_again_later": "De server heeft te veel aanvragen ontvangen, probeer later opnieuw",
"there_is_a_server_error": "Er is een serverfout",
- "an_unexpected_error_occured_did_you_enter_the_correct_url": "Er is een onverwachte fout opgetreden. Heb je de server URL correct ingegeven?",
- "too_old_server_text": "Niet-ondersteunde Jellyfin Server Ontdekt",
- "too_old_server_description": "Werk Jellyfin bij naar de laatste versie"
+ "an_unexpected_error_occured_did_you_enter_the_correct_url": "Er is een onverwachte fout opgetreden. Heb je de server URL correct ingegeven?"
},
"server": {
"enter_url_to_jellyfin_server": "Geef de URL van je Jellyfin server in",
@@ -30,66 +28,21 @@
"connect_button": "Verbinden",
"previous_servers": "vorige servers",
"clear_button": "Wissen",
- "swipe_to_remove": "Swipe to remove",
"search_for_local_servers": "Zoek naar lokale servers",
"searching": "Zoeken...",
- "servers": "Servers",
- "saved": "Opgeslagen",
- "session_expired": "Sessie verlopen",
- "please_login_again": "Uw gebruikerssessie is verlopen. U moet opnieuw inloggen.",
- "remove_saved_login": "Opgeslagen login verwijderen",
- "remove_saved_login_description": "Hiermee worden uw opgeslagen gegevens voor deze server verwijderd. U moet uw gebruikersnaam en wachtwoord de volgende keer opnieuw invoeren.",
- "accounts_count": "{{count}} accounts",
- "select_account": "Select Account",
- "add_account": "Add Account",
- "remove_account_description": "This will remove the saved credentials for {{username}}."
- },
- "save_account": {
- "title": "Save Account",
- "save_for_later": "Save this account",
- "security_option": "Security Option",
- "no_protection": "No protection",
- "no_protection_desc": "Quick login without authentication",
- "pin_code": "PIN code",
- "pin_code_desc": "4-digit PIN required when switching",
- "password": "Re-enter password",
- "password_desc": "Password required when switching",
- "save_button": "Save",
- "cancel_button": "Cancel"
- },
- "pin": {
- "enter_pin": "Enter PIN",
- "enter_pin_for": "Enter PIN for {{username}}",
- "enter_4_digits": "Enter 4 digits",
- "invalid_pin": "Invalid PIN",
- "setup_pin": "Set Up PIN",
- "confirm_pin": "Confirm PIN",
- "pins_dont_match": "PINs don't match",
- "forgot_pin": "Forgot PIN?",
- "forgot_pin_desc": "Your saved credentials will be removed"
- },
- "password": {
- "enter_password": "Enter Password",
- "enter_password_for": "Enter password for {{username}}",
- "invalid_password": "Invalid password"
+ "servers": "Servers"
},
"home": {
- "checking_server_connection": "Serververbinding controleren...",
"no_internet": "Geen Internet",
"no_items": "Geen items",
"no_internet_message": "Geen zorgen, je kan nog steeds\ngedownloade content bekijken",
- "checking_server_connection_message": "Verbinding met server controleren",
"go_to_downloads": "Ga naar downloads",
- "retry": "Opnieuw",
- "server_unreachable": "Server onbereikbaar",
- "server_unreachable_message": "Kon de server niet bereiken.\nControleer uw netwerkverbinding.",
"oops": "Oeps!",
"error_message": "Er ging iets fout\nGelieve af en aan te melden.",
"continue_watching": "Verder Kijken",
"next_up": "Volgende",
- "continue_and_next_up": "Doorgaan & Volgende",
"recently_added_in": "Recent toegevoegd in {{libraryName}}",
- "suggested_movies": "Voorgestelde films",
+ "suggested_movies": "Voorgestelde Films",
"suggested_episodes": "Voorgestelde Afleveringen",
"intro": {
"welcome_to_streamyfin": "Welkom bij Streamyfin",
@@ -103,54 +56,12 @@
"centralised_settings_plugin_title": "Plugin voor gecentraliseerde instellingen",
"centralised_settings_plugin_description": "Configureer instellingen vanaf een centrale locatie op je Jellyfin server. Alle clientinstellingen voor alle gebruikers worden automatisch gesynchroniseerd.",
"done_button": "Gedaan",
- "go_to_settings_button": "Ga naar instellingen",
+ "go_to_settings_button": "Go naar instellingen",
"read_more": "Lees meer"
},
"settings": {
"settings_title": "Instellingen",
"log_out_button": "Afmelden",
- "categories": {
- "title": "Categorieën"
- },
- "playback_controls": {
- "title": "Afspelen & Bediening"
- },
- "audio_subtitles": {
- "title": "Geluid & Ondertiteling"
- },
- "appearance": {
- "title": "Weergave",
- "merge_next_up_continue_watching": "Doorgaan met kijken & Volgende samenvoegen",
- "hide_remote_session_button": "Verberg Knop voor Externe Sessie"
- },
- "network": {
- "title": "Network",
- "local_network": "Local Network",
- "auto_switch_enabled": "Auto-switch when at home",
- "auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
- "local_url": "Local URL",
- "local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
- "local_url_placeholder": "http://192.168.1.100:8096",
- "home_wifi_networks": "Home WiFi Networks",
- "add_current_network": "Add \"{{ssid}}\"",
- "not_connected_to_wifi": "Not connected to WiFi",
- "no_networks_configured": "No networks configured",
- "add_network_hint": "Add your home WiFi network to enable auto-switching",
- "current_wifi": "Current WiFi",
- "using_url": "Using",
- "local": "Local URL",
- "remote": "Remote URL",
- "not_connected": "Not connected",
- "current_server": "Current Server",
- "remote_url": "Remote URL",
- "active_url": "Active URL",
- "not_configured": "Not configured",
- "network_added": "Network added",
- "network_already_added": "Network already added",
- "no_wifi_connected": "Not connected to WiFi",
- "permission_denied": "Location permission denied",
- "permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
- },
"user_info": {
"user_info_title": "Gebruiker Info",
"user": "Gebruiker",
@@ -171,45 +82,24 @@
"media_controls": {
"media_controls_title": "Media Bedieningen",
"forward_skip_length": "Duur voorwaarts overslaan",
- "rewind_length": "Duur terugspoelen",
+ "rewind_length": "Duur terugspeolen",
"seconds_unit": "s"
},
- "gesture_controls": {
- "gesture_controls_title": "Gebaar Bediening",
- "horizontal_swipe_skip": "Horizontale Swipe om over te slaan",
- "horizontal_swipe_skip_description": "Veeg naar links/rechts wanneer de knoppen verborgen zijn om over te slaan",
- "left_side_brightness": "Linker Zijkant Helderheidscontrole",
- "left_side_brightness_description": "Veeg omhoog/omlaag aan linker kant om helderheid aan te passen",
- "right_side_volume": "Rechterkant Volume Controle",
- "right_side_volume_description": "Veeg aan rechterzijde omhoog/omlaag om volume aan te passen",
- "hide_volume_slider": "Verberg Volumeschuifbalk",
- "hide_volume_slider_description": "Volumeschuifbalk in de videospeler verbergen",
- "hide_brightness_slider": "Verberg Helderheidsschuifbalk",
- "hide_brightness_slider_description": "Helderheidsschuifbalk in de videospeler verbergen"
- },
"audio": {
- "audio_title": "Geluid",
+ "audio_title": "Audio",
"set_audio_track": "Gebruik Audio Track Van Vorig Item",
"audio_language": "Audio taal",
"audio_hint": "Kies een standaard audio taal.",
"none": "Geen",
- "language": "Taal",
- "transcode_mode": {
- "title": "Audio Transcoding",
- "description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
- "auto": "Auto",
- "stereo": "Force Stereo",
- "5_1": "Allow 5.1",
- "passthrough": "Passthrough"
- }
+ "language": "Taal"
},
"subtitles": {
"subtitle_title": "Ondertitels",
- "subtitle_hint": "Stel ondertitel voorkeuren in.",
"subtitle_language": "Ondertitel taal",
- "subtitle_mode": "Ondertitelmodus",
+ "subtitle_mode": "Ondertitle Modus",
"set_subtitle_track": "Gebruik Ondertitel Track Van Vorig Item",
"subtitle_size": "Ondertitel Grootte",
+ "subtitle_hint": "Stel ondertitel voorkeuren in.",
"none": "Geen",
"language": "Taal",
"loading": "Laden",
@@ -218,67 +108,12 @@
"Smart": "Slim",
"Always": "Altijd",
"None": "Geen",
- "OnlyForced": "Alleen Geforceerd"
- },
- "text_color": "Tekst kleur",
- "background_color": "Achtergrond Kleur",
- "outline_color": "Kleur omlijning",
- "outline_thickness": "Dikte omlijning",
- "background_opacity": "Transparantie achtergrond",
- "outline_opacity": "Doorzichtigheid omlijning",
- "bold_text": "Bold Text",
- "colors": {
- "Black": "Zwart",
- "Gray": "Grijs",
- "Silver": "Zilver",
- "White": "wit",
- "Maroon": "Kastanjebruin",
- "Red": "Rood",
- "Fuchsia": "Fuchsia",
- "Yellow": "Geel",
- "Olive": "Olijf",
- "Green": "Groen",
- "Teal": "Groenblauw",
- "Lime": "Lichtgroen",
- "Purple": "Paars",
- "Navy": "Marine",
- "Blue": "Blauw",
- "Aqua": "Aqua"
- },
- "thickness": {
- "None": "Geen",
- "Thin": "Dun",
- "Normal": "normaal",
- "Thick": "Dikke"
- },
- "subtitle_color": "Kleur ondertiteling",
- "subtitle_background_color": "Achtergrondkleur",
- "subtitle_font": "Lettertype ondertitels",
- "ksplayer_title": "KSPlayer Instellingen",
- "hardware_decode": "Hardware Acceleratie",
- "hardware_decode_description": "Gebruik hardware acceleratie voor video-decodering. Uitschakelen als u problemen met afspelen ondervindt."
- },
- "vlc_subtitles": {
- "title": "VLC Subtitle Settings",
- "hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
- "text_color": "Text Color",
- "background_color": "Background Color",
- "background_opacity": "Background Opacity",
- "outline_color": "Outline Color",
- "outline_opacity": "Outline Opacity",
- "outline_thickness": "Outline Thickness",
- "bold": "Bold Text",
- "margin": "Bottom Margin"
- },
- "video_player": {
- "title": "Videospeler",
- "video_player": "Videospeler",
- "video_player_description": "Kies welke videospeler gebruikt moet worden op iOS.",
- "ksplayer": "KSPlayer",
- "vlc": "VLC"
+ "OnlyForced": "Alleen Geforceeerd"
+ }
},
"other": {
"other_title": "Andere",
+ "auto_rotate": "Automatisch draaien",
"video_orientation": "Video oriëntatie",
"orientation": "Oriëntatie",
"orientations": {
@@ -294,102 +129,57 @@
"UNKNOWN": "Onbekend"
},
"safe_area_in_controls": "Veilig gebied in bedieningen",
- "video_player": "Video player",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Experimentele + PiP)"
- },
"show_custom_menu_links": "Aangepaste menulinks tonen",
- "show_large_home_carousel": "Toon grote carrousel op startpagina (bèta)",
"hide_libraries": "Verberg Bibliotheken",
- "select_liraries_you_want_to_hide": "Selecteer de bibliotheken die je wil verbergen van de Bibliotheektab en hoofdpagina onderdelen.",
+ "select_liraries_you_want_to_hide": "Selecteer de bibliotheken die je wil verbergen van de Bibliotheek tab en hoofdpagina onderdelen.",
"disable_haptic_feedback": "Haptische feedback uitschakelen",
- "default_quality": "Standaard kwaliteit",
- "default_playback_speed": "Standaard Afspeelsnelheid",
- "auto_play_next_episode": "Auto-play Next Episode",
- "max_auto_play_episode_count": "Max Automatisch Aflevering Aantal",
- "disabled": "Uitgeschakeld"
+ "default_quality": "Standaard kwaliteit"
},
"downloads": {
- "downloads_title": "Downloads"
- },
- "music": {
- "title": "Muziek",
- "playback_title": "Afspelen",
- "playback_description": "Configureer hoe muziek wordt afgespeeld.",
- "prefer_downloaded": "Voorkeur voor Gedownloade Nummers",
- "caching_title": "Cachen",
- "caching_description": "Automatisch aankomende nummers cachen om soepeler af te spelen.",
- "lookahead_enabled": "Look-Ahead Caching inschakelen",
- "lookahead_count": "Nummers om te Pre-cachen",
- "max_cache_size": "Max Cache Grootte"
+ "downloads_title": "Downloads",
+ "download_method": "Download methode",
+ "remux_max_download": "Remux max download",
+ "auto_download": "Auto download",
+ "optimized_versions_server": "Geoptimaliseerde server versies",
+ "save_button": "Opslaan",
+ "optimized_server": "Geoptimailseerde Server",
+ "optimized": "Geoptimaliseerd",
+ "default": "Standaard",
+ "optimized_version_hint": "Vul de URL van de optimalisatieserver in. De URL moet http of https bevatten en eventueel de poort.",
+ "read_more_about_optimized_server": "Lees meer over de optimalisatieserver.",
+ "url":"URL",
+ "server_url_placeholder": "http(s)://domein.org:poort"
},
"plugins": {
- "plugins_title": "Uitbreidingen",
+ "plugins_title": "Plugins",
"jellyseerr": {
"jellyseerr_warning": "Deze integratie is nog in een vroeg stadium. Verwacht dat zaken nog veranderen.",
- "server_url": "Server-URL",
+ "server_url": "Server URL",
"server_url_hint": "Voorbeeld: http(s)://je-host.url\n(indien nodig: voeg de poort toe)",
"server_url_placeholder": "Jellyseerr URL...",
"password": "Wachtwoord",
"password_placeholder": "Voeg het wachtwoord in voor de Jellyfin gebruiker {{username}}",
- "login_button": "Aanmelden",
+ "save_button": "Opslaan",
+ "clear_button": "Wissen",
+ "login_button": "Aannmelden",
"total_media_requests": "Totaal aantal mediaverzoeken",
"movie_quota_limit": "Limiet filmquota",
"movie_quota_days": "Filmquota dagen",
"tv_quota_limit": "Limiet serie quota",
"tv_quota_days": "Serie Quota dagen",
"reset_jellyseerr_config_button": "Jellyseerr opnieuw instellen",
- "unlimited": "Ongelimiteerd",
- "plus_n_more": "+{{n}} more",
- "order_by": {
- "DEFAULT": "Standaard",
- "VOTE_COUNT_AND_AVERAGE": "Aantal stemmen en gemiddeld",
- "POPULARITY": "Populariteit"
- }
+ "unlimited": "Ongelimiteerd"
},
"marlin_search": {
- "enable_marlin_search": "Marlin Search inschakelen ",
+ "enable_marlin_search": "Marlin Search inschakeln ",
"url": "URL",
"server_url_placeholder": "http(s)://domein.org:poort",
"marlin_search_hint": "Vul de URL van de Marlin Search server in. De URL moet http of https bevatten en eventueel de poort.",
"read_more_about_marlin": "Lees meer over Marlin.",
"save_button": "Opslaan",
"toasts": {
- "saved": "Opgeslagen",
- "refreshed": "Instellingen zijn vernieuwd vanaf server"
- },
- "refresh_from_server": "Ververs Instellingen van Server"
- },
- "streamystats": {
- "enable_streamystats": "Streamystats inschakelen",
- "disable_streamystats": "Streamystats Uitschakelen",
- "enable_search": "Gebruik voor Zoeken",
- "url": "URL",
- "server_url_placeholder": "http(s)://streamystats.example.com",
- "streamystats_search_hint": "Vul de URL van de Streamystats server in. De URL moet http of https bevatten en optioneel de poort.",
- "read_more_about_streamystats": "Lees Meer over Streamystats.",
- "save_button": "Opslaan",
- "save": "Opslaan",
- "features_title": "Functies",
- "home_sections_title": "Thuis Secties",
- "enable_movie_recommendations": "Film Aanbevelingen",
- "enable_series_recommendations": "Series Aanbevelingen",
- "enable_promoted_watchlists": "Gepromote Kijklijst",
- "hide_watchlists_tab": "Hide Watchlists Tab",
- "home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
- "recommended_movies": "Recommended Movies",
- "recommended_series": "Recommended Series",
- "toasts": {
- "saved": "Saved",
- "refreshed": "Settings refreshed from server",
- "disabled": "Streamystats disabled"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "kefinTweaks": {
- "watchlist_enabler": "Enable our Watchlist integration",
- "watchlist_button": "Toggle Watchlist integration"
+ "saved": "Opgeslagen"
+ }
}
},
"storage": {
@@ -397,58 +187,43 @@
"app_usage": "App {{usedSpace}}%",
"device_usage": "Toestel {{availableSpace}}%",
"size_used": "{{used}} van {{total}} gebruikt",
- "delete_all_downloaded_files": "Verwijder alle gedownloade bestanden",
- "music_cache_title": "Music Cache",
- "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
- "enable_music_cache": "Enable Music Cache",
- "clear_music_cache": "Clear Music Cache",
- "music_cache_size": "{{size}} cached",
- "music_cache_cleared": "Music cache cleared",
- "delete_all_downloaded_songs": "Delete All Downloaded Songs",
- "downloaded_songs_size": "{{size}} downloaded",
- "downloaded_songs_deleted": "Downloaded songs deleted"
+ "delete_all_downloaded_files": "Verwijder alle gedownloade bestanden"
},
"intro": {
- "title": "Intro",
"show_intro": "Toon intro",
"reset_intro": "intro opnieuw instellen"
},
"logs": {
- "logs_title": "Logboek",
- "export_logs": "Export logs",
- "click_for_more_info": "Click for more info",
- "level": "Niveau",
+ "logs_title": "Logs",
"no_logs_available": "Geen logs beschikbaar",
"delete_all_logs": "Verwijder alle logs"
},
"languages": {
"title": "Talen",
"app_language": "App taal",
+ "app_language_description": "Selecteer een taal voor de app.",
"system": "Systeem"
},
- "toasts": {
- "error_deleting_files": "Fout bij het verwijderen van bestanden",
+ "toasts":{
+ "error_deleting_files": "Fout bij het verwijden van bestanden",
"background_downloads_enabled": "Downloads op de achtergrond ingeschakeld",
- "background_downloads_disabled": "Downloads op de achtergrond uitgeschakeld"
+ "background_downloads_disabled": "Downloads op de achtergrond uitgeschakeld",
+ "connected": "Verbonden",
+ "could_not_connect": "Kon niet verbinden",
+ "invalid_url": "Ongeldige URL"
}
},
- "sessions": {
- "title": "Sessies",
- "no_active_sessions": "Geen actieve sessies"
- },
"downloads": {
"downloads_title": "Downloads",
"tvseries": "Series",
"movies": "Films",
"queue": "Wachtrij",
- "other_media": "Andere media",
"queue_hint": "Wachtrij en downloads verdwijnen bij een herstart van de app",
"no_items_in_queue": "Geen items in wachtrij",
"no_downloaded_items": "Geen gedownloade items",
"delete_all_movies_button": "Verwijder alle films",
"delete_all_tvseries_button": "Verwijder alle Series",
"delete_all_button": "Verwijder alles",
- "delete_all_other_media_button": "Andere media verwijderen",
"active_download": "Actieve download",
"no_active_downloads": "Geen actieve downloads",
"active_downloads": "Actieve downloads",
@@ -459,57 +234,35 @@
"something_went_wrong": "Er ging iets mis",
"could_not_get_stream_url_from_jellyfin": "Kon de URL van de stream niet krijgen van Jellyfin",
"eta": "ETA {{eta}}",
+ "methods": "Methoden",
"toasts": {
"you_are_not_allowed_to_download_files": "Je mag geen bestanden downloaden.",
- "deleted_all_movies_successfully": "Alle films succesvol verwijderd!",
+ "deleted_all_movies_successfully": "Alle filns succesvol verwijderd!",
"failed_to_delete_all_movies": "Alle films zijn niet verwijderd",
"deleted_all_tvseries_successfully": "Alle series succesvol verwijderd!",
"failed_to_delete_all_tvseries": "Alle series zijn niet verwijderd",
- "deleted_media_successfully": "Andere media succesvol verwijderd!",
- "failed_to_delete_media": "Verwijderen van andere media mislukt",
- "download_deleted": "Download verwijderd",
"download_cancelled": "Download geannuleerd",
- "could_not_delete_download": "Kon download niet verwijderen",
- "download_paused": "Download gepauzeerd",
- "could_not_pause_download": "Kan niet pauzeren download",
- "download_resumed": "Download hervat",
- "could_not_resume_download": "Kon de download niet hervatten",
+ "could_not_cancel_download": "Kon de download niet annuleren",
"download_completed": "Download afgerond",
- "download_failed": "Download Mislukt",
+ "download_started_for": "Download gestart voor {{item}}",
+ "item_is_ready_to_be_downloaded": "{{item}} is klaar op te downloaden",
+ "download_stated_for_item": "Download gestart voor {{item}}",
"download_failed_for_item": "Download gefaald voor {{item}} - {{error}}",
"download_completed_for_item": "Download afgerond voor {{item}}",
- "download_started_for_item": "Download gestart voor {{item}}",
- "failed_to_start_download": "Kan download niet starten",
- "item_already_downloading": "{{item}} wordt al gedownload",
- "all_files_deleted": "Alle Bestanden Succesvol Gedownload",
- "files_deleted_by_type": "{{count}} {{type}} verwijderd",
+ "queued_item_for_optimization": "{{item}} in de wachtrij gezet voor optimalisatie",
+ "failed_to_start_download_for_item": "Kon de download voor {{item}} niet starten: {{message}}",
+ "server_responded_with_status_code": "Server heeft geantwoord met {{statusCode}}",
+ "no_response_received_from_server": "Geen antwoord gekregen van de server",
+ "error_setting_up_the_request": "Fout bij het opstellen van de aanvraag",
+ "failed_to_start_download_for_item_unexpected_error": "Kon de download voor {{item}} niet starten: Onverwachte fout",
"all_files_folders_and_jobs_deleted_successfully": "Alle bestanden, mappen en taken succesvol verwijderd",
- "failed_to_clean_cache_directory": "Opschonen cachemap mislukt",
- "could_not_get_download_url_for_item": "Kan download-URL voor {{itemName}} niet ophalen",
- "go_to_downloads": "Ga naar downloads",
- "file_deleted": "{{item}} verwijderd"
+ "an_error_occured_while_deleting_files_and_jobs": "Er is een fout opgetreden tijdens het verwijderen van bestanden en taken",
+ "go_to_downloads": "Ga naar downloads"
}
}
},
- "common": {
- "select": "Selecteren",
- "no_trailer_available": "Geen trailer beschikbaar",
- "video": "Video",
- "audio": "Audio",
- "subtitle": "Ondertitel",
- "play": "Afspelen",
- "none": "Geen",
- "track": "Spoor",
- "cancel": "Cancel",
- "delete": "Delete",
- "ok": "OK",
- "remove": "Remove",
- "next": "Next",
- "back": "Back",
- "continue": "Continue",
- "verifying": "Verifying..."
- },
"search": {
+ "search_here": "Zoek hier...",
"search": "Zoek...",
"x_items": "{{count}} items",
"library": "Bibliotheek",
@@ -521,28 +274,24 @@
"episodes": "Afleveringen",
"collections": "Collecties",
"actors": "Acteurs",
- "artists": "Artists",
- "albums": "Albums",
- "songs": "Songs",
- "playlists": "Playlists",
"request_movies": "Vraag films aan",
"request_series": "Vraag series aan",
"recently_added": "Recent Toegevoegd",
"recent_requests": "Recent Aangevraagd",
"plex_watchlist": "Plex Kijklijst",
- "trending": "Populair",
- "popular_movies": "Populaire films",
+ "trending": "Trending",
+ "popular_movies": "Populaire Films",
"movie_genres": "Film Genres",
- "upcoming_movies": "Aankomende films",
- "studios": "Studio's",
+ "upcoming_movies": "Aankomende Movies",
+ "studios": "Studios",
"popular_tv": "Populaire TV",
"tv_genres": "TV Genres",
- "upcoming_tv": "Aankomende TV",
+ "upcoming_tv": "Opkomend TV",
"networks": "Netwerken",
"tmdb_movie_keyword": "TMDB Film Trefwoord",
- "tmdb_movie_genre": "TMDB Filmgenres",
+ "tmdb_movie_genre": "TMDB Film Genre",
"tmdb_tv_keyword": "TMDB TV Trefwoord",
- "tmdb_tv_genre": "TMDB TV-Genres",
+ "tmdb_tv_genre": "TMDB TV Genre",
"tmdb_search": "TMDB Zoeken",
"tmdb_studio": "TMDB Studio",
"tmdb_network": "TMDB Netwerk",
@@ -550,12 +299,13 @@
"tmdb_tv_streaming_services": "TMDB TV Streaming Diensten"
},
"library": {
+ "no_items_found": "Geen items gevonden",
"no_results": "Geen resultaten",
"no_libraries_found": "Geen bibliotheken gevonden",
"item_types": {
- "movies": "Films",
- "series": "Series",
- "boxsets": "Boxsets",
+ "movies": "films",
+ "series": "series",
+ "boxsets": "box sets",
"items": "items"
},
"options": {
@@ -564,7 +314,7 @@
"list": "Lijst",
"image_style": "Stijl van afbeelding",
"poster": "Poster",
- "cover": "Omslag",
+ "cover": "Cover",
"show_titles": "Toon titels",
"show_stats": "Toon statistieken"
},
@@ -572,7 +322,6 @@
"genres": "Genres",
"years": "Jaren",
"sort_by": "Sorteren op",
- "filter_by": "Filter By",
"sort_order": "Sorteer volgorde",
"tags": "Labels"
}
@@ -583,9 +332,7 @@
"episodes": "Afleveringen",
"videos": "Videos",
"boxsets": "Boxsets",
- "playlists": "Afspeellijsten",
- "noDataTitle": "Nog geen favorieten",
- "noData": "Markeer items als favoriet om ze hier te laten verschijnen voor snelle toegang."
+ "playlists": "Afspeellijsten"
},
"custom_links": {
"no_links": "Geen links"
@@ -596,24 +343,21 @@
"an_error_occured_while_playing_the_video": "Er is een fout opgetreden tijdens het afspelen van de video. Controleer de logs in de instellingen.",
"client_error": "Fout van de client",
"could_not_create_stream_for_chromecast": "Kon geen stream maken voor Chromecast",
- "message_from_server": "Bericht van de server",
+ "message_from_server": "Bericht van de server: {{message}}",
+ "video_has_finished_playing": "Video is gedaan met spelen!",
+ "no_video_source": "Geen video bron...",
"next_episode": "Volgende Aflevering",
"refresh_tracks": "Tracks verversen",
+ "subtitle_tracks": "Ondertitel Tracks:",
"audio_tracks": "Audio Tracks:",
"playback_state": "Afspeelstatus:",
- "index": "Index:",
- "continue_watching": "Verder kijken",
- "go_back": "Terug",
- "downloaded_file_title": "Je hebt dit bestand gedownload",
- "downloaded_file_message": "Wil je het gedownloade bestand afspelen?",
- "downloaded_file_yes": "Ja",
- "downloaded_file_no": "Nee",
- "downloaded_file_cancel": "Annuleren"
+ "no_data_available": "Geen data beschikbaar",
+ "index": "Index:"
},
"item_card": {
"next_up": "Volgende",
"no_items_to_display": "Geen items om te tonen",
- "cast_and_crew": "Cast & bemanning",
+ "cast_and_crew": "Cast & Crew",
"series": "Series",
"seasons": "Seizoenen",
"season": "Seizoen",
@@ -624,12 +368,11 @@
"no_similar_items_found": "Geen gelijkaardige items gevonden",
"video": "Video",
"more_details": "Meer details",
- "media_options": "Media opties",
"quality": "Kwaliteit",
"audio": "Audio",
"subtitles": "Ondertitel",
"show_more": "Toon meer",
- "show_less": "Toon minder",
+ "show_less": "Toon minden",
"appeared_in": "Verschenen in",
"could_not_load_item": "Kon item niet laden",
"none": "Geen",
@@ -639,22 +382,24 @@
"download_episode": "Download Aflevering",
"download_movie": "Download Film",
"download_x_item": "Download {{item_count}} items",
- "download_unwatched_only": "Alleen niet bekeken",
- "download_button": "Downloaden"
+ "download_button": "Download",
+ "using_optimized_server": "Geoptimaliseerde server gebruiken",
+ "using_default_method": "Standaard methode gebruiken"
}
},
"live_tv": {
"next": "Volgende ",
"previous": "Vorige",
+ "live_tv": "Live TV",
"coming_soon": "Binnenkort beschikbaar",
"on_now": "Nu op",
- "shows": "Series",
+ "shows": "Shows",
"movies": "Films",
"sports": "Sport",
"for_kids": "Voor kinderen",
"news": "Nieuws"
},
- "jellyseerr": {
+ "jellyseerr":{
"confirm": "Bevestig",
"cancel": "Annuleer",
"yes": "Ja",
@@ -672,7 +417,7 @@
"details": "Details",
"status": "Status",
"original_title": "Originele titel",
- "series_type": "Serietype",
+ "series_type": "Serie Type",
"release_dates": "Verschijningsdatums",
"first_air_date": "Eerste uitzenddatum",
"next_air_date": "Volgende uitzenddatum",
@@ -688,27 +433,19 @@
"tags": "Labels",
"quality_profile": "Kwaliteitsprofiel",
"root_folder": "Hoofdmap",
- "season_all": "Season (all)",
+ "season_x": "Seizoen {{seasons}}",
"season_number": "Seizoen {{season_number}}",
"number_episodes": "{{episode_number}} Afleveringen",
"born": "Geboren",
"appearances": "Verschijningen",
- "approve": "Goedkeuren",
- "decline": "Weigeren",
- "requested_by": "Aangevraagd door {{user}}",
- "unknown_user": "Onbekende gebruiker",
"toasts": {
"jellyseer_does_not_meet_requirements": "Jellyseerr server voldoet niet aan de minimale versievereisten! Update naar minimaal 2.0.0",
- "jellyseerr_test_failed": "Jellyseerr test mislukt. Probeer opnieuw.",
+ "jellyseerr_test_failed": "Jellyseerr test gefaald. Probeer opnieuw.",
"failed_to_test_jellyseerr_server_url": "Mislukt bij het testen van jellyseerr server url",
"issue_submitted": "Probleem ingediend!",
"requested_item": "{{item}} aangevraagd!",
"you_dont_have_permission_to_request": "Je hebt geen toestemming om aanvragen te doen!",
- "something_went_wrong_requesting_media": "Er ging iets mis met het aanvragen van media!",
- "request_approved": "Verzoek goedgekeurd!",
- "request_declined": "Verzoek geweigerd!",
- "failed_to_approve_request": "Aanvraag goedkeuren mislukt",
- "failed_to_decline_request": "Aanvraag weigeren mislukt"
+ "something_went_wrong_requesting_media": "Er ging iets iets mis met het aavragen van media!"
}
},
"tabs": {
@@ -717,129 +454,5 @@
"library": "Bibliotheek",
"custom_links": "Aangepaste links",
"favorites": "Favorieten"
- },
- "music": {
- "title": "Music",
- "tabs": {
- "suggestions": "Suggestions",
- "albums": "Albums",
- "artists": "Artists",
- "playlists": "Playlists",
- "tracks": "tracks"
- },
- "filters": {
- "all": "All"
- },
- "recently_added": "Recently Added",
- "recently_played": "Recently Played",
- "frequently_played": "Frequently Played",
- "explore": "Explore",
- "top_tracks": "Top Tracks",
- "play": "Play",
- "shuffle": "Shuffle",
- "play_top_tracks": "Play Top Tracks",
- "no_suggestions": "No suggestions available",
- "no_albums": "No albums found",
- "no_artists": "No artists found",
- "no_playlists": "No playlists found",
- "album_not_found": "Album not found",
- "artist_not_found": "Artist not found",
- "playlist_not_found": "Playlist not found",
- "track_options": {
- "play_next": "Play Next",
- "add_to_queue": "Add to Queue",
- "add_to_playlist": "Add to Playlist",
- "download": "Download",
- "downloaded": "Downloaded",
- "downloading": "Downloading...",
- "cached": "Cached",
- "delete_download": "Delete Download",
- "delete_cache": "Remove from Cache",
- "go_to_artist": "Go to Artist",
- "go_to_album": "Go to Album",
- "add_to_favorites": "Add to Favorites",
- "remove_from_favorites": "Remove from Favorites",
- "remove_from_playlist": "Remove from Playlist"
- },
- "playlists": {
- "create_playlist": "Create Playlist",
- "playlist_name": "Playlist Name",
- "enter_name": "Enter playlist name",
- "create": "Create",
- "search_playlists": "Search playlists...",
- "added_to": "Added to {{name}}",
- "added": "Added to playlist",
- "removed_from": "Removed from {{name}}",
- "removed": "Removed from playlist",
- "created": "Playlist created",
- "create_new": "Create New Playlist",
- "failed_to_add": "Failed to add to playlist",
- "failed_to_remove": "Failed to remove from playlist",
- "failed_to_create": "Failed to create playlist",
- "delete_playlist": "Delete Playlist",
- "delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "deleted": "Playlist deleted",
- "failed_to_delete": "Failed to delete playlist"
- },
- "sort": {
- "title": "Sort By",
- "alphabetical": "Alphabetical",
- "date_created": "Date Created"
- }
- },
- "watchlists": {
- "title": "Watchlists",
- "my_watchlists": "My Watchlists",
- "public_watchlists": "Public Watchlists",
- "create_title": "Create Watchlist",
- "edit_title": "Edit Watchlist",
- "create_button": "Create Watchlist",
- "save_button": "Save Changes",
- "delete_button": "Delete",
- "remove_button": "Remove",
- "cancel_button": "Cancel",
- "name_label": "Name",
- "name_placeholder": "Enter watchlist name",
- "description_label": "Description",
- "description_placeholder": "Enter description (optional)",
- "is_public_label": "Public Watchlist",
- "is_public_description": "Allow others to view this watchlist",
- "allowed_type_label": "Content Type",
- "sort_order_label": "Default Sort Order",
- "empty_title": "No Watchlists",
- "empty_description": "Create your first watchlist to start organizing your media",
- "empty_watchlist": "This watchlist is empty",
- "empty_watchlist_hint": "Add items from your library to this watchlist",
- "not_configured_title": "Streamystats Not Configured",
- "not_configured_description": "Configure Streamystats in settings to use watchlists",
- "go_to_settings": "Go to Settings",
- "add_to_watchlist": "Add to Watchlist",
- "remove_from_watchlist": "Remove from Watchlist",
- "select_watchlist": "Select Watchlist",
- "create_new": "Create New Watchlist",
- "item": "item",
- "items": "items",
- "public": "Public",
- "private": "Private",
- "you": "You",
- "by_owner": "By another user",
- "not_found": "Watchlist not found",
- "delete_confirm_title": "Delete Watchlist",
- "delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "remove_item_title": "Remove from Watchlist",
- "remove_item_message": "Remove \"{{name}}\" from this watchlist?",
- "loading": "Loading watchlists...",
- "no_compatible_watchlists": "No compatible watchlists",
- "create_one_first": "Create a watchlist that accepts this content type"
- },
- "playback_speed": {
- "title": "Playback Speed",
- "apply_to": "Apply To",
- "speed": "Speed",
- "scope": {
- "media": "This media only",
- "show": "This show",
- "all": "All media (default)"
- }
}
}
diff --git a/translations/tr.json b/translations/tr.json
index 15bbebf8..7b3a2320 100644
--- a/translations/tr.json
+++ b/translations/tr.json
@@ -7,7 +7,7 @@
"username_placeholder": "Kullanıcı adı",
"password_placeholder": "Şifre",
"login_button": "Giriş yap",
- "quick_connect": "Hızlı Bağlantı",
+ "quick_connect": "Quick Connect",
"enter_code_to_login": "Giriş yapmak için {{code}} kodunu girin",
"failed_to_initiate_quick_connect": "Quick Connect başlatılamadı",
"got_it": "Anlaşıldı",
@@ -20,9 +20,7 @@
"server_is_taking_too_long_to_respond_try_again_later": "Sunucu yanıt vermekte çok uzun sürüyor, lütfen tekrar deneyin",
"server_received_too_many_requests_try_again_later": "Sunucu çok fazla istek aldı, lütfen tekrar deneyin.",
"there_is_a_server_error": "Sunucu hatası var",
- "an_unexpected_error_occured_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin oldunuz mu?",
- "too_old_server_text": "Unsupported Jellyfin Server Discovered",
- "too_old_server_description": "Please update Jellyfin to the latest version"
+ "an_unexpected_error_occured_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin oldunuz mu?"
},
"server": {
"enter_url_to_jellyfin_server": "Jellyfin sunucunusun URL'sini girin",
@@ -30,64 +28,19 @@
"connect_button": "Bağlan",
"previous_servers": "Önceki sunucular",
"clear_button": "Temizle",
- "swipe_to_remove": "Swipe to remove",
"search_for_local_servers": "Yerel sunucuları ara",
"searching": "Aranıyor...",
- "servers": "Sunucular",
- "saved": "Saved",
- "session_expired": "Session Expired",
- "please_login_again": "Your saved session has expired. Please log in again.",
- "remove_saved_login": "Remove Saved Login",
- "remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
- "accounts_count": "{{count}} accounts",
- "select_account": "Select Account",
- "add_account": "Add Account",
- "remove_account_description": "This will remove the saved credentials for {{username}}."
- },
- "save_account": {
- "title": "Save Account",
- "save_for_later": "Save this account",
- "security_option": "Security Option",
- "no_protection": "No protection",
- "no_protection_desc": "Quick login without authentication",
- "pin_code": "PIN code",
- "pin_code_desc": "4-digit PIN required when switching",
- "password": "Re-enter password",
- "password_desc": "Password required when switching",
- "save_button": "Save",
- "cancel_button": "Cancel"
- },
- "pin": {
- "enter_pin": "Enter PIN",
- "enter_pin_for": "Enter PIN for {{username}}",
- "enter_4_digits": "Enter 4 digits",
- "invalid_pin": "Invalid PIN",
- "setup_pin": "Set Up PIN",
- "confirm_pin": "Confirm PIN",
- "pins_dont_match": "PINs don't match",
- "forgot_pin": "Forgot PIN?",
- "forgot_pin_desc": "Your saved credentials will be removed"
- },
- "password": {
- "enter_password": "Enter Password",
- "enter_password_for": "Enter password for {{username}}",
- "invalid_password": "Invalid password"
+ "servers": "Sunucular"
},
"home": {
- "checking_server_connection": "Checking server connection...",
"no_internet": "İnternet Yok",
"no_items": "Öge Yok",
"no_internet_message": "Endişelenmeyin, hala\ndownloaded içerik izleyebilirsiniz.",
- "checking_server_connection_message": "Checking connection to server",
"go_to_downloads": "İndirmelere Git",
- "retry": "Retry",
- "server_unreachable": "Server Unreachable",
- "server_unreachable_message": "Could not reach the server.\nPlease check your network connection.",
"oops": "Hups!",
"error_message": "Bir şeyler ters gitti.\nLütfen çıkış yapın ve tekrar giriş yapın.",
"continue_watching": "İzlemeye Devam Et",
"next_up": "Sonraki",
- "continue_and_next_up": "Continue & Next Up",
"recently_added_in": "{{libraryName}}'de Yakınlarda Eklendi",
"suggested_movies": "Önerilen Filmler",
"suggested_episodes": "Önerilen Bölümler",
@@ -109,48 +62,6 @@
"settings": {
"settings_title": "Ayarlar",
"log_out_button": "Çıkış Yap",
- "categories": {
- "title": "Categories"
- },
- "playback_controls": {
- "title": "Playback & Controls"
- },
- "audio_subtitles": {
- "title": "Audio & Subtitles"
- },
- "appearance": {
- "title": "Appearance",
- "merge_next_up_continue_watching": "Merge Continue Watching & Next Up",
- "hide_remote_session_button": "Hide Remote Session Button"
- },
- "network": {
- "title": "Network",
- "local_network": "Local Network",
- "auto_switch_enabled": "Auto-switch when at home",
- "auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
- "local_url": "Local URL",
- "local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
- "local_url_placeholder": "http://192.168.1.100:8096",
- "home_wifi_networks": "Home WiFi Networks",
- "add_current_network": "Add \"{{ssid}}\"",
- "not_connected_to_wifi": "Not connected to WiFi",
- "no_networks_configured": "No networks configured",
- "add_network_hint": "Add your home WiFi network to enable auto-switching",
- "current_wifi": "Current WiFi",
- "using_url": "Using",
- "local": "Local URL",
- "remote": "Remote URL",
- "not_connected": "Not connected",
- "current_server": "Current Server",
- "remote_url": "Remote URL",
- "active_url": "Active URL",
- "not_configured": "Not configured",
- "network_added": "Network added",
- "network_already_added": "Network already added",
- "no_wifi_connected": "Not connected to WiFi",
- "permission_denied": "Location permission denied",
- "permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
- },
"user_info": {
"user_info_title": "Kullanıcı Bilgisi",
"user": "Kullanıcı",
@@ -174,42 +85,21 @@
"rewind_length": "Geri Sarma Uzunluğu",
"seconds_unit": "s"
},
- "gesture_controls": {
- "gesture_controls_title": "Gesture Controls",
- "horizontal_swipe_skip": "Horizontal Swipe to Skip",
- "horizontal_swipe_skip_description": "Swipe left/right when controls are hidden to skip",
- "left_side_brightness": "Left Side Brightness Control",
- "left_side_brightness_description": "Swipe up/down on left side to adjust brightness",
- "right_side_volume": "Right Side Volume Control",
- "right_side_volume_description": "Swipe up/down on right side to adjust volume",
- "hide_volume_slider": "Hide Volume Slider",
- "hide_volume_slider_description": "Hide the volume slider in the video player",
- "hide_brightness_slider": "Hide Brightness Slider",
- "hide_brightness_slider_description": "Hide the brightness slider in the video player"
- },
"audio": {
"audio_title": "Ses",
"set_audio_track": "Önceki Öğeden Ses Parçası Ayarla",
"audio_language": "Ses Dili",
"audio_hint": "Varsayılan ses dilini seçin.",
"none": "Yok",
- "language": "Dil",
- "transcode_mode": {
- "title": "Audio Transcoding",
- "description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
- "auto": "Auto",
- "stereo": "Force Stereo",
- "5_1": "Allow 5.1",
- "passthrough": "Passthrough"
- }
+ "language": "Dil"
},
"subtitles": {
"subtitle_title": "Altyazılar",
- "subtitle_hint": "Altyazı tercihini yapılandırın.",
"subtitle_language": "Altyazı Dili",
"subtitle_mode": "Altyazı Modu",
"set_subtitle_track": "Önceki Öğeden Altyazı Parçası Ayarla",
"subtitle_size": "Altyazı Boyutu",
+ "subtitle_hint": "Altyazı tercihini yapılandırın.",
"none": "Yok",
"language": "Dil",
"loading": "Yükleniyor",
@@ -219,66 +109,11 @@
"Always": "Her Zaman",
"None": "Yok",
"OnlyForced": "Sadece Zorunlu"
- },
- "text_color": "Text Color",
- "background_color": "Background Color",
- "outline_color": "Outline Color",
- "outline_thickness": "Outline Thickness",
- "background_opacity": "Background Opacity",
- "outline_opacity": "Outline Opacity",
- "bold_text": "Bold Text",
- "colors": {
- "Black": "Black",
- "Gray": "Gray",
- "Silver": "Silver",
- "White": "White",
- "Maroon": "Maroon",
- "Red": "Red",
- "Fuchsia": "Fuchsia",
- "Yellow": "Yellow",
- "Olive": "Olive",
- "Green": "Green",
- "Teal": "Teal",
- "Lime": "Lime",
- "Purple": "Purple",
- "Navy": "Navy",
- "Blue": "Blue",
- "Aqua": "Aqua"
- },
- "thickness": {
- "None": "Hiçbiri",
- "Thin": "Thin",
- "Normal": "Normal",
- "Thick": "Thick"
- },
- "subtitle_color": "Subtitle Color",
- "subtitle_background_color": "Background Color",
- "subtitle_font": "Subtitle Font",
- "ksplayer_title": "KSPlayer Settings",
- "hardware_decode": "Hardware Decoding",
- "hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues."
- },
- "vlc_subtitles": {
- "title": "VLC Subtitle Settings",
- "hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
- "text_color": "Text Color",
- "background_color": "Background Color",
- "background_opacity": "Background Opacity",
- "outline_color": "Outline Color",
- "outline_opacity": "Outline Opacity",
- "outline_thickness": "Outline Thickness",
- "bold": "Bold Text",
- "margin": "Bottom Margin"
- },
- "video_player": {
- "title": "Video Player",
- "video_player": "Video Player",
- "video_player_description": "Choose which video player to use on iOS.",
- "ksplayer": "KSPlayer",
- "vlc": "VLC"
+ }
},
"other": {
"other_title": "Diğer",
+ "auto_rotate": "Otomatik Döndürme",
"video_orientation": "Video Yönü",
"orientation": "Yön",
"orientations": {
@@ -294,35 +129,25 @@
"UNKNOWN": "Bilinmeyen"
},
"safe_area_in_controls": "Kontrollerde Güvenli Alan",
- "video_player": "Video player",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Experimental + PiP)"
- },
"show_custom_menu_links": "Özel Menü Bağlantılarını Göster",
- "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Kütüphaneleri Gizle",
"select_liraries_you_want_to_hide": "Kütüphane sekmesinden ve ana sayfa bölümlerinden gizlemek istediğiniz kütüphaneleri seçin.",
- "disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak",
- "default_quality": "Varsayılan kalite",
- "default_playback_speed": "Default Playback Speed",
- "auto_play_next_episode": "Auto-play Next Episode",
- "max_auto_play_episode_count": "Max Auto Play Episode Count",
- "disabled": "Devre dışı"
+ "disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak"
},
"downloads": {
- "downloads_title": "İndirmeler"
- },
- "music": {
- "title": "Music",
- "playback_title": "Playback",
- "playback_description": "Configure how music is played.",
- "prefer_downloaded": "Prefer Downloaded Songs",
- "caching_title": "Caching",
- "caching_description": "Automatically cache upcoming tracks for smoother playback.",
- "lookahead_enabled": "Enable Look-Ahead Caching",
- "lookahead_count": "Tracks to Pre-cache",
- "max_cache_size": "Max Cache Size"
+ "downloads_title": "İndirmeler",
+ "download_method": "İndirme Yöntemi",
+ "remux_max_download": "Remux max indirme",
+ "auto_download": "Otomatik İndirme",
+ "optimized_versions_server": "Optimize edilmiş sürümler sunucusu",
+ "save_button": "Kaydet",
+ "optimized_server": "Optimize Sunucu",
+ "optimized": "Optimize",
+ "default": "Varsayılan",
+ "optimized_version_hint": "Optimize sunucusu için URL girin. URL, http veya https içermeli ve isteğe bağlı olarak portu içerebilir.",
+ "read_more_about_optimized_server": "Optimize sunucusu hakkında daha fazla oku.",
+ "url": "URL",
+ "server_url_placeholder": "http(s)://domain.org:port"
},
"plugins": {
"plugins_title": "Eklentiler",
@@ -333,6 +158,8 @@
"server_url_placeholder": "Jellyseerr URL...",
"password": "Şifre",
"password_placeholder": "Jellyfin kullanıcısı {{username}} için şifre girin",
+ "save_button": "Kaydet",
+ "clear_button": "Temizle",
"login_button": "Giriş Yap",
"total_media_requests": "Toplam medya istekleri",
"movie_quota_limit": "Film kota limiti",
@@ -340,13 +167,7 @@
"tv_quota_limit": "TV kota limiti",
"tv_quota_days": "TV kota günleri",
"reset_jellyseerr_config_button": "Jellyseerr yapılandırmasını sıfırla",
- "unlimited": "Sınırsız",
- "plus_n_more": "+{{n}} more",
- "order_by": {
- "DEFAULT": "Varsayılan",
- "VOTE_COUNT_AND_AVERAGE": "Vote count and average",
- "POPULARITY": "Popularity"
- }
+ "unlimited": "Sınırsız"
},
"marlin_search": {
"enable_marlin_search": "Marlin Aramasını Etkinleştir ",
@@ -356,40 +177,8 @@
"read_more_about_marlin": "Marlin hakkında daha fazla oku.",
"save_button": "Kaydet",
"toasts": {
- "saved": "Kaydedildi",
- "refreshed": "Settings refreshed from server"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "streamystats": {
- "enable_streamystats": "Enable Streamystats",
- "disable_streamystats": "Disable Streamystats",
- "enable_search": "Use for Search",
- "url": "URL",
- "server_url_placeholder": "http(s)://streamystats.example.com",
- "streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
- "read_more_about_streamystats": "Read More About Streamystats.",
- "save_button": "Save",
- "save": "Save",
- "features_title": "Features",
- "home_sections_title": "Home Sections",
- "enable_movie_recommendations": "Movie Recommendations",
- "enable_series_recommendations": "Series Recommendations",
- "enable_promoted_watchlists": "Promoted Watchlists",
- "hide_watchlists_tab": "Hide Watchlists Tab",
- "home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
- "recommended_movies": "Recommended Movies",
- "recommended_series": "Recommended Series",
- "toasts": {
- "saved": "Saved",
- "refreshed": "Settings refreshed from server",
- "disabled": "Streamystats disabled"
- },
- "refresh_from_server": "Refresh Settings from Server"
- },
- "kefinTweaks": {
- "watchlist_enabler": "Enable our Watchlist integration",
- "watchlist_button": "Toggle Watchlist integration"
+ "saved": "Kaydedildi"
+ }
}
},
"storage": {
@@ -397,58 +186,43 @@
"app_usage": "Uygulama {{usedSpace}}%",
"device_usage": "Cihaz {{availableSpace}}%",
"size_used": "{{used}} / {{total}} kullanıldı",
- "delete_all_downloaded_files": "Tüm indirilen dosyaları sil",
- "music_cache_title": "Music Cache",
- "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
- "enable_music_cache": "Enable Music Cache",
- "clear_music_cache": "Clear Music Cache",
- "music_cache_size": "{{size}} cached",
- "music_cache_cleared": "Music cache cleared",
- "delete_all_downloaded_songs": "Delete All Downloaded Songs",
- "downloaded_songs_size": "{{size}} downloaded",
- "downloaded_songs_deleted": "Downloaded songs deleted"
+ "delete_all_downloaded_files": "Tüm indirilen dosyaları sil"
},
"intro": {
- "title": "Intro",
"show_intro": "Tanıtımı Göster",
"reset_intro": "Tanıtımı Sıfırla"
},
"logs": {
"logs_title": "Günlükler",
- "export_logs": "Export logs",
- "click_for_more_info": "Click for more info",
- "level": "Level",
"no_logs_available": "Günlükler mevcut değil",
"delete_all_logs": "Tüm günlükleri sil"
},
"languages": {
"title": "Diller",
"app_language": "Uygulama dili",
+ "app_language_description": "Uygulama dilini seçin.",
"system": "Sistem"
},
"toasts": {
"error_deleting_files": "Dosyalar silinirken hata oluştu",
"background_downloads_enabled": "Arka plan indirmeleri etkinleştirildi",
- "background_downloads_disabled": "Arka plan indirmeleri devre dışı bırakıldı"
+ "background_downloads_disabled": "Arka plan indirmeleri devre dışı bırakıldı",
+ "connected": "Bağlandı",
+ "could_not_connect": "Bağlanılamadı",
+ "invalid_url": "Geçersiz URL"
}
},
- "sessions": {
- "title": "Sessions",
- "no_active_sessions": "No Active Sessions"
- },
"downloads": {
"downloads_title": "İndirilenler",
"tvseries": "Diziler",
"movies": "Filmler",
"queue": "Sıra",
- "other_media": "Other media",
"queue_hint": "Sıra ve indirmeler uygulama yeniden başlatıldığında kaybolacaktır",
"no_items_in_queue": "Sırada öğe yok",
"no_downloaded_items": "İndirilen öğe yok",
"delete_all_movies_button": "Tüm Filmleri Sil",
"delete_all_tvseries_button": "Tüm Dizileri Sil",
"delete_all_button": "Tümünü Sil",
- "delete_all_other_media_button": "Delete other media",
"active_download": "Aktif indirme",
"no_active_downloads": "Aktif indirme yok",
"active_downloads": "Aktif indirmeler",
@@ -459,57 +233,35 @@
"something_went_wrong": "Bir şeyler ters gitti",
"could_not_get_stream_url_from_jellyfin": "Jellyfin'den yayın URL'si alınamadı",
"eta": "Tahmini Süre {{eta}}",
+ "methods": "Yöntemler",
"toasts": {
"you_are_not_allowed_to_download_files": "Dosyaları indirme izniniz yok.",
"deleted_all_movies_successfully": "Tüm filmler başarıyla silindi!",
"failed_to_delete_all_movies": "Filmler silinemedi",
"deleted_all_tvseries_successfully": "Tüm diziler başarıyla silindi!",
"failed_to_delete_all_tvseries": "Diziler silinemedi",
- "deleted_media_successfully": "Deleted other media Successfully!",
- "failed_to_delete_media": "Failed to Delete other media",
- "download_deleted": "Download Deleted",
"download_cancelled": "İndirme iptal edildi",
- "could_not_delete_download": "Could Not Delete Download",
- "download_paused": "Download Paused",
- "could_not_pause_download": "Could Not Pause Download",
- "download_resumed": "Download Resumed",
- "could_not_resume_download": "Could Not Resume Download",
+ "could_not_cancel_download": "İndirme iptal edilemedi",
"download_completed": "İndirme tamamlandı",
- "download_failed": "Download Failed",
+ "download_started_for": "{{item}} için indirme başlatıldı",
+ "item_is_ready_to_be_downloaded": "{{item}} indirmeye hazır",
+ "download_stated_for_item": "{{item}} için indirme başlatıldı",
"download_failed_for_item": "{{item}} için indirme başarısız oldu - {{error}}",
"download_completed_for_item": "{{item}} için indirme tamamlandı",
- "download_started_for_item": "Download Started for {{item}}",
- "failed_to_start_download": "Failed to start download",
- "item_already_downloading": "{{item}} is already downloading",
- "all_files_deleted": "All Downloads Deleted Successfully",
- "files_deleted_by_type": "{{count}} {{type}} deleted",
+ "queued_item_for_optimization": "{{item}} optimizasyon için sıraya alındı",
+ "failed_to_start_download_for_item": "{{item}} için indirme başlatılamadı: {{message}}",
+ "server_responded_with_status_code": "Sunucu şu durum koduyla yanıt verdi: {{statusCode}}",
+ "no_response_received_from_server": "Sunucudan yanıt alınamadı",
+ "error_setting_up_the_request": "İstek ayarlanırken hata oluştu",
+ "failed_to_start_download_for_item_unexpected_error": "{{item}} için indirme başlatılamadı: Beklenmeyen hata",
"all_files_folders_and_jobs_deleted_successfully": "Tüm dosyalar, klasörler ve işler başarıyla silindi",
- "failed_to_clean_cache_directory": "Failed to clean cache directory",
- "could_not_get_download_url_for_item": "Could not get download URL for {{itemName}}",
- "go_to_downloads": "İndirmelere git",
- "file_deleted": "{{item}} deleted"
+ "an_error_occured_while_deleting_files_and_jobs": "Dosyalar ve işler silinirken hata oluştu",
+ "go_to_downloads": "İndirmelere git"
}
}
},
- "common": {
- "select": "Select",
- "no_trailer_available": "No trailer available",
- "video": "Video",
- "audio": "Ses",
- "subtitle": "Altyazı",
- "play": "Play",
- "none": "None",
- "track": "Track",
- "cancel": "Cancel",
- "delete": "Delete",
- "ok": "OK",
- "remove": "Remove",
- "next": "Next",
- "back": "Back",
- "continue": "Continue",
- "verifying": "Verifying..."
- },
"search": {
+ "search_here": "Burada ara...",
"search": "Ara...",
"x_items": "{{count}} öge(ler)",
"library": "Kütüphane",
@@ -521,10 +273,6 @@
"episodes": "Bölümler",
"collections": "Koleksiyonlar",
"actors": "Oyuncular",
- "artists": "Artists",
- "albums": "Albums",
- "songs": "Songs",
- "playlists": "Playlists",
"request_movies": "Film Talep Et",
"request_series": "Dizi Talep Et",
"recently_added": "Son Eklenenler",
@@ -550,6 +298,7 @@
"tmdb_tv_streaming_services": "TMDB Dizi Yayın Servisleri"
},
"library": {
+ "no_items_found": "Öğe bulunamadı",
"no_results": "Sonuç bulunamadı",
"no_libraries_found": "Kütüphane bulunamadı",
"item_types": {
@@ -572,7 +321,6 @@
"genres": "Türler",
"years": "Yıllar",
"sort_by": "Sırala",
- "filter_by": "Filter By",
"sort_order": "Sıralama düzeni",
"tags": "Etiketler"
}
@@ -583,9 +331,7 @@
"episodes": "Bölümler",
"videos": "Videolar",
"boxsets": "Koleksiyonlar",
- "playlists": "Çalma listeleri",
- "noDataTitle": "Henüz favori yok",
- "noData": "Hızlı erişim için öğeleri favori olarak işaretleyin ve burada görünmelerini sağlayın."
+ "playlists": "Çalma listeleri"
},
"custom_links": {
"no_links": "Bağlantı yok"
@@ -597,18 +343,15 @@
"client_error": "İstemci hatası",
"could_not_create_stream_for_chromecast": "Chromecast için yayın oluşturulamadı",
"message_from_server": "Sunucudan mesaj: {{message}}",
+ "video_has_finished_playing": "Video oynatıldı!",
+ "no_video_source": "Video kaynağı yok...",
"next_episode": "Sonraki bölüm",
"refresh_tracks": "Parçaları yenile",
+ "subtitle_tracks": "Altyazı Parçaları:",
"audio_tracks": "Ses Parçaları:",
"playback_state": "Oynatma Durumu:",
- "index": "İndeks:",
- "continue_watching": "İzlemeye devam et",
- "go_back": "Geri",
- "downloaded_file_title": "You have this file downloaded",
- "downloaded_file_message": "Do you want to play the downloaded file?",
- "downloaded_file_yes": "Yes",
- "downloaded_file_no": "No",
- "downloaded_file_cancel": "Cancel"
+ "no_data_available": "Veri bulunamadı",
+ "index": "İndeks:"
},
"item_card": {
"next_up": "Sıradaki",
@@ -624,7 +367,6 @@
"no_similar_items_found": "Benzer öge bulunamadı",
"video": "Video",
"more_details": "Daha fazla detay",
- "media_options": "Media Options",
"quality": "Kalite",
"audio": "Ses",
"subtitles": "Altyazı",
@@ -639,13 +381,15 @@
"download_episode": "Bölümü indir",
"download_movie": "Filmi indir",
"download_x_item": "{{item_count}} tane ögeyi indir",
- "download_unwatched_only": "Unwatched Only",
- "download_button": "İndir"
+ "download_button": "İndir",
+ "using_optimized_server": "Optimize edilmiş sunucu kullanılıyor",
+ "using_default_method": "Varsayılan yöntem kullanılıyor"
}
},
"live_tv": {
"next": "Sonraki",
"previous": "Önceki",
+ "live_tv": "Canlı TV",
"coming_soon": "Yakında",
"on_now": "Şu anda yayında",
"shows": "Programlar",
@@ -688,15 +432,11 @@
"tags": "Etiketler",
"quality_profile": "Kalite Profili",
"root_folder": "Kök Klasör",
- "season_all": "Season (all)",
+ "season_x": "Sezon {{seasons}}",
"season_number": "Sezon {{season_number}}",
"number_episodes": "Bölüm {{episode_number}}",
"born": "Doğum",
"appearances": "Görünmeler",
- "approve": "Approve",
- "decline": "Decline",
- "requested_by": "Requested by {{user}}",
- "unknown_user": "Unknown User",
"toasts": {
"jellyseer_does_not_meet_requirements": "Jellyseerr sunucusu minimum sürüm gereksinimlerini karşılamıyor! Lütfen en az 2.0.0 sürümüne güncelleyin",
"jellyseerr_test_failed": "Jellyseerr testi başarısız oldu. Lütfen tekrar deneyin.",
@@ -704,11 +444,7 @@
"issue_submitted": "Sorun gönderildi!",
"requested_item": "{{item}} talep edildi!",
"you_dont_have_permission_to_request": "İstek göndermeye izniniz yok!",
- "something_went_wrong_requesting_media": "Medya talep edilirken bir şeyler ters gitti!",
- "request_approved": "Request Approved!",
- "request_declined": "Request Declined!",
- "failed_to_approve_request": "Failed to Approve Request",
- "failed_to_decline_request": "Failed to Decline Request"
+ "something_went_wrong_requesting_media": "Medya talep edilirken bir şeyler ters gitti!"
}
},
"tabs": {
@@ -717,129 +453,5 @@
"library": "Kütüphane",
"custom_links": "Özel Bağlantılar",
"favorites": "Favoriler"
- },
- "music": {
- "title": "Music",
- "tabs": {
- "suggestions": "Suggestions",
- "albums": "Albums",
- "artists": "Artists",
- "playlists": "Playlists",
- "tracks": "tracks"
- },
- "filters": {
- "all": "All"
- },
- "recently_added": "Recently Added",
- "recently_played": "Recently Played",
- "frequently_played": "Frequently Played",
- "explore": "Explore",
- "top_tracks": "Top Tracks",
- "play": "Play",
- "shuffle": "Shuffle",
- "play_top_tracks": "Play Top Tracks",
- "no_suggestions": "No suggestions available",
- "no_albums": "No albums found",
- "no_artists": "No artists found",
- "no_playlists": "No playlists found",
- "album_not_found": "Album not found",
- "artist_not_found": "Artist not found",
- "playlist_not_found": "Playlist not found",
- "track_options": {
- "play_next": "Play Next",
- "add_to_queue": "Add to Queue",
- "add_to_playlist": "Add to Playlist",
- "download": "Download",
- "downloaded": "Downloaded",
- "downloading": "Downloading...",
- "cached": "Cached",
- "delete_download": "Delete Download",
- "delete_cache": "Remove from Cache",
- "go_to_artist": "Go to Artist",
- "go_to_album": "Go to Album",
- "add_to_favorites": "Add to Favorites",
- "remove_from_favorites": "Remove from Favorites",
- "remove_from_playlist": "Remove from Playlist"
- },
- "playlists": {
- "create_playlist": "Create Playlist",
- "playlist_name": "Playlist Name",
- "enter_name": "Enter playlist name",
- "create": "Create",
- "search_playlists": "Search playlists...",
- "added_to": "Added to {{name}}",
- "added": "Added to playlist",
- "removed_from": "Removed from {{name}}",
- "removed": "Removed from playlist",
- "created": "Playlist created",
- "create_new": "Create New Playlist",
- "failed_to_add": "Failed to add to playlist",
- "failed_to_remove": "Failed to remove from playlist",
- "failed_to_create": "Failed to create playlist",
- "delete_playlist": "Delete Playlist",
- "delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "deleted": "Playlist deleted",
- "failed_to_delete": "Failed to delete playlist"
- },
- "sort": {
- "title": "Sort By",
- "alphabetical": "Alphabetical",
- "date_created": "Date Created"
- }
- },
- "watchlists": {
- "title": "Watchlists",
- "my_watchlists": "My Watchlists",
- "public_watchlists": "Public Watchlists",
- "create_title": "Create Watchlist",
- "edit_title": "Edit Watchlist",
- "create_button": "Create Watchlist",
- "save_button": "Save Changes",
- "delete_button": "Delete",
- "remove_button": "Remove",
- "cancel_button": "Cancel",
- "name_label": "Name",
- "name_placeholder": "Enter watchlist name",
- "description_label": "Description",
- "description_placeholder": "Enter description (optional)",
- "is_public_label": "Public Watchlist",
- "is_public_description": "Allow others to view this watchlist",
- "allowed_type_label": "Content Type",
- "sort_order_label": "Default Sort Order",
- "empty_title": "No Watchlists",
- "empty_description": "Create your first watchlist to start organizing your media",
- "empty_watchlist": "This watchlist is empty",
- "empty_watchlist_hint": "Add items from your library to this watchlist",
- "not_configured_title": "Streamystats Not Configured",
- "not_configured_description": "Configure Streamystats in settings to use watchlists",
- "go_to_settings": "Go to Settings",
- "add_to_watchlist": "Add to Watchlist",
- "remove_from_watchlist": "Remove from Watchlist",
- "select_watchlist": "Select Watchlist",
- "create_new": "Create New Watchlist",
- "item": "item",
- "items": "items",
- "public": "Public",
- "private": "Private",
- "you": "You",
- "by_owner": "By another user",
- "not_found": "Watchlist not found",
- "delete_confirm_title": "Delete Watchlist",
- "delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
- "remove_item_title": "Remove from Watchlist",
- "remove_item_message": "Remove \"{{name}}\" from this watchlist?",
- "loading": "Loading watchlists...",
- "no_compatible_watchlists": "No compatible watchlists",
- "create_one_first": "Create a watchlist that accepts this content type"
- },
- "playback_speed": {
- "title": "Playback Speed",
- "apply_to": "Apply To",
- "speed": "Speed",
- "scope": {
- "media": "This media only",
- "show": "This show",
- "all": "All media (default)"
- }
}
}
diff --git a/translations/zh-CN.json b/translations/zh-CN.json
index 2581e94d..4e04ad6f 100644
--- a/translations/zh-CN.json
+++ b/translations/zh-CN.json
@@ -113,6 +113,7 @@
},
"other": {
"other_title": "其他",
+ "auto_rotate": "自动旋转",
"video_orientation": "视频方向",
"orientation": "方向",
"orientations": {
@@ -128,11 +129,6 @@
"UNKNOWN": "未知"
},
"safe_area_in_controls": "控制中的安全区域",
- "video_player": "Video player",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Experimental + PiP)"
- },
"show_custom_menu_links": "显示自定义菜单链接",
"hide_libraries": "隐藏媒体库",
"select_liraries_you_want_to_hide": "选择您想从媒体库页面和主页隐藏的媒体库。",
@@ -140,6 +136,9 @@
},
"downloads": {
"downloads_title": "下载",
+ "download_method": "下载方法",
+ "remux_max_download": "Remux 最大下载",
+ "auto_download": "自动下载",
"optimized_versions_server": "Optimized Version 服务器",
"save_button": "保存",
"optimized_server": "Optimized Server",
@@ -168,13 +167,7 @@
"tv_quota_limit": "剧集配额限制",
"tv_quota_days": "剧集配额天数",
"reset_jellyseerr_config_button": "重置 Jellyseerr 设置",
- "unlimited": "无限制",
- "plus_n_more": "+{{n}} more",
- "order_by": {
- "DEFAULT": "Default",
- "VOTE_COUNT_AND_AVERAGE": "Vote count and average",
- "POPULARITY": "Popularity"
- }
+ "unlimited": "无限制"
},
"marlin_search": {
"enable_marlin_search": "启用 Marlin 搜索",
@@ -201,9 +194,6 @@
},
"logs": {
"logs_title": "日志",
- "export_logs": "Export logs",
- "click_for_more_info": "Click for more info",
- "level": "Level",
"no_logs_available": "无可用日志",
"delete_all_logs": "删除所有日志"
},
@@ -332,8 +322,6 @@
"years": "年份",
"sort_by": "排序依据",
"sort_order": "排序顺序",
- "asc": "Ascending",
- "desc": "Descending",
"tags": "标签"
}
},
@@ -343,9 +331,7 @@
"episodes": "单集",
"videos": "视频",
"boxsets": "套装",
- "playlists": "播放列表",
- "noDataTitle": "暂无收藏",
- "noData": "将项目标记为收藏,它们将显示在此处以便快速访问。"
+ "playlists": "播放列表"
},
"custom_links": {
"no_links": "无链接"
@@ -356,7 +342,7 @@
"an_error_occured_while_playing_the_video": "播放视频时发生错误。请检查设置中的日志。",
"client_error": "客户端错误",
"could_not_create_stream_for_chromecast": "无法为 Chromecast 建立串流",
- "message_from_server": "来自服务器的消息",
+ "message_from_server": "来自服务器的消息:{{message}}",
"video_has_finished_playing": "视频播放完成!",
"no_video_source": "无视频来源...",
"next_episode": "下一集",
@@ -365,9 +351,7 @@
"audio_tracks": "音频轨道:",
"playback_state": "播放状态:",
"no_data_available": "无可用数据",
- "index": "索引:",
- "continue_watching": "继续观看",
- "go_back": "返回"
+ "index": "索引:"
},
"item_card": {
"next_up": "下一个",
@@ -448,7 +432,7 @@
"tags": "标签",
"quality_profile": "质量配置文件",
"root_folder": "根文件夹",
- "season_all": "Season (all)",
+ "season_x": "第 {{seasons}} 季",
"season_number": "第 {{season_number}} 季",
"number_episodes": "{{episode_number}} 集",
"born": "出生",
diff --git a/translations/zh-TW.json b/translations/zh-TW.json
index 0f311af7..bc4e9136 100644
--- a/translations/zh-TW.json
+++ b/translations/zh-TW.json
@@ -113,6 +113,7 @@
},
"other": {
"other_title": "其他",
+ "auto_rotate": "自動旋轉",
"video_orientation": "影片方向",
"orientation": "方向",
"orientations": {
@@ -128,21 +129,16 @@
"UNKNOWN": "未知"
},
"safe_area_in_controls": "控制中的安全區域",
- "video_player": "Video player",
- "video_players": {
- "VLC_3": "VLC 3",
- "VLC_4": "VLC 4 (Experimental + PiP)"
- },
"show_custom_menu_links": "顯示自定義菜單鏈接",
"hide_libraries": "隱藏媒體庫",
"select_liraries_you_want_to_hide": "選擇您想從媒體庫頁面和主頁隱藏的媒體庫。",
- "disable_haptic_feedback": "禁用觸覺回饋",
- "default_quality": "預設品質",
- "max_auto_play_episode_count": "自動播放劇集的最大次數",
- "disabled": "已停用"
+ "disable_haptic_feedback": "禁用觸覺回饋"
},
"downloads": {
"downloads_title": "下載",
+ "download_method": "下載方法",
+ "remux_max_download": "Remux 最大下載",
+ "auto_download": "自動下載",
"optimized_versions_server": "Optimized Version 伺服器",
"save_button": "保存",
"optimized_server": "Optimized Server",
@@ -171,13 +167,7 @@
"tv_quota_limit": "電視配額限制",
"tv_quota_days": "電視配額天數",
"reset_jellyseerr_config_button": "重置 Jellyseerr 配置",
- "unlimited": "無限制",
- "plus_n_more": "+{{n}} more",
- "order_by": {
- "DEFAULT": "Default",
- "VOTE_COUNT_AND_AVERAGE": "Vote count and average",
- "POPULARITY": "Popularity"
- }
+ "unlimited": "無限制"
},
"marlin_search": {
"enable_marlin_search": "啟用 Marlin 搜索",
@@ -204,9 +194,6 @@
},
"logs": {
"logs_title": "日誌",
- "export_logs": "Export logs",
- "click_for_more_info": "Click for more info",
- "level": "Level",
"no_logs_available": "無可用日誌",
"delete_all_logs": "刪除所有日誌"
},
@@ -225,10 +212,6 @@
"invalid_url": "無效的 URL"
}
},
- "sessions": {
- "title": "會話",
- "no_active_sessions": "沒有使用中的會話"
- },
"downloads": {
"downloads_title": "下載",
"tvseries": "電視劇",
@@ -339,8 +322,6 @@
"years": "年份",
"sort_by": "排序依據",
"sort_order": "排序順序",
- "asc": "Ascending",
- "desc": "Descending",
"tags": "標籤"
}
},
@@ -350,9 +331,7 @@
"episodes": "劇集",
"videos": "影片",
"boxsets": "套裝",
- "playlists": "播放列表",
- "noDataTitle": "尚無收藏",
- "noData": "將項目標記為收藏,它們將顯示在此處以便快速訪問。"
+ "playlists": "播放列表"
},
"custom_links": {
"no_links": "無鏈接"
@@ -363,7 +342,7 @@
"an_error_occured_while_playing_the_video": "播放影片時發生錯誤。請檢查設置中的日誌。",
"client_error": "客戶端錯誤",
"could_not_create_stream_for_chromecast": "無法為 Chromecast 建立串流",
- "message_from_server": "來自伺服器的消息",
+ "message_from_server": "來自伺服器的消息:{{message}}",
"video_has_finished_playing": "影片播放完畢!",
"no_video_source": "無影片來源...",
"next_episode": "下一集",
@@ -372,9 +351,7 @@
"audio_tracks": "音頻軌道:",
"playback_state": "播放狀態:",
"no_data_available": "無可用數據",
- "index": "索引:",
- "continue_watching": "繼續觀看",
- "go_back": "返回"
+ "index": "索引:"
},
"item_card": {
"next_up": "下一個",
@@ -455,7 +432,7 @@
"tags": "標籤",
"quality_profile": "質量配置文件",
"root_folder": "根文件夾",
- "season_all": "Season (all)",
+ "season_x": "第 {{seasons}} 季",
"season_number": "第 {{season_number}} 季",
"number_episodes": "{{episode_number}} 集",
"born": "出生",
diff --git a/tsconfig.json b/tsconfig.json
index 69354e8d..ce27fee3 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,43 +2,9 @@
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
- "jsx": "react-jsx",
- "jsxImportSource": "react",
"paths": {
"@/*": ["./*"]
- },
- "incremental": true,
- "tsBuildInfoFile": ".tsbuildinfo",
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "forceConsistentCasingInFileNames": true,
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true
+ }
},
- "include": [
- "app/**/*",
- "assets/**/*",
- "components/**/*",
- "constants/**/*",
- "hooks/**/*",
- "modules/**/*",
- "packages/**/*",
- "plugins/**/*",
- "providers/**/*",
- "scripts/**/*",
- "translations/**/*",
- "*.ts",
- "*.tsx",
- ".expo/types/**/*.ts",
- "expo-env.d.ts"
- ],
- "exclude": [
- "node_modules",
- "babel.config.js",
- "metro.config.js",
- "utils/jellyseerr/**/*"
- ]
+ "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
}
diff --git a/utils/atoms/settings.ts b/utils/atoms/settings.ts
index 28f7d1b4..2426be4f 100644
--- a/utils/atoms/settings.ts
+++ b/utils/atoms/settings.ts
@@ -1,20 +1,21 @@
-import {
- type BaseItemKind,
- type CultureDto,
- type ItemFilter,
- type ItemSortBy,
- type SortOrder,
- SubtitlePlaybackMode,
-} from "@jellyfin/sdk/lib/generated-client";
-import { atom, useAtom, useAtomValue } from "jotai";
+import { atom, useAtom } from "jotai";
import { useCallback, useEffect, useMemo } from "react";
-import { BITRATES, type Bitrate } from "@/components/BitrateSelector";
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
+import { storage } from "../mmkv";
+import { Platform } from "react-native";
+import {
+ CultureDto,
+ SubtitlePlaybackMode,
+ ItemSortBy,
+ SortOrder,
+ BaseItemKind,
+ ItemFilter,
+} from "@jellyfin/sdk/lib/generated-client";
+import { Bitrate, BITRATES } from "@/components/BitrateSelector";
import { apiAtom } from "@/providers/JellyfinProvider";
import { writeInfoLog } from "@/utils/log";
-import { storage } from "../mmkv";
-const _STREAMYFIN_PLUGIN_ID = "1e9e5d386e6746158719e98a5c34f004";
+const STREAMYFIN_PLUGIN_ID = "1e9e5d386e6746158719e98a5c34f004";
const STREAMYFIN_PLUGIN_SETTINGS = "STREAMYFIN_PLUGIN_SETTINGS";
export type DownloadQuality = "original" | "high" | "low";
@@ -25,7 +26,7 @@ export type DownloadOption = {
};
export const ScreenOrientationEnum: Record<
- (typeof ScreenOrientation.OrientationLock)[keyof typeof ScreenOrientation.OrientationLock],
+ ScreenOrientation.OrientationLock,
string
> = {
[ScreenOrientation.OrientationLock.DEFAULT]:
@@ -78,17 +79,19 @@ export type DefaultLanguageOption = {
label: string;
};
+export enum DownloadMethod {
+ Remux = "remux",
+ Optimized = "optimized",
+}
+
export type Home = {
sections: Array;
};
export type HomeSection = {
- title?: string;
orientation?: "horizontal" | "vertical";
items?: HomeSectionItemResolver;
nextUp?: HomeSectionNextUpResolver;
- latest?: HomeSectionLatestResolver;
- custom?: HomeSectionCustomEndpointResolver;
};
export type HomeSectionItemResolver = {
@@ -102,13 +105,6 @@ export type HomeSectionItemResolver = {
filters?: Array;
};
-export type HomeSectionCustomEndpointResolver = {
- title?: string;
- endpoint: string;
- headers?: any;
- query?: any;
-};
-
export type HomeSectionNextUpResolver = {
parentId?: string;
limit?: number;
@@ -116,47 +112,16 @@ export type HomeSectionNextUpResolver = {
enableRewatching?: boolean;
};
-export interface MaxAutoPlayEpisodeCount {
- key: string;
- value: number;
-}
-
-export type HomeSectionLatestResolver = {
- parentId?: string;
- limit?: number;
- groupItems?: boolean;
- isPlayed?: boolean;
- includeItemTypes?: Array;
-};
-
-// Video player enum - currently only MPV is supported
-export enum VideoPlayer {
- MPV = 0,
-}
-
-// Audio transcoding mode - controls how surround audio is handled
-// This controls server-side transcoding behavior for audio streams.
-// MPV decodes via FFmpeg and supports most formats, but mobile devices
-// can't passthrough to external receivers, so this primarily affects
-// bandwidth usage and server load.
-export enum AudioTranscodeMode {
- Auto = "auto", // Platform defaults (recommended)
- ForceStereo = "stereo", // Always transcode to stereo
- Allow51 = "5.1", // Allow up to 5.1, transcode 7.1+
- AllowAll = "passthrough", // Direct play all audio formats
-}
-
export type Settings = {
home?: Home | null;
+ autoRotate?: boolean;
+ forceLandscapeInVideoPlayer?: boolean;
deviceProfile?: "Expo" | "Native" | "Old";
mediaListCollectionIds?: string[];
preferedLanguage?: string;
- searchEngine: "Marlin" | "Jellyfin" | "Streamystats";
+ searchEngine: "Marlin" | "Jellyfin";
marlinServerUrl?: string;
- streamyStatsServerUrl?: string;
- streamyStatsMovieRecommendations?: boolean;
- streamyStatsSeriesRecommendations?: boolean;
- streamyStatsPromotedWatchlists?: boolean;
+ openInVLC?: boolean;
downloadQuality?: DownloadOption;
defaultBitrate?: Bitrate;
libraryOptions: LibraryOptions;
@@ -167,50 +132,20 @@ export type Settings = {
subtitleMode: SubtitlePlaybackMode;
rememberSubtitleSelections: boolean;
showHomeTitles: boolean;
- defaultVideoOrientation: (typeof ScreenOrientation.OrientationLock)[keyof typeof ScreenOrientation.OrientationLock];
+ defaultVideoOrientation: ScreenOrientation.OrientationLock;
forwardSkipTime: number;
rewindSkipTime: number;
+ optimizedVersionsServerUrl?: string | null;
+ downloadMethod: DownloadMethod;
+ autoDownload: boolean;
showCustomMenuLinks: boolean;
disableHapticFeedback: boolean;
subtitleSize: number;
+ remuxConcurrentLimit: 1 | 2 | 3 | 4;
safeAreaInControlsEnabled: boolean;
jellyseerrServerUrl?: string;
- useKefinTweaks: boolean;
hiddenLibraries?: string[];
enableH265ForChromecast: boolean;
- maxAutoPlayEpisodeCount: MaxAutoPlayEpisodeCount;
- autoPlayEpisodeCount: number;
- autoPlayNextEpisode: boolean;
- // Playback speed settings
- defaultPlaybackSpeed: number;
- playbackSpeedPerMedia: Record;
- playbackSpeedPerShow: Record;
- // MPV subtitle settings
- mpvSubtitleScale?: number;
- mpvSubtitleMarginY?: number;
- mpvSubtitleAlignX?: "left" | "center" | "right";
- mpvSubtitleAlignY?: "top" | "center" | "bottom";
- mpvSubtitleFontSize?: number;
- // Gesture controls
- enableHorizontalSwipeSkip: boolean;
- enableLeftSideBrightnessSwipe: boolean;
- enableRightSideVolumeSwipe: boolean;
- hideVolumeSlider: boolean;
- hideBrightnessSlider: boolean;
- usePopularPlugin: boolean;
- showLargeHomeCarousel: boolean;
- mergeNextUpAndContinueWatching: boolean;
- // Appearance
- hideRemoteSessionButton: boolean;
- hideWatchlistsTab: boolean;
- // Audio look-ahead caching
- audioLookaheadEnabled: boolean;
- audioLookaheadCount: number;
- audioMaxCacheSizeMB: number;
- // Music playback
- preferLocalAudio: boolean;
- // Audio transcoding mode
- audioTranscodeMode: AudioTranscodeMode;
};
export interface Lockable {
@@ -225,17 +160,16 @@ export type StreamyfinPluginConfig = {
settings: PluginLockableSettings;
};
-export const defaultValues: Settings = {
+const defaultValues: Settings = {
home: null,
+ autoRotate: true,
+ forceLandscapeInVideoPlayer: false,
deviceProfile: "Expo",
mediaListCollectionIds: [],
preferedLanguage: undefined,
searchEngine: "Jellyfin",
marlinServerUrl: "",
- streamyStatsServerUrl: "",
- streamyStatsMovieRecommendations: false,
- streamyStatsSeriesRecommendations: false,
- streamyStatsPromotedWatchlists: false,
+ openInVLC: false,
downloadQuality: DownloadOptions[0],
defaultBitrate: BITRATES[0],
libraryOptions: {
@@ -255,47 +189,17 @@ export const defaultValues: Settings = {
defaultVideoOrientation: ScreenOrientation.OrientationLock.DEFAULT,
forwardSkipTime: 30,
rewindSkipTime: 10,
+ optimizedVersionsServerUrl: null,
+ downloadMethod: DownloadMethod.Remux,
+ autoDownload: false,
showCustomMenuLinks: false,
disableHapticFeedback: false,
- subtitleSize: 100, // Scale value * 100, so 100 = 1.0x
+ subtitleSize: Platform.OS === "ios" ? 60 : 100,
+ remuxConcurrentLimit: 1,
safeAreaInControlsEnabled: true,
jellyseerrServerUrl: undefined,
- useKefinTweaks: false,
hiddenLibraries: [],
enableH265ForChromecast: false,
- maxAutoPlayEpisodeCount: { key: "3", value: 3 },
- autoPlayEpisodeCount: 0,
- autoPlayNextEpisode: true,
- // Playback speed defaults
- defaultPlaybackSpeed: 1.0,
- playbackSpeedPerMedia: {},
- playbackSpeedPerShow: {},
- // MPV subtitle defaults
- mpvSubtitleScale: undefined,
- mpvSubtitleMarginY: undefined,
- mpvSubtitleAlignX: undefined,
- mpvSubtitleAlignY: undefined,
- mpvSubtitleFontSize: undefined,
- // Gesture controls
- enableHorizontalSwipeSkip: true,
- enableLeftSideBrightnessSwipe: true,
- enableRightSideVolumeSwipe: true,
- hideVolumeSlider: false,
- hideBrightnessSlider: false,
- usePopularPlugin: true,
- showLargeHomeCarousel: false,
- mergeNextUpAndContinueWatching: false,
- // Appearance
- hideRemoteSessionButton: false,
- hideWatchlistsTab: false,
- // Audio look-ahead caching defaults
- audioLookaheadEnabled: true,
- audioLookaheadCount: 1,
- audioMaxCacheSizeMB: 500,
- // Music playback
- preferLocalAudio: true,
- // Audio transcoding mode
- audioTranscodeMode: AudioTranscodeMode.Auto,
};
const loadSettings = (): Partial => {
@@ -307,42 +211,29 @@ const loadSettings = (): Partial => {
return loadedValues;
} catch (error) {
console.error("Failed to load settings:", error);
- return {};
+ return defaultValues;
}
};
const EXCLUDE_FROM_SAVE = ["home"];
const saveSettings = (settings: Settings) => {
- try {
- for (const key of Object.keys(settings)) {
- if (EXCLUDE_FROM_SAVE.includes(key)) {
- delete settings[key as keyof Settings];
- }
+ Object.keys(settings).forEach((key) => {
+ if (EXCLUDE_FROM_SAVE.includes(key)) {
+ delete settings[key as keyof Settings];
}
- const jsonValue = JSON.stringify(settings);
- storage.set("settings", jsonValue);
- } catch (error) {
- console.error("Failed to save settings:", error);
- }
+ });
+ const jsonValue = JSON.stringify(settings);
+ storage.set("settings", jsonValue);
};
export const settingsAtom = atom | null>(null);
-const loadPluginSettings = () => {
- try {
- return storage.get(STREAMYFIN_PLUGIN_SETTINGS);
- } catch (error) {
- console.error("Failed to load plugin settings:", error);
- return undefined;
- }
-};
-
-export const pluginSettingsAtom = atom(
- loadPluginSettings(),
+export const pluginSettingsAtom = atom(
+ storage.get(STREAMYFIN_PLUGIN_SETTINGS)
);
export const useSettings = () => {
- const api = useAtomValue(apiAtom);
+ const [api] = useAtom(apiAtom);
const [_settings, setSettings] = useAtom(settingsAtom);
const [pluginSettings, _setPluginSettings] = useAtom(pluginSettingsAtom);
@@ -358,80 +249,29 @@ export const useSettings = () => {
storage.setAny(STREAMYFIN_PLUGIN_SETTINGS, settings);
_setPluginSettings(settings);
},
- [_setPluginSettings],
+ [_setPluginSettings]
);
- const refreshStreamyfinPluginSettings = useCallback(
- async (forceOverride = false) => {
- if (!api) {
- return;
- }
- const newPluginSettings = await api.getStreamyfinPluginConfig().then(
- ({ data }) => {
- writeInfoLog("Got plugin settings", data?.settings);
- return data?.settings;
- },
- (_err) => undefined,
- );
- setPluginSettings(newPluginSettings);
-
- // Apply plugin values to settings
- if (newPluginSettings && _settings) {
- const updates: Partial = {};
- for (const [key, setting] of Object.entries(newPluginSettings)) {
- if (setting && !setting.locked && setting.value !== undefined) {
- const settingsKey = key as keyof Settings;
- // Apply if forceOverride is true, or if user hasn't explicitly set this value
- if (
- forceOverride ||
- _settings[settingsKey] === undefined ||
- _settings[settingsKey] === ""
- ) {
- (updates as any)[settingsKey] = setting.value;
- }
- }
- }
-
- // Auto-enable Streamystats if server URL is provided
- const streamyStatsUrl = newPluginSettings.streamyStatsServerUrl;
- if (
- streamyStatsUrl?.value &&
- _settings.searchEngine !== "Streamystats"
- ) {
- updates.searchEngine = "Streamystats";
- }
- if (Object.keys(updates).length > 0) {
- const newSettings = {
- ...defaultValues,
- ..._settings,
- ...updates,
- } as Settings;
- setSettings(newSettings);
- saveSettings(newSettings);
- }
- }
-
- return newPluginSettings;
- },
- [api, _settings],
- );
+ const refreshStreamyfinPluginSettings = useCallback(async () => {
+ if (!api) return;
+ const settings = await api.getStreamyfinPluginConfig().then(
+ ({ data }) => {
+ writeInfoLog(`Got remote settings: ${data?.settings}`);
+ return data?.settings;
+ },
+ (err) => undefined
+ );
+ setPluginSettings(settings);
+ return settings;
+ }, [api]);
const updateSettings = (update: Partial) => {
- if (!_settings) {
- return;
- }
- const hasChanges = Object.entries(update).some(
- ([key, value]) => _settings[key as keyof Settings] !== value,
- );
+ if (settings) {
+ const newSettings = { ..._settings, ...update };
- if (hasChanges) {
- // Merge default settings, current settings, and updates to ensure all required properties exist
- const newSettings = {
- ...defaultValues,
- ..._settings,
- ...update,
- } as Settings;
setSettings(newSettings);
+
+ // @ts-expect-error
saveSettings(newSettings);
}
};
@@ -440,29 +280,32 @@ export const useSettings = () => {
// If admin sets locked to false but provides a value,
// use user settings first and fallback on admin setting if required.
const settings: Settings = useMemo(() => {
- const unlockedPluginDefaults: Partial = {};
- const overrideSettings = Object.entries(pluginSettings ?? {}).reduce<
- Partial
- >((acc, [key, setting]) => {
- if (setting) {
- const { value, locked } = setting;
- const settingsKey = key as keyof Settings;
+ let unlockedPluginDefaults = {} as Settings;
+ const overrideSettings = Object.entries(pluginSettings || {}).reduce(
+ (acc, [key, setting]) => {
+ if (setting) {
+ const { value, locked } = setting;
- // Make sure we override default settings with plugin settings when they are not locked.
- if (
- !locked &&
- value !== undefined &&
- _settings?.[settingsKey] !== value
- ) {
- (unlockedPluginDefaults as any)[settingsKey] = value;
+ // Make sure we override default settings with plugin settings when they are not locked.
+ // Admin decided what users defaults should be and grants them the ability to change them too.
+ if (
+ locked === false &&
+ value &&
+ _settings?.[key as keyof Settings] !== value
+ ) {
+ unlockedPluginDefaults = Object.assign(unlockedPluginDefaults, {
+ [key as keyof Settings]: value,
+ });
+ }
+
+ acc = Object.assign(acc, {
+ [key]: locked ? value : _settings?.[key as keyof Settings] ?? value,
+ });
}
-
- (acc as any)[settingsKey] = locked
- ? value
- : (_settings?.[settingsKey] ?? value);
- }
- return acc;
- }, {});
+ return acc;
+ },
+ {} as Settings
+ );
return {
...defaultValues,
@@ -471,11 +314,11 @@ export const useSettings = () => {
};
}, [_settings, pluginSettings]);
- return {
+ return [
settings,
updateSettings,
pluginSettings,
setPluginSettings,
refreshStreamyfinPluginSettings,
- };
+ ] as const;
};
diff --git a/utils/bitrate.ts b/utils/bitrate.ts
index 5e753f3d..7f1d0f47 100644
--- a/utils/bitrate.ts
+++ b/utils/bitrate.ts
@@ -3,9 +3,6 @@ export const formatBitrate = (bitrate?: number | null) => {
const sizes = ["bps", "Kbps", "Mbps", "Gbps", "Tbps"];
if (bitrate === 0) return "0 bps";
- const i = Number.parseInt(
- Math.floor(Math.log(bitrate) / Math.log(1000)).toString(),
- 10,
- );
- return `${Math.round((bitrate / 1000 ** i) * 100) / 100} ${sizes[i]}`;
+ const i = parseInt(Math.floor(Math.log(bitrate) / Math.log(1000)).toString());
+ return Math.round((bitrate / Math.pow(1000, i)) * 100) / 100 + " " + sizes[i];
};
diff --git a/utils/chromecastLoadMedia.ts b/utils/chromecastLoadMedia.ts
new file mode 100644
index 00000000..cae78f6f
--- /dev/null
+++ b/utils/chromecastLoadMedia.ts
@@ -0,0 +1,59 @@
+import { SelectedOptions } from "@/components/ItemContent";
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
+import { RemoteMediaClient, WebImage } from "react-native-google-cast";
+import { ticksToSeconds } from "./time";
+
+export function chromecastLoadMedia({
+ client,
+ item,
+ contentUrl,
+ sessionId,
+ mediaSourceId,
+ images,
+ playbackOptions,
+}: {
+ client: RemoteMediaClient;
+ item: BaseItemDto;
+ contentUrl: string;
+ sessionId?: string;
+ mediaSourceId?: string;
+ images: WebImage[];
+ playbackOptions?: SelectedOptions;
+}) {
+ return client.loadMedia({
+ mediaInfo: {
+ contentId: item.Id,
+ contentUrl,
+ contentType: "video/mp4",
+ customData: {
+ playbackOptions,
+ sessionId,
+ mediaSourceId,
+ },
+ metadata:
+ item.Type === "Episode"
+ ? {
+ type: "tvShow",
+ title: item.Name || "",
+ episodeNumber: item.IndexNumber || 0,
+ seasonNumber: item.ParentIndexNumber || 0,
+ seriesTitle: item.SeriesName || "",
+ images,
+ }
+ : item.Type === "Movie"
+ ? {
+ type: "movie",
+ title: item.Name || "",
+ subtitle: item.Overview || "",
+ images,
+ }
+ : {
+ type: "generic",
+ title: item.Name || "",
+ subtitle: item.Overview || "",
+ images,
+ },
+ },
+ startTime: ticksToSeconds(item.UserData?.PlaybackPositionTicks) || 0,
+ });
+}
diff --git a/utils/eventBus.ts b/utils/eventBus.ts
index 97062c70..4df6b19f 100644
--- a/utils/eventBus.ts
+++ b/utils/eventBus.ts
@@ -14,14 +14,12 @@ class EventBus {
off(event: string, callback: Listener): void {
if (!this.listeners[event]) return;
this.listeners[event] = this.listeners[event].filter(
- (fn) => fn !== callback,
+ (fn) => fn !== callback
);
}
emit(event: string, data?: T): void {
- this.listeners[event]?.forEach((callback) => {
- callback(data);
- });
+ this.listeners[event]?.forEach((callback) => callback(data));
}
}
diff --git a/utils/jellyfin/getDefaultPlaySettings.ts b/utils/jellyfin/getDefaultPlaySettings.ts
index d5ac5032..068d6058 100644
--- a/utils/jellyfin/getDefaultPlaySettings.ts
+++ b/utils/jellyfin/getDefaultPlaySettings.ts
@@ -1,103 +1,105 @@
-/**
- * getDefaultPlaySettings.ts
- *
- * Determines default audio/subtitle tracks and bitrate for playback.
- *
- * Two use cases:
- * 1. INITIAL PLAY: No previous state, uses media defaults + user language preferences
- * 2. SEQUENTIAL PLAY: Has previous state (e.g., next episode), uses StreamRanker
- * to find matching tracks in the new media
- */
-
-import type {
+// utils/getDefaultPlaySettings.ts
+import { BITRATES } from "@/components/BitrateSelector";
+import {
BaseItemDto,
MediaSourceInfo,
} from "@jellyfin/sdk/lib/generated-client";
-import { BITRATES } from "@/components/BitrateSelector";
-import { type Settings } from "../atoms/settings";
+import { Settings, useSettings } from "../atoms/settings";
import {
AudioStreamRanker,
StreamRanker,
SubtitleStreamRanker,
} from "../streamRanker";
-export interface PlaySettings {
+interface PlaySettings {
item: BaseItemDto;
bitrate: (typeof BITRATES)[0];
mediaSource?: MediaSourceInfo | null;
+ audioIndex?: number | undefined;
+ subtitleIndex?: number | undefined;
+}
+
+export interface previousIndexes {
audioIndex?: number;
subtitleIndex?: number;
}
-export interface PreviousIndexes {
- audioIndex?: number;
- subtitleIndex?: number;
+interface TrackOptions {
+ DefaultAudioStreamIndex: number | undefined;
+ DefaultSubtitleStreamIndex: number | undefined;
}
-/**
- * Get default play settings for an item.
- *
- * @param item - The media item to play
- * @param settings - User settings (language preferences, bitrate, etc.)
- * @param previous - Optional previous track selections to carry over (for sequential play)
- */
+// Used getting default values for the next player.
export function getDefaultPlaySettings(
item: BaseItemDto,
- settings: Settings | null,
- previous?: { indexes?: PreviousIndexes; source?: MediaSourceInfo },
+ settings: Settings,
+ previousIndexes?: previousIndexes,
+ previousSource?: MediaSourceInfo
): PlaySettings {
- const bitrate = settings?.defaultBitrate ?? BITRATES[0];
-
- // Live TV programs don't have media sources
if (item.Type === "Program") {
- return { item, bitrate };
+ return {
+ item,
+ bitrate: BITRATES[0],
+ mediaSource: undefined,
+ audioIndex: undefined,
+ subtitleIndex: undefined,
+ };
}
+ // 1. Get first media source
+
const mediaSource = item.MediaSources?.[0];
- const streams = mediaSource?.MediaStreams ?? [];
- // Start with media source defaults
- let audioIndex = mediaSource?.DefaultAudioStreamIndex;
- let subtitleIndex = mediaSource?.DefaultSubtitleStreamIndex ?? -1;
+ // 2. Get default or preferred audio
+ const defaultAudioIndex = mediaSource?.DefaultAudioStreamIndex;
+ const preferedAudioIndex = mediaSource?.MediaStreams?.find(
+ (x) => x.Type === "Audio" && x.Language === settings?.defaultAudioLanguage
+ )?.Index;
+ const firstAudioIndex = mediaSource?.MediaStreams?.find(
+ (x) => x.Type === "Audio"
+ )?.Index;
- // Try to match previous selections (sequential play)
- if (previous?.indexes && previous?.source && settings) {
- if (
- settings.rememberSubtitleSelections &&
- previous.indexes.subtitleIndex !== undefined
- ) {
- const ranker = new StreamRanker(new SubtitleStreamRanker());
- const result = { DefaultSubtitleStreamIndex: subtitleIndex };
+ // We prefer the previous track over the default track.
+ let trackOptions: TrackOptions = {
+ DefaultAudioStreamIndex: defaultAudioIndex ?? -1,
+ DefaultSubtitleStreamIndex: mediaSource?.DefaultSubtitleStreamIndex ?? -1,
+ };
+
+ const mediaStreams = mediaSource?.MediaStreams ?? [];
+ if (settings?.rememberSubtitleSelections && previousIndexes) {
+ if (previousIndexes.subtitleIndex !== undefined && previousSource) {
+ const subtitleRanker = new SubtitleStreamRanker();
+ const ranker = new StreamRanker(subtitleRanker);
ranker.rankStream(
- previous.indexes.subtitleIndex,
- previous.source,
- streams,
- result,
+ previousIndexes.subtitleIndex,
+ previousSource,
+ mediaStreams,
+ trackOptions
);
- subtitleIndex = result.DefaultSubtitleStreamIndex;
- }
-
- if (
- settings.rememberAudioSelections &&
- previous.indexes.audioIndex !== undefined
- ) {
- const ranker = new StreamRanker(new AudioStreamRanker());
- const result = { DefaultAudioStreamIndex: audioIndex };
- ranker.rankStream(
- previous.indexes.audioIndex,
- previous.source,
- streams,
- result,
- );
- audioIndex = result.DefaultAudioStreamIndex;
}
}
+ if (settings?.rememberAudioSelections && previousIndexes) {
+ if (previousIndexes.audioIndex !== undefined && previousSource) {
+ const audioRanker = new AudioStreamRanker();
+ const ranker = new StreamRanker(audioRanker);
+ ranker.rankStream(
+ previousIndexes.audioIndex,
+ previousSource,
+ mediaStreams,
+ trackOptions
+ );
+ }
+ }
+
+ // 4. Get default bitrate from settings or fallback to max
+ const bitrate = settings.defaultBitrate ?? BITRATES[0];
+
return {
item,
bitrate,
mediaSource,
- audioIndex: audioIndex ?? undefined,
- subtitleIndex: subtitleIndex ?? undefined,
+ audioIndex: trackOptions.DefaultAudioStreamIndex,
+ subtitleIndex: trackOptions.DefaultSubtitleStreamIndex,
};
}
diff --git a/utils/jellyfin/media/getStreamUrl.ts b/utils/jellyfin/media/getStreamUrl.ts
index c2124720..982bb413 100644
--- a/utils/jellyfin/media/getStreamUrl.ts
+++ b/utils/jellyfin/media/getStreamUrl.ts
@@ -1,138 +1,12 @@
-import type { Api } from "@jellyfin/sdk";
-import type {
+import native from "@/utils/profiles/native";
+import { Api } from "@jellyfin/sdk";
+import {
BaseItemDto,
MediaSourceInfo,
+ PlaybackInfoResponse,
} from "@jellyfin/sdk/lib/generated-client/models";
-import { BaseItemKind } from "@jellyfin/sdk/lib/generated-client/models/base-item-kind";
import { getMediaInfoApi } from "@jellyfin/sdk/lib/utils/api";
-import { generateDownloadProfile } from "@/utils/profiles/download";
-import type { AudioTranscodeModeType } from "@/utils/profiles/native";
-
-interface StreamResult {
- url: string;
- sessionId: string | null;
- mediaSource: MediaSourceInfo | undefined;
-}
-
-/**
- * Gets the actual streaming URL - handles both transcoded and direct play logic
- * Returns only the URL string
- */
-const getPlaybackUrl = (
- api: Api,
- itemId: string,
- mediaSource: MediaSourceInfo | undefined,
- params: {
- subtitleStreamIndex?: number;
- audioStreamIndex?: number;
- deviceId?: string | null;
- startTimeTicks?: number;
- maxStreamingBitrate?: number;
- userId: string;
- playSessionId?: string | null;
- container?: string;
- static?: string;
- },
-): string => {
- let transcodeUrl = mediaSource?.TranscodingUrl;
-
- // Handle transcoded URL if available
- if (transcodeUrl) {
- // For regular streaming, change subtitle method to HLS for transcoded URL
- if (params.subtitleStreamIndex === -1) {
- transcodeUrl = transcodeUrl.replace(
- "SubtitleMethod=Encode",
- "SubtitleMethod=Hls",
- );
- }
-
- console.log("Video is being transcoded:", transcodeUrl);
- return `${api.basePath}${transcodeUrl}`;
- }
-
- // Fall back to direct play
- const streamParams = new URLSearchParams({
- static: params.static || "true",
- container: params.container || "mp4",
- mediaSourceId: mediaSource?.Id || "",
- subtitleStreamIndex: params.subtitleStreamIndex?.toString() || "",
- audioStreamIndex: params.audioStreamIndex?.toString() || "",
- deviceId: params.deviceId || api.deviceInfo.id,
- api_key: api.accessToken,
- startTimeTicks: params.startTimeTicks?.toString() || "0",
- maxStreamingBitrate: params.maxStreamingBitrate?.toString() || "",
- userId: params.userId,
- });
-
- // Add additional parameters if provided
- if (params.playSessionId) {
- streamParams.append("playSessionId", params.playSessionId);
- }
-
- const directPlayUrl = `${api.basePath}/Videos/${itemId}/stream?${streamParams.toString()}`;
-
- console.log("Video is being direct played:", directPlayUrl);
- return directPlayUrl;
-};
-
-/** Wrapper around {@link getPlaybackUrl} that applies download-specific transformations */
-const getDownloadUrl = (
- api: Api,
- itemId: string,
- mediaSource: MediaSourceInfo | undefined,
- sessionId: string | null | undefined,
- params: {
- subtitleStreamIndex?: number;
- audioStreamIndex?: number;
- deviceId?: string | null;
- startTimeTicks?: number;
- maxStreamingBitrate?: number;
- userId: string;
- playSessionId?: string | null;
- },
-): StreamResult => {
- // First, handle download-specific transcoding modifications
- let downloadMediaSource = mediaSource;
- if (mediaSource?.TranscodingUrl) {
- downloadMediaSource = {
- ...mediaSource,
- TranscodingUrl: mediaSource.TranscodingUrl.replace(
- "master.m3u8",
- "stream",
- ),
- };
- }
-
- // Get the base URL with download-specific parameters
- let url = getPlaybackUrl(api, itemId, downloadMediaSource, {
- ...params,
- container: "ts",
- static: "false",
- });
-
- // If it's a direct play URL, add download-specific parameters
- if (!mediaSource?.TranscodingUrl) {
- const urlObj = new URL(url);
- const downloadParams = {
- subtitleMethod: "Embed",
- enableSubtitlesInManifest: "true",
- allowVideoStreamCopy: "true",
- allowAudioStreamCopy: "true",
- };
-
- Object.entries(downloadParams).forEach(([key, value]) => {
- urlObj.searchParams.append(key, value);
- });
-
- url = urlObj.toString();
- }
-
- return {
- url,
- sessionId: sessionId || null,
- mediaSource,
- };
-};
+import { Alert } from "react-native";
export const getStreamUrl = async ({
api,
@@ -140,25 +14,23 @@ export const getStreamUrl = async ({
userId,
startTimeTicks = 0,
maxStreamingBitrate,
- playSessionId,
- deviceProfile,
+ sessionData,
+ deviceProfile = native,
audioStreamIndex = 0,
subtitleStreamIndex = undefined,
mediaSourceId,
- deviceId,
}: {
api: Api | null | undefined;
item: BaseItemDto | null | undefined;
userId: string | null | undefined;
startTimeTicks: number;
maxStreamingBitrate?: number;
- playSessionId?: string | null;
- deviceProfile: any;
+ sessionData?: PlaybackInfoResponse | null;
+ deviceProfile?: any;
audioStreamIndex?: number;
subtitleStreamIndex?: number;
height?: number;
mediaSourceId?: string | null;
- deviceId?: string | null;
}): Promise<{
url: string | null;
sessionId: string | null;
@@ -172,10 +44,9 @@ export const getStreamUrl = async ({
let mediaSource: MediaSourceInfo | undefined;
let sessionId: string | null | undefined;
- // Please do not remove this we need this for live TV to be working correctly.
- if (item.Type === BaseItemKind.Program) {
+ if (item.Type === "Program") {
console.log("Item is of type program...");
- const res = await getMediaInfoApi(api).getPlaybackInfo(
+ const res0 = await getMediaInfoApi(api).getPlaybackInfo(
{
userId,
itemId: item.ChannelId!,
@@ -192,135 +63,93 @@ export const getStreamUrl = async ({
data: {
deviceProfile,
},
- },
+ }
);
+ const transcodeUrl = res0.data.MediaSources?.[0].TranscodingUrl;
+ sessionId = res0.data.PlaySessionId || null;
- sessionId = res.data.PlaySessionId || null;
- mediaSource = res.data.MediaSources?.[0];
- const url = getPlaybackUrl(api, item.ChannelId!, mediaSource, {
- subtitleStreamIndex,
- audioStreamIndex,
- deviceId,
- startTimeTicks: 0,
- maxStreamingBitrate,
- userId,
- });
-
- return {
- url,
- sessionId: sessionId || null,
- mediaSource,
- };
+ if (transcodeUrl) {
+ return {
+ url: `${api.basePath}${transcodeUrl}`,
+ sessionId,
+ mediaSource: res0.data.MediaSources?.[0],
+ };
+ }
}
- const res = await getMediaInfoApi(api).getPlaybackInfo(
+ const itemId = item.Id;
+
+ const res2 = await getMediaInfoApi(api).getPlaybackInfo(
{
itemId: item.Id!,
},
{
method: "POST",
data: {
- userId,
deviceProfile,
- subtitleStreamIndex,
- startTimeTicks,
- isPlayback: true,
- autoOpenLiveStream: true,
- maxStreamingBitrate,
- audioStreamIndex,
- mediaSourceId,
- },
- },
- );
-
- if (res.status !== 200) {
- console.error("Error getting playback info:", res.status, res.statusText);
- }
-
- sessionId = res.data.PlaySessionId || null;
- mediaSource = res.data.MediaSources?.[0];
-
- const url = getPlaybackUrl(api, item.Id!, mediaSource, {
- subtitleStreamIndex,
- audioStreamIndex,
- deviceId,
- startTimeTicks,
- maxStreamingBitrate,
- userId,
- playSessionId: playSessionId || undefined,
- });
-
- return {
- url,
- sessionId: sessionId || null,
- mediaSource,
- };
-};
-
-export const getDownloadStreamUrl = async ({
- api,
- item,
- userId,
- maxStreamingBitrate,
- audioStreamIndex = 0,
- subtitleStreamIndex = undefined,
- mediaSourceId,
- deviceId,
- audioMode = "auto",
-}: {
- api: Api | null | undefined;
- item: BaseItemDto | null | undefined;
- userId: string | null | undefined;
- maxStreamingBitrate?: number;
- audioStreamIndex?: number;
- subtitleStreamIndex?: number;
- mediaSourceId?: string | null;
- deviceId?: string | null;
- audioMode?: AudioTranscodeModeType;
-}): Promise<{
- url: string | null;
- sessionId: string | null;
- mediaSource: MediaSourceInfo | undefined;
-} | null> => {
- if (!api || !userId || !item?.Id) {
- console.warn("Missing required parameters for getStreamUrl");
- return null;
- }
-
- const res = await getMediaInfoApi(api).getPlaybackInfo(
- {
- itemId: item.Id!,
- },
- {
- method: "POST",
- data: {
userId,
- deviceProfile: generateDownloadProfile(audioMode),
- subtitleStreamIndex,
- startTimeTicks: 0,
- isPlayback: true,
- autoOpenLiveStream: true,
maxStreamingBitrate,
- audioStreamIndex,
+ startTimeTicks,
+ autoOpenLiveStream: true,
mediaSourceId,
+ audioStreamIndex,
+ subtitleStreamIndex,
},
- },
+ }
);
- if (res.status !== 200) {
- console.error("Error getting playback info:", res.status, res.statusText);
+ if (res2.status !== 200) {
+ console.error("Error getting playback info:", res2.status, res2.statusText);
}
- const sessionId = res.data.PlaySessionId || null;
- const mediaSource = res.data.MediaSources?.[0];
+ sessionId = res2.data.PlaySessionId || null;
- return getDownloadUrl(api, item.Id!, mediaSource, sessionId, {
- subtitleStreamIndex,
- audioStreamIndex,
- deviceId,
- startTimeTicks: 0,
- maxStreamingBitrate,
- userId,
- playSessionId: sessionId || undefined,
- });
+ mediaSource = res2.data.MediaSources?.find(
+ (source: MediaSourceInfo) => source.Id === mediaSourceId
+ );
+
+ if (item.MediaType === "Video") {
+ if (mediaSource?.TranscodingUrl) {
+ const urlObj = new URL(api.basePath + mediaSource?.TranscodingUrl); // Create a URL object
+
+ // Get the updated URL
+ const transcodeUrl = urlObj.toString();
+
+ console.log("Video has transcoding URL:", `${transcodeUrl}`);
+ return {
+ url: transcodeUrl,
+ sessionId: sessionId,
+ mediaSource,
+ };
+ } else {
+ const searchParams = new URLSearchParams({
+ playSessionId: sessionData?.PlaySessionId || "",
+ mediaSourceId: mediaSource?.Id || "",
+ static: "true",
+ subtitleStreamIndex: subtitleStreamIndex?.toString() || "",
+ audioStreamIndex: audioStreamIndex?.toString() || "",
+ deviceId: api.deviceInfo.id,
+ api_key: api.accessToken,
+ startTimeTicks: startTimeTicks.toString(),
+ maxStreamingBitrate: maxStreamingBitrate?.toString() || "",
+ userId: userId || "",
+ });
+
+ const directPlayUrl = `${
+ api.basePath
+ }/Videos/${itemId}/stream.mp4?${searchParams.toString()}`;
+
+ console.log("Video is being direct played:", directPlayUrl);
+
+ return {
+ url: directPlayUrl,
+ sessionId: sessionId,
+ mediaSource,
+ };
+ }
+ }
+
+ Alert.alert("Error", "Could not play this item");
+
+ return null;
};
diff --git a/utils/profiles/chromecasth265.ts b/utils/profiles/chromecasth265.ts
index 42bb1712..e74827b2 100644
--- a/utils/profiles/chromecasth265.ts
+++ b/utils/profiles/chromecasth265.ts
@@ -1,4 +1,4 @@
-import type { DeviceProfile } from "@jellyfin/sdk/lib/generated-client/models";
+import { DeviceProfile } from "@jellyfin/sdk/lib/generated-client/models";
export const chromecasth265: DeviceProfile = {
Name: "Chromecast Video Profile",
diff --git a/utils/profiles/native.js b/utils/profiles/native.js
index ec74f4b6..72d4e3b6 100644
--- a/utils/profiles/native.js
+++ b/utils/profiles/native.js
@@ -3,202 +3,134 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-import { Platform } from "react-native";
import MediaTypes from "../../constants/MediaTypes";
-import { getSubtitleProfiles } from "./subtitles";
/**
- * @typedef {"ios" | "android"} PlatformType
- * @typedef {"mpv"} PlayerType
- * @typedef {"auto" | "stereo" | "5.1" | "passthrough"} AudioTranscodeModeType
- *
- * @typedef {Object} ProfileOptions
- * @property {PlatformType} [platform] - Target platform
- * @property {PlayerType} [player] - Video player being used (MPV only)
- * @property {AudioTranscodeModeType} [audioMode] - Audio transcoding mode
+ * Device profile for Native video player
*/
-
-/**
- * Audio direct play profiles for standalone audio items in MPV player.
- * These define which audio file formats can be played directly without transcoding.
- */
-const getAudioDirectPlayProfile = (platform) => {
- if (platform === "ios") {
- // iOS audio formats supported by MPV
- return {
+export default {
+ Name: "1. Vlc Player",
+ MaxStaticBitrate: 999_999_999,
+ MaxStreamingBitrate: 999_999_999,
+ CodecProfiles: [
+ {
+ Type: MediaTypes.Video,
+ Codec: "h264,h265,hevc,mpeg4,divx,xvid,wmv,vc1,vp8,vp9,av1",
+ },
+ {
Type: MediaTypes.Audio,
- Container: "mp3,m4a,aac,flac,alac,wav,aiff,caf",
- AudioCodec: "mp3,aac,alac,flac,opus,pcm",
- };
- }
-
- // Android audio formats supported by MPV
- return {
- Type: MediaTypes.Audio,
- Container: "mp3,m4a,aac,ogg,flac,wav,webm,mka",
- AudioCodec: "mp3,aac,flac,vorbis,opus,pcm",
- };
-};
-
-/**
- * Audio codec profiles for standalone audio items in MPV player.
- * These define codec constraints for audio file playback.
- */
-const getAudioCodecProfile = (platform) => {
- if (platform === "ios") {
- // iOS audio codec constraints for MPV
- return {
+ Codec: "aac,ac3,eac3,mp3,flac,alac,opus,vorbis,pcm,wma",
+ },
+ ],
+ DirectPlayProfiles: [
+ {
+ Type: MediaTypes.Video,
+ Container: "mp4,mkv,avi,mov,flv,ts,m2ts,webm,ogv,3gp,hls",
+ VideoCodec:
+ "h264,hevc,mpeg4,divx,xvid,wmv,vc1,vp8,vp9,av1,avi,mpeg,mpeg2video",
+ AudioCodec: "aac,ac3,eac3,mp3,flac,alac,opus,vorbis,wma",
+ },
+ {
Type: MediaTypes.Audio,
- Codec: "aac,ac3,eac3,mp3,flac,alac,opus,pcm",
- };
- }
+ Container: "mp3,aac,flac,alac,wav,ogg,wma",
+ AudioCodec:
+ "mp3,aac,flac,alac,opus,vorbis,wma,pcm,mpa,wav,ogg,oga,webma,ape",
+ },
+ ],
+ TranscodingProfiles: [
+ {
+ Type: MediaTypes.Video,
+ Context: "Streaming",
+ Protocol: "hls",
+ Container: "fmp4",
+ VideoCodec: "h264, hevc",
+ AudioCodec: "aac,mp3,ac3,dts",
+ },
+ {
+ Type: MediaTypes.Audio,
+ Context: "Streaming",
+ Protocol: "http",
+ Container: "mp3",
+ AudioCodec: "mp3",
+ MaxAudioChannels: "2",
+ },
+ ],
+ SubtitleProfiles: [
+ // Official formats
+ { Format: "vtt", Method: "Embed" },
+ { Format: "vtt", Method: "External" },
- // Android audio codec constraints for MPV
- return {
- Type: MediaTypes.Audio,
- Codec: "aac,ac3,eac3,mp3,flac,vorbis,opus,pcm",
- };
+ { Format: "webvtt", Method: "Embed" },
+ { Format: "webvtt", Method: "External" },
+
+ { Format: "srt", Method: "Embed" },
+ { Format: "srt", Method: "External" },
+
+ { Format: "subrip", Method: "Embed" },
+ { Format: "subrip", Method: "External" },
+
+ { Format: "ttml", Method: "Embed" },
+ { Format: "ttml", Method: "External" },
+
+ { Format: "dvbsub", Method: "Embed" },
+ { Format: "dvdsub", Method: "Encode" },
+
+ { Format: "ass", Method: "Embed" },
+ { Format: "ass", Method: "External" },
+
+ { Format: "idx", Method: "Embed" },
+ { Format: "idx", Method: "Encode" },
+
+ { Format: "pgs", Method: "Embed" },
+ { Format: "pgs", Method: "Encode" },
+
+ { Format: "pgssub", Method: "Embed" },
+ { Format: "pgssub", Method: "Encode" },
+
+ { Format: "ssa", Method: "Embed" },
+ { Format: "ssa", Method: "External" },
+
+ // Other formats
+ { Format: "microdvd", Method: "Embed" },
+ { Format: "microdvd", Method: "External" },
+
+ { Format: "mov_text", Method: "Embed" },
+ { Format: "mov_text", Method: "External" },
+
+ { Format: "mpl2", Method: "Embed" },
+ { Format: "mpl2", Method: "External" },
+
+ { Format: "pjs", Method: "Embed" },
+ { Format: "pjs", Method: "External" },
+
+ { Format: "realtext", Method: "Embed" },
+ { Format: "realtext", Method: "External" },
+
+ { Format: "scc", Method: "Embed" },
+ { Format: "scc", Method: "External" },
+
+ { Format: "smi", Method: "Embed" },
+ { Format: "smi", Method: "External" },
+
+ { Format: "stl", Method: "Embed" },
+ { Format: "stl", Method: "External" },
+
+ { Format: "sub", Method: "Embed" },
+ { Format: "sub", Method: "External" },
+
+ { Format: "subviewer", Method: "Embed" },
+ { Format: "subviewer", Method: "External" },
+
+ { Format: "teletext", Method: "Embed" },
+ { Format: "teletext", Method: "Encode" },
+
+ { Format: "text", Method: "Embed" },
+ { Format: "text", Method: "External" },
+
+ { Format: "vplayer", Method: "Embed" },
+ { Format: "vplayer", Method: "External" },
+
+ { Format: "xsub", Method: "Embed" },
+ { Format: "xsub", Method: "External" },
+ ],
};
-
-/**
- * Gets the video audio codec configuration based on platform and audio mode.
- *
- * MPV (via FFmpeg) can decode all audio codecs including TrueHD and DTS-HD MA.
- * The audioMode setting only controls the maximum channel count - MPV will
- * decode and downmix as needed.
- *
- * @param {PlatformType} platform
- * @param {AudioTranscodeModeType} audioMode
- * @returns {{ directPlayCodec: string, maxAudioChannels: string }}
- */
-const getVideoAudioCodecs = (platform, audioMode) => {
- // Base codecs
- const baseCodecs = "aac,mp3,flac,opus,vorbis";
-
- // Surround codecs
- const surroundCodecs = "ac3,eac3,dts";
-
- // Lossless HD codecs - MPV decodes these and downmixes as needed
- const losslessHdCodecs = "truehd";
-
- // Platform-specific codecs
- const platformCodecs = platform === "ios" ? "alac,wma" : "wma";
-
- // MPV can decode all codecs - only channel count varies by mode
- const allCodecs = `${baseCodecs},${surroundCodecs},${losslessHdCodecs},${platformCodecs}`;
-
- switch (audioMode) {
- case "stereo":
- // Limit to 2 channels - MPV will decode and downmix
- return {
- directPlayCodec: allCodecs,
- maxAudioChannels: "2",
- };
-
- case "5.1":
- // Limit to 6 channels
- return {
- directPlayCodec: allCodecs,
- maxAudioChannels: "6",
- };
-
- case "passthrough":
- // Allow up to 8 channels - for external DAC/receiver setups
- return {
- directPlayCodec: allCodecs,
- maxAudioChannels: "8",
- };
-
- default:
- // Auto mode: default to 5.1 (6 channels)
- return {
- directPlayCodec: allCodecs,
- maxAudioChannels: "6",
- };
- }
-};
-
-/**
- * Generates a device profile for Jellyfin playback.
- *
- * @param {ProfileOptions} [options] - Profile configuration options
- * @returns {Object} Jellyfin device profile
- */
-export const generateDeviceProfile = (options = {}) => {
- const platform = options.platform || Platform.OS;
- const audioMode = options.audioMode || "auto";
-
- const { directPlayCodec, maxAudioChannels } = getVideoAudioCodecs(
- platform,
- audioMode,
- );
-
- /**
- * Device profile for MPV player
- */
- const profile = {
- Name: "1. MPV",
- MaxStaticBitrate: 999_999_999,
- MaxStreamingBitrate: 999_999_999,
- CodecProfiles: [
- {
- Type: MediaTypes.Video,
- Codec: "h264,mpeg4,divx,xvid,wmv,vc1,vp8,vp9,av1",
- },
- {
- Type: MediaTypes.Video,
- Codec: "hevc,h265",
- Conditions: [
- {
- Condition: "LessThanEqual",
- Property: "VideoLevel",
- Value: "153",
- IsRequired: false,
- },
- {
- Condition: "NotEquals",
- Property: "VideoRangeType",
- Value: "DOVI", //no dolby vision at all
- IsRequired: true,
- },
- ],
- },
- getAudioCodecProfile(platform),
- ],
- DirectPlayProfiles: [
- {
- Type: MediaTypes.Video,
- Container: "mp4,mkv,avi,mov,flv,ts,m2ts,webm,ogv,3gp,hls",
- VideoCodec:
- "h264,hevc,mpeg4,divx,xvid,wmv,vc1,vp8,vp9,av1,avi,mpeg,mpeg2video",
- AudioCodec: directPlayCodec,
- },
- getAudioDirectPlayProfile(platform),
- ],
- TranscodingProfiles: [
- {
- Type: MediaTypes.Video,
- Context: "Streaming",
- Protocol: "hls",
- Container: "ts",
- VideoCodec: "h264, hevc",
- AudioCodec: "aac,mp3,ac3,dts",
- MaxAudioChannels: maxAudioChannels,
- },
- {
- Type: MediaTypes.Audio,
- Context: "Streaming",
- Protocol: "http",
- Container: "mp3",
- AudioCodec: "mp3",
- MaxAudioChannels: "2",
- },
- ],
- SubtitleProfiles: getSubtitleProfiles(),
- };
-
- return profile;
-};
-
-// Default export for backward compatibility
-export default generateDeviceProfile();