Repository: IgnisDa/ryot (language: rust)

Issue:
[FEATURE REQUEST] - Make filters and sorting persistent again
With the frontend changes in `v4`, filters and sorting are no longer persistent.
I really liked persistent filters because I usually only want to see my completed movies in the movies tab and always manually applying the filter is tedious.

I think the best solution would be to specify the default filters and sorting in preferences and then use those if no filters are applied manually.


Discussion / hints:
Huh, storing filters in preferences does not sound like a good idea. Going to preferences settings every time you want to set a default is not a good UX, not to mention it would increase the preferences page by a lot.

I thought of a cookie based approach to solving it, but I haven't fleshed out a good solution yet. I'll put some more thought to it once I iron out all the little bugs v4 introduced.
You could add a button "Save as default" directly in filter popup. So if you would actually need to change it, no preferences is needed. And for different rarely used filtering you can do it manually.
@lapius I am revisiting this issue. Your idea sounds good to me.

So if I understand correctly, the filters modal will have a btn to save. Once that is clicked, these filters will be saved to cookies. Next time the user visits a bare URL (i.e., `/media/movies`), then reload the page with the filters applied. But if the user visits with search params already supplied (i.e., `/media/movies?collectionFilter=whatever`) then the filters wont be respected.

Basically the filters in the url take precedence over the filters in the cookies.

Does this sound like a good approach?

Fix patch:
diff --git a/apps/backend/src/importer/audiobookshelf.rs b/apps/backend/src/importer/audiobookshelf.rs
index 05570e9bcf..6e3fdb1218 100644
--- a/apps/backend/src/importer/audiobookshelf.rs
+++ b/apps/backend/src/importer/audiobookshelf.rs
@@ -14,10 +14,7 @@ use crate::{
     entities::metadata,
     importer::{ImportFailStep, ImportFailedItem, ImportResult},
     miscellaneous::{audiobookshelf_models, itunes_podcast_episode_by_name},
-    models::media::{
-        CommitMediaInput, ImportOrExportItemIdentifier, ImportOrExportMediaItem,
-        ImportOrExportMediaItemSeen,
-    },
+    models::media::{CommitMediaInput, ImportOrExportMediaItem, ImportOrExportMediaItemSeen},
     providers::google_books::GoogleBooksService,
     utils::get_base_http_client,
 };
@@ -159,10 +156,9 @@ where
             media.push(ImportOrExportMediaItem {
                 lot,
                 source,
-                source_id: metadata.title,
-                identifier: "".to_string(),
-                internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(identifier)),
+                identifier,
                 seen_history,
+                source_id: metadata.title,
                 collections: vec![],
                 reviews: vec![],
             })
diff --git a/apps/backend/src/importer/generic_json.rs b/apps/backend/src/importer/generic_json.rs
index 860fc71c57..69aebb2e8f 100644
--- a/apps/backend/src/importer/generic_json.rs
+++ b/apps/backend/src/importer/generic_json.rs
@@ -7,7 +7,7 @@ use itertools::Itertools;
 use crate::{
     fitness::resolver::ExerciseService,
     importer::{DeployJsonImportInput, ImportResult},
-    models::{media::ImportOrExportItemIdentifier, CompleteExport},
+    models::CompleteExport,
 };
 
 pub async fn import(
@@ -22,9 +22,6 @@ pub async fn import(
         .unwrap_or_default()
         .iter_mut()
         .map(|m| {
-            m.internal_identifier = Some(ImportOrExportItemIdentifier::NeedsDetails(
-                m.identifier.clone(),
-            ));
             m.seen_history.iter_mut().for_each(|s| {
                 s.provider_watched_on = Some(ImportSource::GenericJson.to_string());
             });
diff --git a/apps/backend/src/importer/goodreads.rs b/apps/backend/src/importer/goodreads.rs
index ac7834c50b..410b3b0798 100644
--- a/apps/backend/src/importer/goodreads.rs
+++ b/apps/backend/src/importer/goodreads.rs
@@ -11,8 +11,8 @@ use serde::Deserialize;
 use crate::{
     importer::{DeployGenericCsvImportInput, ImportFailStep, ImportFailedItem, ImportResult},
     models::media::{
-        ImportOrExportItemIdentifier, ImportOrExportItemRating, ImportOrExportItemReview,
-        ImportOrExportMediaItem, ImportOrExportMediaItemSeen,
+        ImportOrExportItemRating, ImportOrExportItemReview, ImportOrExportMediaItem,
+        ImportOrExportMediaItemSeen,
     },
     providers::google_books::GoogleBooksService,
 };
@@ -127,8 +127,7 @@ pub async fn import(
                 source_id: record.title.clone(),
                 lot,
                 source,
-                identifier: "".to_string(),
-                internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(identifier)),
+                identifier,
                 seen_history,
                 reviews,
                 collections,
diff --git a/apps/backend/src/importer/igdb.rs b/apps/backend/src/importer/igdb.rs
index 911e502fcd..1a488f9bf1 100644
--- a/apps/backend/src/importer/igdb.rs
+++ b/apps/backend/src/importer/igdb.rs
@@ -7,9 +7,7 @@ use serde::Deserialize;
 
 use crate::{
     miscellaneous::DefaultCollection,
-    models::media::{
-        ImportOrExportItemIdentifier, ImportOrExportMediaItem, ImportOrExportMediaItemSeen,
-    },
+    models::media::{ImportOrExportMediaItem, ImportOrExportMediaItemSeen},
 };
 
 use super::{DeployIgdbImportInput, ImportFailStep, ImportFailedItem, ImportResult};
@@ -56,13 +54,12 @@ pub async fn import(input: DeployIgdbImportInput) -> Result<ImportResult> {
             }
         };
         media.push(ImportOrExportMediaItem {
-            collections: vec![collection.clone()],
-            internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(record.id)),
             lot,
             source,
             source_id: record.game,
-            identifier: "".to_string(),
+            identifier: record.id,
             seen_history: seen_history.clone(),
+            collections: vec![collection.clone()],
             reviews: vec![],
         });
     }
diff --git a/apps/backend/src/importer/imdb.rs b/apps/backend/src/importer/imdb.rs
index e9cfd44b88..7cc2d530ff 100644
--- a/apps/backend/src/importer/imdb.rs
+++ b/apps/backend/src/importer/imdb.rs
@@ -7,7 +7,7 @@ use serde::Deserialize;
 use crate::{
     importer::{DeployGenericCsvImportInput, ImportFailStep, ImportFailedItem, ImportResult},
     miscellaneous::DefaultCollection,
-    models::media::{ImportOrExportItemIdentifier, ImportOrExportMediaItem},
+    models::media::ImportOrExportMediaItem,
     providers::tmdb::NonMediaTmdbService,
 };
 
@@ -58,7 +58,7 @@ pub async fn import(
                 continue;
             }
         };
-        let tmdb_identifier = match tmdb_service
+        let identifier = match tmdb_service
             .find_by_external_id(&record.id, "imdb_id")
             .await
         {
@@ -73,14 +73,13 @@ pub async fn import(
                 continue;
             }
         };
-        tracing::debug!("Found tmdb id: {} ({}/{})", tmdb_identifier, idx + 1, total);
+        tracing::debug!("Found tmdb id: {} ({}/{})", identifier, idx + 1, total);
         media.push(ImportOrExportMediaItem {
-            collections: vec![DefaultCollection::Watchlist.to_string()],
-            identifier: "".to_string(),
-            internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(tmdb_identifier)),
+            identifier,
             lot,
             source,
             source_id: record.id,
+            collections: vec![DefaultCollection::Watchlist.to_string()],
             reviews: vec![],
             seen_history: vec![],
         });
diff --git a/apps/backend/src/importer/jellyfin.rs b/apps/backend/src/importer/jellyfin.rs
index 8225ebed6b..d6ddab5315 100644
--- a/apps/backend/src/importer/jellyfin.rs
+++ b/apps/backend/src/importer/jellyfin.rs
@@ -13,9 +13,7 @@ use crate::{
     importer::{
         DeployUrlAndKeyAndUsernameImportInput, ImportFailStep, ImportFailedItem, ImportResult,
     },
-    models::media::{
-        ImportOrExportItemIdentifier, ImportOrExportMediaItem, ImportOrExportMediaItemSeen,
-    },
+    models::media::{ImportOrExportMediaItem, ImportOrExportMediaItemSeen},
     utils::{JSON, USER_AGENT_STR},
 };
 
@@ -175,9 +173,6 @@ pub async fn import(input: DeployUrlAndKeyAndUsernameImportInput) -> Result<Impo
                 lot,
                 source_id: item.series_name.unwrap_or(item.name),
                 source: MediaSource::Tmdb,
-                internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(
-                    tmdb_id.clone(),
-                )),
                 seen_history: vec![seen],
                 identifier: tmdb_id,
                 reviews: vec![],
diff --git a/apps/backend/src/importer/mal.rs b/apps/backend/src/importer/mal.rs
index b73992df25..edb53daf0c 100644
--- a/apps/backend/src/importer/mal.rs
+++ b/apps/backend/src/importer/mal.rs
@@ -16,8 +16,7 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use crate::{
     importer::{DeployMalImportInput, ImportResult},
     models::media::{
-        ImportOrExportItemIdentifier, ImportOrExportItemRating, ImportOrExportMediaItem,
-        ImportOrExportMediaItemSeen,
+        ImportOrExportItemRating, ImportOrExportMediaItem, ImportOrExportMediaItemSeen,
     },
 };
 
@@ -91,14 +90,11 @@ fn convert_to_format(item: Item, lot: MediaLot) -> ImportOrExportMediaItem {
         ..Default::default()
     };
     ImportOrExportMediaItem {
-        source_id: item.title.clone(),
         lot,
         source: MediaSource::Mal,
-        identifier: "".to_string(),
-        internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(
-            item.identifier.to_string(),
-        )),
+        identifier: item.identifier.to_string(),
         seen_history,
+        source_id: item.title.clone(),
         reviews: vec![review_item],
         collections: vec![],
     }
diff --git a/apps/backend/src/importer/media_tracker.rs b/apps/backend/src/importer/media_tracker.rs
index 0fb5991727..f2b51a5553 100644
--- a/apps/backend/src/importer/media_tracker.rs
+++ b/apps/backend/src/importer/media_tracker.rs
@@ -1,6 +1,5 @@
 use async_graphql::Result;
 use database::{ImportSource, MediaLot, MediaSource};
-use nanoid::nanoid;
 use reqwest::{
     header::{HeaderMap, HeaderValue, USER_AGENT},
     ClientBuilder,
@@ -18,9 +17,8 @@ use crate::{
     },
     models::{
         media::{
-            BookSpecifics, CreateOrUpdateCollectionInput, ImportOrExportItemIdentifier,
-            ImportOrExportItemRating, ImportOrExportItemReview, ImportOrExportMediaItemSeen,
-            MediaDetails, MetadataFreeCreator,
+            CreateOrUpdateCollectionInput, ImportOrExportItemRating, ImportOrExportItemReview,
+            ImportOrExportMediaItemSeen,
         },
         IdObject,
     },
@@ -116,13 +114,6 @@ struct ItemSeen {
     episode_id: Option<i32>,
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
-#[serde(untagged)]
-enum ItemNumberOfPages {
-    Nothing(String),
-    Something(i32),
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone)]
 #[serde(rename_all = "camelCase")]
 struct ItemDetails {
@@ -135,10 +126,6 @@ struct ItemDetails {
     tmdb_id: Option<i32>,
     openlibrary_id: Option<String>,
     goodreads_id: Option<i32>,
-    title: String,
-    overview: Option<String>,
-    authors: Option<Vec<String>>,
-    number_of_pages: Option<ItemNumberOfPages>,
 }
 
 pub async fn import(input: DeployUrlAndKeyImportInput) -> Result<ImportResult> {
@@ -244,7 +231,13 @@ pub async fn import(input: DeployUrlAndKeyImportInput) -> Result<ImportResult> {
         let (identifier, source) = match media_type {
             MediaType::Book => {
                 if let Some(_g_id) = details.goodreads_id {
-                    (nanoid!(10), MediaSource::Custom)
+                    failed_items.push(ImportFailedItem {
+                        lot: Some(lot),
+                        step: ImportFailStep::ItemDetailsFromSource,
+                        identifier: d.id.to_string(),
+                        error: Some("Goodreads ID not supported".to_string()),
+                    });
+                    continue;
                 } else {
                     (
                         get_key(&details.openlibrary_id.clone().unwrap()),
@@ -265,7 +258,6 @@ pub async fn import(input: DeployUrlAndKeyImportInput) -> Result<ImportResult> {
             total = data_len,
             seen = details.seen_history.len()
         );
-        let need_details = details.goodreads_id.is_none();
 
         let mut collections = vec![];
         for list in lists.iter() {
@@ -276,52 +268,12 @@ pub async fn import(input: DeployUrlAndKeyImportInput) -> Result<ImportResult> {
             }
         }
 
-        let num_pages = details.number_of_pages.and_then(|d| match d {
-            ItemNumberOfPages::Nothing(_) => None,
-            ItemNumberOfPages::Something(s) => Some(s),
-        });
-
         let item = ImportOrExportMediaItem {
             source_id: d.id.to_string(),
             source,
             lot,
             collections,
-            identifier: "".to_string(),
-            internal_identifier: Some(match need_details {
-                false => ImportOrExportItemIdentifier::AlreadyFilled(Box::new(MediaDetails {
-                    identifier,
-                    title: details.title,
-                    description: details.overview,
-                    lot,
-                    source: MediaSource::Custom,
-                    creators: details
-                        .authors
-                        .unwrap_or_default()
-                        .into_iter()
-                        .map(|a| MetadataFreeCreator {
-                            name: a,
-                            role: "Author".to_owned(),
-                            image: None,
-                        })
-                        .collect(),
-                    provider_rating: None,
-                    genres: vec![],
-                    url_images: vec![],
-                    videos: vec![],
-                    publish_year: None,
-                    publish_date: None,
-                    suggestions: vec![],
-                    group_identifiers: vec![],
-                    is_nsfw: None,
-                    production_status: None,
-                    people: vec![],
-                    s3_images: vec![],
-                    original_language: None,
-                    book_specifics: Some(BookSpecifics { pages: num_pages }),
-                    ..Default::default()
-                })),
-                true => ImportOrExportItemIdentifier::NeedsDetails(identifier),
-            }),
+            identifier,
             reviews: Vec::from_iter(details.user_rating.map(|r| {
                 let review = if let Some(_s) = r.clone().review {
                     Some(ImportOrExportItemReview {
diff --git a/apps/backend/src/importer/mod.rs b/apps/backend/src/importer/mod.rs
index 739e76faf6..d7db9c05cc 100644
--- a/apps/backend/src/importer/mod.rs
+++ b/apps/backend/src/importer/mod.rs
@@ -23,9 +23,8 @@ use crate::{
         fitness::UserWorkoutInput,
         media::{
             CommitMediaInput, CommitPersonInput, CreateOrUpdateCollectionInput,
-            ImportOrExportItemIdentifier, ImportOrExportItemRating, ImportOrExportMediaGroupItem,
-            ImportOrExportMediaItem, ImportOrExportPersonItem, PostReviewInput,
-            ProgressUpdateInput,
+            ImportOrExportItemRating, ImportOrExportMediaGroupItem, ImportOrExportMediaItem,
+            ImportOrExportPersonItem, PostReviewInput, ProgressUpdateInput,
         },
         BackgroundJob, ChangeCollectionToEntityInput,
     },
@@ -341,24 +340,16 @@ impl ImporterService {
                 iden = &item.source_id
             );
             let rev_length = item.reviews.len();
-            let identifier = item.internal_identifier.clone().unwrap();
-            let data = match identifier {
-                ImportOrExportItemIdentifier::NeedsDetails(identifier) => {
-                    self.media_service
-                        .commit_metadata(CommitMediaInput {
-                            identifier,
-                            lot: item.lot,
-                            source: item.source,
-                            force_update: Some(true),
-                        })
-                        .await
-                }
-                ImportOrExportItemIdentifier::AlreadyFilled(a) => {
-                    self.media_service
-                        .commit_metadata_internal(*a.clone(), None)
-                        .await
-                }
-            };
+            let identifier = item.identifier.clone();
+            let data = self
+                .media_service
+                .commit_metadata(CommitMediaInput {
+                    identifier,
+                    lot: item.lot,
+                    source: item.source,
+                    force_update: Some(true),
+                })
+                .await;
             let metadata = match data {
                 Ok(r) => r,
                 Err(e) => {
diff --git a/apps/backend/src/importer/movary.rs b/apps/backend/src/importer/movary.rs
index e8c78915e9..b8c0df0f44 100644
--- a/apps/backend/src/importer/movary.rs
+++ b/apps/backend/src/importer/movary.rs
@@ -9,8 +9,8 @@ use serde::{Deserialize, Serialize};
 
 use crate::{
     importer::{
-        DeployMovaryImportInput, ImportFailStep, ImportFailedItem, ImportOrExportItemIdentifier,
-        ImportOrExportMediaItem, ImportResult,
+        DeployMovaryImportInput, ImportFailStep, ImportFailedItem, ImportOrExportMediaItem,
+        ImportResult,
     },
     miscellaneous::DefaultCollection,
     models::media::{
@@ -65,10 +65,7 @@ pub async fn import(input: DeployMovaryImportInput) -> Result<ImportResult> {
             source_id: record.common.title.clone(),
             lot,
             source,
-            internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(
-                record.common.tmdb_id.to_string(),
-            )),
-            identifier: "".to_string(),
+            identifier: record.common.tmdb_id.to_string(),
             seen_history: vec![],
             reviews: vec![ImportOrExportItemRating {
                 // DEV: Rates items out of 10
@@ -96,10 +93,7 @@ pub async fn import(input: DeployMovaryImportInput) -> Result<ImportResult> {
             source_id: record.title.clone(),
             lot,
             source,
-            internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(
-                record.tmdb_id.to_string(),
-            )),
-            identifier: "".to_string(),
+            identifier: record.tmdb_id.to_string(),
             seen_history: vec![],
             reviews: vec![],
             collections: vec![DefaultCollection::Watchlist.to_string()],
@@ -159,10 +153,7 @@ pub async fn import(input: DeployMovaryImportInput) -> Result<ImportResult> {
                 source_id: record.common.title.clone(),
                 lot,
                 source,
-                identifier: "".to_string(),
-                internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(
-                    record.common.tmdb_id.to_string(),
-                )),
+                identifier: record.common.tmdb_id.to_string(),
                 seen_history: vec![seen_item],
                 reviews,
                 collections: vec![],
diff --git a/apps/backend/src/importer/story_graph.rs b/apps/backend/src/importer/story_graph.rs
index 4287c71d5b..e8593b4fa9 100644
--- a/apps/backend/src/importer/story_graph.rs
+++ b/apps/backend/src/importer/story_graph.rs
@@ -9,10 +9,7 @@ use rust_decimal_macros::dec;
 use serde::{Deserialize, Serialize};
 
 use crate::{
-    importer::{
-        ImportFailStep, ImportFailedItem, ImportOrExportItemIdentifier, ImportOrExportMediaItem,
-        ImportResult,
-    },
+    importer::{ImportFailStep, ImportFailedItem, ImportOrExportMediaItem, ImportResult},
     models::media::{
         ImportOrExportItemRating, ImportOrExportItemReview, ImportOrExportMediaItemSeen,
     },
@@ -109,10 +106,7 @@ pub async fn import(
                     source_id: record.title.clone(),
                     lot,
                     source,
-                    identifier: "".to_string(),
-                    internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(
-                        identifier,
-                    )),
+                    identifier,
                     seen_history,
                     reviews: vec![ImportOrExportItemRating {
                         rating: record
diff --git a/apps/backend/src/importer/trakt.rs b/apps/backend/src/importer/trakt.rs
index cc81e062ee..66199491c8 100644
--- a/apps/backend/src/importer/trakt.rs
+++ b/apps/backend/src/importer/trakt.rs
@@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize};
 
 use crate::{
     importer::{
-        DeployTraktImportInput, ImportFailStep, ImportFailedItem, ImportOrExportItemIdentifier,
-        ImportOrExportMediaItem, ImportResult,
+        DeployTraktImportInput, ImportFailStep, ImportFailedItem, ImportOrExportMediaItem,
+        ImportResult,
     },
     models::media::{
         CreateOrUpdateCollectionInput, ImportOrExportItemRating, ImportOrExportItemReview,
@@ -255,8 +255,7 @@ fn process_item(
         Some(i) => Ok(ImportOrExportMediaItem {
             source_id: source_id.to_string(),
             lot,
-            identifier: "".to_string(),
-            internal_identifier: Some(ImportOrExportItemIdentifier::NeedsDetails(i.to_string())),
+            identifier: i.to_string(),
             source: MediaSource::Tmdb,
             seen_history: vec![],
             reviews: vec![],
diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs
index 9053200198..ad4606d744 100644
--- a/apps/backend/src/miscellaneous/resolver.rs
+++ b/apps/backend/src/miscellaneous/resolver.rs
@@ -5415,6 +5415,9 @@ impl MiscellaneousService {
                         "disable_yank_integrations" => {
                             preferences.general.disable_yank_integrations = value_bool.unwrap();
                         }
+                        "persist_queries" => {
+                            preferences.general.persist_queries = value_bool.unwrap();
+                        }
                         "disable_navigation_animation" => {
                             preferences.general.disable_navigation_animation = value_bool.unwrap();
                         }
@@ -6584,7 +6587,6 @@ impl MiscellaneousService {
                 lot: m.lot,
                 source: m.source,
                 identifier: m.identifier.clone(),
-                internal_identifier: None,
                 seen_history,
                 reviews,
                 collections,
diff --git a/apps/backend/src/models.rs b/apps/backend/src/models.rs
index 95b7d54534..e08521f2ff 100644
--- a/apps/backend/src/models.rs
+++ b/apps/backend/src/models.rs
@@ -984,15 +984,6 @@ pub mod media {
         pub manga_specifics: Option<MangaSpecifics>,
     }
 
-    #[derive(Debug, Serialize, Deserialize, Clone)]
-    #[serde(untagged)]
-    pub enum ImportOrExportItemIdentifier {
-        // the identifier in case we need to fetch details
-        NeedsDetails(String),
-        // details are already filled and just need to be committed to database
-        AlreadyFilled(Box<MediaDetails>),
-    }
-
     /// A specific instance when an entity was seen.
     #[skip_serializing_none]
     #[derive(Debug, Serialize, Deserialize, Clone, Default, Schematic)]
@@ -1071,10 +1062,6 @@ pub mod media {
         pub source: MediaSource,
         /// The provider identifier. For eg: TMDB-ID, Openlibrary ID and so on.
         pub identifier: String,
-        // DEV: Only to be used internally.
-        #[serde(skip)]
-        #[schema(exclude)]
-        pub internal_identifier: Option<ImportOrExportItemIdentifier>,
         /// The seen history for the user.
         pub seen_history: Vec<ImportOrExportMediaItemSeen>,
         /// The review history for the user.
diff --git a/apps/backend/src/users.rs b/apps/backend/src/users.rs
index 7b1e45aa27..17d3c68911 100644
--- a/apps/backend/src/users.rs
+++ b/apps/backend/src/users.rs
@@ -284,15 +284,16 @@ pub struct UserGeneralDashboardElement {
     Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult,
 )]
 pub struct UserGeneralPreferences {
-    pub review_scale: UserReviewScale,
     pub display_nsfw: bool,
-    pub dashboard: Vec<UserGeneralDashboardElement>,
-    pub disable_yank_integrations: bool,
-    pub disable_navigation_animation: bool,
     pub disable_videos: bool,
-    pub disable_watch_providers: bool,
-    pub watch_providers: Vec<String>,
     pub disable_reviews: bool,
+    pub persist_queries: bool,
+    pub watch_providers: Vec<String>,
+    pub review_scale: UserReviewScale,
+    pub disable_watch_providers: bool,
+    pub disable_yank_integrations: bool,
+    pub disable_navigation_animation: bool,
+    pub dashboard: Vec<UserGeneralDashboardElement>,
 }
 
 impl Default for UserGeneralPreferences {
@@ -317,6 +318,7 @@ impl Default for UserGeneralPreferences {
                     num_elements: None,
                 },
             ],
+            persist_queries: true,
             disable_yank_integrations: false,
             disable_navigation_animation: false,
             disable_videos: false,
diff --git a/apps/frontend/app/components/common.tsx b/apps/frontend/app/components/common.tsx
index d74cc785ff..f1ff5b2e02 100644
--- a/apps/frontend/app/components/common.tsx
+++ b/apps/frontend/app/components/common.tsx
@@ -26,6 +26,7 @@ import { useState } from "react";
 import { withFragment, withoutHost } from "ufo";
 import { getSurroundingElements, redirectToQueryParam } from "~/lib/generals";
 import {
+	useCookieEnhancedSearchParam,
 	useCoreDetails,
 	useFallbackImageUrl,
 	useSearchParam,
@@ -137,13 +138,18 @@ export const DebouncedSearchInput = (props: {
 	initialValue?: string;
 	queryParam?: string;
 	placeholder?: string;
+	enhancedQueryParams?: string;
 }) => {
 	const [query, setQuery] = useState(props.initialValue || "");
 	const [debounced] = useDebouncedValue(query, 1000);
-	const [_, { setP }] = useSearchParam();
+	const [_p, { setP }] = useSearchParam();
+	const [_e, { setP: setEnhancedP }] = useCookieEnhancedSearchParam(
+		props.enhancedQueryParams || "query",
+	);
 
 	useDidUpdate(() => {
-		setP(props.queryParam || "query", debounced);
+		const fn = props.enhancedQueryParams ? setEnhancedP : setP;
+		fn(props.queryParam || "query", debounced);
 	}, [debounced]);
 
 	return (
diff --git a/apps/frontend/app/lib/generals.ts b/apps/frontend/app/lib/generals.ts
index 8ff89e7574..8ede4e7b2f 100644
--- a/apps/frontend/app/lib/generals.ts
+++ b/apps/frontend/app/lib/generals.ts
@@ -36,6 +36,17 @@ export { dayjs as dayjsLib };
 export const CurrentWorkoutKey = "CurrentWorkout";
 export const LOGO_IMAGE_URL =
 	"https://raw.githubusercontent.com/IgnisDa/ryot/main/libs/assets/icon-512x512.png";
+export const redirectToQueryParam = "redirectTo";
+export const AUTH_COOKIE_NAME = "Auth";
+export const USER_PREFERENCES_COOKIE_NAME = "UserPreferences";
+export const CORE_DETAILS_COOKIE_NAME = "CoreDetails";
+export const USER_DETAILS_COOKIE_NAME = "UserDetails";
+
+const enhancedSearchParamCookie = "SearchParams__";
+export const enhancedCookieName = (name: string) =>
+	`${enhancedSearchParamCookie}${name}`;
+
+export const queryClient = new QueryClient();
 
 export const getSetColor = (l: SetLot) =>
 	match(l)
@@ -196,15 +207,6 @@ export const getMetadataIcon = (lot: MediaLot) => {
 		.exhaustive();
 };
 
-export const redirectToQueryParam = "redirectTo";
-
-export const queryClient = new QueryClient();
-
-export const AUTH_COOKIE_NAME = "Auth";
-export const USER_PREFERENCES_COOKIE_NAME = "UserPreferences";
-export const CORE_DETAILS_COOKIE_NAME = "CoreDetails";
-export const USER_DETAILS_COOKIE_NAME = "UserDetails";
-
 const baseUrl = typeof window !== "undefined" ? window.location.origin : "";
 
 export const clientGqlService = new GraphQLClient(
diff --git a/apps/frontend/app/lib/hooks.ts b/apps/frontend/app/lib/hooks.ts
index 4d342ed933..943549b609 100644
--- a/apps/frontend/app/lib/hooks.ts
+++ b/apps/frontend/app/lib/hooks.ts
@@ -18,7 +18,7 @@ import {
 	getStringAsciiValue,
 	queryFactory,
 } from "~/lib/generals";
-import { type InProgressWorkout, useCurrentWorkout } from "~/lib/state/workout";
+import { type InProgressWorkout, useCurrentWorkout } from "~/lib/state/fitness";
 import type { loader } from "~/routes/_dashboard";
 
 export const useGetMantineColor = () => {
@@ -60,6 +60,30 @@ export const useSearchParam = () => {
 	return [searchParams, { setP, delP }] as const;
 };
 
+export const useCookieEnhancedSearchParam = (cookieKey: string) => {
+	const [searchParams, { setP, delP }] = useSearchParam();
+
+	const updateCookieP = (key: string, value?: string | null) => {
+		const cookieValue = Cookies.get(cookieKey);
+		const cookieSearchParams = new URLSearchParams(cookieValue);
+		if (value === undefined || value === null) cookieSearchParams.delete(key);
+		else cookieSearchParams.set(key, value);
+		Cookies.set(cookieKey, cookieSearchParams.toString());
+	};
+
+	const delCookieP = (key: string) => {
+		delP(key);
+		updateCookieP(key);
+	};
+
+	const setCookieP = (key: string, value?: string | null) => {
+		setP(key, value);
+		updateCookieP(key, value);
+	};
+
+	return [searchParams, { setP: setCookieP, delP: delCookieP }] as const;
+};
+
 export const getWorkoutStarter = () => {
 	const navigate = useNavigate();
 	const [_, setCurrentWorkout] = useCurrentWorkout();
diff --git a/apps/frontend/app/lib/state/workout.ts b/apps/frontend/app/lib/state/fitness.ts
similarity index 96%
rename from apps/frontend/app/lib/state/workout.ts
rename to apps/frontend/app/lib/state/fitness.ts
index ab27a5e51e..7ea1fa4ff6 100644
--- a/apps/frontend/app/lib/state/workout.ts
+++ b/apps/frontend/app/lib/state/fitness.ts
@@ -12,7 +12,7 @@ import {
 import { queryOptions } from "@tanstack/react-query";
 import type { Dayjs } from "dayjs";
 import { createDraft, finishDraft } from "immer";
-import { useAtom } from "jotai";
+import { atom, useAtom } from "jotai";
 import { atomWithReset, atomWithStorage } from "jotai/utils";
 import { v4 as randomUUID } from "uuid";
 import {
@@ -275,4 +275,11 @@ type Timer = {
 	triggeredBy?: { exerciseIdentifier: string; setIdx: number };
 };
 
-export const timerAtom = atomWithReset<Timer | null>(null);
+const timerAtom = atomWithReset<Timer | null>(null);
+
+export const useTimerAtom = () => useAtom(timerAtom);
+
+const measurementsDrawerOpenAtom = atom(false);
+
+export const useMeasurementsDrawerOpen = () =>
+	useAtom(measurementsDrawerOpenAtom);
diff --git a/apps/frontend/app/lib/utilities.server.ts b/apps/frontend/app/lib/utilities.server.ts
index f22f9647b1..0978749397 100644
--- a/apps/frontend/app/lib/utilities.server.ts
+++ b/apps/frontend/app/lib/utilities.server.ts
@@ -18,6 +18,7 @@ import {
 	UserPreferencesDocument,
 } from "@ryot/generated/graphql/backend/graphql";
 import { UserDetailsDocument } from "@ryot/generated/graphql/backend/graphql";
+import { isEmpty } from "@ryot/ts-utils";
 import { type CookieSerializeOptions, parse, serialize } from "cookie";
 import { GraphQLClient } from "graphql-request";
 import { withoutHost } from "ufo";
@@ -26,6 +27,7 @@ import { type ZodTypeAny, type output, z } from "zod";
 import {
 	AUTH_COOKIE_NAME,
 	CORE_DETAILS_COOKIE_NAME,
+	CurrentWorkoutKey,
 	USER_DETAILS_COOKIE_NAME,
 	USER_PREFERENCES_COOKIE_NAME,
 	dayjsLib,
@@ -379,3 +381,21 @@ export const extendResponseHeaders = (
 		responseHeaders.append(key, value);
 	return responseHeaders;
 };
+
+export const isWorkoutActive = (request: Request) => {
+	const cookies = request.headers.get("cookie");
+	const inProgress = parse(cookies || "")[CurrentWorkoutKey] === "true";
+	return inProgress;
+};
+
+export const redirectUsingEnhancedCookieSearchParams = async (
+	request: Request,
+	cookieName: string,
+) => {
+	const preferences = await getUserPreferences(request);
+	const searchParams = new URL(request.url).searchParams;
+	if (searchParams.size > 0 || !preferences.general.persistQueries) return;
+	const cookies = parse(request.headers.get("cookie") || "");
+	const savedSearchParams = cookies[cookieName];
+	if (!isEmpty(savedSearchParams)) throw redirect(`?${savedSearchParams}`);
+};
diff --git a/apps/frontend/app/routes/_dashboard.collections.$id._index.tsx b/apps/frontend/app/routes/_dashboard.collections.$id._index.tsx
index d9597cca6a..3c2ed34c3d 100644
--- a/apps/frontend/app/routes/_dashboard.collections.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.collections.$id._index.tsx
@@ -39,6 +39,7 @@ import {
 	IconSortDescending,
 	IconUser,
 } from "@tabler/icons-react";
+import Cookies from "js-cookie";
 import invariant from "tiny-invariant";
 import { z } from "zod";
 import { zx } from "zodix";
@@ -47,15 +48,16 @@ import {
 	MediaItemWithoutUpdateModal,
 	ReviewItemDisplay,
 } from "~/components/media";
-import { dayjsLib } from "~/lib/generals";
+import { dayjsLib, enhancedCookieName } from "~/lib/generals";
 import {
+	useCookieEnhancedSearchParam,
 	useCoreDetails,
-	useSearchParam,
 	useUserPreferences,
 } from "~/lib/hooks";
 import { useReviewEntity } from "~/lib/state/media";
 import {
 	getAuthorizationHeader,
+	redirectUsingEnhancedCookieSearchParams,
 	serverGqlService,
 } from "~/lib/utilities.server";
 
@@ -65,7 +67,7 @@ const defaultFiltersValue = {
 };
 
 const searchParamsSchema = z.object({
-	defaultTab: z.string().optional().default("contents"),
+	defaultTab: z.string().optional(),
 	page: zx.IntAsString.optional(),
 	query: z.string().optional(),
 	sortBy: z
@@ -81,6 +83,8 @@ export type SearchParams = z.infer<typeof searchParamsSchema>;
 export const loader = unstable_defineLoader(async ({ request, params }) => {
 	const collectionId = params.id;
 	invariant(collectionId);
+	const cookieName = enhancedCookieName(`collections.details.${collectionId}`);
+	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const [{ collectionContents }] = await Promise.all([
 		serverGqlService.request(
@@ -99,7 +103,7 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 			getAuthorizationHeader(request),
 		),
 	]);
-	return { collectionId, query, collectionContents };
+	return { collectionId, query, collectionContents, cookieName };
 });
 
 export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
@@ -111,7 +115,7 @@ export default function Page() {
 	const userPreferences = useUserPreferences();
 	const coreDetails = useCoreDetails();
 	const navigate = useNavigate();
-	const [_, { setP }] = useSearchParam();
+	const [_, { setP }] = useCookieEnhancedSearchParam(loaderData.cookieName);
 	const [_r, setEntityToReview] = useReviewEntity();
 	const [
 		filtersModalOpened,
@@ -132,7 +136,7 @@ export default function Page() {
 					</Text>
 				</Box>
 				<Text>{loaderData.collectionContents.details.description}</Text>
-				<Tabs defaultValue={loaderData.query.defaultTab}>
+				<Tabs defaultValue={loaderData.query.defaultTab || "contents"}>
 					<Tabs.List mb="xs">
 						<Tabs.Tab
 							value="contents"
@@ -156,8 +160,9 @@ export default function Page() {
 						<Stack>
 							<Group wrap="nowrap">
 								<DebouncedSearchInput
-									placeholder="Search in the collection"
 									initialValue={loaderData.query.query}
+									placeholder="Search in the collection"
+									enhancedQueryParams={loaderData.cookieName}
 								/>
 								<ActionIcon
 									onClick={openFiltersModal}
@@ -179,12 +184,13 @@ export default function Page() {
 									withCloseButton={false}
 								>
 									<Stack>
-										<Group>
+										<Group justify="space-between">
 											<Title order={3}>Filters</Title>
 											<ActionIcon
 												onClick={() => {
 													navigate(".");
 													closeFiltersModal();
+													Cookies.remove(loaderData.cookieName);
 												}}
 											>
 												<IconFilterOff size={24} />
diff --git a/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx
index 48568dde53..76c4662167 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx
@@ -53,8 +53,8 @@ import { ExerciseHistory } from "~/components/fitness";
 import { DisplayCollection, MediaScrollArea } from "~/components/media";
 import { dayjsLib } from "~/lib/generals";
 import { useUserDetails, useUserPreferences } from "~/lib/hooks";
+import { addExerciseToWorkout, useCurrentWorkout } from "~/lib/state/fitness";
 import { useAddEntityToCollection } from "~/lib/state/media";
-import { addExerciseToWorkout, useCurrentWorkout } from "~/lib/state/workout";
 import {
 	getAuthorizationHeader,
 	serverGqlService,
@@ -62,7 +62,6 @@ import {
 
 const searchParamsSchema = z.object({
 	defaultTab: z.string().optional(),
-	selectionEnabled: zx.BoolAsString.optional(),
 });
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
@@ -401,7 +400,7 @@ export default function Page() {
 					</Tabs.Panel>
 				</Tabs>
 			</Stack>
-			{currentWorkout && loaderData.query.selectionEnabled ? (
+			{currentWorkout ? (
 				<Affix position={{ bottom: rem(40), right: rem(30) }}>
 					<ActionIcon
 						color="blue"
diff --git a/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
index 5f3f6b862f..65176a3860 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
@@ -49,18 +49,20 @@ import {
 	IconFilterOff,
 	IconPlus,
 } from "@tabler/icons-react";
+import Cookies from "js-cookie";
 import { z } from "zod";
 import { zx } from "zodix";
 import { DebouncedSearchInput } from "~/components/common";
-import { dayjsLib } from "~/lib/generals";
+import { dayjsLib, enhancedCookieName } from "~/lib/generals";
 import {
+	useCookieEnhancedSearchParam,
 	useCoreDetails,
-	useSearchParam,
 	useUserCollections,
 } from "~/lib/hooks";
-import { addExerciseToWorkout, useCurrentWorkout } from "~/lib/state/workout";
+import { addExerciseToWorkout, useCurrentWorkout } from "~/lib/state/fitness";
 import {
 	getAuthorizationHeader,
+	redirectUsingEnhancedCookieSearchParams,
 	serverGqlService,
 } from "~/lib/utilities.server";
 
@@ -76,12 +78,9 @@ const defaultFiltersValue = {
 };
 
 const searchParamsSchema = z.object({
-	page: zx.IntAsString.optional().default("1"),
+	page: zx.IntAsString.optional(),
 	query: z.string().optional(),
-	sortBy: z
-		.nativeEnum(ExerciseSortBy)
-		.optional()
-		.default(defaultFiltersValue.sortBy),
+	sortBy: z.nativeEnum(ExerciseSortBy).optional(),
 	type: z.nativeEnum(ExerciseLot).optional(),
 	level: z.nativeEnum(ExerciseLevel).optional(),
 	force: z.nativeEnum(ExerciseForce).optional(),
@@ -89,13 +88,16 @@ const searchParamsSchema = z.object({
 	equipment: z.nativeEnum(ExerciseEquipment).optional(),
 	muscle: z.nativeEnum(ExerciseMuscle).optional(),
 	collection: z.string().optional(),
-	selectionEnabled: zx.BoolAsString.optional(),
 });
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
 
 export const loader = unstable_defineLoader(async ({ request }) => {
+	const cookieName = enhancedCookieName("exercises.list");
+	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 	const query = zx.parseQuery(request, searchParamsSchema);
+	query.sortBy = query.sortBy ?? ExerciseSortBy.NumTimesPerformed;
+	query.page = query.page ?? 1;
 	const [{ exerciseParameters }, { exercisesList }] = await Promise.all([
 		serverGqlService.request(ExerciseParametersDocument, {}),
 		serverGqlService.request(
@@ -121,7 +123,7 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 			getAuthorizationHeader(request),
 		),
 	]);
-	return { query, exerciseParameters, exercisesList };
+	return { query, exerciseParameters, exercisesList, cookieName };
 });
 
 export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
@@ -133,7 +135,7 @@ export default function Page() {
 	const coreDetails = useCoreDetails();
 	const collections = useUserCollections();
 	const navigate = useNavigate();
-	const [_, { setP }] = useSearchParam();
+	const [_, { setP }] = useCookieEnhancedSearchParam(loaderData.cookieName);
 	const [selectedExercises, setSelectedExercises] = useListState<{
 		name: string;
 		lot: ExerciseLot;
@@ -146,7 +148,7 @@ export default function Page() {
 	const [currentWorkout, setCurrentWorkout] = useCurrentWorkout();
 
 	const isFilterChanged = Object.keys(defaultFiltersValue)
-		.filter((k) => k !== "page" && k !== "query" && k !== "selectionEnabled")
+		.filter((k) => k !== "page" && k !== "query")
 		.some(
 			// biome-ignore lint/suspicious/noExplicitAny: required here
 			(k) => (loaderData.query as any)[k] !== (defaultFiltersValue as any)[k],
@@ -182,8 +184,9 @@ export default function Page() {
 					<>
 						<Group wrap="nowrap">
 							<DebouncedSearchInput
-								placeholder="Search for exercises by name or instructions"
 								initialValue={loaderData.query.query}
+								enhancedQueryParams={loaderData.cookieName}
+								placeholder="Search for exercises by name or instructions"
 							/>
 							<ActionIcon
 								onClick={openFiltersModal}
@@ -205,12 +208,13 @@ export default function Page() {
 									}}
 								>
 									<Stack gap={4}>
-										<Group>
+										<Group justify="space-between">
 											<Title order={3}>Filters</Title>
 											<ActionIcon
 												onClick={() => {
 													navigate(".");
 													closeFiltersModal();
+													Cookies.remove(loaderData.cookieName);
 												}}
 											>
 												<IconFilterOff size={24} />
@@ -278,7 +282,7 @@ export default function Page() {
 										{loaderData.exercisesList.details.total}
 									</Text>{" "}
 									items found
-									{loaderData.query.selectionEnabled ? (
+									{currentWorkout ? (
 										<>
 											{" "}
 											and{" "}
@@ -297,7 +301,7 @@ export default function Page() {
 											align="center"
 											data-exercise-id={exercise.id}
 										>
-											{loaderData.query.selectionEnabled ? (
+											{currentWorkout ? (
 												<Checkbox
 													onChange={(e) => {
 														if (e.currentTarget.checked)
@@ -328,13 +332,9 @@ export default function Page() {
 												/>
 											</Indicator>
 											<Link
-												to={$path(
-													"/fitness/exercises/item/:id",
-													{ id: exercise.id },
-													{
-														selectionEnabled: loaderData.query.selectionEnabled,
-													},
-												)}
+												to={$path("/fitness/exercises/item/:id", {
+													id: exercise.id,
+												})}
 												style={{ all: "unset", cursor: "pointer" }}
 											>
 												<Flex direction="column" justify="space-around">
@@ -379,7 +379,7 @@ export default function Page() {
 					</>
 				)}
 			</Stack>
-			{currentWorkout && loaderData.query.selectionEnabled ? (
+			{currentWorkout ? (
 				<Affix position={{ bottom: rem(40), right: rem(30) }}>
 					<ActionIcon
 						color="blue"
diff --git a/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx
index de74889de5..4ed7830e04 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx
@@ -3,40 +3,28 @@ import "@mantine/charts/styles.css";
 import {
 	ActionIcon,
 	Box,
-	Button,
 	Container,
-	Drawer,
 	Flex,
 	MultiSelect,
-	NumberInput,
 	Select,
 	SimpleGrid,
 	Stack,
 	Tabs,
 	Text,
-	TextInput,
-	Textarea,
 	Title,
 } from "@mantine/core";
-import { DateTimePicker } from "@mantine/dates";
-import "@mantine/dates/styles.css";
-import { useDisclosure, useLocalStorage } from "@mantine/hooks";
-import {
-	redirect,
-	unstable_defineAction,
-	unstable_defineLoader,
-} from "@remix-run/node";
+import { useLocalStorage } from "@mantine/hooks";
+import { unstable_defineAction, unstable_defineLoader } from "@remix-run/node";
 import {
 	Form,
 	type MetaArgs_SingleFetch,
 	useLoaderData,
 } from "@remix-run/react";
 import {
-	CreateUserMeasurementDocument,
 	DeleteUserMeasurementDocument,
 	UserMeasurementsListDocument,
 } from "@ryot/generated/graphql/backend/graphql";
-import { changeCase, isEmpty, set, snakeCase, startCase } from "@ryot/ts-utils";
+import { startCase } from "@ryot/ts-utils";
 import {
 	IconChartArea,
 	IconPlus,
@@ -46,12 +34,11 @@ import {
 import { DataTable } from "mantine-datatable";
 import { namedAction } from "remix-utils/named-action";
 import { match } from "ts-pattern";
-import { withQuery } from "ufo";
 import { z } from "zod";
 import { zx } from "zodix";
-import events from "~/lib/events";
-import { dayjsLib, redirectToQueryParam } from "~/lib/generals";
+import { dayjsLib } from "~/lib/generals";
 import { useSearchParam, useUserPreferences } from "~/lib/hooks";
+import { useMeasurementsDrawerOpen } from "~/lib/state/fitness";
 import {
 	createToastHeaders,
 	getAuthorizationHeader,
@@ -69,8 +56,6 @@ enum TimeSpan {
 
 const searchParamsSchema = z.object({
 	timeSpan: z.nativeEnum(TimeSpan).optional(),
-	openModal: zx.BoolAsString.optional(),
-	[redirectToQueryParam]: z.string().optional(),
 });
 
 export type SearchParams = z.infer<typeof searchParamsSchema>;
@@ -109,31 +94,6 @@ export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
 export const action = unstable_defineAction(async ({ request }) => {
 	const formData = await request.clone().formData();
 	return namedAction(request, {
-		create: async () => {
-			// biome-ignore lint/suspicious/noExplicitAny: the form values ensure that the submission is valid
-			const input: any = {};
-			for (const [name, value] of formData.entries()) {
-				if (!isEmpty(value) && name !== redirectToQueryParam)
-					set(input, name, value);
-			}
-			await serverGqlService.request(
-				CreateUserMeasurementDocument,
-				{ input },
-				getAuthorizationHeader(request),
-			);
-			const toastHeaders = {
-				headers: await createToastHeaders({
-					type: "success",
-					message: "Measurement submitted successfully",
-				}),
-			};
-			const redirectTo = formData.get(redirectToQueryParam);
-			if (redirectTo) return redirect(redirectTo.toString(), toastHeaders);
-			return Response.json(
-				{ status: "success", submission: input } as const,
-				toastHeaders,
-			);
-		},
 		delete: async () => {
 			const submission = processSubmission(formData, deleteSchema);
 			await serverGqlService.request(
@@ -171,77 +131,24 @@ export default function Page() {
 			timestamp: tickFormatter(m.timestamp),
 		};
 	});
-	const [opened, { open, close }] = useDisclosure(
-		loaderData.query.openModal || false,
-	);
 	const [selectedStats, setSelectedStats] = useLocalStorage({
 		defaultValue: ["weight"],
 		key: "SavedMeasurementsDisplaySelectedStats",
 		getInitialValueInEffect: true,
 	});
-	const [searchParams, { setP }] = useSearchParam();
+	const [_p, { setP }] = useSearchParam();
+	const [_m, setMeasurementsDrawerOpen] = useMeasurementsDrawerOpen();
 
-	const redirectToFormEntry = searchParams.get(redirectToQueryParam);
 	return (
 		<Container>
-			<Drawer opened={opened} onClose={close} title="Add new measurement">
-				<Form
-					replace
-					method="POST"
-					action={withQuery("", { intent: "create" })}
-					onSubmit={() => {
-						events.createMeasurement();
-						close();
-					}}
-				>
-					{redirectToFormEntry ? (
-						<input
-							type="hidden"
-							name={redirectToQueryParam}
-							value={redirectToFormEntry}
-						/>
-					) : null}
-					<Stack>
-						<DateTimePicker
-							label="Timestamp"
-							defaultValue={new Date()}
-							name="timestamp"
-							required
-						/>
-						<TextInput label="Name" name="name" />
-						<SimpleGrid cols={2} style={{ alignItems: "end" }}>
-							{Object.keys(userPreferences.fitness.measurements.inbuilt)
-								.filter((n) => n !== "custom")
-								.filter(
-									(n) =>
-										// biome-ignore lint/suspicious/noExplicitAny: required
-										(userPreferences as any).fitness.measurements.inbuilt[n],
-								)
-								.map((v) => (
-									<NumberInput
-										decimalScale={3}
-										key={v}
-										label={changeCase(snakeCase(v))}
-										name={`stats.${v}`}
-									/>
-								))}
-							{userPreferences.fitness.measurements.custom.map(({ name }) => (
-								<NumberInput
-									key={name}
-									label={changeCase(snakeCase(name))}
-									name={`stats.custom.${name}`}
-								/>
-							))}
-						</SimpleGrid>
-						<Textarea label="Comment" name="comment" />
-						<Button type="submit">Submit</Button>
-					</Stack>
-				</Form>
-			</Drawer>
 			<Stack>
 				<Flex align="center" gap="md">
 					<Title>Measurements</Title>
-					<ActionIcon color="green" variant="outline" onClick={open}>
+					<ActionIcon
+						color="green"
+						variant="outline"
+						onClick={() => setMeasurementsDrawerOpen(true)}
+					>
 						<IconPlus size={20} />
 					</ActionIcon>
 				</Flex>
diff --git a/apps/frontend/app/routes/_dashboard.fitness.workouts.$id._index.tsx b/apps/frontend/app/routes/_dashboard.fitness.workouts.$id._index.tsx
index 954775679c..72214f5b25 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.workouts.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.workouts.$id._index.tsx
@@ -72,7 +72,7 @@ import {
 import {
 	duplicateOldWorkout,
 	getExerciseDetailsQuery,
-} from "~/lib/state/workout";
+} from "~/lib/state/fitness";
 import {
 	createToastHeaders,
 	getAuthorizationHeader,
diff --git a/apps/frontend/app/routes/_dashboard.fitness.workouts.current.tsx b/apps/frontend/app/routes/_dashboard.fitness.workouts.current.tsx
index 7694a70bd0..6987cc58c3 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.workouts.current.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.workouts.current.tsx
@@ -52,7 +52,6 @@ import type { MetaArgs_SingleFetch } from "@remix-run/react";
 import {
 	CreateUserWorkoutDocument,
 	ExerciseLot,
-	ExerciseSortBy,
 	SetLot,
 	UserUnitSystem,
 	type WorkoutSetStatistic,
@@ -81,10 +80,8 @@ import {
 	IconTrash,
 	IconZzz,
 } from "@tabler/icons-react";
-import { parse } from "cookie";
 import { Howl } from "howler";
 import { produce } from "immer";
-import { useAtom } from "jotai";
 import { RESET } from "jotai/utils";
 import Cookies from "js-cookie";
 import { useEffect, useRef, useState } from "react";
@@ -109,15 +106,17 @@ import {
 	type InProgressWorkout,
 	currentWorkoutToCreateWorkoutInput,
 	exerciseHasDetailsToShow,
-	timerAtom,
 	useCurrentWorkout,
 	useGetExerciseAtIndex,
 	useGetSetAtIndex,
-} from "~/lib/state/workout";
+	useMeasurementsDrawerOpen,
+	useTimerAtom,
+} from "~/lib/state/fitness";
 import {
 	createToastHeaders,
 	getAuthorizationHeader,
 	getCoreEnabledFeatures,
+	isWorkoutActive,
 	redirectWithToast,
 	serverGqlService,
 } from "~/lib/utilities.server";
@@ -126,8 +125,7 @@ const workoutCookieName = CurrentWorkoutKey;
 const defaultTimerLocalStorageKey = "DefaultExerciseRestTimer";
 
 export const loader = unstable_defineLoader(async ({ request }) => {
-	const cookies = request.headers.get("cookie");
-	const inProgress = parse(cookies || "")[workoutCookieName] === "true";
+	const inProgress = isWorkoutActive(request);
 	if (!inProgress)
 		throw await redirectWithToast($path("/"), {
 			type: "error",
@@ -198,7 +196,8 @@ export default function Page() {
 		reorderDrawerOpened,
 		{ close: reorderDrawerClose, toggle: reorderDrawerToggle },
 	] = useDisclosure(false);
-	const [currentTimer, setCurrentTimer] = useAtom(timerAtom);
+	const [_, setMeasurementsDrawerOpen] = useMeasurementsDrawerOpen();
+	const [currentTimer, setCurrentTimer] = useTimerAtom();
 	const interval = useInterval(() => {
 		setTime((s) => s + 1);
 	}, 1000);
@@ -445,13 +444,9 @@ export default function Page() {
 								<Group justify="center">
 									{userPreferences.featuresEnabled.fitness.measurements ? (
 										<Button
-											component={Link}
 											variant="subtle"
 											color="teal"
-											to={$path("/fitness/measurements/list", {
-												openModal: true,
-												redirectTo: $path("/fitness/workouts/current"),
-											})}
+											onClick={() => setMeasurementsDrawerOpen(true)}
 										>
 											Add measurement
 										</Button>
@@ -459,11 +454,7 @@ export default function Page() {
 									<Button
 										component={Link}
 										variant="subtle"
-										to={$path("/fitness/exercises/list", {
-											selectionEnabled: true,
-											page: 1,
-											sortBy: ExerciseSortBy.LastPerformed,
-										})}
+										to={$path("/fitness/exercises/list")}
 									>
 										Add an exercise
 									</Button>
@@ -679,7 +670,7 @@ const ExerciseDisplay = (props: {
 	const [currentWorkout, setCurrentWorkout] = useCurrentWorkout();
 	const exercise = useGetExerciseAtIndex(props.exerciseIdx);
 	invariant(exercise);
-	const [currentTimer] = useAtom(timerAtom);
+	const [currentTimer] = useTimerAtom();
 
 	const playAddSetSound = () => {
 		const sound = new Howl({ src: ["/add-set.mp3"] });
@@ -1103,7 +1094,7 @@ const SetDisplay = (props: {
 	startTimer: FuncStartTimer;
 	toBeDisplayedColumns: number;
 }) => {
-	const [currentTimer, _] = useAtom(timerAtom);
+	const [currentTimer, _] = useTimerAtom();
 	const [currentWorkout, setCurrentWorkout] = useCurrentWorkout();
 	const exercise = useGetExerciseAtIndex(props.exerciseIdx);
 	const set = useGetSetAtIndex(props.exerciseIdx, props.setIdx);
@@ -1327,7 +1318,7 @@ const TimerDrawer = (props: {
 	stopTimer: () => void;
 	startTimer: (duration: number) => void;
 }) => {
-	const [currentTimer, setCurrentTimer] = useAtom(timerAtom);
+	const [currentTimer, setCurrentTimer] = useTimerAtom();
 
 	return (
 		<Drawer
diff --git a/apps/frontend/app/routes/_dashboard.fitness.workouts.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.workouts.list.tsx
index 9193ed6b41..7fc3236053 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.workouts.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.workouts.list.tsx
@@ -47,7 +47,7 @@ import {
 	useSearchParam,
 	useUserPreferences,
 } from "~/lib/hooks";
-import { getDefaultWorkout } from "~/lib/state/workout";
+import { getDefaultWorkout } from "~/lib/state/fitness";
 import {
 	getAuthorizationHeader,
 	serverGqlService,
diff --git a/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx b/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
index f0e5c43b13..f39c2cd0c7 100644
--- a/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
@@ -50,6 +50,7 @@ import {
 	IconSortDescending,
 	IconStarFilled,
 } from "@tabler/icons-react";
+import Cookies from "js-cookie";
 import { useState } from "react";
 import invariant from "tiny-invariant";
 import { match } from "ts-pattern";
@@ -64,10 +65,10 @@ import {
 	commitMedia,
 } from "~/components/media";
 import events from "~/lib/events";
-import { Verb, getLot, getVerb } from "~/lib/generals";
+import { Verb, enhancedCookieName, getLot, getVerb } from "~/lib/generals";
 import {
+	useCookieEnhancedSearchParam,
 	useCoreDetails,
-	useSearchParam,
 	useUserCollections,
 	useUserDetails,
 	useUserPreferences,
@@ -79,6 +80,7 @@ import {
 } from "~/lib/state/media";
 import {
 	getAuthorizationHeader,
+	redirectUsingEnhancedCookieSearchParams,
 	serverGqlService,
 } from "~/lib/utilities.server";
 import classes from "~/styles/common.module.css";
@@ -130,10 +132,12 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 	const numPage = Number(page);
 	const lot = getLot(params.lot);
 	invariant(lot);
+	const cookieName = enhancedCookieName(`media.action.lot.${lot}`);
 	const action = params.action as Action;
 	invariant(action && Object.values(Action).includes(action as Action));
 	const [mediaList, mediaSearch] = await match(action)
 		.with(Action.List, async () => {
+			await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 			const urlParse = zx.parseQuery(request, {
 				sortOrder: z
 					.nativeEnum(GraphqlSortOrder)
@@ -194,6 +198,7 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 		action,
 		numPage,
 		mediaList,
+		cookieName,
 		mediaSearch,
 		url: withoutHost(url.href),
 		mediaInteractedWith: latestUserSummary.media.metadataOverall.interactedWith,
@@ -215,7 +220,7 @@ export default function Page() {
 	const userPreferences = useUserPreferences();
 	const coreDetails = useCoreDetails();
 	const collections = useUserCollections();
-	const [_, { setP }] = useSearchParam();
+	const [_, { setP }] = useCookieEnhancedSearchParam(loaderData.cookieName);
 	const [_r, setEntityToReview] = useReviewEntity();
 	const [
 		filtersModalOpened,
@@ -273,10 +278,11 @@ export default function Page() {
 					<>
 						<Group wrap="nowrap">
 							<DebouncedSearchInput
+								initialValue={loaderData.query}
+								enhancedQueryParams={loaderData.cookieName}
 								placeholder={`Sift through your ${changeCase(
 									loaderData.lot.toLowerCase(),
 								).toLowerCase()}s`}
-								initialValue={loaderData.query}
 							/>
 							<ActionIcon
 								onClick={openFiltersModal}
@@ -291,12 +297,13 @@ export default function Page() {
 								withCloseButton={false}
 							>
 								<Stack>
-									<Group>
+									<Group justify="space-between">
 										<Title order={3}>Filters</Title>
 										<ActionIcon
 											onClick={() => {
 												navigate(".");
 												closeFiltersModal();
+												Cookies.remove(loaderData.cookieName);
 											}}
 										>
 											<IconFilterOff size={24} />
diff --git a/apps/frontend/app/routes/_dashboard.media.people.$action.tsx b/apps/frontend/app/routes/_dashboard.media.people.$action.tsx
index 9458d04326..6de5320baa 100644
--- a/apps/frontend/app/routes/_dashboard.media.people.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.people.$action.tsx
@@ -38,6 +38,7 @@ import {
 	IconSortAscending,
 	IconSortDescending,
 } from "@tabler/icons-react";
+import Cookies from "js-cookie";
 import { useState } from "react";
 import { match } from "ts-pattern";
 import { withoutHost } from "ufo";
@@ -49,14 +50,16 @@ import {
 	type Item,
 	MediaItemWithoutUpdateModal,
 } from "~/components/media";
-import { redirectToQueryParam } from "~/lib/generals";
+import { enhancedCookieName, redirectToQueryParam } from "~/lib/generals";
 import {
+	useCookieEnhancedSearchParam,
 	useCoreDetails,
 	useSearchParam,
 	useUserPreferences,
 } from "~/lib/hooks";
 import {
 	getAuthorizationHeader,
+	redirectUsingEnhancedCookieSearchParams,
 	serverGqlService,
 } from "~/lib/utilities.server";
 
@@ -85,6 +88,7 @@ const SEARCH_SOURCES_ALLOWED = [
 ] as const;
 
 export const loader = unstable_defineLoader(async ({ request, params }) => {
+	const cookieName = enhancedCookieName("people.action");
 	const action = params.action as Action;
 	const { query, page } = zx.parseQuery(request, {
 		query: z.string().optional(),
@@ -92,6 +96,7 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 	});
 	const [peopleList, peopleSearch] = await match(action)
 		.with(Action.List, async () => {
+			await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 			const urlParse = zx.parseQuery(
 				request,
 				z.object({
@@ -139,7 +144,7 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
 			return [undefined, { search: peopleSearch, url: urlParse }] as const;
 		})
 		.exhaustive();
-	return { action, query, page, peopleList, peopleSearch };
+	return { action, query, page, peopleList, peopleSearch, cookieName };
 });
 
 export const meta = ({ params }: MetaArgs_SingleFetch<typeof loader>) => {
@@ -150,7 +155,10 @@ export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
 	const coreDetails = useCoreDetails();
 	const navigate = useNavigate();
-	const [_, { setP }] = useSearchParam();
+	const [_p, { setP }] = useSearchParam();
+	const [_e, { setP: setEnhancedP }] = useCookieEnhancedSearchParam(
+		loaderData.cookieName,
+	);
 	const [
 		filtersModalOpened,
 		{ open: openFiltersModal, close: closeFiltersModal },
@@ -188,6 +196,11 @@ export default function Page() {
 					<DebouncedSearchInput
 						placeholder="Search for people"
 						initialValue={loaderData.query}
+						enhancedQueryParams={
+							loaderData.action === Action.List
+								? loaderData.cookieName
+								: undefined
+						}
 					/>
 					{loaderData.action === Action.List ? (
 						<>
@@ -210,12 +223,13 @@ export default function Page() {
 								withCloseButton={false}
 							>
 								<Stack>
-									<Group>
+									<Group justify="space-between">
 										<Title order={3}>Sort by</Title>
 										<ActionIcon
 											onClick={() => {
 												navigate(".");
 												closeFiltersModal();
+												Cookies.remove(loaderData.cookieName);
 											}}
 										>
 											<IconFilterOff size={24} />
@@ -229,7 +243,7 @@ export default function Page() {
 												label: startCase(o.toLowerCase()),
 											}))}
 											defaultValue={loaderData.peopleList?.url.sortBy}
-											onChange={(v) => setP("sortBy", v)}
+											onChange={(v) => setEnhancedP("sortBy", v)}
 										/>
 										<ActionIcon
 											onClick={() => {
@@ -237,8 +251,8 @@ export default function Page() {
 													loaderData.peopleList?.url.orderBy ===
 													GraphqlSortOrder.Asc
 												)
-													setP("orderBy", GraphqlSortOrder.Desc);
-												else setP("orderBy", GraphqlSortOrder.Asc);
+													setEnhancedP("orderBy", GraphqlSortOrder.Desc);
+												else setEnhancedP("orderBy", GraphqlSortOrder.Asc);
 											}}
 										>
 											{loaderData.peopleList?.url.orderBy ===
@@ -310,7 +324,7 @@ export default function Page() {
 									<Pagination
 										size="sm"
 										value={loaderData.page}
-										onChange={(v) => setP("page", v.toString())}
+										onChange={(v) => setEnhancedP("page", v.toString())}
 										total={Math.ceil(
 											loaderData.peopleList.list.details.total /
 												coreDetails.pageLimit,
diff --git a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
index 59527ca0a5..ded3d9fe14 100644
--- a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
@@ -293,6 +293,7 @@ export default function Page() {
 										"disableVideos",
 										"disableReviews",
 										"disableWatchProviders",
+										"persistQueries",
 									] as const
 								).map((name) => (
 									<Switch
@@ -317,6 +318,10 @@ export default function Page() {
 												"disableWatchProviders",
 												() => 'Do not display the "Watch On" tab',
 											)
+											.with(
+												"persistQueries",
+												() => "Persist queries in the URL",
+											)
 											.exhaustive()}
 										defaultChecked={userPreferences.general[name]}
 										disabled={!!userDetails.isDemo}
diff --git a/apps/frontend/app/routes/_dashboard.tsx b/apps/frontend/app/routes/_dashboard.tsx
index 6b38a6a5c2..06b110f681 100644
--- a/apps/frontend/app/routes/_dashboard.tsx
+++ b/apps/frontend/app/routes/_dashboard.tsx
@@ -12,6 +12,7 @@ import {
 	Center,
 	Checkbox,
 	Collapse,
+	Drawer,
 	Flex,
 	Group,
 	Image,
@@ -23,6 +24,7 @@ import {
 	ScrollArea,
 	SegmentedControl,
 	Select,
+	SimpleGrid,
 	Slider,
 	Stack,
 	Text,
@@ -30,6 +32,7 @@ import {
 	Textarea,
 	ThemeIcon,
 	Title,
+	Tooltip,
 	UnstyledButton,
 	rem,
 	useDirection,
@@ -69,6 +72,7 @@ import {
 	formatDateToNaiveDate,
 	groupBy,
 	isNumber,
+	snakeCase,
 } from "@ryot/ts-utils";
 import {
 	IconAlertCircle,
@@ -89,7 +93,6 @@ import {
 	IconStretching,
 	IconSun,
 } from "@tabler/icons-react";
-import { parse } from "cookie";
 import { produce } from "immer";
 import { useState } from "react";
 import { Fragment } from "react/jsx-runtime";
@@ -99,7 +102,6 @@ import { HiddenLocationInput } from "~/components/common";
 import events from "~/lib/events";
 import {
 	CORE_DETAILS_COOKIE_NAME,
-	CurrentWorkoutKey,
 	LOGO_IMAGE_URL,
 	Verb,
 	getLot,
@@ -114,6 +116,7 @@ import {
 	useUserMetadataDetails,
 	useUserPreferences,
 } from "~/lib/hooks";
+import { useMeasurementsDrawerOpen } from "~/lib/state/fitness";
 import {
 	type UpdateProgressData,
 	useAddEntityToCollection,
@@ -125,9 +128,11 @@ import {
 	getCachedUserCollectionsList,
 	getCookieValue,
 	getUserPreferences,
+	isWorkoutActive,
 	redirectIfNotAuthenticatedOrUpdated,
 } from "~/lib/utilities.server";
 import { colorSchemeCookie } from "~/lib/utilities.server";
+import "@mantine/dates/styles.css";
 import classes from "~/styles/dashboard.module.css";
 
 export const loader = unstable_defineLoader(async ({ request }) => {
@@ -228,8 +233,7 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		!envData.DISABLE_TELEMETRY &&
 		!userDetails.isDemo;
 
-	const cookies = request.headers.get("cookie");
-	const workoutInProgress = parse(cookies || "")[CurrentWorkoutKey] === "true";
+	const workoutInProgress = isWorkoutActive(request);
 
 	return {
 		envData,
@@ -239,9 +243,9 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		fitnessLinks,
 		settingsLinks,
 		userPreferences,
-		workoutInProgress,
 		shouldHaveUmami,
 		userCollections,
+		workoutInProgress,
 		currentColorScheme,
 	};
 });
@@ -280,22 +284,38 @@ export default function Layout() {
 		useAddEntityToCollection();
 	const closeAddEntityToCollectionModal = () =>
 		setAddEntityToCollectionData(null);
+	const [measurementsDrawerOpen, setMeasurementsDrawerOpen] =
+		useMeasurementsDrawerOpen();
+	const closeMeasurementsDrawer = () => setMeasurementsDrawerOpen(false);
 
 	return (
 		<>
 			{loaderData.workoutInProgress &&
 			location.pathname !== $path("/fitness/workouts/current") ? (
-				<Affix position={{ bottom: rem(30), right: rem(30) }}>
-					<ActionIcon
-						variant="filled"
-						color="orange"
-						radius="xl"
-						size="xl"
-						onClick={() => navigate($path("/fitness/workouts/current"))}
+				<Tooltip label="You have an active workout" position="left">
+					<Affix
+						position={{
+							bottom: rem(40),
+							right: rem(
+								location.pathname === $path("/fitness/exercises/list") ||
+									location.pathname.includes("/fitness/exercises/item")
+									? 90
+									: 40,
+							),
+						}}
+						style={{ transition: "all 0.3s" }}
 					>
-						<IconStretching size={32} />
-					</ActionIcon>
-				</Affix>
+						<ActionIcon
+							variant="filled"
+							color="orange"
+							radius="xl"
+							size="xl"
+							onClick={() => navigate($path("/fitness/workouts/current"))}
+						>
+							<IconStretching size={32} />
+						</ActionIcon>
+					</Affix>
+				</Tooltip>
 			) : null}
 			<Modal
 				onClose={closeMetadataProgressUpdateModal}
@@ -325,6 +345,15 @@ export default function Layout() {
 					closeAddEntityToCollectionModal={closeAddEntityToCollectionModal}
 				/>
 			</Modal>
+			<Drawer
+				onClose={closeMeasurementsDrawer}
+				opened={measurementsDrawerOpen}
+				title="Add new measurement"
+			>
+				<CreateMeasurementForm
+					closeMeasurementModal={closeMeasurementsDrawer}
+				/>
+			</Drawer>
 			<AppShell
 				w="100%"
 				padding={0}
@@ -1471,3 +1500,58 @@ const AddEntityToCollectionForm = ({
 		</Form>
 	);
 };
+
+const CreateMeasurementForm = (props: {
+	closeMeasurementModal: () => void;
+}) => {
+	const userPreferences = useUserPreferences();
+
+	return (
+		<Form
+			replace
+			method="POST"
+			action={withQuery($path("/actions"), { intent: "createMeasurement" })}
+			onSubmit={() => {
+				events.createMeasurement();
+				props.closeMeasurementModal();
+			}}
+		>
+			<Stack>
+				<DateTimePicker
+					label="Timestamp"
+					defaultValue={new Date()}
+					name="timestamp"
+					required
+				/>
+				<HiddenLocationInput />
+				<TextInput label="Name" name="name" />
+				<SimpleGrid cols={2} style={{ alignItems: "end" }}>
+					{Object.keys(userPreferences.fitness.measurements.inbuilt)
+						.filter((n) => n !== "custom")
+						.filter(
+							(n) =>
+								// biome-ignore lint/suspicious/noExplicitAny: required
+								(userPreferences as any).fitness.measurements.inbuilt[n],
+						)
+						.map((v) => (
+							<NumberInput
+								decimalScale={3}
+								key={v}
+								label={changeCase(snakeCase(v))}
+								name={`stats.${v}`}
+							/>
+						))}
+					{userPreferences.fitness.measurements.custom.map(({ name }) => (
+						<NumberInput
+							key={name}
+							label={changeCase(snakeCase(name))}
+							name={`stats.custom.${name}`}
+						/>
+					))}
+				</SimpleGrid>
+				<Textarea label="Comment" name="comment" />
+				<Button type="submit">Submit</Button>
+			</Stack>
+		</Form>
+	);
+};
diff --git a/apps/frontend/app/routes/actions.tsx b/apps/frontend/app/routes/actions.tsx
index d8338a4548..206ee59918 100644
--- a/apps/frontend/app/routes/actions.tsx
+++ b/apps/frontend/app/routes/actions.tsx
@@ -10,6 +10,7 @@ import {
 	CommitMetadataGroupDocument,
 	CommitPersonDocument,
 	CreateReviewCommentDocument,
+	CreateUserMeasurementDocument,
 	DeleteReviewDocument,
 	DeleteS3ObjectDocument,
 	DeployBulkProgressUpdateDocument,
@@ -21,7 +22,7 @@ import {
 	SeenState,
 	Visibility,
 } from "@ryot/generated/graphql/backend/graphql";
-import { isEmpty, omitBy } from "@ryot/ts-utils";
+import { isEmpty, omitBy, set } from "@ryot/ts-utils";
 import invariant from "tiny-invariant";
 import { match } from "ts-pattern";
 import { z } from "zod";
@@ -348,6 +349,26 @@ export const action = unstable_defineAction(async ({ request, response }) => {
 				}),
 			);
 		})
+		.with("createMeasurement", async () => {
+			// biome-ignore lint/suspicious/noExplicitAny: the form values ensure that the submission is valid
+			const input: any = {};
+			for (const [name, value] of formData.entries()) {
+				if (!isEmpty(value) && name !== redirectToQueryParam)
+					set(input, name, value);
+			}
+			await serverGqlService.request(
+				CreateUserMeasurementDocument,
+				{ input },
+				getAuthorizationHeader(request),
+			);
+			response.headers = extendResponseHeaders(
+				response.headers,
+				await createToastHeaders({
+					type: "success",
+					message: "Measurement submitted successfully",
+				}),
+			);
+		})
 		.run();
 	if (redirectTo) {
 		response.headers.append("Location", redirectTo.toString());
diff --git a/apps/frontend/app/routes/api.auth.tsx b/apps/frontend/app/routes/api.auth.tsx
index bc2d60c719..c1f60c829d 100644
--- a/apps/frontend/app/routes/api.auth.tsx
+++ b/apps/frontend/app/routes/api.auth.tsx
@@ -23,16 +23,19 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		GetOidcTokenDocument,
 		input,
 	);
+	console.log("OIDC token response:", getOidcToken);
 	const oidcInput = {
 		email: getOidcToken.email,
 		issuerId: getOidcToken.subject,
 	};
-	await Promise.all([
+	const [_, { registerUser }] = await Promise.all([
 		serverGqlService.request(CoreDetailsDocument),
 		serverGqlService.request(RegisterUserDocument, {
 			input: { oidc: oidcInput },
 		}),
 	]);
+	if (registerUser.__typename === "RegisterError")
+		console.error("Registration failed:", registerUser);
 	const { loginUser } = await serverGqlService.request(LoginUserDocument, {
 		input: { oidc: oidcInput },
 	});
@@ -40,5 +43,6 @@ export const loader = unstable_defineLoader(async ({ request }) => {
 		const headers = await getCookiesForApplication(loginUser.apiKey);
 		return redirect($path("/"), { headers });
 	}
+	console.error("Login failed:", loginUser);
 	return Response.json({ input });
 });
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
index 6ec61c28a5..a921f5dcd9 100644
--- a/apps/frontend/package.json
+++ b/apps/frontend/package.json
@@ -20,15 +20,15 @@
 		"@mantine/form": "7.11.1",
 		"@mantine/hooks": "7.11.1",
 		"@mantine/notifications": "7.11.1",
-		"@remix-run/node": "2.10.0",
-		"@remix-run/react": "2.10.0",
-		"@remix-run/serve": "2.10.0",
+		"@remix-run/node": "2.10.2",
+		"@remix-run/react": "2.10.2",
+		"@remix-run/serve": "2.10.2",
 		"@ryot/generated": "workspace:*",
 		"@ryot/graphql": "workspace:*",
 		"@ryot/ts-utils": "workspace:*",
-		"@tabler/icons-react": "3.7.0",
-		"@tanstack/react-query": "5.48.0",
-		"@tanstack/react-query-devtools": "5.48.0",
+		"@tabler/icons-react": "3.8.0",
+		"@tanstack/react-query": "5.49.2",
+		"@tanstack/react-query-devtools": "5.49.2",
 		"buffer": "6.0.3",
 		"clsx": "2.1.1",
 		"cookie": "0.6.0",
@@ -41,9 +41,9 @@
 		"humanize-duration-ts": "2.1.1",
 		"immer": "10.1.1",
 		"isbot": "5.1.11",
-		"jotai": "2.8.3",
+		"jotai": "2.8.4",
 		"js-cookie": "3.0.5",
-		"mantine-datatable": "7.10.4",
+		"mantine-datatable": "7.11.1",
 		"react": "18.3.1",
 		"react-confirm": "0.3.0-7",
 		"react-dom": "18.3.1",
@@ -60,7 +60,7 @@
 	},
 	"devDependencies": {
 		"@bitprojects/umami-logger-typescript": "1.0.10",
-		"@remix-run/dev": "2.10.0",
+		"@remix-run/dev": "2.10.2",
 		"@types/cookie": "0.6.0",
 		"@types/crypto-js": "4.2.2",
 		"@types/howler": "2.2.11",
@@ -68,12 +68,12 @@
 		"@types/react": "18.3.3",
 		"@types/react-dom": "18.3.0",
 		"@types/uuid": "10.0.0",
-		"postcss": "8.4.38",
+		"postcss": "8.4.39",
 		"postcss-preset-mantine": "1.15.0",
 		"postcss-simple-vars": "7.0.1",
 		"ts-essentials": "10.0.1",
 		"typescript-plugin-css-modules": "5.1.0",
-		"vite": "5.3.1",
+		"vite": "5.3.3",
 		"vite-tsconfig-paths": "4.3.2"
 	},
 	"engines": {
diff --git a/apps/landing/package.json b/apps/landing/package.json
index 4d264061bf..c124a3005b 100644
--- a/apps/landing/package.json
+++ b/apps/landing/package.json
@@ -11,7 +11,7 @@
 		"@astrojs/sitemap": "3.1.6",
 		"@astrojs/tailwind": "5.1.0",
 		"@fontsource-variable/rubik": "5.0.22",
-		"astro": "4.11.3",
+		"astro": "4.11.5",
 		"astro-robots-txt": "1.0.0",
 		"tailwindcss": "3.4.4"
 	}
diff --git a/libs/database/src/migrations/m20240704_add_new_preference_for_persisted_queries.rs b/libs/database/src/migrations/m20240704_add_new_preference_for_persisted_queries.rs
new file mode 100644
index 0000000000..7db9975dc7
--- /dev/null
+++ b/libs/database/src/migrations/m20240704_add_new_preference_for_persisted_queries.rs
@@ -0,0 +1,24 @@
+use sea_orm_migration::prelude::*;
+
+#[derive(DeriveMigrationName)]
+pub struct Migration;
+
+#[async_trait::async_trait]
+impl MigrationTrait for Migration {
+    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
+        let db = manager.get_connection();
+        db.execute_unprepared(
+            r#"
+UPDATE "user"
+SET preferences = jsonb_set(preferences, '{general, persist_queries}', 'true', true);
+"#,
+        )
+        .await?;
+
+        Ok(())
+    }
+
+    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
+        Ok(())
+    }
+}
diff --git a/libs/database/src/migrations/mod.rs b/libs/database/src/migrations/mod.rs
index abd654ff6d..b85894974a 100644
--- a/libs/database/src/migrations/mod.rs
+++ b/libs/database/src/migrations/mod.rs
@@ -27,6 +27,7 @@ mod m20240608_add_created_on_column_to_collection_to_entity;
 mod m20240619_remove_seen_from_media_reason_of_user_to_entity;
 mod m20240620_add_minimum_and_maximum_progress_columns_to_integration;
 mod m20240620_delete_invalid_calendar_events;
+mod m20240704_add_new_preference_for_persisted_queries;
 
 pub use m20230410_create_metadata::Metadata as AliasedMetadata;
 pub use m20230413_create_person::Person as AliasedPerson;
@@ -75,6 +76,7 @@ impl MigratorTrait for Migrator {
             Box::new(m20240619_remove_seen_from_media_reason_of_user_to_entity::Migration),
             Box::new(m20240620_delete_invalid_calendar_events::Migration),
             Box::new(m20240620_add_minimum_and_maximum_progress_columns_to_integration::Migration),
+            Box::new(m20240704_add_new_preference_for_persisted_queries::Migration),
         ]
     }
 }
diff --git a/libs/generated/package.json b/libs/generated/package.json
index 1db6632b7c..d9b9300d07 100644
--- a/libs/generated/package.json
+++ b/libs/generated/package.json
@@ -6,6 +6,6 @@
 	},
 	"devDependencies": {
 		"@graphql-codegen/cli": "5.0.2",
-		"@graphql-codegen/client-preset": "4.3.0"
+		"@graphql-codegen/client-preset": "4.3.2"
 	}
 }
diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts
index 33bc43df24..e376876769 100644
--- a/libs/generated/src/graphql/backend/gql.ts
+++ b/libs/generated/src/graphql/backend/gql.ts
@@ -40,7 +40,7 @@ const documents = {
     "query UserMetadataDetails($metadataId: String!) {\n  userMetadataDetails(metadataId: $metadataId) {\n    collections {\n      ...CollectionPart\n    }\n    inProgress {\n      ...SeenPart\n    }\n    history {\n      ...SeenPart\n    }\n    averageRating\n    unitsConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    seenByAllCount\n    seenByUserCount\n    nextEntry {\n      season\n      episode\n      chapter\n    }\n    showProgress {\n      timesSeen\n      seasonNumber\n      episodes {\n        episodeNumber\n        timesSeen\n      }\n    }\n    podcastProgress {\n      episodeNumber\n      timesSeen\n    }\n  }\n}": types.UserMetadataDetailsDocument,
     "query UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}": types.UserMetadataGroupDetailsDocument,
     "query UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}": types.UserPersonDetailsDocument,
-    "query UserPreferences {\n  userPreferences {\n    general {\n      reviewScale\n      displayNsfw\n      dashboard {\n        section\n        hidden\n        numElements\n      }\n      disableYankIntegrations\n      disableNavigationAnimation\n      disableVideos\n      watchProviders\n      disableReviews\n      disableWatchProviders\n    }\n    fitness {\n      measurements {\n        custom {\n          name\n          dataType\n        }\n        inbuilt {\n          weight\n          bodyMassIndex\n          totalBodyWater\n          muscle\n          leanBodyMass\n          bodyFat\n          boneMass\n          visceralFat\n          waistCircumference\n          waistToHeightRatio\n          hipCircumference\n          waistToHipRatio\n          chestCircumference\n          thighCircumference\n          bicepsCircumference\n          neckCircumference\n          bodyFatCaliper\n          chestSkinfold\n          abdominalSkinfold\n          thighSkinfold\n          basalMetabolicRate\n          totalDailyEnergyExpenditure\n          calories\n        }\n      }\n      exercises {\n        saveHistory\n        unitSystem\n      }\n    }\n    notifications {\n      toSend\n      enabled\n    }\n    featuresEnabled {\n      others {\n        calendar\n        collections\n      }\n      fitness {\n        enabled\n        workouts\n        measurements\n      }\n      media {\n        enabled\n        anime\n        audioBook\n        book\n        manga\n        movie\n        podcast\n        show\n        videoGame\n        visualNovel\n        people\n        groups\n        genres\n      }\n    }\n  }\n}": types.UserPreferencesDocument,
+    "query UserPreferences {\n  userPreferences {\n    general {\n      reviewScale\n      displayNsfw\n      disableVideos\n      persistQueries\n      watchProviders\n      disableReviews\n      disableWatchProviders\n      disableYankIntegrations\n      disableNavigationAnimation\n      dashboard {\n        section\n        hidden\n        numElements\n      }\n    }\n    fitness {\n      measurements {\n        custom {\n          name\n          dataType\n        }\n        inbuilt {\n          weight\n          bodyMassIndex\n          totalBodyWater\n          muscle\n          leanBodyMass\n          bodyFat\n          boneMass\n          visceralFat\n          waistCircumference\n          waistToHeightRatio\n          hipCircumference\n          waistToHipRatio\n          chestCircumference\n          thighCircumference\n          bicepsCircumference\n          neckCircumference\n          bodyFatCaliper\n          chestSkinfold\n          abdominalSkinfold\n          thighSkinfold\n          basalMetabolicRate\n          totalDailyEnergyExpenditure\n          calories\n        }\n      }\n      exercises {\n        saveHistory\n        unitSystem\n      }\n    }\n    notifications {\n      toSend\n      enabled\n    }\n    featuresEnabled {\n      others {\n        calendar\n        collections\n      }\n      fitness {\n        enabled\n        workouts\n        measurements\n      }\n      media {\n        enabled\n        anime\n        audioBook\n        book\n        manga\n        movie\n        podcast\n        show\n        videoGame\n        visualNovel\n        people\n        groups\n        genres\n      }\n    }\n  }\n}": types.UserPreferencesDocument,
     "query UserWorkoutList($input: SearchInput!) {\n  userWorkoutList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      name\n      startTime\n      endTime\n      summary {\n        ...WorkoutSummaryPart\n      }\n    }\n  }\n}": types.UserWorkoutListDocument,
     "query WorkoutDetails($workoutId: String!) {\n  workoutDetails(workoutId: $workoutId) {\n    id\n    name\n    comment\n    startTime\n    endTime\n    repeatedFrom\n    summary {\n      ...WorkoutSummaryPart\n    }\n    information {\n      assets {\n        ...EntityAssetsPart\n      }\n      exercises {\n        name\n        lot\n        notes\n        restTime\n        total {\n          ...WorkoutOrExerciseTotalsPart\n        }\n        supersetWith\n        assets {\n          ...EntityAssetsPart\n        }\n        sets {\n          statistic {\n            ...WorkoutSetStatisticPart\n          }\n          lot\n          personalBests\n          confirmedAt\n        }\n      }\n    }\n  }\n}": types.WorkoutDetailsDocument,
     "query CoreEnabledFeatures {\n  coreEnabledFeatures {\n    fileStorage\n    signupAllowed\n  }\n}\n\nquery GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery ProvidersLanguageInformation {\n  providersLanguageInformation {\n    supported\n    default\n    source\n  }\n}\n\nquery UserExports {\n  userExports {\n    startedAt\n    endedAt\n    url\n    exported\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    source\n    createdOn\n    lastTriggeredOn\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    description\n    timestamp\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    name\n    lot\n  }\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}": types.CoreEnabledFeaturesDocument,
@@ -172,7 +172,7 @@ export function graphql(source: "query UserPersonDetails($personId: String!) {\n
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "query UserPreferences {\n  userPreferences {\n    general {\n      reviewScale\n      displayNsfw\n      dashboard {\n        section\n        hidden\n        numElements\n      }\n      disableYankIntegrations\n      disableNavigationAnimation\n      disableVideos\n      watchProviders\n      disableReviews\n      disableWatchProviders\n    }\n    fitness {\n      measurements {\n        custom {\n          name\n          dataType\n        }\n        inbuilt {\n          weight\n          bodyMassIndex\n          totalBodyWater\n          muscle\n          leanBodyMass\n          bodyFat\n          boneMass\n          visceralFat\n          waistCircumference\n          waistToHeightRatio\n          hipCircumference\n          waistToHipRatio\n          chestCircumference\n          thighCircumference\n          bicepsCircumference\n          neckCircumference\n          bodyFatCaliper\n          chestSkinfold\n          abdominalSkinfold\n          thighSkinfold\n          basalMetabolicRate\n          totalDailyEnergyExpenditure\n          calories\n        }\n      }\n      exercises {\n        saveHistory\n        unitSystem\n      }\n    }\n    notifications {\n      toSend\n      enabled\n    }\n    featuresEnabled {\n      others {\n        calendar\n        collections\n      }\n      fitness {\n        enabled\n        workouts\n        measurements\n      }\n      media {\n        enabled\n        anime\n        audioBook\n        book\n        manga\n        movie\n        podcast\n        show\n        videoGame\n        visualNovel\n        people\n        groups\n        genres\n      }\n    }\n  }\n}"): (typeof documents)["query UserPreferences {\n  userPreferences {\n    general {\n      reviewScale\n      displayNsfw\n      dashboard {\n        section\n        hidden\n        numElements\n      }\n      disableYankIntegrations\n      disableNavigationAnimation\n      disableVideos\n      watchProviders\n      disableReviews\n      disableWatchProviders\n    }\n    fitness {\n      measurements {\n        custom {\n          name\n          dataType\n        }\n        inbuilt {\n          weight\n          bodyMassIndex\n          totalBodyWater\n          muscle\n          leanBodyMass\n          bodyFat\n          boneMass\n          visceralFat\n          waistCircumference\n          waistToHeightRatio\n          hipCircumference\n          waistToHipRatio\n          chestCircumference\n          thighCircumference\n          bicepsCircumference\n          neckCircumference\n          bodyFatCaliper\n          chestSkinfold\n          abdominalSkinfold\n          thighSkinfold\n          basalMetabolicRate\n          totalDailyEnergyExpenditure\n          calories\n        }\n      }\n      exercises {\n        saveHistory\n        unitSystem\n      }\n    }\n    notifications {\n      toSend\n      enabled\n    }\n    featuresEnabled {\n      others {\n        calendar\n        collections\n      }\n      fitness {\n        enabled\n        workouts\n        measurements\n      }\n      media {\n        enabled\n        anime\n        audioBook\n        book\n        manga\n        movie\n        podcast\n        show\n        videoGame\n        visualNovel\n        people\n        groups\n        genres\n      }\n    }\n  }\n}"];
+export function graphql(source: "query UserPreferences {\n  userPreferences {\n    general {\n      reviewScale\n      displayNsfw\n      disableVideos\n      persistQueries\n      watchProviders\n      disableReviews\n      disableWatchProviders\n      disableYankIntegrations\n      disableNavigationAnimation\n      dashboard {\n        section\n        hidden\n        numElements\n      }\n    }\n    fitness {\n      measurements {\n        custom {\n          name\n          dataType\n        }\n        inbuilt {\n          weight\n          bodyMassIndex\n          totalBodyWater\n          muscle\n          leanBodyMass\n          bodyFat\n          boneMass\n          visceralFat\n          waistCircumference\n          waistToHeightRatio\n          hipCircumference\n          waistToHipRatio\n          chestCircumference\n          thighCircumference\n          bicepsCircumference\n          neckCircumference\n          bodyFatCaliper\n          chestSkinfold\n          abdominalSkinfold\n          thighSkinfold\n          basalMetabolicRate\n          totalDailyEnergyExpenditure\n          calories\n        }\n      }\n      exercises {\n        saveHistory\n        unitSystem\n      }\n    }\n    notifications {\n      toSend\n      enabled\n    }\n    featuresEnabled {\n      others {\n        calendar\n        collections\n      }\n      fitness {\n        enabled\n        workouts\n        measurements\n      }\n      media {\n        enabled\n        anime\n        audioBook\n        book\n        manga\n        movie\n        podcast\n        show\n        videoGame\n        visualNovel\n        people\n        groups\n        genres\n      }\n    }\n  }\n}"): (typeof documents)["query UserPreferences {\n  userPreferences {\n    general {\n      reviewScale\n      displayNsfw\n      disableVideos\n      persistQueries\n      watchProviders\n      disableReviews\n      disableWatchProviders\n      disableYankIntegrations\n      disableNavigationAnimation\n      dashboard {\n        section\n        hidden\n        numElements\n      }\n    }\n    fitness {\n      measurements {\n        custom {\n          name\n          dataType\n        }\n        inbuilt {\n          weight\n          bodyMassIndex\n          totalBodyWater\n          muscle\n          leanBodyMass\n          bodyFat\n          boneMass\n          visceralFat\n          waistCircumference\n          waistToHeightRatio\n          hipCircumference\n          waistToHipRatio\n          chestCircumference\n          thighCircumference\n          bicepsCircumference\n          neckCircumference\n          bodyFatCaliper\n          chestSkinfold\n          abdominalSkinfold\n          thighSkinfold\n          basalMetabolicRate\n          totalDailyEnergyExpenditure\n          calories\n        }\n      }\n      exercises {\n        saveHistory\n        unitSystem\n      }\n    }\n    notifications {\n      toSend\n      enabled\n    }\n    featuresEnabled {\n      others {\n        calendar\n        collections\n      }\n      fitness {\n        enabled\n        workouts\n        measurements\n      }\n      media {\n        enabled\n        anime\n        audioBook\n        book\n        manga\n        movie\n        podcast\n        show\n        videoGame\n        visualNovel\n        people\n        groups\n        genres\n      }\n    }\n  }\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
diff --git a/libs/generated/src/graphql/backend/graphql.ts b/libs/generated/src/graphql/backend/graphql.ts
index e39a72a084..f6dd50cace 100644
--- a/libs/generated/src/graphql/backend/graphql.ts
+++ b/libs/generated/src/graphql/backend/graphql.ts
@@ -1985,6 +1985,7 @@ export type UserGeneralPreferences = {
   disableWatchProviders: Scalars['Boolean']['output'];
   disableYankIntegrations: Scalars['Boolean']['output'];
   displayNsfw: Scalars['Boolean']['output'];
+  persistQueries: Scalars['Boolean']['output'];
   reviewScale: UserReviewScale;
   watchProviders: Array<Scalars['String']['output']>;
 };
@@ -2866,7 +2867,7 @@ export type UserPersonDetailsQuery = { userPersonDetails: { collections: Array<{
 export type UserPreferencesQueryVariables = Exact<{ [key: string]: never; }>;
 
 
-export type UserPreferencesQuery = { userPreferences: { general: { reviewScale: UserReviewScale, displayNsfw: boolean, disableYankIntegrations: boolean, disableNavigationAnimation: boolean, disableVideos: boolean, watchProviders: Array<string>, disableReviews: boolean, disableWatchProviders: boolean, dashboard: Array<{ section: DashboardElementLot, hidden: boolean, numElements?: number | null }> }, fitness: { measurements: { custom: Array<{ name: string, dataType: UserCustomMeasurementDataType }>, inbuilt: { weight: boolean, bodyMassIndex: boolean, totalBodyWater: boolean, muscle: boolean, leanBodyMass: boolean, bodyFat: boolean, boneMass: boolean, visceralFat: boolean, waistCircumference: boolean, waistToHeightRatio: boolean, hipCircumference: boolean, waistToHipRatio: boolean, chestCircumference: boolean, thighCircumference: boolean, bicepsCircumference: boolean, neckCircumference: boolean, bodyFatCaliper: boolean, chestSkinfold: boolean, abdominalSkinfold: boolean, thighSkinfold: boolean, basalMetabolicRate: boolean, totalDailyEnergyExpenditure: boolean, calories: boolean } }, exercises: { saveHistory: number, unitSystem: UserUnitSystem } }, notifications: { toSend: Array<MediaStateChanged>, enabled: boolean }, featuresEnabled: { others: { calendar: boolean, collections: boolean }, fitness: { enabled: boolean, workouts: boolean, measurements: boolean }, media: { enabled: boolean, anime: boolean, audioBook: boolean, book: boolean, manga: boolean, movie: boolean, podcast: boolean, show: boolean, videoGame: boolean, visualNovel: boolean, people: boolean, groups: boolean, genres: boolean } } } };
+export type UserPreferencesQuery = { userPreferences: { general: { reviewScale: UserReviewScale, displayNsfw: boolean, disableVideos: boolean, persistQueries: boolean, watchProviders: Array<string>, disableReviews: boolean, disableWatchProviders: boolean, disableYankIntegrations: boolean, disableNavigationAnimation: boolean, dashboard: Array<{ section: DashboardElementLot, hidden: boolean, numElements?: number | null }> }, fitness: { measurements: { custom: Array<{ name: string, dataType: UserCustomMeasurementDataType }>, inbuilt: { weight: boolean, bodyMassIndex: boolean, totalBodyWater: boolean, muscle: boolean, leanBodyMass: boolean, bodyFat: boolean, boneMass: boolean, visceralFat: boolean, waistCircumference: boolean, waistToHeightRatio: boolean, hipCircumference: boolean, waistToHipRatio: boolean, chestCircumference: boolean, thighCircumference: boolean, bicepsCircumference: boolean, neckCircumference: boolean, bodyFatCaliper: boolean, chestSkinfold: boolean, abdominalSkinfold: boolean, thighSkinfold: boolean, basalMetabolicRate: boolean, totalDailyEnergyExpenditure: boolean, calories: boolean } }, exercises: { saveHistory: number, unitSystem: UserUnitSystem } }, notifications: { toSend: Array<MediaStateChanged>, enabled: boolean }, featuresEnabled: { others: { calendar: boolean, collections: boolean }, fitness: { enabled: boolean, workouts: boolean, measurements: boolean }, media: { enabled: boolean, anime: boolean, audioBook: boolean, book: boolean, manga: boolean, movie: boolean, podcast: boolean, show: boolean, videoGame: boolean, visualNovel: boolean, people: boolean, groups: boolean, genres: boolean } } } };
 
 export type UserWorkoutListQueryVariables = Exact<{
   input: SearchInput;
@@ -3060,7 +3061,7 @@ export const UserMeasurementsListDocument = {"kind":"Document","definitions":[{"
 export const UserMetadataDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMetadataDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userMetadataDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"history"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"averageRating"}},{"kind":"Field","name":{"kind":"Name","value":"unitsConsumed"}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"seenByAllCount"}},{"kind":"Field","name":{"kind":"Name","value":"seenByUserCount"}},{"kind":"Field","name":{"kind":"Name","value":"nextEntry"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"chapter"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timesSeen"}},{"kind":"Field","name":{"kind":"Name","value":"seasonNumber"}},{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"timesSeen"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"timesSeen"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenShowExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenShowExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenPodcastExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenAnimeExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenMangaExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapter"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CollectionPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Collection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Seen"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"progress"}},{"kind":"Field","name":{"kind":"Name","value":"providerWatchedOn"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"startedOn"}},{"kind":"Field","name":{"kind":"Name","value":"finishedOn"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedOn"}},{"kind":"Field","name":{"kind":"Name","value":"totalTimeSpent"}},{"kind":"Field","name":{"kind":"Name","value":"numTimesUpdated"}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenShowExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ReviewItemPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReviewItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"textOriginal"}},{"kind":"Field","name":{"kind":"Name","value":"textRendered"}},{"kind":"Field","name":{"kind":"Name","value":"isSpoiler"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"postedOn"}},{"kind":"Field","name":{"kind":"Name","value":"postedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"likedBy"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenShowExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"}}]}}]}}]} as unknown as DocumentNode<UserMetadataDetailsQuery, UserMetadataDetailsQueryVariables>;
 export const UserMetadataGroupDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMetadataGroupDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataGroupId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userMetadataGroupDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataGroupId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataGroupId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenShowExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenShowExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenPodcastExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenAnimeExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenMangaExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapter"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ReviewItemPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReviewItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"textOriginal"}},{"kind":"Field","name":{"kind":"Name","value":"textRendered"}},{"kind":"Field","name":{"kind":"Name","value":"isSpoiler"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"postedOn"}},{"kind":"Field","name":{"kind":"Name","value":"postedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"likedBy"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenShowExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CollectionPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Collection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode<UserMetadataGroupDetailsQuery, UserMetadataGroupDetailsQueryVariables>;
 export const UserPersonDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserPersonDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"personId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userPersonDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"personId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"personId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenShowExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenShowExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenPodcastExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenAnimeExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenMangaExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapter"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CollectionPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Collection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ReviewItemPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReviewItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"textOriginal"}},{"kind":"Field","name":{"kind":"Name","value":"textRendered"}},{"kind":"Field","name":{"kind":"Name","value":"isSpoiler"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"postedOn"}},{"kind":"Field","name":{"kind":"Name","value":"postedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"likedBy"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenShowExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"}}]}}]}}]} as unknown as DocumentNode<UserPersonDetailsQuery, UserPersonDetailsQueryVariables>;
-export const UserPreferencesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserPreferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userPreferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"general"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewScale"}},{"kind":"Field","name":{"kind":"Name","value":"displayNsfw"}},{"kind":"Field","name":{"kind":"Name","value":"dashboard"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}},{"kind":"Field","name":{"kind":"Name","value":"numElements"}}]}},{"kind":"Field","name":{"kind":"Name","value":"disableYankIntegrations"}},{"kind":"Field","name":{"kind":"Name","value":"disableNavigationAnimation"}},{"kind":"Field","name":{"kind":"Name","value":"disableVideos"}},{"kind":"Field","name":{"kind":"Name","value":"watchProviders"}},{"kind":"Field","name":{"kind":"Name","value":"disableReviews"}},{"kind":"Field","name":{"kind":"Name","value":"disableWatchProviders"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"measurements"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"custom"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inbuilt"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"bodyMassIndex"}},{"kind":"Field","name":{"kind":"Name","value":"totalBodyWater"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"leanBodyMass"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFat"}},{"kind":"Field","name":{"kind":"Name","value":"boneMass"}},{"kind":"Field","name":{"kind":"Name","value":"visceralFat"}},{"kind":"Field","name":{"kind":"Name","value":"waistCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHeightRatio"}},{"kind":"Field","name":{"kind":"Name","value":"hipCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHipRatio"}},{"kind":"Field","name":{"kind":"Name","value":"chestCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"thighCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bicepsCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"neckCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFatCaliper"}},{"kind":"Field","name":{"kind":"Name","value":"chestSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"abdominalSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"thighSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"basalMetabolicRate"}},{"kind":"Field","name":{"kind":"Name","value":"totalDailyEnergyExpenditure"}},{"kind":"Field","name":{"kind":"Name","value":"calories"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"saveHistory"}},{"kind":"Field","name":{"kind":"Name","value":"unitSystem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"toSend"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"featuresEnabled"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"others"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calendar"}},{"kind":"Field","name":{"kind":"Name","value":"collections"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"workouts"}},{"kind":"Field","name":{"kind":"Name","value":"measurements"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"anime"}},{"kind":"Field","name":{"kind":"Name","value":"audioBook"}},{"kind":"Field","name":{"kind":"Name","value":"book"}},{"kind":"Field","name":{"kind":"Name","value":"manga"}},{"kind":"Field","name":{"kind":"Name","value":"movie"}},{"kind":"Field","name":{"kind":"Name","value":"podcast"}},{"kind":"Field","name":{"kind":"Name","value":"show"}},{"kind":"Field","name":{"kind":"Name","value":"videoGame"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovel"}},{"kind":"Field","name":{"kind":"Name","value":"people"}},{"kind":"Field","name":{"kind":"Name","value":"groups"}},{"kind":"Field","name":{"kind":"Name","value":"genres"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserPreferencesQuery, UserPreferencesQueryVariables>;
+export const UserPreferencesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserPreferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userPreferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"general"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewScale"}},{"kind":"Field","name":{"kind":"Name","value":"displayNsfw"}},{"kind":"Field","name":{"kind":"Name","value":"disableVideos"}},{"kind":"Field","name":{"kind":"Name","value":"persistQueries"}},{"kind":"Field","name":{"kind":"Name","value":"watchProviders"}},{"kind":"Field","name":{"kind":"Name","value":"disableReviews"}},{"kind":"Field","name":{"kind":"Name","value":"disableWatchProviders"}},{"kind":"Field","name":{"kind":"Name","value":"disableYankIntegrations"}},{"kind":"Field","name":{"kind":"Name","value":"disableNavigationAnimation"}},{"kind":"Field","name":{"kind":"Name","value":"dashboard"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}},{"kind":"Field","name":{"kind":"Name","value":"numElements"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"measurements"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"custom"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inbuilt"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"bodyMassIndex"}},{"kind":"Field","name":{"kind":"Name","value":"totalBodyWater"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"leanBodyMass"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFat"}},{"kind":"Field","name":{"kind":"Name","value":"boneMass"}},{"kind":"Field","name":{"kind":"Name","value":"visceralFat"}},{"kind":"Field","name":{"kind":"Name","value":"waistCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHeightRatio"}},{"kind":"Field","name":{"kind":"Name","value":"hipCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHipRatio"}},{"kind":"Field","name":{"kind":"Name","value":"chestCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"thighCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bicepsCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"neckCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFatCaliper"}},{"kind":"Field","name":{"kind":"Name","value":"chestSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"abdominalSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"thighSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"basalMetabolicRate"}},{"kind":"Field","name":{"kind":"Name","value":"totalDailyEnergyExpenditure"}},{"kind":"Field","name":{"kind":"Name","value":"calories"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"saveHistory"}},{"kind":"Field","name":{"kind":"Name","value":"unitSystem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"toSend"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"featuresEnabled"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"others"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calendar"}},{"kind":"Field","name":{"kind":"Name","value":"collections"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"workouts"}},{"kind":"Field","name":{"kind":"Name","value":"measurements"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"anime"}},{"kind":"Field","name":{"kind":"Name","value":"audioBook"}},{"kind":"Field","name":{"kind":"Name","value":"book"}},{"kind":"Field","name":{"kind":"Name","value":"manga"}},{"kind":"Field","name":{"kind":"Name","value":"movie"}},{"kind":"Field","name":{"kind":"Name","value":"podcast"}},{"kind":"Field","name":{"kind":"Name","value":"show"}},{"kind":"Field","name":{"kind":"Name","value":"videoGame"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovel"}},{"kind":"Field","name":{"kind":"Name","value":"people"}},{"kind":"Field","name":{"kind":"Name","value":"groups"}},{"kind":"Field","name":{"kind":"Name","value":"genres"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserPreferencesQuery, UserPreferencesQueryVariables>;
 export const UserWorkoutListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userWorkoutList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"startTime"}},{"kind":"Field","name":{"kind":"Name","value":"endTime"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSummaryPart"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSetStatisticPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSetStatistic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"oneRm"}},{"kind":"Field","name":{"kind":"Name","value":"pace"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"statistic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetStatisticPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"personalBests"}}]}}]}}]}}]} as unknown as DocumentNode<UserWorkoutListQuery, UserWorkoutListQueryVariables>;
 export const WorkoutDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkoutDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workoutId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workoutDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workoutId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workoutId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"startTime"}},{"kind":"Field","name":{"kind":"Name","value":"endTime"}},{"kind":"Field","name":{"kind":"Name","value":"repeatedFrom"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSummaryPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"information"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"notes"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"supersetWith"}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"statistic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetStatisticPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"personalBests"}},{"kind":"Field","name":{"kind":"Name","value":"confirmedAt"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSetStatisticPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSetStatistic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"oneRm"}},{"kind":"Field","name":{"kind":"Name","value":"pace"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"statistic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetStatisticPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"personalBests"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EntityAssetsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EntityAssets"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"}}]}}]} as unknown as DocumentNode<WorkoutDetailsQuery, WorkoutDetailsQueryVariables>;
 export const CoreEnabledFeaturesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CoreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"coreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fileStorage"}},{"kind":"Field","name":{"kind":"Name","value":"signupAllowed"}}]}}]}}]} as unknown as DocumentNode<CoreEnabledFeaturesQuery, CoreEnabledFeaturesQueryVariables>;
diff --git a/libs/graphql/src/backend/queries/UserPreferences.gql b/libs/graphql/src/backend/queries/UserPreferences.gql
index 945a16c0b3..847638dbdb 100644
--- a/libs/graphql/src/backend/queries/UserPreferences.gql
+++ b/libs/graphql/src/backend/queries/UserPreferences.gql
@@ -3,17 +3,18 @@ query UserPreferences {
     general {
       reviewScale
       displayNsfw
+      disableVideos
+      persistQueries
+      watchProviders
+      disableReviews
+      disableWatchProviders
+      disableYankIntegrations
+      disableNavigationAnimation
       dashboard {
         section
         hidden
         numElements
       }
-      disableYankIntegrations
-      disableNavigationAnimation
-      disableVideos
-      watchProviders
-      disableReviews
-      disableWatchProviders
     }
     fitness {
       measurements {
diff --git a/libs/ts-utils/package.json b/libs/ts-utils/package.json
index dd0f4f01cc..3822e718d2 100644
--- a/libs/ts-utils/package.json
+++ b/libs/ts-utils/package.json
@@ -7,6 +7,6 @@
 		"lodash": "4.17.21"
 	},
 	"devDependencies": {
-		"@types/lodash": "4.17.5"
+		"@types/lodash": "4.17.6"
 	}
 }
diff --git a/package.json b/package.json
index 30df53dbfb..5a59f7b0a9 100644
--- a/package.json
+++ b/package.json
@@ -6,8 +6,8 @@
 		"node": "20.10.0"
 	},
 	"devDependencies": {
-		"@biomejs/biome": "1.8.2",
+		"@biomejs/biome": "1.8.3",
 		"tsconfig-moon": "1.3.0",
-		"typescript": "5.5.2"
+		"typescript": "5.5.3"
 	}
 }
diff --git a/yarn.lock b/yarn.lock
index 4c78141b6e..2020c538a9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1780,18 +1780,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@biomejs/biome@npm:1.8.2":
-  version: 1.8.2
-  resolution: "@biomejs/biome@npm:1.8.2"
-  dependencies:
-    "@biomejs/cli-darwin-arm64": "npm:1.8.2"
-    "@biomejs/cli-darwin-x64": "npm:1.8.2"
-    "@biomejs/cli-linux-arm64": "npm:1.8.2"
-    "@biomejs/cli-linux-arm64-musl": "npm:1.8.2"
-    "@biomejs/cli-linux-x64": "npm:1.8.2"
-    "@biomejs/cli-linux-x64-musl": "npm:1.8.2"
-    "@biomejs/cli-win32-arm64": "npm:1.8.2"
-    "@biomejs/cli-win32-x64": "npm:1.8.2"
+"@biomejs/biome@npm:1.8.3":
+  version: 1.8.3
+  resolution: "@biomejs/biome@npm:1.8.3"
+  dependencies:
+    "@biomejs/cli-darwin-arm64": "npm:1.8.3"
+    "@biomejs/cli-darwin-x64": "npm:1.8.3"
+    "@biomejs/cli-linux-arm64": "npm:1.8.3"
+    "@biomejs/cli-linux-arm64-musl": "npm:1.8.3"
+    "@biomejs/cli-linux-x64": "npm:1.8.3"
+    "@biomejs/cli-linux-x64-musl": "npm:1.8.3"
+    "@biomejs/cli-win32-arm64": "npm:1.8.3"
+    "@biomejs/cli-win32-x64": "npm:1.8.3"
   dependenciesMeta:
     "@biomejs/cli-darwin-arm64":
       optional: true
@@ -1811,62 +1811,62 @@ __metadata:
       optional: true
   bin:
     biome: bin/biome
-  checksum: 10/7721e685c476997cf31d2c39df2a15d85e5cb00e85d6b917fb24b782eba6a43afe40258073f63c0eafb3530267251d473c50736d280263ac968fa912b35ba873
+  checksum: 10/62dfa5147712ef21c384ea7b3c93c0ccac58291a85f2bbd2dee22c8381da5e347cd07bdb7bfcafcecb07fc112349e9d101e697774155553bde987fd47f9b12a1
   languageName: node
   linkType: hard
 
-"@biomejs/cli-darwin-arm64@npm:1.8.2":
-  version: 1.8.2
-  resolution: "@biomejs/cli-darwin-arm64@npm:1.8.2"
+"@biomejs/cli-darwin-arm64@npm:1.8.3":
+  version: 1.8.3
+  resolution: "@biomejs/cli-darwin-arm64@npm:1.8.3"
   conditions: os=darwin & cpu=arm64
   languageName: node
   linkType: hard
 
-"@biomejs/cli-darwin-x64@npm:1.8.2":
-  version: 1.8.2
-  resolution: "@biomejs/cli-darwin-x64@npm:1.8.2"
+"@biomejs/cli-darwin-x64@npm:1.8.3":
+  version: 1.8.3
+  resolution: "@biomejs/cli-darwin-x64@npm:1.8.3"
   conditions: os=darwin & cpu=x64
   languageName: node
   linkType: hard
 
-"@biomejs/cli-linux-arm64-musl@npm:1.8.2":
-  version: 1.8.2
-  resolution: "@biomejs/cli-linux-arm64-musl@npm:1.8.2"
+"@biomejs/cli-linux-arm64-musl@npm:1.8.3":
+  version: 1.8.3
+  resolution: "@biomejs/cli-linux-arm64-musl@npm:1.8.3"
   conditions: os=linux & cpu=arm64 & libc=musl
   languageName: node
   linkType: hard
 
-"@biomejs/cli-linux-arm64@npm:1.8.2":
-  version: 1.8.2
-  resolution: "@biomejs/cli-linux-arm64@npm:1.8.2"
+"@biomejs/cli-linux-arm64@npm:1.8.3":
+  version: 1.8.3
+  resolution: "@biomejs/cli-linux-arm64@npm:1.8.3"
   conditions: os=linux & cpu=arm64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@biomejs/cli-linux-x64-musl@npm:1.8.2":
-  version: 1.8.2
-  resolution: "@biomejs/cli-linux-x64-musl@npm:1.8.2"
+"@biomejs/cli-linux-x64-musl@npm:1.8.3":
+  version: 1.8.3
+  resolution: "@biomejs/cli-linux-x64-musl@npm:1.8.3"
   conditions: os=linux & cpu=x64 & libc=musl
   languageName: node
   linkType: hard
 
-"@biomejs/cli-linux-x64@npm:1.8.2":
-  version: 1.8.2
-  resolution: "@biomejs/cli-linux-x64@npm:1.8.2"
+"@biomejs/cli-linux-x64@npm:1.8.3":
+  version: 1.8.3
+  resolution: "@biomejs/cli-linux-x64@npm:1.8.3"
   conditions: os=linux & cpu=x64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@biomejs/cli-win32-arm64@npm:1.8.2":
-  version: 1.8.2
-  resolution: "@biomejs/cli-win32-arm64@npm:1.8.2"
+"@biomejs/cli-win32-arm64@npm:1.8.3":
+  version: 1.8.3
+  resolution: "@biomejs/cli-win32-arm64@npm:1.8.3"
   conditions: os=win32 & cpu=arm64
   languageName: node
   linkType: hard
 
-"@biomejs/cli-win32-x64@npm:1.8.2":
-  version: 1.8.2
-  resolution: "@biomejs/cli-win32-x64@npm:1.8.2"
+"@biomejs/cli-win32-x64@npm:1.8.3":
+  version: 1.8.3
+  resolution: "@biomejs/cli-win32-x64@npm:1.8.3"
   conditions: os=win32 & cpu=x64
   languageName: node
   linkType: hard
@@ -2603,26 +2603,26 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/client-preset@npm:4.3.0":
-  version: 4.3.0
-  resolution: "@graphql-codegen/client-preset@npm:4.3.0"
+"@graphql-codegen/client-preset@npm:4.3.2":
+  version: 4.3.2
+  resolution: "@graphql-codegen/client-preset@npm:4.3.2"
   dependencies:
     "@babel/helper-plugin-utils": "npm:^7.20.2"
     "@babel/template": "npm:^7.20.7"
     "@graphql-codegen/add": "npm:^5.0.3"
-    "@graphql-codegen/gql-tag-operations": "npm:4.0.7"
+    "@graphql-codegen/gql-tag-operations": "npm:4.0.9"
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
-    "@graphql-codegen/typed-document-node": "npm:^5.0.7"
-    "@graphql-codegen/typescript": "npm:^4.0.7"
-    "@graphql-codegen/typescript-operations": "npm:^4.2.1"
-    "@graphql-codegen/visitor-plugin-common": "npm:^5.2.0"
+    "@graphql-codegen/typed-document-node": "npm:^5.0.9"
+    "@graphql-codegen/typescript": "npm:^4.0.9"
+    "@graphql-codegen/typescript-operations": "npm:^4.2.3"
+    "@graphql-codegen/visitor-plugin-common": "npm:^5.3.1"
     "@graphql-tools/documents": "npm:^1.0.0"
     "@graphql-tools/utils": "npm:^10.0.0"
     "@graphql-typed-document-node/core": "npm:3.2.0"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/34ccb36565e77b15c12c998f2f6d2c368710a78d2d0d1b7e9a039d1a01d8a84e78413076906a88617464efb646be914f9bbb839190ca98a9bfdddaf387dcdb54
+  checksum: 10/52c66f1f0de4a1537726d2d6915f307aa650ab425a9b6ba5e24718a412f5b2aa7b27c706074d7fa086cbede7c0a0fb74ff1016ba378cc729a40e301d207a42f8
   languageName: node
   linkType: hard
 
@@ -2678,18 +2678,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/gql-tag-operations@npm:4.0.7":
-  version: 4.0.7
-  resolution: "@graphql-codegen/gql-tag-operations@npm:4.0.7"
+"@graphql-codegen/gql-tag-operations@npm:4.0.9":
+  version: 4.0.9
+  resolution: "@graphql-codegen/gql-tag-operations@npm:4.0.9"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
-    "@graphql-codegen/visitor-plugin-common": "npm:5.2.0"
+    "@graphql-codegen/visitor-plugin-common": "npm:5.3.1"
     "@graphql-tools/utils": "npm:^10.0.0"
     auto-bind: "npm:~4.0.0"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/ce94719790cd03de386b184b7fdbd9029d3c61a6dfcdbee626b61f58c92cdff40802521398350ed1400fd6d8ec3c40b0faa3b05ec439ca365b20b5f2259c78f2
+  checksum: 10/90eaecbe0742ee65f0e25df471760664d615bb6396d7e35c2d33615128849c1bdb1c0479c75de589479b7b1a0ee99b34684a2faeeff6245e01ffa7dcfff4b0c6
   languageName: node
   linkType: hard
 
@@ -2753,18 +2753,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/typed-document-node@npm:^5.0.7":
-  version: 5.0.7
-  resolution: "@graphql-codegen/typed-document-node@npm:5.0.7"
+"@graphql-codegen/typed-document-node@npm:^5.0.9":
+  version: 5.0.9
+  resolution: "@graphql-codegen/typed-document-node@npm:5.0.9"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
-    "@graphql-codegen/visitor-plugin-common": "npm:5.2.0"
+    "@graphql-codegen/visitor-plugin-common": "npm:5.3.1"
     auto-bind: "npm:~4.0.0"
     change-case-all: "npm:1.0.15"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/1139e0ad8c90d48a25be4a0e8ded5e0c0faca01c7acea4f6142315cb0a5c497b7e5d6d3e87c4e9167f0b55347350c7b031213c8e4b202a257c454eb3f15a5daf
+  checksum: 10/0cac39bf2d8304660dc99b46b9c6ed811be79aca8a2cd1f58d9a16ff83fb644e985d872456eed5fdba27fa1727e76c3d3190e251c0171d83aff1af58cbcbc3c8
   languageName: node
   linkType: hard
 
@@ -2783,18 +2783,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/typescript-operations@npm:^4.2.1":
-  version: 4.2.1
-  resolution: "@graphql-codegen/typescript-operations@npm:4.2.1"
+"@graphql-codegen/typescript-operations@npm:^4.2.3":
+  version: 4.2.3
+  resolution: "@graphql-codegen/typescript-operations@npm:4.2.3"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
-    "@graphql-codegen/typescript": "npm:^4.0.7"
-    "@graphql-codegen/visitor-plugin-common": "npm:5.2.0"
+    "@graphql-codegen/typescript": "npm:^4.0.9"
+    "@graphql-codegen/visitor-plugin-common": "npm:5.3.1"
     auto-bind: "npm:~4.0.0"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/98e52f4acbd418e61614f42f529a8137c88f438cc0c52cb62ab50c4a9789715caa79e8a0ef1762ba62d43b3915e900fd94357418eaeed308c9c6c3d838cc0e41
+  checksum: 10/b501b43c5a686847e12812690e0aea3842c6efa10d53703db063d82f2a4f3c469bb02c430a29ac1d1b450ffc82cf97c0ca1b9caa8e8979279f72646f010301bf
   languageName: node
   linkType: hard
 
@@ -2813,18 +2813,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/typescript@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@graphql-codegen/typescript@npm:4.0.7"
+"@graphql-codegen/typescript@npm:^4.0.9":
+  version: 4.0.9
+  resolution: "@graphql-codegen/typescript@npm:4.0.9"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
     "@graphql-codegen/schema-ast": "npm:^4.0.2"
-    "@graphql-codegen/visitor-plugin-common": "npm:5.2.0"
+    "@graphql-codegen/visitor-plugin-common": "npm:5.3.1"
     auto-bind: "npm:~4.0.0"
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/a5354745297369d490a4fe778e99bb8474aa8bb2c12764c70a08434e4b1ef40394a405236356dc6d644052a5a87ef840b15b598cce1fe283f93b5fd316fa2d5f
+  checksum: 10/304026adfe622530b8a2827569dd5bbd390177051be8c214fb79873ec64ef21793635c91657703bfd229a3d06f1a8a6f1addd8ae7eab20d1eff2efe6fb044df7
   languageName: node
   linkType: hard
 
@@ -2848,9 +2848,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@graphql-codegen/visitor-plugin-common@npm:5.2.0, @graphql-codegen/visitor-plugin-common@npm:^5.2.0":
-  version: 5.2.0
-  resolution: "@graphql-codegen/visitor-plugin-common@npm:5.2.0"
+"@graphql-codegen/visitor-plugin-common@npm:5.3.1, @graphql-codegen/visitor-plugin-common@npm:^5.3.1":
+  version: 5.3.1
+  resolution: "@graphql-codegen/visitor-plugin-common@npm:5.3.1"
   dependencies:
     "@graphql-codegen/plugin-helpers": "npm:^5.0.4"
     "@graphql-tools/optimize": "npm:^2.0.0"
@@ -2864,7 +2864,7 @@ __metadata:
     tslib: "npm:~2.6.0"
   peerDependencies:
     graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
-  checksum: 10/f2505d4641fe3b90e93d3f9a4fca57eddf707ed5efcc1abc407196835f718b6dcce5f0a736d3f9450e52230200962eca8e48429af20f48885c567c2d252ef295
+  checksum: 10/6dd0464d9099d5aeabeb766515fc8dd2fc84bcae4cb0e3653d7f38aea716d6622d35d7cbb57a1954e6bc1cde10f4dd8c4a75ceb4e8bb8cdbba16219615666a5f
   languageName: node
   linkType: hard
 
@@ -3862,9 +3862,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@remix-run/dev@npm:2.10.0":
-  version: 2.10.0
-  resolution: "@remix-run/dev@npm:2.10.0"
+"@remix-run/dev@npm:2.10.2":
+  version: 2.10.2
+  resolution: "@remix-run/dev@npm:2.10.2"
   dependencies:
     "@babel/core": "npm:^7.21.8"
     "@babel/generator": "npm:^7.21.5"
@@ -3876,9 +3876,9 @@ __metadata:
     "@babel/types": "npm:^7.22.5"
     "@mdx-js/mdx": "npm:^2.3.0"
     "@npmcli/package-json": "npm:^4.0.1"
-    "@remix-run/node": "npm:2.10.0"
-    "@remix-run/router": "npm:1.17.0"
-    "@remix-run/server-runtime": "npm:2.10.0"
+    "@remix-run/node": "npm:2.10.2"
+    "@remix-run/router": "npm:1.17.1"
+    "@remix-run/server-runtime": "npm:2.10.2"
     "@types/mdx": "npm:^2.0.5"
     "@vanilla-extract/integration": "npm:^6.2.0"
     arg: "npm:^5.0.1"
@@ -3920,8 +3920,8 @@ __metadata:
     tsconfig-paths: "npm:^4.0.0"
     ws: "npm:^7.4.5"
   peerDependencies:
-    "@remix-run/react": ^2.10.0
-    "@remix-run/serve": ^2.10.0
+    "@remix-run/react": ^2.10.2
+    "@remix-run/serve": ^2.10.2
     typescript: ^5.1.0
     vite: ^5.1.0
     wrangler: ^3.28.2
@@ -3936,30 +3936,30 @@ __metadata:
       optional: true
   bin:
     remix: dist/cli.js
-  checksum: 10/67c3e0114bd44d88f50bbecfb64f62c561791f12311b2981188dac73be6980f41a5e5aee652d347bb6fdca9a004db8245a3e783e6ff91fba3e1cb7784b64d68d
+  checksum: 10/774def480c572f914ae9189b8339903afe31c317a531c2e2ef3a6885463d780b57c9a65a940348a23560dbe39e6c9eeb61943dd8b3e582d9519d3190dc3eed39
   languageName: node
   linkType: hard
 
-"@remix-run/express@npm:2.10.0":
-  version: 2.10.0
-  resolution: "@remix-run/express@npm:2.10.0"
+"@remix-run/express@npm:2.10.2":
+  version: 2.10.2
+  resolution: "@remix-run/express@npm:2.10.2"
   dependencies:
-    "@remix-run/node": "npm:2.10.0"
+    "@remix-run/node": "npm:2.10.2"
   peerDependencies:
     express: ^4.19.2
     typescript: ^5.1.0
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 10/38a88c9097df327d38bf31e5edde1600ee002dac514cd7af0dd370e5d17625ad93289c2c487a1734c39338d6330b50a1a37b6297b1df4a0030291b544ba21b59
+  checksum: 10/9ca184d9825a6b780c59ddf356a6e60e312a787098e462867cd8dee9cff6b9a86ebe74e8a29b52ecd4147439c99490a8acd16316daf268bb04c5ae3a63a90f2a
   languageName: node
   linkType: hard
 
-"@remix-run/node@npm:2.10.0":
-  version: 2.10.0
-  resolution: "@remix-run/node@npm:2.10.0"
+"@remix-run/node@npm:2.10.2":
+  version: 2.10.2
+  resolution: "@remix-run/node@npm:2.10.2"
   dependencies:
-    "@remix-run/server-runtime": "npm:2.10.0"
+    "@remix-run/server-runtime": "npm:2.10.2"
     "@remix-run/web-fetch": "npm:^4.4.2"
     "@web3-storage/multipart-parser": "npm:^1.0.0"
     cookie-signature: "npm:^1.1.0"
@@ -3971,18 +3971,18 @@ __metadata:
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 10/9fbfed7504fb94ba791dc674b21bcf05732291b3e3c4d572b1508dab5ef3116cd2057a5ede567a617196143b59d3f291c06b7706f020b3bf161bba8e9b0f0daa
+  checksum: 10/8a092a6d7e0bfd9a2245882cf916a2097fbb3fa20848b30f038d435d0334f238bf39afc0684be4b48afe4f18d11ac6601d8e7bc54afa8057122444b896473425
   languageName: node
   linkType: hard
 
-"@remix-run/react@npm:2.10.0":
-  version: 2.10.0
-  resolution: "@remix-run/react@npm:2.10.0"
+"@remix-run/react@npm:2.10.2":
+  version: 2.10.2
+  resolution: "@remix-run/react@npm:2.10.2"
   dependencies:
-    "@remix-run/router": "npm:1.17.0"
-    "@remix-run/server-runtime": "npm:2.10.0"
-    react-router: "npm:6.24.0"
-    react-router-dom: "npm:6.24.0"
+    "@remix-run/router": "npm:1.17.1"
+    "@remix-run/server-runtime": "npm:2.10.2"
+    react-router: "npm:6.24.1"
+    react-router-dom: "npm:6.24.1"
     turbo-stream: "npm:2.2.0"
   peerDependencies:
     react: ^18.0.0
@@ -3991,23 +3991,23 @@ __metadata:
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 10/859b3e00b16a2739a9a44e46c4c11ccd0dbf767cd7aecb5c7c9b63a7f24bd693c5d3f589ce348068d3584593c08c130bbeb76f0905af72316248516dbbac359b
+  checksum: 10/9d3ecf6b53ff5fc4be9a65c374893f1e9e327f2249da5fd3d93d598b39154e066c7fbb4656243f923d76f1cdc5aaaf92095e91a615f37db6c8dd124de90dfcf8
   languageName: node
   linkType: hard
 
-"@remix-run/router@npm:1.17.0":
-  version: 1.17.0
-  resolution: "@remix-run/router@npm:1.17.0"
-  checksum: 10/bffc96ebe5c5658c2ea0585f7b2b7fd4760366ad63cdc05062f84ea84ba0f88dd70e75d802ed938f08b17be5348a8add8e4eef30e1d6422ea27a0ecb02cda66e
+"@remix-run/router@npm:1.17.1":
+  version: 1.17.1
+  resolution: "@remix-run/router@npm:1.17.1"
+  checksum: 10/5efc598626cd81688ac26e0abd08204b980831ead8cd2c4b8a27e0c169ee4777fc609fa289c093b93efc3a1e335304698c6961276c2309348444ec7209836c83
   languageName: node
   linkType: hard
 
-"@remix-run/serve@npm:2.10.0":
-  version: 2.10.0
-  resolution: "@remix-run/serve@npm:2.10.0"
+"@remix-run/serve@npm:2.10.2":
+  version: 2.10.2
+  resolution: "@remix-run/serve@npm:2.10.2"
   dependencies:
-    "@remix-run/express": "npm:2.10.0"
-    "@remix-run/node": "npm:2.10.0"
+    "@remix-run/express": "npm:2.10.2"
+    "@remix-run/node": "npm:2.10.2"
     chokidar: "npm:^3.5.3"
     compression: "npm:^1.7.4"
     express: "npm:^4.19.2"
@@ -4016,15 +4016,15 @@ __metadata:
     source-map-support: "npm:^0.5.21"
   bin:
     remix-serve: dist/cli.js
-  checksum: 10/3a4e4566fc1a3c9edba79945567d1068ba62bf1f54870b26e2cf61d4d238bbb4882888486acb2fe5ac7adeb93f090f26d7d85b4566f98729d98fcbc38f749ce2
+  checksum: 10/fe099b30eb567bdbf6cf99886b8265c30d43db0bfb4862db805c565203e479b8a13ad3442eaa7bdc6ab1acafe6b0cc7765b056a9c5f204a309314286295f92a1
   languageName: node
   linkType: hard
 
-"@remix-run/server-runtime@npm:2.10.0":
-  version: 2.10.0
-  resolution: "@remix-run/server-runtime@npm:2.10.0"
+"@remix-run/server-runtime@npm:2.10.2":
+  version: 2.10.2
+  resolution: "@remix-run/server-runtime@npm:2.10.2"
   dependencies:
-    "@remix-run/router": "npm:1.17.0"
+    "@remix-run/router": "npm:1.17.1"
     "@types/cookie": "npm:^0.6.0"
     "@web3-storage/multipart-parser": "npm:^1.0.0"
     cookie: "npm:^0.6.0"
@@ -4036,7 +4036,7 @@ __metadata:
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 10/0626c75f0167385a2ac714b7f3fcfbb375816ba564005aacaa70d99d7ee8ee124aeef78a3d591588c455a8ab4c15c4bcf8bc8d3c9e004f8b0e60bdcf568ede5f
+  checksum: 10/a9906dd479f173676c7b72b7b019c8b13a67c38e80790ea4280b4748587e26d7ffaf1423b61c98888e88c087a139e8023e191ac0e21ae5e3165d990015ddc9ed
   languageName: node
   linkType: hard
 
@@ -4209,16 +4209,16 @@ __metadata:
     "@mantine/form": "npm:7.11.1"
     "@mantine/hooks": "npm:7.11.1"
     "@mantine/notifications": "npm:7.11.1"
-    "@remix-run/dev": "npm:2.10.0"
-    "@remix-run/node": "npm:2.10.0"
-    "@remix-run/react": "npm:2.10.0"
-    "@remix-run/serve": "npm:2.10.0"
+    "@remix-run/dev": "npm:2.10.2"
+    "@remix-run/node": "npm:2.10.2"
+    "@remix-run/react": "npm:2.10.2"
+    "@remix-run/serve": "npm:2.10.2"
     "@ryot/generated": "workspace:*"
     "@ryot/graphql": "workspace:*"
     "@ryot/ts-utils": "workspace:*"
-    "@tabler/icons-react": "npm:3.7.0"
-    "@tanstack/react-query": "npm:5.48.0"
-    "@tanstack/react-query-devtools": "npm:5.48.0"
+    "@tabler/icons-react": "npm:3.8.0"
+    "@tanstack/react-query": "npm:5.49.2"
+    "@tanstack/react-query-devtools": "npm:5.49.2"
     "@types/cookie": "npm:0.6.0"
     "@types/crypto-js": "npm:4.2.2"
     "@types/howler": "npm:2.2.11"
@@ -4238,10 +4238,10 @@ __metadata:
     humanize-duration-ts: "npm:2.1.1"
     immer: "npm:10.1.1"
     isbot: "npm:5.1.11"
-    jotai: "npm:2.8.3"
+    jotai: "npm:2.8.4"
     js-cookie: "npm:3.0.5"
-    mantine-datatable: "npm:7.10.4"
-    postcss: "npm:8.4.38"
+    mantine-datatable: "npm:7.11.1"
+    postcss: "npm:8.4.39"
     postcss-preset-mantine: "npm:1.15.0"
     postcss-simple-vars: "npm:7.0.1"
     react: "npm:18.3.1"
@@ -4257,7 +4257,7 @@ __metadata:
     typescript-plugin-css-modules: "npm:5.1.0"
     ufo: "npm:1.5.3"
     uuid: "npm:10.0.0"
-    vite: "npm:5.3.1"
+    vite: "npm:5.3.3"
     vite-tsconfig-paths: "npm:4.3.2"
     zod: "npm:3.23.8"
     zodix: "npm:0.4.4"
@@ -4269,7 +4269,7 @@ __metadata:
   resolution: "@ryot/generated@workspace:libs/generated"
   dependencies:
     "@graphql-codegen/cli": "npm:5.0.2"
-    "@graphql-codegen/client-preset": "npm:4.3.0"
+    "@graphql-codegen/client-preset": "npm:4.3.2"
     "@graphql-typed-document-node/core": "npm:3.2.0"
     graphql: "npm:16.9.0"
   languageName: unknown
@@ -4293,7 +4293,7 @@ __metadata:
     "@astrojs/tailwind": "npm:5.1.0"
     "@fontsource-variable/rubik": "npm:5.0.22"
     "@tailwindcss/typography": "npm:0.5.13"
-    astro: "npm:4.11.3"
+    astro: "npm:4.11.5"
     astro-robots-txt: "npm:1.0.0"
     tailwind-scrollbar: "npm:3.1.0"
     tailwindcss: "npm:3.4.4"
@@ -4304,9 +4304,9 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@ryot/root@workspace:."
   dependencies:
-    "@biomejs/biome": "npm:1.8.2"
+    "@biomejs/biome": "npm:1.8.3"
     tsconfig-moon: "npm:1.3.0"
-    typescript: "npm:5.5.2"
+    typescript: "npm:5.5.3"
   languageName: unknown
   linkType: soft
 
@@ -4315,13 +4315,20 @@ __metadata:
   resolution: "@ryot/ts-utils@workspace:libs/ts-utils"
   dependencies:
     "@ryot/generated": "workspace:*"
-    "@types/lodash": "npm:4.17.5"
+    "@types/lodash": "npm:4.17.6"
     dayjs: "npm:1.11.11"
     humanize-duration-ts: "npm:2.1.1"
     lodash: "npm:4.17.21"
   languageName: unknown
   linkType: soft
 
+"@shikijs/core@npm:1.10.1":
+  version: 1.10.1
+  resolution: "@shikijs/core@npm:1.10.1"
+  checksum: 10/59e80e4793a8fe4b990750a2d9866748402a92930928a63efbaeb0db66a09d7afe69ea0f723853e4c7685e11d8a133b4af3ebbac24d9eb6a158622276d84233d
+  languageName: node
+  linkType: hard
+
 "@shikijs/core@npm:1.9.1":
   version: 1.9.1
   resolution: "@shikijs/core@npm:1.9.1"
@@ -4329,21 +4336,21 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@tabler/icons-react@npm:3.7.0":
-  version: 3.7.0
-  resolution: "@tabler/icons-react@npm:3.7.0"
+"@tabler/icons-react@npm:3.8.0":
+  version: 3.8.0
+  resolution: "@tabler/icons-react@npm:3.8.0"
   dependencies:
-    "@tabler/icons": "npm:3.7.0"
+    "@tabler/icons": "npm:3.8.0"
   peerDependencies:
     react: ">= 16"
-  checksum: 10/d3fd07e03b08e0be2380fd8e3003d5a8a4edbedf88e4e557571926b1aa4674559c0c1e1bed68b6d8dc2ed7e2cb411c22bc02887dd6727ad4e90de758acc7300b
+  checksum: 10/c59975f311e4c6fef6d4f9df6e711f4d7679fc71dd7a99cdffeecff06f2878fb8964c9ce84d05de958b359f7d4eb22ae44ef21c33df32534da84ce6cfbdc0b49
   languageName: node
   linkType: hard
 
-"@tabler/icons@npm:3.7.0":
-  version: 3.7.0
-  resolution: "@tabler/icons@npm:3.7.0"
-  checksum: 10/2abf197bf2dffddbb8c2cc80c4485333b49bd377290a723e22f9e2f0858d0f6908265016f6acccd3f53d527a5594ec3f44f42b13a87837ab21c6947588f80357
+"@tabler/icons@npm:3.8.0":
+  version: 3.8.0
+  resolution: "@tabler/icons@npm:3.8.0"
+  checksum: 10/a9a816790e13ec3f07ad9c1f1b87bb90ac1c570228c2a2168dbf489113bfd9d3020db26db43fcfd66fe2f0762c36a32f616dabe3f4515bb8043f39a8f87c60ef
   languageName: node
   linkType: hard
 
@@ -4361,40 +4368,40 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@tanstack/query-core@npm:5.48.0":
-  version: 5.48.0
-  resolution: "@tanstack/query-core@npm:5.48.0"
-  checksum: 10/b07b31044370c2493df89d0cbb9a32827ecbd5d93af5790fa32c5b8ff7cd09a49d1154fd1d4e085515e226e45cd1d4c5282d707c443eeda3f077a0a3b5ee0f27
+"@tanstack/query-core@npm:5.49.1":
+  version: 5.49.1
+  resolution: "@tanstack/query-core@npm:5.49.1"
+  checksum: 10/38c4a6dbf61ec9d8490717702a18254b26672b7601f793c7d26f35bd4d9f5a5ec739c3d2eed09a3d2b3e8d3c36c9c21a51b6808214d01a29818e2803d21e1e5c
   languageName: node
   linkType: hard
 
-"@tanstack/query-devtools@npm:5.47.0":
-  version: 5.47.0
-  resolution: "@tanstack/query-devtools@npm:5.47.0"
-  checksum: 10/9bb44dc40463b76b792b864ca94aed0267037a31414f93b73e2b535c8e4962e47e1c692eb8b4d367adf2580d396f0cdf29ac0af9adc4ad69ec9416527b6d4cfa
+"@tanstack/query-devtools@npm:5.49.1":
+  version: 5.49.1
+  resolution: "@tanstack/query-devtools@npm:5.49.1"
+  checksum: 10/66c4131ab5f7e9bb1b3ed8d774f0dfc2c569e0e9513bdda30549865da9ebbfdb7b23bfc5e24b2ccdaf17178f4a59aadfb6f632a05792b0b32bfc1f1c33f979e0
   languageName: node
   linkType: hard
 
-"@tanstack/react-query-devtools@npm:5.48.0":
-  version: 5.48.0
-  resolution: "@tanstack/react-query-devtools@npm:5.48.0"
+"@tanstack/react-query-devtools@npm:5.49.2":
+  version: 5.49.2
+  resolution: "@tanstack/react-query-devtools@npm:5.49.2"
   dependencies:
-    "@tanstack/query-devtools": "npm:5.47.0"
+    "@tanstack/query-devtools": "npm:5.49.1"
   peerDependencies:
-    "@tanstack/react-query": ^5.48.0
+    "@tanstack/react-query": ^5.49.2
     react: ^18 || ^19
-  checksum: 10/47cb24bf5709eceba81178795323c1bc3ee11f4d9e29f6a3ae0732e08ac9f9fc51b5f29d2b28d8459080227ca8d84bbd1d0f94ae01ae25b8f20fc58e1280b6ef
+  checksum: 10/7b43989128af6332827119008322bfd9822162e65631e13e684ba005b1dd289ca6db2836de3000ee5d89748ad5f9e5bbf809693f61ffddfc8529fa2b63b4dc8f
   languageName: node
   linkType: hard
 
-"@tanstack/react-query@npm:5.48.0":
-  version: 5.48.0
-  resolution: "@tanstack/react-query@npm:5.48.0"
+"@tanstack/react-query@npm:5.49.2":
+  version: 5.49.2
+  resolution: "@tanstack/react-query@npm:5.49.2"
   dependencies:
-    "@tanstack/query-core": "npm:5.48.0"
+    "@tanstack/query-core": "npm:5.49.1"
   peerDependencies:
     react: ^18.0.0
-  checksum: 10/7e7b64a7aec837e2e3af7dca414eb749350cbf3c00ad8890613fec9b6916502d61725b507082371a80facd3489116d6bc5cc9e9d4d0d6ab0e45b0f89be79118b
+  checksum: 10/16232d373ac805cc79c23bdce5a355e6ab2aaf29ac3ef1436388f068eac799bcdfbc375d1569f549405a3ace1f5b7ba0d68b18175ea6abf0396d53923c4b4de2
   languageName: node
   linkType: hard
 
@@ -4626,10 +4633,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/lodash@npm:4.17.5":
-  version: 4.17.5
-  resolution: "@types/lodash@npm:4.17.5"
-  checksum: 10/10e2e9cbeb16998026f4071f9f5f2a38b651eba15302f512e0b8ab904c07c197ca0282d2821f64e53c2b692d7046af0a1ce3ead190fb077cbe4036948fce1924
+"@types/lodash@npm:4.17.6":
+  version: 4.17.6
+  resolution: "@types/lodash@npm:4.17.6"
+  checksum: 10/6d3a68b3e795381f4aaf946855134d24eeb348ad5d66e9a44461d30026da82b215d55b92b70486d811ca45d54d4ab956aa2dced37fd04e19d49afe160ae3da2e
   languageName: node
   linkType: hard
 
@@ -5376,9 +5383,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"astro@npm:4.11.3":
-  version: 4.11.3
-  resolution: "astro@npm:4.11.3"
+"astro@npm:4.11.5":
+  version: 4.11.5
+  resolution: "astro@npm:4.11.5"
   dependencies:
     "@astrojs/compiler": "npm:^2.8.1"
     "@astrojs/internal-helpers": "npm:0.4.1"
@@ -5431,13 +5438,13 @@ __metadata:
     rehype: "npm:^13.0.1"
     semver: "npm:^7.6.2"
     sharp: "npm:^0.33.3"
-    shiki: "npm:^1.9.0"
-    string-width: "npm:^7.1.0"
+    shiki: "npm:^1.10.0"
+    string-width: "npm:^7.2.0"
     strip-ansi: "npm:^7.1.0"
-    tsconfck: "npm:^3.1.0"
+    tsconfck: "npm:^3.1.1"
     unist-util-visit: "npm:^5.0.0"
     vfile: "npm:^6.0.1"
-    vite: "npm:^5.3.1"
+    vite: "npm:^5.3.2"
     vitefu: "npm:^0.2.5"
     which-pm: "npm:^2.2.0"
     yargs-parser: "npm:^21.1.1"
@@ -5448,7 +5455,7 @@ __metadata:
       optional: true
   bin:
     astro: astro.js
-  checksum: 10/dac84657581011c349a00903128f14d5977b3338039e06e408b952ac221430f0c7c88f7ffcd301dc5c50b8b41d53db3ad20bb552e9a43ed99a99af0a18b8df2f
+  checksum: 10/95c478176497ee5710d4853dbfd448a1e71c655a2f37a451493c805a4a02daf7cb38789aa09313693863c3d85bf8fbec7ba1045fbc4fdb5fd9d6bbd0ebdfbc01
   languageName: node
   linkType: hard
 
@@ -9361,9 +9368,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jotai@npm:2.8.3":
-  version: 2.8.3
-  resolution: "jotai@npm:2.8.3"
+"jotai@npm:2.8.4":
+  version: 2.8.4
+  resolution: "jotai@npm:2.8.4"
   peerDependencies:
     "@types/react": ">=17.0.0"
     react: ">=17.0.0"
@@ -9372,7 +9379,7 @@ __metadata:
       optional: true
     react:
       optional: true
-  checksum: 10/9dd603d854849e9ef3dc4fb8ed6deae9034310f55cd8de9c54df53f3ac2a62b8f90c773f4bbc28da40ced08d8195805210e658c670f0c8973d78e7c898fb4179
+  checksum: 10/b0931bdb6b38c1f5285b6ffd6b9ec84286fc3233b15d20cc7723b6b1e1e0d2c153ceb8aa5615c34d4d7fd16cbb060fedea5b97de5b33369b45b9268eacaf2748
   languageName: node
   linkType: hard
 
@@ -9869,15 +9876,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"mantine-datatable@npm:7.10.4":
-  version: 7.10.4
-  resolution: "mantine-datatable@npm:7.10.4"
+"mantine-datatable@npm:7.11.1":
+  version: 7.11.1
+  resolution: "mantine-datatable@npm:7.11.1"
   peerDependencies:
     "@mantine/core": ">=7.8"
     "@mantine/hooks": ">=7.8"
     clsx: ">=2"
     react: ">=18.2"
-  checksum: 10/c94bc20cfd8e21e06bd1a68473dc130e35925dc441c6dc64a80fc1c9fbab57630c74b9bc5a489eff77cf9011314967bc9cb0ae359085cf8f1935545a71efd8b6
+  checksum: 10/d9308f5f894da079e233c1317702e7ab4d57eb0be9b2c7de7ba808e33f737495136a902e083ac1272dd50a752b71aed9d1fd7329aba13de58630d0fe21639b43
   languageName: node
   linkType: hard
 
@@ -12053,6 +12060,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"picocolors@npm:^1.0.1":
+  version: 1.0.1
+  resolution: "picocolors@npm:1.0.1"
+  checksum: 10/fa68166d1f56009fc02a34cdfd112b0dd3cf1ef57667ac57281f714065558c01828cdf4f18600ad6851cbe0093952ed0660b1e0156bddf2184b6aaf5817553a5
+  languageName: node
+  linkType: hard
+
 "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1":
   version: 2.3.1
   resolution: "picomatch@npm:2.3.1"
@@ -12356,14 +12370,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"postcss@npm:8.4.38, postcss@npm:^8.4.23, postcss@npm:^8.4.28, postcss@npm:^8.4.38":
-  version: 8.4.38
-  resolution: "postcss@npm:8.4.38"
+"postcss@npm:8.4.39, postcss@npm:^8.4.39":
+  version: 8.4.39
+  resolution: "postcss@npm:8.4.39"
   dependencies:
     nanoid: "npm:^3.3.7"
-    picocolors: "npm:^1.0.0"
+    picocolors: "npm:^1.0.1"
     source-map-js: "npm:^1.2.0"
-  checksum: 10/6e44a7ed835ffa9a2b096e8d3e5dfc6bcf331a25c48aeb862dd54e3aaecadf814fa22be224fd308f87d08adf2299164f88c5fd5ab1c4ef6cbd693ceb295377f4
+  checksum: 10/ad9c1add892c96433b9a5502878201ede4a20c4ce02d056251f61f8d9a3e5426dab3683fe5a086edfa78a1a19f2b4988c8cea02c5122136d29758cb5a17e2621
   languageName: node
   linkType: hard
 
@@ -12378,6 +12392,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"postcss@npm:^8.4.23, postcss@npm:^8.4.28":
+  version: 8.4.38
+  resolution: "postcss@npm:8.4.38"
+  dependencies:
+    nanoid: "npm:^3.3.7"
+    picocolors: "npm:^1.0.0"
+    source-map-js: "npm:^1.2.0"
+  checksum: 10/6e44a7ed835ffa9a2b096e8d3e5dfc6bcf331a25c48aeb862dd54e3aaecadf814fa22be224fd308f87d08adf2299164f88c5fd5ab1c4ef6cbd693ceb295377f4
+  languageName: node
+  linkType: hard
+
 "postcss@npm:^8.4.35":
   version: 8.4.35
   resolution: "postcss@npm:8.4.35"
@@ -12743,27 +12768,27 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-router-dom@npm:6.24.0":
-  version: 6.24.0
-  resolution: "react-router-dom@npm:6.24.0"
+"react-router-dom@npm:6.24.1":
+  version: 6.24.1
+  resolution: "react-router-dom@npm:6.24.1"
   dependencies:
-    "@remix-run/router": "npm:1.17.0"
-    react-router: "npm:6.24.0"
+    "@remix-run/router": "npm:1.17.1"
+    react-router: "npm:6.24.1"
   peerDependencies:
     react: ">=16.8"
     react-dom: ">=16.8"
-  checksum: 10/a6622bc53dd7652bbfb9f5c6f2c1bff4aa93a24cc91e048aa2908d096f7106de3707b5d4cf4bd9cf0b67d4475c7718add7fd96045430f7435c7d78da04708a30
+  checksum: 10/98eeeec3d36695b3d6d8000d8373ba3cefa9afc49ee44f646f3b721e8b47a80f5ce3737ad1f752cccf19caf01e370918750a4bc86a06a99e491731b454d5ec55
   languageName: node
   linkType: hard
 
-"react-router@npm:6.24.0":
-  version: 6.24.0
-  resolution: "react-router@npm:6.24.0"
+"react-router@npm:6.24.1":
+  version: 6.24.1
+  resolution: "react-router@npm:6.24.1"
   dependencies:
-    "@remix-run/router": "npm:1.17.0"
+    "@remix-run/router": "npm:1.17.1"
   peerDependencies:
     react: ">=16.8"
-  checksum: 10/71d750e4422d74e1981b38f54c0dd02a7af7b1059cab471d96e4dc3374824557f6eec8449fe557c0ed8af18569554de8d565bbfd708c1fc90d3421b3d6c6ac82
+  checksum: 10/18ac968171dee286a2f067dc8568faf73c759f833e88e09f1b34ff6e9376d1fd5eade8697a86be83093225956b256b398d935ce2f681c1bf711fb3c058c19839
   languageName: node
   linkType: hard
 
@@ -13834,6 +13859,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"shiki@npm:^1.10.0":
+  version: 1.10.1
+  resolution: "shiki@npm:1.10.1"
+  dependencies:
+    "@shikijs/core": "npm:1.10.1"
+  checksum: 10/eb1d69b4964bed256e54240a9459c521be6b62718dd6b62cbcf4bf0cae2075d9fc17c5ea9da05c5ab581d088ba5e21e4e1fc85fa0a10ed88553b7fa0861dfed3
+  languageName: node
+  linkType: hard
+
 "shiki@npm:^1.9.0":
   version: 1.9.1
   resolution: "shiki@npm:1.9.1"
@@ -14172,7 +14206,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"string-width@npm:^7.0.0, string-width@npm:^7.1.0":
+"string-width@npm:^7.0.0":
   version: 7.1.0
   resolution: "string-width@npm:7.1.0"
   dependencies:
@@ -14183,6 +14217,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"string-width@npm:^7.2.0":
+  version: 7.2.0
+  resolution: "string-width@npm:7.2.0"
+  dependencies:
+    emoji-regex: "npm:^10.3.0"
+    get-east-asian-width: "npm:^1.0.0"
+    strip-ansi: "npm:^7.1.0"
+  checksum: 10/42f9e82f61314904a81393f6ef75b832c39f39761797250de68c041d8ba4df2ef80db49ab6cd3a292923a6f0f409b8c9980d120f7d32c820b4a8a84a2598a295
+  languageName: node
+  linkType: hard
+
 "string_decoder@npm:^1.1.1":
   version: 1.3.0
   resolution: "string_decoder@npm:1.3.0"
@@ -14618,9 +14663,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"tsconfck@npm:^3.1.0":
-  version: 3.1.0
-  resolution: "tsconfck@npm:3.1.0"
+"tsconfck@npm:^3.1.1":
+  version: 3.1.1
+  resolution: "tsconfck@npm:3.1.1"
   peerDependencies:
     typescript: ^5.0.0
   peerDependenciesMeta:
@@ -14628,7 +14673,7 @@ __metadata:
       optional: true
   bin:
     tsconfck: bin/tsconfck.js
-  checksum: 10/24fdd664f521c26dfaebba1e4845f2de9d0743285011a6da527dff6b588357a32a2a0b694c1b76d670b4177d7ef576dcf39e9bb54906c03da67ce8dcca6202d3
+  checksum: 10/a4456577f540212516d7eb530005893739aadd6da00787914a8ed9aa19c3f2f306b8912920aa440b9b8978f10c9dadbd062b8c2a2f0ff1f6c2d4272b5be2ef34
   languageName: node
   linkType: hard
 
@@ -14779,23 +14824,23 @@ __metadata:
   languageName: node
   linkType: hard
 
-"typescript@npm:5.5.2":
-  version: 5.5.2
-  resolution: "typescript@npm:5.5.2"
+"typescript@npm:5.5.3":
+  version: 5.5.3
+  resolution: "typescript@npm:5.5.3"
   bin:
     tsc: bin/tsc
     tsserver: bin/tsserver
-  checksum: 10/9118b20f248e76b0dbff8737fef65dfa89d02668d4e633d2c5ceac99033a0ca5e8a1c1a53bc94da68e8f67677a88f318663dde859c9e9a09c1e116415daec2ba
+  checksum: 10/11a867312419ed497929aafd2f1d28b2cd41810a5eb6c6e9e169559112e9ea073d681c121a29102e67cd4478d0a4ae37a306a5800f3717f59c4337e6a9bd5e8d
   languageName: node
   linkType: hard
 
-"typescript@patch:typescript@npm%3A5.5.2#optional!builtin<compat/typescript>":
-  version: 5.5.2
-  resolution: "typescript@patch:typescript@npm%3A5.5.2#optional!builtin<compat/typescript>::version=5.5.2&hash=5adc0c"
+"typescript@patch:typescript@npm%3A5.5.3#optional!builtin<compat/typescript>":
+  version: 5.5.3
+  resolution: "typescript@patch:typescript@npm%3A5.5.3#optional!builtin<compat/typescript>::version=5.5.3&hash=5adc0c"
   bin:
     tsc: bin/tsc
     tsserver: bin/tsserver
-  checksum: 10/28b3de2ddaf63a7620e7ddbe5d377af71ce93ecc558c41bf0e3d88661d8e6e7aa6c7739164fef98055f69819e41faca49252938ef3633a3dff2734cca6a9042e
+  checksum: 10/b61b8bb4b4d6a8a00f9d5f931f8c67070eed6ad11feabf4c41744a326987080bfc806a621596c70fbf2e5974eca3ed65bafeeeb22a078071bdfb51d8abd7c013
   languageName: node
   linkType: hard
 
@@ -15460,13 +15505,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"vite@npm:5.3.1, vite@npm:^5.3.1":
-  version: 5.3.1
-  resolution: "vite@npm:5.3.1"
+"vite@npm:5.3.3, vite@npm:^5.3.2":
+  version: 5.3.3
+  resolution: "vite@npm:5.3.3"
   dependencies:
     esbuild: "npm:^0.21.3"
     fsevents: "npm:~2.3.3"
-    postcss: "npm:^8.4.38"
+    postcss: "npm:^8.4.39"
     rollup: "npm:^4.13.0"
   peerDependencies:
     "@types/node": ^18.0.0 || >=20.0.0
@@ -15496,7 +15541,7 @@ __metadata:
       optional: true
   bin:
     vite: bin/vite.js
-  checksum: 10/180ca1795389f1ebc0b09f2ce61846943d34df597c4719e68d1d5ecba3e6cbd5b3313a4a321119b18290de3ef543df433659ba8b678de84df152e0386342697f
+  checksum: 10/e7a094cefedad9e204b715588502118e07d1b9c00c617f55b810169181907f55144f0a82f650995d6a74f12e3695fca65afc348b475b91a81dcbd0274d30a088
   languageName: node
   linkType: hard
 


Test patch: